Files
esp32-racer/software/zig_main/imports/bluetooth.zig

521 lines
18 KiB
Zig

const sys = @import("sys");
const errors = @import("error");
// ---------------------------------------------------------------------------
// Shim — BT_CONTROLLER_INIT_CONFIG_DEFAULT() macro cannot be translated by
// zig translate-c; placeholder.c exposes it as a regular C function.
// ---------------------------------------------------------------------------
extern fn zig_bt_controller_default_cfg() sys.esp_bt_controller_config_t;
// ---------------------------------------------------------------------------
// BT mode
// ---------------------------------------------------------------------------
pub const Mode = enum(sys.esp_bt_mode_t) {
idle = sys.ESP_BT_MODE_IDLE,
ble = sys.ESP_BT_MODE_BLE,
classic = sys.ESP_BT_MODE_CLASSIC_BT,
dual = sys.ESP_BT_MODE_BTDM,
};
// ---------------------------------------------------------------------------
// BLE power
// ---------------------------------------------------------------------------
pub const PowerType = sys.esp_ble_power_type_t;
pub const PowerLevel = sys.esp_power_level_t;
pub fn txPowerSet(power_type: PowerType, level: PowerLevel) !void {
try errors.espCheckError(sys.esp_ble_tx_power_set(power_type, level));
}
pub fn txPowerGet(power_type: PowerType) PowerLevel {
return sys.esp_ble_tx_power_get(power_type);
}
// ---------------------------------------------------------------------------
// Controller lifecycle
// ---------------------------------------------------------------------------
pub const Controller = struct {
/// Return the default controller config.
/// Wraps the BT_CONTROLLER_INIT_CONFIG_DEFAULT() macro via a static
/// inline C function in include/bt_stubs.h (translated by translate-c).
pub fn defaultConfig() sys.esp_bt_controller_config_t {
return sys.zig_bt_controller_default_cfg();
}
pub fn init(cfg: *sys.esp_bt_controller_config_t) !void {
try errors.espCheckError(sys.esp_bt_controller_init(cfg));
}
pub fn deinit() !void {
try errors.espCheckError(sys.esp_bt_controller_deinit());
}
pub fn enable(mode: Mode) !void {
try errors.espCheckError(sys.esp_bt_controller_enable(@intFromEnum(mode)));
}
pub fn disable() !void {
try errors.espCheckError(sys.esp_bt_controller_disable());
}
pub fn getStatus() sys.esp_bt_controller_status_t {
return sys.esp_bt_controller_get_status();
}
/// Release memory for a BT mode that won't be used (saves heap).
/// Call before `init` when only BLE or only Classic BT is needed.
pub fn memRelease(mode: Mode) !void {
try errors.espCheckError(sys.esp_bt_controller_mem_release(@intFromEnum(mode)));
}
/// Release both controller and host memory for unused BT mode.
pub fn memReleaseAll(mode: Mode) !void {
try errors.espCheckError(sys.esp_bt_mem_release(@intFromEnum(mode)));
}
pub fn sleepEnable() !void {
try errors.espCheckError(sys.esp_bt_sleep_enable());
}
pub fn sleepDisable() !void {
try errors.espCheckError(sys.esp_bt_sleep_disable());
}
};
// ---------------------------------------------------------------------------
// Bluedroid host stack lifecycle
// ---------------------------------------------------------------------------
pub const Bluedroid = struct {
pub fn init() !void {
try errors.espCheckError(sys.esp_bluedroid_init());
}
pub fn initWithCfg(cfg: *sys.esp_bluedroid_config_t) !void {
try errors.espCheckError(sys.esp_bluedroid_init_with_cfg(cfg));
}
pub fn deinit() !void {
try errors.espCheckError(sys.esp_bluedroid_deinit());
}
pub fn enable() !void {
try errors.espCheckError(sys.esp_bluedroid_enable());
}
pub fn disable() !void {
try errors.espCheckError(sys.esp_bluedroid_disable());
}
pub fn getStatus() sys.esp_bluedroid_status_t {
return sys.esp_bluedroid_get_status();
}
};
// ---------------------------------------------------------------------------
// Convenience: full BLE init / deinit sequence
// ---------------------------------------------------------------------------
/// Initialise BT controller (BLE-only mode) + Bluedroid stack.
/// Typical call sequence for BLE applications:
/// 1. `idf.nvs.flashInitOrErase()`
/// 2. `bluetooth.bleInit()`
/// 3. Register GAP + GATT callbacks
/// 4. `bluetooth.Gap.startAdvertising(&params)`
pub fn bleInit() !void {
var cfg = Controller.defaultConfig();
// Release Classic BT memory — we only need BLE.
try Controller.memRelease(.classic);
try Controller.init(&cfg);
try Controller.enable(.ble);
try Bluedroid.init();
try Bluedroid.enable();
}
/// Graceful BLE teardown (reverse of `bleInit`).
pub fn bleDeinit() !void {
try Bluedroid.disable();
try Bluedroid.deinit();
try Controller.disable();
try Controller.deinit();
}
// ---------------------------------------------------------------------------
// Device
// ---------------------------------------------------------------------------
pub const Device = struct {
/// Register a callback for generic BT device events.
pub fn registerCallback(cb: sys.esp_bt_dev_cb_t) !void {
try errors.espCheckError(sys.esp_bt_dev_register_callback(cb));
}
/// Get the local BT/BLE MAC address (6 bytes, big-endian).
pub fn getAddress() [*]const u8 {
return sys.esp_bt_dev_get_address();
}
};
// ---------------------------------------------------------------------------
// GAP BLE
// ---------------------------------------------------------------------------
pub const Gap = struct {
pub const CbT = sys.esp_gap_ble_cb_t;
pub const AdvData = sys.esp_ble_adv_data_t;
pub const AdvParams = sys.esp_ble_adv_params_t;
pub const ScanParams = sys.esp_ble_scan_params_t;
pub const ConnUpdateParams = sys.esp_ble_conn_update_params_t;
pub const AddrType = sys.esp_ble_addr_type_t;
pub const SmParam = sys.esp_ble_sm_param_t;
pub const BondDev = sys.esp_ble_bond_dev_t;
pub fn registerCallback(cb: CbT) !void {
try errors.espCheckError(sys.esp_ble_gap_register_callback(cb));
}
// -- Advertising ---------------------------------------------------------
pub fn configAdvData(adv_data: *AdvData) !void {
try errors.espCheckError(sys.esp_ble_gap_config_adv_data(adv_data));
}
pub fn configAdvDataRaw(raw_data: []u8) !void {
try errors.espCheckError(sys.esp_ble_gap_config_adv_data_raw(raw_data.ptr, @intCast(raw_data.len)));
}
pub fn configScanRspDataRaw(raw_data: []u8) !void {
try errors.espCheckError(sys.esp_ble_gap_config_scan_rsp_data_raw(raw_data.ptr, @intCast(raw_data.len)));
}
pub fn startAdvertising(params: *AdvParams) !void {
try errors.espCheckError(sys.esp_ble_gap_start_advertising(params));
}
pub fn stopAdvertising() !void {
try errors.espCheckError(sys.esp_ble_gap_stop_advertising());
}
// -- Scanning ------------------------------------------------------------
pub fn setScanParams(scan_params: *ScanParams) !void {
try errors.espCheckError(sys.esp_ble_gap_set_scan_params(scan_params));
}
/// Start scanning for `duration` seconds (0 = indefinite).
pub fn startScanning(duration: u32) !void {
try errors.espCheckError(sys.esp_ble_gap_start_scanning(duration));
}
pub fn stopScanning() !void {
try errors.espCheckError(sys.esp_ble_gap_stop_scanning());
}
// -- Connection ----------------------------------------------------------
pub fn updateConnParams(params: *ConnUpdateParams) !void {
try errors.espCheckError(sys.esp_ble_gap_update_conn_params(params));
}
pub fn disconnect(remote_bda: []u8) !void {
try errors.espCheckError(sys.esp_ble_gap_disconnect(remote_bda.ptr));
}
pub fn readRssi(remote_addr: []u8) !void {
try errors.espCheckError(sys.esp_ble_gap_read_rssi(remote_addr.ptr));
}
// -- Local identity ------------------------------------------------------
pub fn setDeviceName(name: [*:0]const u8) !void {
try errors.espCheckError(sys.esp_ble_gap_set_device_name(name));
}
pub fn configLocalPrivacy(enable: bool) !void {
try errors.espCheckError(sys.esp_ble_gap_config_local_privacy(enable));
}
// -- Security ------------------------------------------------------------
pub fn setSecurityParam(param: SmParam, value: *anyopaque, len: u8) !void {
try errors.espCheckError(sys.esp_ble_gap_set_security_param(param, value, len));
}
pub fn securityReply(bd_addr: []u8, accept: bool) !void {
try errors.espCheckError(sys.esp_ble_gap_security_rsp(bd_addr.ptr, accept));
}
pub fn passkeyReply(bd_addr: []u8, accept: bool, passkey: u32) !void {
try errors.espCheckError(sys.esp_ble_passkey_reply(bd_addr.ptr, accept, passkey));
}
pub fn confirmReply(bd_addr: []u8, accept: bool) !void {
try errors.espCheckError(sys.esp_ble_confirm_reply(bd_addr.ptr, accept));
}
// -- Bond management -----------------------------------------------------
pub fn removeBondDevice(bd_addr: []u8) !void {
try errors.espCheckError(sys.esp_ble_remove_bond_device(bd_addr.ptr));
}
pub fn getBondDeviceNum() c_int {
return sys.esp_ble_get_bond_device_num();
}
pub fn getBondDeviceList(dev_num: *c_int, dev_list: [*]BondDev) !void {
try errors.espCheckError(sys.esp_ble_get_bond_device_list(dev_num, dev_list));
}
// -- Whitelist -----------------------------------------------------------
pub fn updateWhitelist(add: bool, remote_bda: []u8, addr_type: sys.esp_ble_wl_addr_type_t) !void {
try errors.espCheckError(sys.esp_ble_gap_update_whitelist(add, remote_bda.ptr, addr_type));
}
pub fn clearWhitelist() !void {
try errors.espCheckError(sys.esp_ble_gap_clear_whitelist());
}
};
// ---------------------------------------------------------------------------
// GATT common
// ---------------------------------------------------------------------------
pub const GattIf = sys.esp_gatt_if_t;
pub const Uuid = sys.esp_bt_uuid_t;
pub const GattStatus = sys.esp_gatt_status_t;
pub const GattPerm = sys.esp_gatt_perm_t;
pub const GattProp = sys.esp_gatt_char_prop_t;
pub const AttrValue = sys.esp_attr_value_t;
pub const AttrControl = sys.esp_attr_control_t;
// ---------------------------------------------------------------------------
// GATT Server (GATTS)
// ---------------------------------------------------------------------------
pub const GattServer = struct {
pub const CbT = sys.esp_gatts_cb_t;
pub const SrvcId = sys.esp_gatt_srvc_id_t;
pub const AttrDb = sys.esp_gatts_attr_db_t;
pub const Rsp = sys.esp_gatt_rsp_t;
pub fn registerCallback(cb: CbT) !void {
try errors.espCheckError(sys.esp_ble_gatts_register_callback(cb));
}
pub fn appRegister(app_id: u16) !void {
try errors.espCheckError(sys.esp_ble_gatts_app_register(app_id));
}
pub fn appUnregister(gatts_if: GattIf) !void {
try errors.espCheckError(sys.esp_ble_gatts_app_unregister(gatts_if));
}
pub fn createService(gatts_if: GattIf, service_id: *SrvcId, num_handle: u16) !void {
try errors.espCheckError(sys.esp_ble_gatts_create_service(gatts_if, service_id, num_handle));
}
/// Create all attributes in one call using an attribute table.
pub fn createAttrTab(attr_db: []const AttrDb, gatts_if: GattIf, srvc_inst_id: u8) !void {
try errors.espCheckError(sys.esp_ble_gatts_create_attr_tab(attr_db.ptr, gatts_if, @intCast(attr_db.len), srvc_inst_id));
}
pub fn addChar(
service_handle: u16,
char_uuid: *Uuid,
perm: GattPerm,
property: GattProp,
char_val: ?*AttrValue,
control: ?*AttrControl,
) !void {
try errors.espCheckError(sys.esp_ble_gatts_add_char(service_handle, char_uuid, perm, property, char_val, control));
}
pub fn addCharDescr(
service_handle: u16,
descr_uuid: *Uuid,
perm: GattPerm,
val: ?*AttrValue,
control: ?*AttrControl,
) !void {
try errors.espCheckError(sys.esp_ble_gatts_add_char_descr(service_handle, descr_uuid, perm, val, control));
}
pub fn deleteService(service_handle: u16) !void {
try errors.espCheckError(sys.esp_ble_gatts_delete_service(service_handle));
}
pub fn startService(service_handle: u16) !void {
try errors.espCheckError(sys.esp_ble_gatts_start_service(service_handle));
}
pub fn stopService(service_handle: u16) !void {
try errors.espCheckError(sys.esp_ble_gatts_stop_service(service_handle));
}
/// Send a notification or indication to a connected client.
pub fn sendIndicate(
gatts_if: GattIf,
conn_id: u16,
attr_handle: u16,
value: []u8,
need_confirm: bool,
) !void {
try errors.espCheckError(sys.esp_ble_gatts_send_indicate(
gatts_if,
conn_id,
attr_handle,
@intCast(value.len),
value.ptr,
need_confirm,
));
}
pub fn sendResponse(
gatts_if: GattIf,
conn_id: u16,
trans_id: u32,
status: GattStatus,
rsp: *Rsp,
) !void {
try errors.espCheckError(sys.esp_ble_gatts_send_response(gatts_if, conn_id, trans_id, status, rsp));
}
pub fn setAttrValue(attr_handle: u16, value: []const u8) !void {
try errors.espCheckError(sys.esp_ble_gatts_set_attr_value(attr_handle, @intCast(value.len), value.ptr));
}
pub fn getAttrValue(attr_handle: u16, length: *u16, value: *[*]const u8) GattStatus {
return sys.esp_ble_gatts_get_attr_value(attr_handle, length, value);
}
pub fn open(gatts_if: GattIf, remote_bda: []u8, is_direct: bool) !void {
try errors.espCheckError(sys.esp_ble_gatts_open(gatts_if, remote_bda.ptr, is_direct));
}
pub fn close(gatts_if: GattIf, conn_id: u16) !void {
try errors.espCheckError(sys.esp_ble_gatts_close(gatts_if, conn_id));
}
pub fn showLocalDatabase() !void {
try errors.espCheckError(sys.esp_ble_gatts_show_local_database());
}
};
// ---------------------------------------------------------------------------
// GATT Client (GATTC)
// ---------------------------------------------------------------------------
pub const GattClient = struct {
pub const CbT = sys.esp_gattc_cb_t;
pub const SvcElem = sys.esp_gattc_service_elem_t;
pub const CharElem = sys.esp_gattc_char_elem_t;
pub const DescrElem = sys.esp_gattc_descr_elem_t;
pub const WriteType = sys.esp_gatt_write_type_t;
pub const AuthReq = sys.esp_gatt_auth_req_t;
pub fn registerCallback(cb: CbT) !void {
try errors.espCheckError(sys.esp_ble_gattc_register_callback(cb));
}
pub fn appRegister(app_id: u16) !void {
try errors.espCheckError(sys.esp_ble_gattc_app_register(app_id));
}
pub fn appUnregister(gattc_if: GattIf) !void {
try errors.espCheckError(sys.esp_ble_gattc_app_unregister(gattc_if));
}
pub fn open(
gattc_if: GattIf,
remote_bda: []u8,
addr_type: sys.esp_ble_addr_type_t,
is_direct: bool,
) !void {
try errors.espCheckError(sys.esp_ble_gattc_open(gattc_if, remote_bda.ptr, addr_type, is_direct));
}
pub fn close(gattc_if: GattIf, conn_id: u16) !void {
try errors.espCheckError(sys.esp_ble_gattc_close(gattc_if, conn_id));
}
pub fn sendMtuReq(gattc_if: GattIf, conn_id: u16) !void {
try errors.espCheckError(sys.esp_ble_gattc_send_mtu_req(gattc_if, conn_id));
}
pub fn searchService(gattc_if: GattIf, conn_id: u16, filter_uuid: ?*Uuid) !void {
try errors.espCheckError(sys.esp_ble_gattc_search_service(gattc_if, conn_id, filter_uuid));
}
pub fn readChar(gattc_if: GattIf, conn_id: u16, handle: u16, auth_req: AuthReq) !void {
try errors.espCheckError(sys.esp_ble_gattc_read_char(gattc_if, conn_id, handle, auth_req));
}
pub fn writeChar(
gattc_if: GattIf,
conn_id: u16,
handle: u16,
value: []u8,
write_type: WriteType,
auth_req: AuthReq,
) !void {
try errors.espCheckError(sys.esp_ble_gattc_write_char(
gattc_if,
conn_id,
handle,
@intCast(value.len),
value.ptr,
write_type,
auth_req,
));
}
pub fn writeCharDescr(
gattc_if: GattIf,
conn_id: u16,
handle: u16,
value: []u8,
write_type: WriteType,
auth_req: AuthReq,
) !void {
try errors.espCheckError(sys.esp_ble_gattc_write_char_descr(
gattc_if,
conn_id,
handle,
@intCast(value.len),
value.ptr,
write_type,
auth_req,
));
}
pub fn registerForNotify(gattc_if: GattIf, server_bda: []u8, handle: u16) !void {
try errors.espCheckError(sys.esp_ble_gattc_register_for_notify(gattc_if, server_bda.ptr, handle));
}
pub fn unregisterForNotify(gattc_if: GattIf, server_bda: []u8, handle: u16) !void {
try errors.espCheckError(sys.esp_ble_gattc_unregister_for_notify(gattc_if, server_bda.ptr, handle));
}
pub fn cacheRefresh(remote_bda: []u8) !void {
try errors.espCheckError(sys.esp_ble_gattc_cache_refresh(remote_bda.ptr));
}
};
// ---------------------------------------------------------------------------
// WiFi/BT power domain (shared with wifi.zig)
// ---------------------------------------------------------------------------
pub const PowerDomain = struct {
pub fn on() void {
sys.esp_wifi_bt_power_domain_on();
}
pub fn off() void {
sys.esp_wifi_bt_power_domain_off();
}
};