mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	add ble nus
This commit is contained in:
		| @@ -41,15 +41,22 @@ void Logger::write_header_(int level, const char *tag, int line) { | |||||||
|   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; | ||||||
|  |  | ||||||
|  | #define DEVICE_NAME CONFIG_BT_DEVICE_NAME | ||||||
|  | #define DEVICE_NAME_LEN	(sizeof(DEVICE_NAME) - 1) | ||||||
|  |  | ||||||
| static const struct bt_data AD[] = { | static const struct bt_data AD[] = { | ||||||
| 	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), | 	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), | ||||||
| #ifdef USE_OTA | 	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