[rp2040_ble] Add BLE component for RP2040/RP2350 (#14603)

This commit is contained in:
J. Nick Koston
2026-03-10 09:12:28 -10:00
committed by GitHub
parent 4d2ef09a29
commit 9404eadaf8
8 changed files with 212 additions and 0 deletions

View File

@@ -410,6 +410,7 @@ esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz
esphome/components/rgbct/* @jesserockz
esphome/components/rp2040/* @jesserockz
esphome/components/rp2040_ble/* @bdraco
esphome/components/rp2040_pio_led_strip/* @Papa-DMan
esphome/components/rp2040_pwm/* @jesserockz
esphome/components/rpi_dpi_rgb/* @clydebarrow

View File

@@ -0,0 +1,31 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ID
from esphome.types import ConfigType
DEPENDENCIES = ["rp2040"]
CODEOWNERS = ["@bdraco"]
rp2040_ble_ns = cg.esphome_ns.namespace("rp2040_ble")
RP2040BLE = rp2040_ble_ns.class_("RP2040BLE", cg.Component)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(RP2040BLE),
cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean,
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT]))
# Enable Bluetooth in the arduino-pico build
# This switches linking from liblwip.a to liblwip-bt.a and defines
# ENABLE_CLASSIC, ENABLE_BLE, CYW43_ENABLE_BLUETOOTH
cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH")
cg.add_define("USE_RP2040_BLE")

View File

@@ -0,0 +1,124 @@
#include "rp2040_ble.h"
#ifdef USE_RP2040_BLE
#include "esphome/core/log.h"
namespace esphome::rp2040_ble {
static const char *const TAG = "rp2040_ble";
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
RP2040BLE *global_ble = nullptr;
void RP2040BLE::setup() {
global_ble = this;
if (this->enable_on_boot_) {
this->enable();
} else {
this->state_ = BLEComponentState::DISABLED;
}
}
void RP2040BLE::enable() {
if (this->state_ == BLEComponentState::ACTIVE || this->state_ == BLEComponentState::ENABLING) {
return;
}
ESP_LOGD(TAG, "Enabling BLE...");
this->state_ = BLEComponentState::ENABLING;
this->active_logged_ = false;
if (!this->btstack_initialized_) {
// BTstack init functions are not idempotent — only call once
l2cap_init();
sm_init();
this->hci_event_callback_registration_.callback = &RP2040BLE::packet_handler_;
hci_add_event_handler(&this->hci_event_callback_registration_);
this->sm_event_callback_registration_.callback = &RP2040BLE::packet_handler_;
sm_add_event_handler(&this->sm_event_callback_registration_);
this->btstack_initialized_ = true;
}
hci_power_control(HCI_POWER_ON);
}
void RP2040BLE::disable() {
if (this->state_ == BLEComponentState::DISABLED || this->state_ == BLEComponentState::OFF) {
return;
}
ESP_LOGD(TAG, "Disabling BLE...");
this->state_ = BLEComponentState::DISABLING;
hci_power_control(HCI_POWER_OFF);
this->state_ = BLEComponentState::DISABLED;
ESP_LOGD(TAG, "BLE disabled");
}
void RP2040BLE::loop() {
if (this->state_ == BLEComponentState::ACTIVE && !this->active_logged_) {
this->active_logged_ = true;
ESP_LOGI(TAG, "BLE active");
}
}
static const char *state_to_str(BLEComponentState state) {
switch (state) {
case BLEComponentState::OFF:
return "OFF";
case BLEComponentState::ENABLING:
return "ENABLING";
case BLEComponentState::ACTIVE:
return "ACTIVE";
case BLEComponentState::DISABLING:
return "DISABLING";
case BLEComponentState::DISABLED:
return "DISABLED";
default:
return "UNKNOWN";
}
}
void RP2040BLE::dump_config() {
ESP_LOGCONFIG(TAG,
"RP2040 BLE:\n"
" Enable on boot: %s\n"
" State: %s",
YESNO(this->enable_on_boot_), state_to_str(this->state_));
}
float RP2040BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; }
void RP2040BLE::packet_handler_(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size) {
if (global_ble == nullptr) {
return;
}
if (type != HCI_EVENT_PACKET) {
return;
}
uint8_t event_type = hci_event_packet_get_type(packet);
switch (event_type) {
case BTSTACK_EVENT_STATE: {
uint8_t state = btstack_event_state_get_state(packet);
if (state == HCI_STATE_WORKING && global_ble->state_ == BLEComponentState::ENABLING) {
global_ble->state_ = BLEComponentState::ACTIVE;
}
break;
}
default:
break;
}
}
} // namespace esphome::rp2040_ble
#endif // USE_RP2040_BLE

View File

@@ -0,0 +1,51 @@
#pragma once
#include "esphome/core/defines.h" // Must be included before conditional includes
#ifdef USE_RP2040_BLE
#include "esphome/core/component.h"
#include <btstack.h>
namespace esphome::rp2040_ble {
enum class BLEComponentState : uint8_t {
OFF = 0,
ENABLING,
ACTIVE,
DISABLING,
DISABLED,
};
class RP2040BLE : public Component {
public:
void setup() override;
void loop() override;
void dump_config() override;
float get_setup_priority() const override;
void enable();
void disable();
bool is_active() const { return this->state_ == BLEComponentState::ACTIVE; }
void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; }
protected:
static void packet_handler_(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size);
btstack_packet_callback_registration_t hci_event_callback_registration_{};
btstack_packet_callback_registration_t sm_event_callback_registration_{};
BLEComponentState state_{BLEComponentState::OFF};
bool enable_on_boot_{true};
bool btstack_initialized_{false};
bool active_logged_{false};
};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
extern RP2040BLE *global_ble;
} // namespace esphome::rp2040_ble
#endif // USE_RP2040_BLE

View File

@@ -339,6 +339,7 @@
#define USE_I2C
#define USE_LOGGER_USB_CDC
#define USE_SOCKET_IMPL_LWIP_TCP
#define USE_RP2040_BLE
#define USE_SPI
#endif

View File

@@ -0,0 +1 @@
rp2040_ble:

View File

@@ -0,0 +1,2 @@
rp2040_ble:
enable_on_boot: false

View File

@@ -0,0 +1 @@
<<: !include common.yaml