mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +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]; | ||||
| #ifdef USE_ARDUINO | ||||
|   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_) { | ||||
| #endif | ||||
|     this->printf_to_buffer_("%s[%s][%s:%03u]: ", color, letter, tag, line); | ||||
| #ifdef USE_ARDUINO | ||||
|   } else { | ||||
|     this->printf_to_buffer_("%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line, | ||||
|                             ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), pcTaskGetName(current_task), color); | ||||
|   } | ||||
| #ifdef USE_ARDUINO | ||||
|     const char *thread_name = pcTaskGetName(current_task); | ||||
| #elif defined(USE_ZEPHYR) | ||||
|     const char *thread_name = k_thread_name_get(current_task); | ||||
| #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 | ||||
| @@ -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 | ||||
| #ifdef USE_ARDUINO | ||||
|   this->main_task_ = xTaskGetCurrentTaskHandle(); | ||||
| #elif defined(USE_ZEPHYR) | ||||
|   this->main_task_ = k_current_get(); | ||||
| #endif | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -77,6 +77,9 @@ void Logger::pre_setup() { | ||||
|  | ||||
| #ifdef USE_ZEPHYR | ||||
| void HOT Logger::write_msg_(const char *msg) { | ||||
| #ifdef CONFIG_PRINTK | ||||
|   printk("%s\n", msg); | ||||
| #endif | ||||
|   if (nullptr == uart_dev_) { | ||||
|     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 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[] = { | ||||
| 	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, | ||||
|                   0xdc, 0x53, 0x8d), | ||||
| #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) { | ||||
|   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) { | ||||
|     ESP_LOGE(TAG, "Advertising failed to start (rc %d)", rc); | ||||
|     return; | ||||
|   | ||||
| @@ -30,11 +30,6 @@ interval: | ||||
|     then: | ||||
|       - switch.toggle: gpio_15 | ||||
|  | ||||
| # sensor: | ||||
| #   - platform: uptime | ||||
| #     name: Uptime Sensor | ||||
| #     update_interval: 5s | ||||
|  | ||||
| output: | ||||
|   - platform: gpio | ||||
|     pin: | ||||
| @@ -71,3 +66,6 @@ ota: | ||||
|             - logger.log: "OTA start" | ||||
|  | ||||
| zephyr_ble_server: | ||||
|  | ||||
| zephyr_ble_nus: | ||||
|   log: true | ||||
|   | ||||
		Reference in New Issue
	
	Block a user