generated from sirlilpanda/kicad-project-template-actionless
Added the zig_main project to software for zig based implementation of code for robot
This commit is contained in:
520
software/zig_main/imports/bluetooth.zig
Normal file
520
software/zig_main/imports/bluetooth.zig
Normal file
@@ -0,0 +1,520 @@
|
||||
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(¶ms)`
|
||||
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();
|
||||
}
|
||||
};
|
||||
631
software/zig_main/imports/bootloader.zig
Normal file
631
software/zig_main/imports/bootloader.zig
Normal file
@@ -0,0 +1,631 @@
|
||||
const sys = @import("sys");
|
||||
const std = @import("std");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Re-export register/device types directly from sys — they are opaque bitfield
|
||||
// unions that zig translate-c demoted; there is no benefit in redeclaring them.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const EfuseDev = sys.efuse_dev_t;
|
||||
pub const EfuseBlock = sys.ets_efuse_block_t;
|
||||
pub const EfusePurpose = sys.ets_efuse_purpose_t;
|
||||
pub const EtsStatus = sys.ets_status_t;
|
||||
pub const SecureBootStatus = sys.ets_secure_boot_status_t;
|
||||
pub const SecureBootSigBlock = sys.ets_secure_boot_sig_block_t;
|
||||
pub const SecureBootSignature = sys.ets_secure_boot_signature_t;
|
||||
pub const SecureBootKeyDigests = sys.ets_secure_boot_key_digests_t;
|
||||
pub const SecureBootSigBlockV1 = sys.esp_secure_boot_sig_block_t;
|
||||
pub const SecureBootIvDigest = sys.esp_secure_boot_iv_digest_t;
|
||||
pub const ImageSigPubKeyDigests = sys.esp_image_sig_public_key_digests_t;
|
||||
pub const RsaPubkey = sys.ets_rsa_pubkey_t;
|
||||
|
||||
pub const OtaSelectEntry = sys.esp_ota_select_entry_t;
|
||||
pub const PartitionPos = sys.esp_partition_pos_t;
|
||||
pub const PartitionInfo = sys.esp_partition_info_t;
|
||||
pub const BootloaderState = sys.bootloader_state_t;
|
||||
|
||||
pub const ImageHeader = sys.esp_image_header_t; // opaque
|
||||
pub const ImageSegmentHeader = sys.esp_image_segment_header_t;
|
||||
pub const ImageMetadata = sys.esp_image_metadata_t;
|
||||
pub const ImageFlashMapping = sys.esp_image_flash_mapping_t;
|
||||
pub const RtcRetainMem = sys.rtc_retain_mem_t;
|
||||
|
||||
pub const ChipId = sys.esp_chip_id_t;
|
||||
pub const SpiMode = sys.esp_image_spi_mode_t;
|
||||
pub const SpiFreq = sys.esp_image_spi_freq_t;
|
||||
pub const FlashSize = sys.esp_image_flash_size_t;
|
||||
pub const ImageLoadMode = sys.esp_image_load_mode_t;
|
||||
pub const ImageType = sys.esp_image_type;
|
||||
pub const GpioHold = sys.esp_comm_gpio_hold_t;
|
||||
|
||||
pub const EtsEvent = sys.ETSEvent;
|
||||
pub const EtsTask = sys.ETSTask;
|
||||
pub const EtsTimer = sys.ETSTimer;
|
||||
pub const EtsTimerFunc = sys.ETSTimerFunc;
|
||||
pub const EtsIsrFn = sys.ets_isr_t;
|
||||
pub const EtsIdleCb = sys.ets_idle_cb_t;
|
||||
|
||||
// The EFUSE peripheral register bank — memory-mapped, volatile.
|
||||
pub extern var EFUSE: EfuseDev;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Efuse — high-level ESP-IDF API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Efuse = struct {
|
||||
pub fn setTiming(clock: u32) !void {
|
||||
if (sys.ets_efuse_set_timing(clock) != 0) return error.EfuseTimingFailed;
|
||||
}
|
||||
|
||||
pub fn read() !void {
|
||||
if (sys.ets_efuse_read() != 0) return error.EfuseReadFailed;
|
||||
}
|
||||
|
||||
pub fn program(block: EfuseBlock) !void {
|
||||
if (sys.ets_efuse_program(block) != 0) return error.EfuseProgramFailed;
|
||||
}
|
||||
|
||||
pub fn clearProgramRegisters() void {
|
||||
sys.ets_efuse_clear_program_registers();
|
||||
}
|
||||
|
||||
pub fn writeKey(
|
||||
key_block: EfuseBlock,
|
||||
purpose: EfusePurpose,
|
||||
data: []const u8,
|
||||
) !void {
|
||||
if (sys.ets_efuse_write_key(key_block, purpose, data.ptr, data.len) != 0)
|
||||
return error.EfuseWriteKeyFailed;
|
||||
}
|
||||
|
||||
pub fn getReadRegisterAddress(block: EfuseBlock) u32 {
|
||||
return sys.ets_efuse_get_read_register_address(block);
|
||||
}
|
||||
|
||||
pub fn getKeyPurpose(block: EfuseBlock) EfusePurpose {
|
||||
return sys.ets_efuse_get_key_purpose(block);
|
||||
}
|
||||
|
||||
pub fn findPurpose(purpose: EfusePurpose, out_block: *EfuseBlock) bool {
|
||||
return sys.ets_efuse_find_purpose(purpose, out_block);
|
||||
}
|
||||
|
||||
pub fn keyBlockUnused(block: EfuseBlock) bool {
|
||||
return sys.ets_efuse_key_block_unused(block);
|
||||
}
|
||||
|
||||
pub fn findUnusedKeyBlock() EfuseBlock {
|
||||
return sys.ets_efuse_find_unused_key_block();
|
||||
}
|
||||
|
||||
pub fn countUnusedKeyBlocks() c_uint {
|
||||
return sys.ets_efuse_count_unused_key_blocks();
|
||||
}
|
||||
|
||||
pub fn rsCalculate(data: []const u8, rs_values: []u8) void {
|
||||
sys.ets_efuse_rs_calculate(data.ptr, rs_values.ptr);
|
||||
}
|
||||
|
||||
pub fn getSpiConfig() u32 {
|
||||
return sys.ets_efuse_get_spiconfig();
|
||||
}
|
||||
pub fn getWpPad() u32 {
|
||||
return sys.ets_efuse_get_wp_pad();
|
||||
}
|
||||
|
||||
pub fn downloadModesDisabled() bool {
|
||||
return sys.ets_efuse_download_modes_disabled();
|
||||
}
|
||||
pub fn legacySpiBootModeDisabled() bool {
|
||||
return sys.ets_efuse_legacy_spi_boot_mode_disabled();
|
||||
}
|
||||
pub fn getUartPrintControl() u32 {
|
||||
return sys.ets_efuse_get_uart_print_control();
|
||||
}
|
||||
pub fn usbSerialJtagPrintDisabled() u32 {
|
||||
return sys.ets_efuse_usb_serial_jtag_print_is_disabled();
|
||||
}
|
||||
pub fn usbDownloadModeDisabled() bool {
|
||||
return sys.ets_efuse_usb_download_mode_disabled();
|
||||
}
|
||||
pub fn usbModuleDisabled() bool {
|
||||
return sys.ets_efuse_usb_module_disabled();
|
||||
}
|
||||
pub fn securityDownloadModesEnabled() bool {
|
||||
return sys.ets_efuse_security_download_modes_enabled();
|
||||
}
|
||||
pub fn secureBootEnabled() bool {
|
||||
return sys.ets_efuse_secure_boot_enabled();
|
||||
}
|
||||
pub fn secureBootAggressiveRevokeEnabled() bool {
|
||||
return sys.ets_efuse_secure_boot_aggressive_revoke_enabled();
|
||||
}
|
||||
pub fn cacheEncryptionEnabled() bool {
|
||||
return sys.ets_efuse_cache_encryption_enabled();
|
||||
}
|
||||
pub fn flashOpi5padsPowerSelVddspi() bool {
|
||||
return sys.ets_efuse_flash_opi_5pads_power_sel_vddspi();
|
||||
}
|
||||
pub fn forceSendResume() bool {
|
||||
return sys.ets_efuse_force_send_resume();
|
||||
}
|
||||
pub fn getFlashDelayUs() u32 {
|
||||
return sys.ets_efuse_get_flash_delay_us();
|
||||
}
|
||||
|
||||
pub fn enableJtagTemporarily(hmac_key: []const u8, block: EfuseBlock) !void {
|
||||
if (sys.ets_jtag_enable_temporarily(hmac_key.ptr, block) != 0)
|
||||
return error.JtagEnableFailed;
|
||||
}
|
||||
|
||||
pub fn romMacAddressCrc8(data: []const u8) u8 {
|
||||
return sys.esp_rom_efuse_mac_address_crc8(data.ptr, @intCast(data.len));
|
||||
}
|
||||
pub fn romGetFlashGpioInfo() u32 {
|
||||
return sys.esp_rom_efuse_get_flash_gpio_info();
|
||||
}
|
||||
pub fn romGetFlashWpGpio() u32 {
|
||||
return sys.esp_rom_efuse_get_flash_wp_gpio();
|
||||
}
|
||||
pub fn romIsSecureBootEnabled() bool {
|
||||
return sys.esp_rom_efuse_is_secure_boot_enabled();
|
||||
}
|
||||
|
||||
/// CRC-8 over arbitrary data (ESP ROM implementation).
|
||||
pub fn crc8(data: []const u8) u8 {
|
||||
return sys.esp_crc8(data.ptr, @intCast(data.len));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// EFUSE register-level inline accessors (mirror efuse_ll_* from esp-idf).
|
||||
// These read directly from the memory-mapped EFUSE peripheral.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const EfuseLl = struct {
|
||||
pub fn getFlashCryptCnt() u32 {
|
||||
return sys.efuse_ll_get_flash_crypt_cnt();
|
||||
}
|
||||
pub fn getWdtDelaySel() u32 {
|
||||
return sys.efuse_ll_get_wdt_delay_sel();
|
||||
}
|
||||
pub fn getMac0() u32 {
|
||||
return sys.efuse_ll_get_mac0();
|
||||
}
|
||||
pub fn getMac1() u32 {
|
||||
return sys.efuse_ll_get_mac1();
|
||||
}
|
||||
pub fn getSecureBootV2En() bool {
|
||||
return sys.efuse_ll_get_secure_boot_v2_en();
|
||||
}
|
||||
pub fn getErrRstEnable() bool {
|
||||
return sys.efuse_ll_get_err_rst_enable();
|
||||
}
|
||||
pub fn getWaferVersionMajor() u32 {
|
||||
return sys.efuse_ll_get_chip_wafer_version_major();
|
||||
}
|
||||
pub fn getWaferVersionMinor() u32 {
|
||||
return sys.efuse_ll_get_chip_wafer_version_minor();
|
||||
}
|
||||
pub fn getDisableWaferVersionMajor() bool {
|
||||
return sys.efuse_ll_get_disable_wafer_version_major();
|
||||
}
|
||||
pub fn getBlkVersionMajor() u32 {
|
||||
return sys.efuse_ll_get_blk_version_major();
|
||||
}
|
||||
pub fn getBlkVersionMinor() u32 {
|
||||
return sys.efuse_ll_get_blk_version_minor();
|
||||
}
|
||||
pub fn getDisableBlkVersionMajor() bool {
|
||||
return sys.efuse_ll_get_disable_blk_version_major();
|
||||
}
|
||||
pub fn getChipVerPkg() u32 {
|
||||
return sys.efuse_ll_get_chip_ver_pkg();
|
||||
}
|
||||
pub fn getOcode() u32 {
|
||||
return sys.efuse_ll_get_ocode();
|
||||
}
|
||||
pub fn getKRtcLdo() u32 {
|
||||
return sys.efuse_ll_get_k_rtc_ldo();
|
||||
}
|
||||
pub fn getKDigLdo() u32 {
|
||||
return sys.efuse_ll_get_k_dig_ldo();
|
||||
}
|
||||
pub fn getVRtcDbias20() u32 {
|
||||
return sys.efuse_ll_get_v_rtc_dbias20();
|
||||
}
|
||||
pub fn getVDigDbias20() u32 {
|
||||
return sys.efuse_ll_get_v_dig_dbias20();
|
||||
}
|
||||
pub fn getDigDbias_hvt() u32 {
|
||||
return sys.efuse_ll_get_dig_dbias_hvt();
|
||||
}
|
||||
|
||||
pub fn isReadCmdPending() bool {
|
||||
return sys.efuse_ll_get_read_cmd();
|
||||
}
|
||||
pub fn isPgmCmdPending() bool {
|
||||
return sys.efuse_ll_get_pgm_cmd();
|
||||
}
|
||||
pub fn triggerReadCmd() void {
|
||||
sys.efuse_ll_set_read_cmd();
|
||||
}
|
||||
pub fn triggerPgmCmd(block: u32) void {
|
||||
sys.efuse_ll_set_pgm_cmd(block);
|
||||
}
|
||||
pub fn setConfReadOpCode() void {
|
||||
sys.efuse_ll_set_conf_read_op_code();
|
||||
}
|
||||
pub fn setConfWriteOpCode() void {
|
||||
sys.efuse_ll_set_conf_write_op_code();
|
||||
}
|
||||
pub fn setDacNum(val: u8) void {
|
||||
sys.efuse_ll_set_dac_num(val);
|
||||
}
|
||||
pub fn setDacClkDiv(val: u8) void {
|
||||
sys.efuse_ll_set_dac_clk_div(val);
|
||||
}
|
||||
pub fn setPwrOnNum(val: u16) void {
|
||||
sys.efuse_ll_set_pwr_on_num(val);
|
||||
}
|
||||
pub fn setPwrOffNum(val: u16) void {
|
||||
sys.efuse_ll_set_pwr_off_num(val);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Partition table
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Partition = struct {
|
||||
pub fn tableVerify(
|
||||
table: [*]const PartitionInfo,
|
||||
log_errors: bool,
|
||||
num_partitions: *c_int,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_partition_table_verify(table, log_errors, num_partitions));
|
||||
}
|
||||
|
||||
pub fn isFlashRegionWritable(addr: usize, size: usize) bool {
|
||||
return sys.esp_partition_is_flash_region_writable(addr, size);
|
||||
}
|
||||
|
||||
pub fn mainFlashRegionSafe(addr: usize, size: usize) bool {
|
||||
return sys.esp_partition_main_flash_region_safe(addr, size);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Image verification / loading
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Image = struct {
|
||||
pub fn verify(
|
||||
mode: ImageLoadMode,
|
||||
part: *const PartitionPos,
|
||||
data: ?*ImageMetadata,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_image_verify(mode, part, data));
|
||||
}
|
||||
|
||||
pub fn getMetadata(part: *const PartitionPos, metadata: *ImageMetadata) !void {
|
||||
try errors.espCheckError(sys.esp_image_get_metadata(part, metadata));
|
||||
}
|
||||
|
||||
pub fn getFlashSize(app_flash_size: FlashSize) c_int {
|
||||
return sys.esp_image_get_flash_size(app_flash_size);
|
||||
}
|
||||
|
||||
pub fn verifyBootloader(out_length: *u32) !void {
|
||||
try errors.espCheckError(sys.esp_image_verify_bootloader(out_length));
|
||||
}
|
||||
|
||||
pub fn verifyBootloaderData(data: *ImageMetadata) !void {
|
||||
try errors.espCheckError(sys.esp_image_verify_bootloader_data(data));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Bootloader
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Bootloader = struct {
|
||||
pub fn loadImage(part: *const PartitionPos, data: *ImageMetadata) !void {
|
||||
try errors.espCheckError(sys.bootloader_load_image(part, data));
|
||||
}
|
||||
|
||||
pub fn loadImageNoVerify(part: *const PartitionPos, data: *ImageMetadata) !void {
|
||||
try errors.espCheckError(sys.bootloader_load_image_no_verify(part, data));
|
||||
}
|
||||
|
||||
pub fn readOtadata(
|
||||
ota_info: *const PartitionPos,
|
||||
two_otadata: *[2]OtaSelectEntry,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.bootloader_common_read_otadata(ota_info, two_otadata));
|
||||
}
|
||||
|
||||
pub fn otaSelectCrc(s: *const OtaSelectEntry) u32 {
|
||||
return sys.bootloader_common_ota_select_crc(s);
|
||||
}
|
||||
|
||||
pub fn otaSelectValid(s: *const OtaSelectEntry) bool {
|
||||
return sys.bootloader_common_ota_select_valid(s);
|
||||
}
|
||||
|
||||
pub fn otaSelectInvalid(s: *const OtaSelectEntry) bool {
|
||||
return sys.bootloader_common_ota_select_invalid(s);
|
||||
}
|
||||
|
||||
pub fn checkLongHoldGpio(pin: u32, delay_sec: u32) GpioHold {
|
||||
return sys.bootloader_common_check_long_hold_gpio(pin, delay_sec);
|
||||
}
|
||||
|
||||
pub fn checkLongHoldGpioLevel(pin: u32, delay_sec: u32, level: bool) GpioHold {
|
||||
return sys.bootloader_common_check_long_hold_gpio_level(pin, delay_sec, level);
|
||||
}
|
||||
|
||||
pub fn erasePartTypeData(list_erase: [*:0]const u8, ota_data_erase: bool) bool {
|
||||
return sys.bootloader_common_erase_part_type_data(list_erase, ota_data_erase);
|
||||
}
|
||||
|
||||
pub fn labelSearch(list: [*:0]const u8, label: [*:0]u8) bool {
|
||||
return sys.bootloader_common_label_search(list, label);
|
||||
}
|
||||
|
||||
pub fn configureSpiPins(drv: c_int) void {
|
||||
sys.bootloader_configure_spi_pins(drv);
|
||||
}
|
||||
|
||||
pub fn getSha256OfPartition(
|
||||
address: u32,
|
||||
size: u32,
|
||||
part_type: c_int,
|
||||
out_sha_256: *[32]u8,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.bootloader_common_get_sha256_of_partition(address, size, part_type, out_sha_256));
|
||||
}
|
||||
|
||||
pub fn getActiveOtadata(two_otadata: *[2]OtaSelectEntry) c_int {
|
||||
return sys.bootloader_common_get_active_otadata(two_otadata);
|
||||
}
|
||||
|
||||
pub fn selectOtadata(
|
||||
two_otadata: *const [2]OtaSelectEntry,
|
||||
valid_two_otadata: *[2]bool,
|
||||
max: bool,
|
||||
) c_int {
|
||||
return sys.bootloader_common_select_otadata(two_otadata, valid_two_otadata, max);
|
||||
}
|
||||
|
||||
pub fn getChipVerPkg() u32 {
|
||||
return sys.bootloader_common_get_chip_ver_pkg();
|
||||
}
|
||||
|
||||
pub fn checkChipValidity(img_hdr: *const ImageHeader, kind: ImageType) !void {
|
||||
try errors.espCheckError(sys.bootloader_common_check_chip_validity(img_hdr, kind));
|
||||
}
|
||||
|
||||
pub fn vddsdioConf() void {
|
||||
sys.bootloader_common_vddsdio_configure();
|
||||
}
|
||||
|
||||
pub fn randomEnable() void {
|
||||
sys.bootloader_random_enable();
|
||||
}
|
||||
pub fn randomDisable() void {
|
||||
sys.bootloader_random_disable();
|
||||
}
|
||||
pub fn fillRandom(buf: []u8) void {
|
||||
sys.bootloader_fill_random(buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
pub fn readBootloaderHeader() !void {
|
||||
try errors.espCheckError(sys.bootloader_read_bootloader_header());
|
||||
}
|
||||
|
||||
pub fn checkBootloaderValidity() !void {
|
||||
try errors.espCheckError(sys.bootloader_check_bootloader_validity());
|
||||
}
|
||||
|
||||
pub fn clearBssSection() void {
|
||||
sys.bootloader_clear_bss_section();
|
||||
}
|
||||
pub fn configWdt() void {
|
||||
sys.bootloader_config_wdt();
|
||||
}
|
||||
pub fn enableRandom() void {
|
||||
sys.bootloader_enable_random();
|
||||
}
|
||||
pub fn printBanner() void {
|
||||
sys.bootloader_print_banner();
|
||||
}
|
||||
|
||||
pub fn init() !void {
|
||||
try errors.espCheckError(sys.bootloader_init());
|
||||
}
|
||||
|
||||
pub fn flashEncrypt(bs: *BootloaderState) bool {
|
||||
return sys.flash_encrypt(bs);
|
||||
}
|
||||
|
||||
/// True if two flash regions [start1,end1) and [start2,end2) overlap.
|
||||
pub fn regionsOverlap(start1: isize, end1: isize, start2: isize, end2: isize) bool {
|
||||
return (end1 > start2) and (end2 > start1);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Secure boot
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const SecureBoot = struct {
|
||||
pub fn enabled() bool {
|
||||
// Reads directly from the efuse secure_boot_v2 bit — no ESP-IDF call needed.
|
||||
return EfuseLl.getSecureBootV2En();
|
||||
}
|
||||
|
||||
pub fn generateDigest() !void {
|
||||
try errors.espCheckError(sys.esp_secure_boot_generate_digest());
|
||||
}
|
||||
|
||||
pub fn permanentlyEnable() !void {
|
||||
try errors.espCheckError(sys.esp_secure_boot_permanently_enable());
|
||||
}
|
||||
|
||||
pub fn v2PermanentlyEnable(image_data: *const ImageMetadata) !void {
|
||||
try errors.espCheckError(sys.esp_secure_boot_v2_permanently_enable(image_data));
|
||||
}
|
||||
|
||||
pub fn verifySignature(src_addr: u32, length: u32) !void {
|
||||
try errors.espCheckError(sys.esp_secure_boot_verify_signature(src_addr, length));
|
||||
}
|
||||
|
||||
pub fn verifyEcdsaSignatureBlock(
|
||||
sig_block: *const SecureBootSigBlockV1,
|
||||
image_digest: [*:0]const u8,
|
||||
verified_digest: *u8,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_secure_boot_verify_ecdsa_signature_block(sig_block, image_digest, verified_digest));
|
||||
}
|
||||
|
||||
pub fn initChecks() void {
|
||||
sys.esp_secure_boot_init_checks();
|
||||
}
|
||||
|
||||
pub fn enableSecureFeatures() !void {
|
||||
try errors.espCheckError(sys.esp_secure_boot_enable_secure_features());
|
||||
}
|
||||
|
||||
pub fn cfgVerifyReleaseMode() bool {
|
||||
return sys.esp_secure_boot_cfg_verify_release_mode();
|
||||
}
|
||||
|
||||
// ROM-level secure boot ---------------------------------------------------
|
||||
|
||||
pub fn romVerifyBootloaderWithKeys(
|
||||
verified_hash: *u8,
|
||||
trusted_keys: *const SecureBootKeyDigests,
|
||||
stage_load: bool,
|
||||
) SecureBootStatus {
|
||||
return sys.ets_secure_boot_verify_bootloader_with_keys(verified_hash, trusted_keys, stage_load);
|
||||
}
|
||||
|
||||
pub fn romReadKeyDigests(trusted_keys: *SecureBootKeyDigests) EtsStatus {
|
||||
return sys.ets_secure_boot_read_key_digests(trusted_keys);
|
||||
}
|
||||
|
||||
pub fn romVerifySignature(
|
||||
sig: *const SecureBootSignature,
|
||||
image_digest: [*:0]const u8,
|
||||
trusted_keys: *const SecureBootKeyDigests,
|
||||
verified_digest: *u8,
|
||||
) SecureBootStatus {
|
||||
return sys.ets_secure_boot_verify_signature(sig, image_digest, trusted_keys, verified_digest);
|
||||
}
|
||||
|
||||
pub fn romRevokePublicKeyDigest(index: c_int) void {
|
||||
sys.ets_secure_boot_revoke_public_key_digest(index);
|
||||
}
|
||||
|
||||
pub fn romRsaPssVerify(
|
||||
key: *const RsaPubkey,
|
||||
sig: [*:0]const u8,
|
||||
digest: [*:0]const u8,
|
||||
verified_digest: *u8,
|
||||
) bool {
|
||||
return sys.ets_rsa_pss_verify(key, sig, digest, verified_digest);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ETS (ROM system services)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Ets = struct {
|
||||
pub fn printf(fmt: [*:0]const u8, args: anytype) c_int {
|
||||
return @call(.auto, sys.ets_printf, .{fmt} ++ args);
|
||||
}
|
||||
|
||||
pub fn delayUs(us: u32) void {
|
||||
sys.ets_delay_us(us);
|
||||
}
|
||||
pub fn getCpuFrequency() u32 {
|
||||
return sys.ets_get_cpu_frequency();
|
||||
}
|
||||
pub fn updateCpuFrequency(ticks_per_us: u32) void {
|
||||
sys.ets_update_cpu_frequency(ticks_per_us);
|
||||
}
|
||||
pub fn getXtalFreq() u32 {
|
||||
return sys.ets_get_xtal_freq();
|
||||
}
|
||||
pub fn getXtalDiv() u32 {
|
||||
return sys.ets_get_xtal_div();
|
||||
}
|
||||
pub fn getApbFreq() u32 {
|
||||
return sys.ets_get_apb_freq();
|
||||
}
|
||||
|
||||
pub fn isrAttach(irq: c_int, func: EtsIsrFn, arg: ?*anyopaque) void {
|
||||
sys.ets_isr_attach(irq, func, arg);
|
||||
}
|
||||
pub fn isrMask(mask: u32) void {
|
||||
sys.ets_isr_mask(mask);
|
||||
}
|
||||
pub fn isrUnmask(mask: u32) void {
|
||||
sys.ets_isr_unmask(mask);
|
||||
}
|
||||
pub fn intrLock() void {
|
||||
sys.ets_intr_lock();
|
||||
}
|
||||
pub fn intrUnlock() void {
|
||||
sys.ets_intr_unlock();
|
||||
}
|
||||
|
||||
pub fn setUserStart(start: u32) void {
|
||||
sys.ets_set_user_start(start);
|
||||
}
|
||||
pub fn installPutc1(p: ?*const fn (u8) callconv(.c) void) void {
|
||||
sys.ets_install_putc1(p);
|
||||
}
|
||||
pub fn installPutc2(p: ?*const fn (u8) callconv(.c) void) void {
|
||||
sys.ets_install_putc2(p);
|
||||
}
|
||||
pub fn installUartPrintf() void {
|
||||
sys.ets_install_uart_printf();
|
||||
}
|
||||
|
||||
pub fn intr_matrix_set(cpu_no: c_int, model_num: u32, intr_num: u32) void {
|
||||
sys.intr_matrix_set(cpu_no, model_num, intr_num);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ETS Timer
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const EtsTimerApi = struct {
|
||||
pub fn init() void {
|
||||
sys.ets_timer_init();
|
||||
}
|
||||
pub fn deinit() void {
|
||||
sys.ets_timer_deinit();
|
||||
}
|
||||
|
||||
pub fn arm(timer: *EtsTimer, timeout_ms: u32, repeat: bool) void {
|
||||
sys.ets_timer_arm(timer, timeout_ms, repeat);
|
||||
}
|
||||
pub fn armUs(timer: *EtsTimer, us: u32, repeat: bool) void {
|
||||
sys.ets_timer_arm_us(timer, us, repeat);
|
||||
}
|
||||
pub fn disarm(timer: *EtsTimer) void {
|
||||
sys.ets_timer_disarm(timer);
|
||||
}
|
||||
pub fn setFn(timer: *EtsTimer, func: ?*const EtsTimerFunc, arg: ?*anyopaque) void {
|
||||
sys.ets_timer_setfn(timer, func, arg);
|
||||
}
|
||||
pub fn done(timer: *EtsTimer) void {
|
||||
sys.ets_timer_done(timer);
|
||||
}
|
||||
};
|
||||
51
software/zig_main/imports/crc.zig
Normal file
51
software/zig_main/imports/crc.zig
Normal file
@@ -0,0 +1,51 @@
|
||||
const sys = @import("sys");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CRC helpers — thin wrappers around the ESP-ROM implementations.
|
||||
//
|
||||
// All functions take a running `crc` (pass 0 for the first block) and a
|
||||
// data slice, and return the updated CRC value.
|
||||
//
|
||||
// Le = reflected polynomial (little-endian / standard CRC convention).
|
||||
// Be = non-reflected polynomial (big-endian convention).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// ── CRC-8 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// CRC-8 with reflected (little-endian) polynomial.
|
||||
pub fn crc8Le(crc: u8, buf: []const u8) u8 {
|
||||
return sys.esp_rom_crc8_le(crc, buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
/// CRC-8 with non-reflected (big-endian) polynomial.
|
||||
pub fn crc8Be(crc: u8, buf: []const u8) u8 {
|
||||
return sys.esp_rom_crc8_be(crc, buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
// ── CRC-16 ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// CRC-16 with reflected (little-endian) polynomial.
|
||||
pub fn crc16Le(crc: u16, buf: []const u8) u16 {
|
||||
return sys.esp_rom_crc16_le(crc, buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
/// CRC-16 with non-reflected (big-endian) polynomial.
|
||||
pub fn crc16Be(crc: u16, buf: []const u8) u16 {
|
||||
return sys.esp_rom_crc16_be(crc, buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
// ── CRC-32 ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// CRC-32 with reflected (little-endian) polynomial — compatible with
|
||||
/// zlib / Ethernet / standard CRC-32.
|
||||
pub fn crc32Le(crc: u32, buf: []const u8) u32 {
|
||||
return sys.esp_rom_crc32_le(crc, buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
/// CRC-32 with non-reflected (big-endian) polynomial.
|
||||
pub fn crc32Be(crc: u32, buf: []const u8) u32 {
|
||||
return sys.esp_rom_crc32_be(crc, buf.ptr, buf.len);
|
||||
}
|
||||
|
||||
// ── Zig stdlib CRC (kept for pure-Zig use without ROM calls) ──────────────
|
||||
pub const zigCRC32 = @import("std").hash.crc;
|
||||
440
software/zig_main/imports/dsp.zig
Normal file
440
software/zig_main/imports/dsp.zig
Normal file
@@ -0,0 +1,440 @@
|
||||
// ESP-DSP (Digital Signal Processing) Library Wrapper
|
||||
// requires: idf.py add-dependency espressif/esp-dsp
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
const std = @import("std");
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Complex number types
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// Complex number with 16-bit integer components (short complex)
|
||||
pub const Complex16 = extern struct {
|
||||
re: i16,
|
||||
im: i16,
|
||||
|
||||
pub fn init(re: i16, im: i16) Complex16 {
|
||||
return .{ .re = re, .im = im };
|
||||
}
|
||||
|
||||
pub fn fromUnion(u: sys.sc16_t) Complex16 {
|
||||
return .{ .re = u.unnamed_0.re, .im = u.unnamed_0.im };
|
||||
}
|
||||
|
||||
pub fn toUnion(self: Complex16) sys.sc16_t {
|
||||
return .{ .unnamed_0 = .{ .re = self.re, .im = self.im } };
|
||||
}
|
||||
};
|
||||
|
||||
/// Complex number with 32-bit float components (float complex)
|
||||
pub const Complex32 = extern struct {
|
||||
re: f32,
|
||||
im: f32,
|
||||
|
||||
pub fn init(re: f32, im: f32) Complex32 {
|
||||
return .{ .re = re, .im = im };
|
||||
}
|
||||
|
||||
pub fn fromUnion(u: sys.fc32_t) Complex32 {
|
||||
return .{ .re = u.unnamed_0.re, .im = u.unnamed_0.im };
|
||||
}
|
||||
|
||||
pub fn toUnion(self: Complex32) sys.fc32_t {
|
||||
return .{ .unnamed_0 = .{ .re = self.re, .im = self.im } };
|
||||
}
|
||||
|
||||
pub fn magnitude(self: Complex32) f32 {
|
||||
return @sqrt(self.re * self.re + self.im * self.im);
|
||||
}
|
||||
|
||||
pub fn phase(self: Complex32) f32 {
|
||||
return std.math.atan2(self.im, self.re);
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Image/2D data structure
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const Image2D = extern struct {
|
||||
data: ?*anyopaque = null,
|
||||
step_x: c_int = 0,
|
||||
step_y: c_int = 0,
|
||||
stride_x: c_int = 0,
|
||||
stride_y: c_int = 0,
|
||||
|
||||
pub fn init(data: *anyopaque, step_x: i32, step_y: i32, stride_x: i32, stride_y: i32) Image2D {
|
||||
return .{ .data = data, .step_x = step_x, .step_y = step_y, .stride_x = stride_x, .stride_y = stride_y };
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Filter structures
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// FIR filter with float32 coefficients
|
||||
pub const FirF32 = struct {
|
||||
inner: sys.fir_f32_t,
|
||||
|
||||
pub fn init(coeffs: []f32, delay: []f32) !FirF32 {
|
||||
if (coeffs.len != delay.len) return error.LengthMismatch;
|
||||
|
||||
var fir: sys.fir_f32_t = undefined;
|
||||
try errors.espCheckError(sys.dsps_fir_init_f32(&fir, coeffs.ptr, delay.ptr, @intCast(coeffs.len)));
|
||||
|
||||
return .{ .inner = fir };
|
||||
}
|
||||
|
||||
pub fn initDecimated(coeffs: []f32, delay: []f32, decimation: u32) !FirF32 {
|
||||
if (coeffs.len != delay.len) return error.LengthMismatch;
|
||||
|
||||
var fir: sys.fir_f32_t = undefined;
|
||||
try errors.espCheckError(sys.dsps_fird_init_f32(&fir, coeffs.ptr, delay.ptr, @intCast(coeffs.len), @intCast(decimation)));
|
||||
|
||||
return .{ .inner = fir };
|
||||
}
|
||||
|
||||
pub fn process(self: *FirF32, input: []const f32, output: []f32) !void {
|
||||
if (input.len != output.len) return error.LengthMismatch;
|
||||
try errors.espCheckError(sys.dsps_fir_f32_ae32(&self.inner, input.ptr, output.ptr, @intCast(input.len)));
|
||||
}
|
||||
|
||||
pub fn deinit(self: *FirF32) !void {
|
||||
try errors.espCheckError(sys.dsps_fir_f32_free(&self.inner));
|
||||
}
|
||||
};
|
||||
|
||||
/// FIR filter with int16 coefficients
|
||||
pub const FirS16 = struct {
|
||||
inner: sys.fir_s16_t,
|
||||
|
||||
pub fn initDecimated(coeffs: []i16, delay: []i16, decimation: u16, start_pos: u16, shift: u16) !FirS16 {
|
||||
if (coeffs.len != delay.len) return error.LengthMismatch;
|
||||
|
||||
var fir: sys.fir_s16_t = undefined;
|
||||
try errors.espCheckError(sys.dsps_fird_init_s16(&fir, coeffs.ptr, delay.ptr, @intCast(coeffs.len), @intCast(decimation), @intCast(start_pos), @intCast(shift)));
|
||||
|
||||
return .{ .inner = fir };
|
||||
}
|
||||
|
||||
pub fn process(self: *FirS16, input: []const i16, output: []i16) !u32 {
|
||||
if (input.len != output.len) return error.LengthMismatch;
|
||||
const result = sys.dsps_fird_s16_aes3(&self.inner, input.ptr, output.ptr, @intCast(input.len));
|
||||
return @intCast(result);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *FirS16) !void {
|
||||
try errors.espCheckError(sys.dsps_fird_s16_aexx_free(&self.inner));
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// FFT operations
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const FFT = struct {
|
||||
/// Initialize FFT with radix-2 (power of 2 sizes)
|
||||
pub fn initRadix2F32(table_buffer: []f32, table_size: u32) !void {
|
||||
try errors.espCheckError(sys.dsps_fft2r_init_fc32(table_buffer.ptr, @intCast(table_size)));
|
||||
}
|
||||
|
||||
pub fn deinitRadix2F32() void {
|
||||
sys.dsps_fft2r_deinit_fc32();
|
||||
}
|
||||
|
||||
/// Perform radix-2 FFT on complex data (in-place)
|
||||
/// data must be [re0, im0, re1, im1, ...] format
|
||||
pub fn fft2rF32(data: []f32, fft_size: u32, w_table: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_fft2r_fc32_aes3_(data.ptr, @intCast(fft_size), w_table.ptr));
|
||||
}
|
||||
|
||||
/// Bit-reverse reordering for FFT
|
||||
pub fn bitReverseF32(data: []f32, fft_size: u32) !void {
|
||||
try errors.espCheckError(sys.dsps_bit_rev_fc32_ansi(data.ptr, @intCast(fft_size)));
|
||||
}
|
||||
|
||||
/// Initialize FFT with radix-4 (optimized for power of 4 sizes)
|
||||
pub fn initRadix4F32(table_buffer: []f32, max_fft_size: u32) !void {
|
||||
try errors.espCheckError(sys.dsps_fft4r_init_fc32(table_buffer.ptr, @intCast(max_fft_size)));
|
||||
}
|
||||
|
||||
pub fn deinitRadix4F32() void {
|
||||
sys.dsps_fft4r_deinit_fc32();
|
||||
}
|
||||
|
||||
/// Perform radix-4 FFT
|
||||
pub fn fft4rF32(data: []f32, fft_size: u32, w_table: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_fft4r_fc32_ae32_(data.ptr, @intCast(fft_size), w_table.ptr, @intCast(w_table.len)));
|
||||
}
|
||||
|
||||
/// Convert complex FFT output to real signal
|
||||
pub fn complex2RealF32(data: []f32, fft_size: u32, w_table: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_cplx2real_fc32_ae32_(data.ptr, @intCast(fft_size), w_table.ptr, @intCast(w_table.len)));
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Basic math operations
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const Math = struct {
|
||||
/// Vector addition: output = input1 + input2
|
||||
pub fn addF32(input1: []const f32, input2: []const f32, output: []f32) !void {
|
||||
if (input1.len != input2.len or input1.len != output.len) {
|
||||
return error.LengthMismatch;
|
||||
}
|
||||
try errors.espCheckError(sys.dsps_add_f32_ae32(input1.ptr, input2.ptr, output.ptr, @intCast(input1.len), 1, 1, 1));
|
||||
}
|
||||
|
||||
/// Vector addition (16-bit): output = input1 + input2
|
||||
pub fn addS16(input1: []const i16, input2: []const i16, output: []i16, shift: i32) !void {
|
||||
if (input1.len != input2.len or input1.len != output.len) {
|
||||
return error.LengthMismatch;
|
||||
}
|
||||
try errors.espCheckError(sys.dsps_add_s16_aes3(input1.ptr, input2.ptr, output.ptr, @intCast(input1.len), 1, 1, 1, shift));
|
||||
}
|
||||
|
||||
/// Vector subtraction: output = input1 - input2
|
||||
pub fn subF32(input1: []const f32, input2: []const f32, output: []f32) !void {
|
||||
if (input1.len != input2.len or input1.len != output.len) {
|
||||
return error.LengthMismatch;
|
||||
}
|
||||
try errors.espCheckError(sys.dsps_sub_f32_ae32(input1.ptr, input2.ptr, output.ptr, @intCast(input1.len), 1, 1, 1));
|
||||
}
|
||||
|
||||
/// Vector multiplication: output = input1 * input2
|
||||
pub fn mulF32(input1: []const f32, input2: []const f32, output: []f32) !void {
|
||||
if (input1.len != input2.len or input1.len != output.len) {
|
||||
return error.LengthMismatch;
|
||||
}
|
||||
try errors.espCheckError(sys.dsps_mul_f32_ae32(input1.ptr, input2.ptr, output.ptr, @intCast(input1.len), 1, 1, 1));
|
||||
}
|
||||
|
||||
/// Scalar addition: output = input + C
|
||||
pub fn addConstF32(input: []const f32, output: []f32, constant: f32) !void {
|
||||
if (input.len != output.len) return error.LengthMismatch;
|
||||
try errors.espCheckError(sys.dsps_addc_f32_ae32(input.ptr, output.ptr, @intCast(input.len), constant, 1, 1));
|
||||
}
|
||||
|
||||
/// Scalar multiplication: output = input * C
|
||||
pub fn mulConstF32(input: []const f32, output: []f32, constant: f32) !void {
|
||||
if (input.len != output.len) return error.LengthMismatch;
|
||||
try errors.espCheckError(sys.dsps_mulc_f32_ae32(input.ptr, output.ptr, @intCast(input.len), constant, 1, 1));
|
||||
}
|
||||
|
||||
/// Element-wise square root
|
||||
pub fn sqrtF32(input: []const f32, output: []f32) !void {
|
||||
if (input.len != output.len) return error.LengthMismatch;
|
||||
try errors.espCheckError(sys.dsps_sqrt_f32_ansi(input.ptr, output.ptr, @intCast(input.len)));
|
||||
}
|
||||
|
||||
/// Dot product: result = sum(src1[i] * src2[i])
|
||||
pub fn dotProductF32(src1: []const f32, src2: []const f32) !f32 {
|
||||
if (src1.len != src2.len) return error.LengthMismatch;
|
||||
var result: f32 = 0;
|
||||
try errors.espCheckError(sys.dsps_dotprod_f32_aes3(src1.ptr, src2.ptr, &result, @intCast(src1.len)));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Dot product (16-bit)
|
||||
pub fn dotProductS16(src1: []const i16, src2: []const i16, shift: i8) !i16 {
|
||||
if (src1.len != src2.len) return error.LengthMismatch;
|
||||
var result: i16 = 0;
|
||||
try errors.espCheckError(sys.dsps_dotprod_s16_ae32(src1.ptr, src2.ptr, &result, @intCast(src1.len), shift));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Matrix operations
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const Matrix = struct {
|
||||
/// Matrix multiplication: C = A * B
|
||||
/// A: m x k, B: k x n, C: m x n
|
||||
pub fn multiplyF32(A: []const f32, B: []const f32, C: []f32, m: u32, n: u32, k: u32) !void {
|
||||
try errors.espCheckError(sys.dspm_mult_f32_aes3(A.ptr, B.ptr, C.ptr, @intCast(m), @intCast(n), @intCast(k)));
|
||||
}
|
||||
|
||||
/// Matrix addition: output = input1 + input2
|
||||
pub fn addF32(input1: []const f32, input2: []const f32, output: []f32, rows: u32, cols: u32) !void {
|
||||
try errors.espCheckError(sys.dspm_add_f32_ae32(input1.ptr, input2.ptr, output.ptr, @intCast(rows), @intCast(cols), 0, 0, 0, 1, 1, 1));
|
||||
}
|
||||
|
||||
/// Matrix subtraction: output = input1 - input2
|
||||
pub fn subF32(input1: []const f32, input2: []const f32, output: []f32, rows: u32, cols: u32) !void {
|
||||
try errors.espCheckError(sys.dspm_sub_f32_ae32(input1.ptr, input2.ptr, output.ptr, @intCast(rows), @intCast(cols), 0, 0, 0, 1, 1, 1));
|
||||
}
|
||||
|
||||
/// Scalar matrix multiplication: output = input * C
|
||||
pub fn mulConstF32(input: []const f32, output: []f32, constant: f32, rows: u32, cols: u32) !void {
|
||||
try errors.espCheckError(sys.dspm_mulc_f32_ae32(input.ptr, output.ptr, constant, @intCast(rows), @intCast(cols), 0, 0, 1, 1));
|
||||
}
|
||||
|
||||
/// Optimized 3x3 matrix multiplication
|
||||
pub fn multiply3x3F32(A: []const f32, B: []const f32, C: []f32) !void {
|
||||
if (A.len < 9 or B.len < 9 or C.len < 9) return error.InvalidSize;
|
||||
try errors.espCheckError(sys.dspm_mult_3x3x3_f32_ae32(A.ptr, B.ptr, C.ptr));
|
||||
}
|
||||
|
||||
/// Optimized 4x4 matrix multiplication
|
||||
pub fn multiply4x4F32(A: []const f32, B: []const f32, C: []f32) !void {
|
||||
if (A.len < 16 or B.len < 16 or C.len < 16) return error.InvalidSize;
|
||||
try errors.espCheckError(sys.dspm_mult_4x4x4_f32_ae32(A.ptr, B.ptr, C.ptr));
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Window functions
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const Window = struct {
|
||||
pub fn hann(window: []f32) void {
|
||||
sys.dsps_wind_hann_f32(window.ptr, @intCast(window.len));
|
||||
}
|
||||
|
||||
pub fn blackman(window: []f32) void {
|
||||
sys.dsps_wind_blackman_f32(window.ptr, @intCast(window.len));
|
||||
}
|
||||
|
||||
pub fn blackmanHarris(window: []f32) void {
|
||||
sys.dsps_wind_blackman_harris_f32(window.ptr, @intCast(window.len));
|
||||
}
|
||||
|
||||
pub fn blackmanNuttall(window: []f32) void {
|
||||
sys.dsps_wind_blackman_nuttall_f32(window.ptr, @intCast(window.len));
|
||||
}
|
||||
|
||||
pub fn nuttall(window: []f32) void {
|
||||
sys.dsps_wind_nuttall_f32(window.ptr, @intCast(window.len));
|
||||
}
|
||||
|
||||
pub fn flatTop(window: []f32) void {
|
||||
sys.dsps_wind_flat_top_f32(window.ptr, @intCast(window.len));
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Biquad IIR filter
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const BiquadFilterType = enum {
|
||||
lowpass,
|
||||
highpass,
|
||||
bandpass,
|
||||
bandpass_0db,
|
||||
notch,
|
||||
allpass_360,
|
||||
allpass_180,
|
||||
peaking_eq,
|
||||
low_shelf,
|
||||
high_shelf,
|
||||
};
|
||||
|
||||
pub const BiquadFilter = struct {
|
||||
coeffs: [5]f32,
|
||||
state: [2]f32,
|
||||
|
||||
pub fn init(filter_type: BiquadFilterType, freq: f32, q_factor: f32, gain: f32) !BiquadFilter {
|
||||
var coeffs: [5]f32 = undefined;
|
||||
|
||||
const result = switch (filter_type) {
|
||||
.lowpass => sys.dsps_biquad_gen_lpf_f32(&coeffs, freq, q_factor),
|
||||
.highpass => sys.dsps_biquad_gen_hpf_f32(&coeffs, freq, q_factor),
|
||||
.bandpass => sys.dsps_biquad_gen_bpf_f32(&coeffs, freq, q_factor),
|
||||
.bandpass_0db => sys.dsps_biquad_gen_bpf0db_f32(&coeffs, freq, q_factor),
|
||||
.notch => sys.dsps_biquad_gen_notch_f32(&coeffs, freq, gain, q_factor),
|
||||
.allpass_360 => sys.dsps_biquad_gen_allpass360_f32(&coeffs, freq, q_factor),
|
||||
.allpass_180 => sys.dsps_biquad_gen_allpass180_f32(&coeffs, freq, q_factor),
|
||||
.peaking_eq => sys.dsps_biquad_gen_peakingEQ_f32(&coeffs, freq, q_factor),
|
||||
.low_shelf => sys.dsps_biquad_gen_lowShelf_f32(&coeffs, freq, gain, q_factor),
|
||||
.high_shelf => sys.dsps_biquad_gen_highShelf_f32(&coeffs, freq, gain, q_factor),
|
||||
};
|
||||
|
||||
try errors.espCheckError(result);
|
||||
|
||||
return .{ .coeffs = coeffs, .state = std.mem.zeroes([2]f32) };
|
||||
}
|
||||
|
||||
pub fn process(self: *BiquadFilter, input: []const f32, output: []f32) !void {
|
||||
if (input.len != output.len) return error.LengthMismatch;
|
||||
try errors.espCheckError(sys.dsps_biquad_f32_aes3(input.ptr, output.ptr, @intCast(input.len), &self.coeffs, &self.state));
|
||||
}
|
||||
|
||||
pub fn reset(self: *BiquadFilter) void {
|
||||
self.state = std.mem.zeroes([2]f32);
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Convolution and correlation
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const Signal = struct {
|
||||
/// Convolution: convout = Signal ⊛ Kernel
|
||||
pub fn convolveF32(signal: []const f32, kernel: []const f32, output: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_conv_f32_ae32(signal.ptr, @intCast(signal.len), kernel.ptr, @intCast(kernel.len), output.ptr));
|
||||
}
|
||||
|
||||
/// Cross-correlation: dest = Signal ⋆ Pattern
|
||||
pub fn correlateF32(signal: []const f32, pattern: []const f32, output: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_corr_f32_ae32(signal.ptr, @intCast(signal.len), pattern.ptr, @intCast(pattern.len), output.ptr));
|
||||
}
|
||||
|
||||
/// Generate tone: output = Ampl * sin(2π * freq * t + phase)
|
||||
pub fn generateToneF32(output: []f32, amplitude: f32, frequency: f32, phase: f32) !void {
|
||||
try errors.espCheckError(sys.dsps_tone_gen_f32(output.ptr, @intCast(output.len), amplitude, frequency, phase));
|
||||
}
|
||||
|
||||
/// Calculate Signal-to-Noise Ratio
|
||||
pub fn snrF32(input: []const f32, use_dc: bool) f32 {
|
||||
return sys.dsps_snr_f32(input.ptr, @intCast(input.len), if (use_dc) 1 else 0);
|
||||
}
|
||||
|
||||
/// Calculate Spurious-Free Dynamic Range
|
||||
pub fn sfdrF32(input: []const f32, use_dc: bool) f32 {
|
||||
return sys.dsps_sfdr_f32(input.ptr, @intCast(input.len), if (use_dc) 1 else 0);
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// DCT (Discrete Cosine Transform)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const DCT = struct {
|
||||
/// Forward DCT (in-place)
|
||||
pub fn forwardF32(data: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_dct_f32(data.ptr, @intCast(data.len)));
|
||||
}
|
||||
|
||||
/// Inverse DCT (in-place)
|
||||
pub fn inverseF32(data: []f32) !void {
|
||||
try errors.espCheckError(sys.dsps_dct_inv_f32(data.ptr, @intCast(data.len)));
|
||||
}
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Utility functions
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
pub const Utils = struct {
|
||||
/// Check if a number is a power of two
|
||||
pub fn isPowerOfTwo(x: i32) bool {
|
||||
return sys.dsp_is_power_of_two(x);
|
||||
}
|
||||
|
||||
/// Get the power of two value (e.g., 3 → 8, 4 → 16)
|
||||
pub fn powerOfTwo(x: i32) i32 {
|
||||
return sys.dsp_power_of_two(x);
|
||||
}
|
||||
|
||||
/// Visualize signal data (for debugging)
|
||||
pub fn viewSignalF32(data: []const f32, width: u32, height: u32, min: f32, max: f32) void {
|
||||
sys.dsps_view(data.ptr, @intCast(data.len), @intCast(width), @intCast(height), min, max, '*');
|
||||
}
|
||||
|
||||
/// Visualize spectrum data (for debugging)
|
||||
pub fn viewSpectrumF32(data: []const f32, min: f32, max: f32) void {
|
||||
sys.dsps_view_spectrum(data.ptr, @intCast(data.len), min, max);
|
||||
}
|
||||
};
|
||||
58
software/zig_main/imports/error.zig
Normal file
58
software/zig_main/imports/error.zig
Normal file
@@ -0,0 +1,58 @@
|
||||
const sys = @import("sys");
|
||||
const std = @import("std");
|
||||
|
||||
// Zig error
|
||||
const esp_error = error{
|
||||
Fail,
|
||||
ErrorNoMem,
|
||||
ErrorInvalidArg,
|
||||
ErrorInvalidState,
|
||||
ErrorInvalidSize,
|
||||
ErrorNotFound,
|
||||
ErrorNotSupported,
|
||||
ErrorTimeout,
|
||||
ErrorInvalidResponse,
|
||||
ErrorInvalidCRC,
|
||||
ErrorInvalidVersion,
|
||||
ErrorInvalidMAC,
|
||||
ErrorNotFinished,
|
||||
ErrorNotAllowed,
|
||||
ErrorWiFiBase,
|
||||
ErrorMeshBase,
|
||||
ErrorFlashBase,
|
||||
ErrorHWCryptoBase,
|
||||
ErrorMemProtectBase,
|
||||
};
|
||||
|
||||
// C to Zig error
|
||||
pub fn espError(err: sys.esp_err_t) esp_error!sys.esp_err_t {
|
||||
return switch (@as(sys.esp_err_t, err)) {
|
||||
@as(sys.esp_err_t, sys.ESP_FAIL) => esp_error.Fail,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_NO_MEM) => esp_error.ErrorNoMem,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_ARG) => esp_error.ErrorInvalidArg,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_STATE) => esp_error.ErrorInvalidState,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_SIZE) => esp_error.ErrorInvalidSize,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_NOT_FOUND) => esp_error.ErrorNotFound,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_NOT_SUPPORTED) => esp_error.ErrorNotSupported,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_TIMEOUT) => esp_error.ErrorTimeout,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_RESPONSE) => esp_error.ErrorInvalidResponse,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_CRC) => esp_error.ErrorInvalidCRC,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_VERSION) => esp_error.ErrorInvalidVersion,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_INVALID_MAC) => esp_error.ErrorInvalidMAC,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_NOT_FINISHED) => esp_error.ErrorNotFinished,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_NOT_ALLOWED) => esp_error.ErrorNotAllowed,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_WIFI_BASE) => esp_error.ErrorWiFiBase,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_MESH_BASE) => esp_error.ErrorMeshBase,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_FLASH_BASE) => esp_error.ErrorFlashBase,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_HW_CRYPTO_BASE) => esp_error.ErrorHWCryptoBase,
|
||||
@as(sys.esp_err_t, sys.ESP_ERR_MEMPROT_BASE) => esp_error.ErrorMemProtectBase,
|
||||
else => err, // Return the original `sys.esp_err_t` if it's not mapped
|
||||
};
|
||||
}
|
||||
|
||||
pub fn espCheckError(errc: sys.esp_err_t) esp_error!void {
|
||||
if (errc == @as(sys.esp_err_t, sys.ESP_OK)) return;
|
||||
// Try to surface a specific error variant; unknown non-zero codes become Fail.
|
||||
_ = try espError(errc);
|
||||
return error.Fail;
|
||||
}
|
||||
202
software/zig_main/imports/event.zig
Normal file
202
software/zig_main/imports/event.zig
Normal file
@@ -0,0 +1,202 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Base = sys.esp_event_base_t;
|
||||
pub const LoopHandle = sys.esp_event_loop_handle_t;
|
||||
pub const HandlerT = sys.esp_event_handler_t;
|
||||
pub const HandlerInstance = sys.esp_event_handler_instance_t;
|
||||
pub const LoopArgs = sys.esp_event_loop_args_t;
|
||||
|
||||
/// Match any event base when registering handlers.
|
||||
pub const ANY_BASE: Base = sys.ESP_EVENT_ANY_BASE;
|
||||
/// Match any event ID when registering handlers.
|
||||
pub const ANY_ID: i32 = sys.ESP_EVENT_ANY_ID;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Default event loop
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Create the default event loop. Must be called once before posting events
|
||||
/// or registering handlers on the default loop.
|
||||
pub fn loopCreateDefault() !void {
|
||||
try errors.espCheckError(sys.esp_event_loop_create_default());
|
||||
}
|
||||
|
||||
/// Delete the default event loop.
|
||||
pub fn loopDeleteDefault() !void {
|
||||
try errors.espCheckError(sys.esp_event_loop_delete_default());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Custom event loop
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Create a custom event loop with the given configuration.
|
||||
pub fn loopCreate(args: *const LoopArgs) !LoopHandle {
|
||||
var loop: LoopHandle = null;
|
||||
try errors.espCheckError(sys.esp_event_loop_create(args, &loop));
|
||||
return loop;
|
||||
}
|
||||
|
||||
/// Delete a custom event loop.
|
||||
pub fn loopDelete(loop: LoopHandle) !void {
|
||||
try errors.espCheckError(sys.esp_event_loop_delete(loop));
|
||||
}
|
||||
|
||||
/// Run a custom event loop for up to `ticks_to_run` ticks.
|
||||
pub fn loopRun(loop: LoopHandle, ticks_to_run: sys.TickType_t) !void {
|
||||
try errors.espCheckError(sys.esp_event_loop_run(loop, ticks_to_run));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handler registration — default loop
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Register an event handler on the default event loop.
|
||||
///
|
||||
/// Pass `ANY_BASE` / `ANY_ID` as wildcards.
|
||||
pub fn handlerRegister(
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
handler: HandlerT,
|
||||
arg: ?*anyopaque,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_handler_register(event_base, event_id, handler, arg));
|
||||
}
|
||||
|
||||
/// Unregister a previously registered handler from the default loop.
|
||||
pub fn handlerUnregister(
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
handler: HandlerT,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_handler_unregister(event_base, event_id, handler));
|
||||
}
|
||||
|
||||
/// Register an instance-based handler on the default loop.
|
||||
/// Returns an `HandlerInstance` token usable with `handlerInstanceUnregister`.
|
||||
pub fn handlerInstanceRegister(
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
handler: HandlerT,
|
||||
arg: ?*anyopaque,
|
||||
) !HandlerInstance {
|
||||
var instance: HandlerInstance = null;
|
||||
try errors.espCheckError(sys.esp_event_handler_instance_register(event_base, event_id, handler, arg, &instance));
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// Unregister an instance-based handler from the default loop.
|
||||
pub fn handlerInstanceUnregister(
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
instance: HandlerInstance,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_handler_instance_unregister(event_base, event_id, instance));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handler registration — custom loop
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Register a handler on a custom event loop.
|
||||
pub fn handlerRegisterWith(
|
||||
loop: LoopHandle,
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
handler: HandlerT,
|
||||
arg: ?*anyopaque,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_handler_register_with(loop, event_base, event_id, handler, arg));
|
||||
}
|
||||
|
||||
/// Unregister a handler from a custom event loop.
|
||||
pub fn handlerUnregisterWith(
|
||||
loop: LoopHandle,
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
handler: HandlerT,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_handler_unregister_with(loop, event_base, event_id, handler));
|
||||
}
|
||||
|
||||
/// Register an instance-based handler on a custom event loop.
|
||||
pub fn handlerInstanceRegisterWith(
|
||||
loop: LoopHandle,
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
handler: HandlerT,
|
||||
arg: ?*anyopaque,
|
||||
) !HandlerInstance {
|
||||
var instance: HandlerInstance = null;
|
||||
try errors.espCheckError(sys.esp_event_handler_instance_register_with(loop, event_base, event_id, handler, arg, &instance));
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// Unregister an instance-based handler from a custom event loop.
|
||||
pub fn handlerInstanceUnregisterWith(
|
||||
loop: LoopHandle,
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
instance: HandlerInstance,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_handler_instance_unregister_with(loop, event_base, event_id, instance));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Event posting
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Post an event to the default event loop.
|
||||
///
|
||||
/// `event_data` may be null when `event_data_size` is 0.
|
||||
/// `ticks_to_wait` is how long to block if the queue is full.
|
||||
pub fn post(
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
event_data: ?*const anyopaque,
|
||||
event_data_size: usize,
|
||||
ticks_to_wait: sys.TickType_t,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_post(event_base, event_id, event_data, event_data_size, ticks_to_wait));
|
||||
}
|
||||
|
||||
/// Post an event to a custom event loop.
|
||||
pub fn postTo(
|
||||
loop: LoopHandle,
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
event_data: ?*const anyopaque,
|
||||
event_data_size: usize,
|
||||
ticks_to_wait: sys.TickType_t,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_post_to(loop, event_base, event_id, event_data, event_data_size, ticks_to_wait));
|
||||
}
|
||||
|
||||
/// Post an event from an ISR to the default event loop.
|
||||
/// Sets `*task_unblocked` to true if a higher-priority task was unblocked.
|
||||
pub fn isrPost(
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
event_data: ?*const anyopaque,
|
||||
event_data_size: usize,
|
||||
task_unblocked: *sys.BaseType_t,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_isr_post(event_base, event_id, event_data, event_data_size, task_unblocked));
|
||||
}
|
||||
|
||||
/// Post an event from an ISR to a custom event loop.
|
||||
pub fn isrPostTo(
|
||||
loop: LoopHandle,
|
||||
event_base: Base,
|
||||
event_id: i32,
|
||||
event_data: ?*const anyopaque,
|
||||
event_data_size: usize,
|
||||
task_unblocked: *sys.BaseType_t,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_event_isr_post_to(loop, event_base, event_id, event_data, event_data_size, task_unblocked));
|
||||
}
|
||||
383
software/zig_main/imports/gpio.zig
Normal file
383
software/zig_main/imports/gpio.zig
Normal file
@@ -0,0 +1,383 @@
|
||||
const sys = @import("sys");
|
||||
const std = @import("std");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Num — generated enum containing only pins that exist on the current target.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn Num() type {
|
||||
comptime var names: []const []const u8 = &.{};
|
||||
comptime var values: []const sys.gpio_num_t = &.{};
|
||||
|
||||
// Always-present sentinels.
|
||||
names = names ++ &[_][]const u8{ "NC", "MAX" };
|
||||
values = values ++ &[_]sys.gpio_num_t{
|
||||
@intCast(sys.GPIO_NUM_NC),
|
||||
@intCast(sys.GPIO_NUM_MAX),
|
||||
};
|
||||
|
||||
// Add only the pins the current target actually declares in sys.
|
||||
inline for (0..49) |n| {
|
||||
@setEvalBranchQuota(200000);
|
||||
const decl = std.fmt.comptimePrint("GPIO_NUM_{d}", .{n});
|
||||
if (@hasDecl(sys, decl)) {
|
||||
names = names ++ &[_][]const u8{std.fmt.comptimePrint("{d}", .{n})};
|
||||
values = values ++ &[_]sys.gpio_num_t{@intCast(@field(sys, decl))};
|
||||
}
|
||||
}
|
||||
|
||||
// @Enum(TagInt, mode, field_names, field_values)
|
||||
return @Enum(
|
||||
sys.gpio_num_t,
|
||||
.exhaustive,
|
||||
names,
|
||||
values[0..],
|
||||
);
|
||||
}
|
||||
|
||||
/// The GPIO pin enum for the current target.
|
||||
/// Only pins declared by the BSP/sys module are present as fields.
|
||||
/// Referencing a missing pin (e.g. `.@"22"` on ESP32-C3) is a compile error.
|
||||
pub const GpioNum = Num();
|
||||
|
||||
/// Convert a GpioNum to the raw C type expected by esp-idf APIs.
|
||||
pub inline fn numToC(gpio_num: GpioNum) sys.gpio_num_t {
|
||||
return @intFromEnum(gpio_num);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Other enumerations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Port = enum(sys.gpio_port_t) {
|
||||
GPIO_PORT_0 = sys.GPIO_PORT_0,
|
||||
GPIO_PORT_MAX = sys.GPIO_PORT_MAX,
|
||||
};
|
||||
|
||||
pub const IntType = enum(sys.gpio_int_type_t) {
|
||||
disable = sys.GPIO_INTR_DISABLE,
|
||||
posedge = sys.GPIO_INTR_POSEDGE,
|
||||
negedge = sys.GPIO_INTR_NEGEDGE,
|
||||
anyedge = sys.GPIO_INTR_ANYEDGE,
|
||||
low_level = sys.GPIO_INTR_LOW_LEVEL,
|
||||
high_level = sys.GPIO_INTR_HIGH_LEVEL,
|
||||
max = sys.GPIO_INTR_MAX,
|
||||
};
|
||||
|
||||
pub const Mode = enum(sys.gpio_mode_t) {
|
||||
disable = sys.GPIO_MODE_DISABLE,
|
||||
input = sys.GPIO_MODE_INPUT,
|
||||
output = sys.GPIO_MODE_OUTPUT,
|
||||
output_od = sys.GPIO_MODE_OUTPUT_OD,
|
||||
input_output_od = sys.GPIO_MODE_INPUT_OUTPUT_OD,
|
||||
input_output = sys.GPIO_MODE_INPUT_OUTPUT,
|
||||
};
|
||||
|
||||
pub const Pullup = enum(sys.gpio_pullup_t) {
|
||||
disable = sys.GPIO_PULLUP_DISABLE,
|
||||
enable = sys.GPIO_PULLUP_ENABLE,
|
||||
};
|
||||
|
||||
pub const Pulldown = enum(sys.gpio_pulldown_t) {
|
||||
disable = sys.GPIO_PULLDOWN_DISABLE,
|
||||
enable = sys.GPIO_PULLDOWN_ENABLE,
|
||||
};
|
||||
|
||||
pub const PullMode = enum(sys.gpio_pull_mode_t) {
|
||||
pullup_only = sys.GPIO_PULLUP_ONLY,
|
||||
pulldown_only = sys.GPIO_PULLDOWN_ONLY,
|
||||
pullup_pulldown = sys.GPIO_PULLUP_PULLDOWN,
|
||||
floating = sys.GPIO_FLOATING,
|
||||
};
|
||||
|
||||
pub const DriveCap = enum(sys.gpio_drive_cap_t) {
|
||||
cap_0 = sys.GPIO_DRIVE_CAP_0,
|
||||
cap_1 = sys.GPIO_DRIVE_CAP_1,
|
||||
cap_2 = sys.GPIO_DRIVE_CAP_2,
|
||||
default = sys.GPIO_DRIVE_CAP_DEFAULT,
|
||||
cap_3 = sys.GPIO_DRIVE_CAP_3,
|
||||
max = sys.GPIO_DRIVE_CAP_MAX,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn config(cfg: [*c]const sys.gpio_config_t) !void {
|
||||
try errors.espCheckError(sys.gpio_config(cfg));
|
||||
}
|
||||
|
||||
pub fn resetPin(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_reset_pin(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Interrupts
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn setIntrType(gpio_num: GpioNum, intr_type: IntType) !void {
|
||||
try errors.espCheckError(sys.gpio_set_intr_type(numToC(gpio_num), @intFromEnum(intr_type)));
|
||||
}
|
||||
|
||||
pub fn intrEnable(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_intr_enable(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn intrDisable(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_intr_disable(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn installISRService(intr_alloc_flags: c_int) !void {
|
||||
try errors.espCheckError(sys.gpio_install_isr_service(intr_alloc_flags));
|
||||
}
|
||||
|
||||
pub fn uninstallISRService() void {
|
||||
sys.gpio_uninstall_isr_service();
|
||||
}
|
||||
|
||||
pub fn isrRegister(
|
||||
handler: ?*const fn (?*anyopaque) callconv(.c) void,
|
||||
arg: ?*anyopaque,
|
||||
intr_alloc_flags: c_int,
|
||||
handle: [*c]sys.gpio_isr_handle_t,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.gpio_isr_register(handler, arg, intr_alloc_flags, handle));
|
||||
}
|
||||
|
||||
pub fn isrHandlerAdd(gpio_num: GpioNum, isr_handler: sys.gpio_isr_t, args: ?*anyopaque) !void {
|
||||
try errors.espCheckError(sys.gpio_isr_handler_add(numToC(gpio_num), isr_handler, args));
|
||||
}
|
||||
|
||||
pub fn isrHandlerRemove(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_isr_handler_remove(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Level / Direction
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Level = struct {
|
||||
pub fn set(gpio_num: GpioNum, level: u32) !void {
|
||||
try errors.espCheckError(sys.gpio_set_level(numToC(gpio_num), level));
|
||||
}
|
||||
/// Returns true if the pin is high, false if low.
|
||||
pub fn get(gpio_num: GpioNum) bool {
|
||||
return sys.gpio_get_level(numToC(gpio_num)) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Direction = struct {
|
||||
pub fn set(gpio_num: GpioNum, mode: Mode) !void {
|
||||
try errors.espCheckError(sys.gpio_set_direction(numToC(gpio_num), @intFromEnum(mode)));
|
||||
}
|
||||
pub fn sleepSet(gpio_num: GpioNum, mode: Mode) !void {
|
||||
try errors.espCheckError(sys.gpio_sleep_set_direction(numToC(gpio_num), @intFromEnum(mode)));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Pull resistors
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn setPullMode(gpio_num: GpioNum, pull: PullMode) !void {
|
||||
try errors.espCheckError(sys.gpio_set_pull_mode(numToC(gpio_num), @intFromEnum(pull)));
|
||||
}
|
||||
|
||||
pub fn sleepSetPullMode(gpio_num: GpioNum, pull: PullMode) !void {
|
||||
try errors.espCheckError(sys.gpio_sleep_set_pull_mode(numToC(gpio_num), @intFromEnum(pull)));
|
||||
}
|
||||
|
||||
pub const PULL = struct {
|
||||
pub fn upEn(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_pullup_en(numToC(gpio_num)));
|
||||
}
|
||||
pub fn upDis(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_pullup_dis(numToC(gpio_num)));
|
||||
}
|
||||
pub fn downEn(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_pulldown_en(numToC(gpio_num)));
|
||||
}
|
||||
pub fn downDis(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_pulldown_dis(numToC(gpio_num)));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Drive strength
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn setDriveCapability(gpio_num: GpioNum, strength: DriveCap) !void {
|
||||
try errors.espCheckError(sys.gpio_set_drive_capability(numToC(gpio_num), @intFromEnum(strength)));
|
||||
}
|
||||
|
||||
/// Returns the drive capability of the given pin.
|
||||
pub fn getDriveCapability(gpio_num: GpioNum) !DriveCap {
|
||||
var raw: sys.gpio_drive_cap_t = undefined;
|
||||
try errors.espCheckError(sys.gpio_get_drive_capability(numToC(gpio_num), &raw));
|
||||
return @enumFromInt(raw);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Hold / deep-sleep
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn holdEn(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_hold_en(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn holdDis(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_hold_dis(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn forceHoldAll() !void {
|
||||
try errors.espCheckError(sys.gpio_force_hold_all());
|
||||
}
|
||||
|
||||
pub fn forceUnholdAll() !void {
|
||||
try errors.espCheckError(sys.gpio_force_unhold_all());
|
||||
}
|
||||
|
||||
pub fn deepSleepHoldEn() void {
|
||||
sys.gpio_deep_sleep_hold_en();
|
||||
}
|
||||
|
||||
pub fn deepSleepHoldDis() void {
|
||||
sys.gpio_deep_sleep_hold_dis();
|
||||
}
|
||||
|
||||
pub fn deepSleepWakeupEnable(gpio_num: GpioNum, intr_type: IntType) !void {
|
||||
try errors.espCheckError(sys.gpio_deep_sleep_wakeup_enable(numToC(gpio_num), @intFromEnum(intr_type)));
|
||||
}
|
||||
|
||||
pub fn deepSleepWakeupDisable(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_deep_sleep_wakeup_disable(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Sleep select
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn sleepSelEn(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_sleep_sel_en(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn sleepSelDis(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_sleep_sel_dis(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Wakeup
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn wakeupEnable(gpio_num: GpioNum, intr_type: IntType) !void {
|
||||
try errors.espCheckError(sys.gpio_wakeup_enable(numToC(gpio_num), @intFromEnum(intr_type)));
|
||||
}
|
||||
|
||||
pub fn wakeupDisable(gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_wakeup_disable(numToC(gpio_num)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IOMUX
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn iomuxIn(gpio_num: GpioNum, signal_idx: u32) void {
|
||||
sys.gpio_iomux_in(numToC(gpio_num), signal_idx);
|
||||
}
|
||||
|
||||
pub fn iomuxOut(gpio_num: GpioNum, func: c_int, oen_inv: bool) void {
|
||||
sys.gpio_iomux_out(numToC(gpio_num), func, oen_inv);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Debug
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn dumpIOConfiguration(out_stream: ?*std.c.FILE, io_bit_mask: u64) !void {
|
||||
try errors.espCheckError(sys.gpio_dump_io_configuration(out_stream, io_bit_mask));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ROM helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const ROM = struct {
|
||||
pub fn padSelectGPIO(iopad_num: u32) void {
|
||||
sys.esp_rom_gpio_pad_select_gpio(iopad_num);
|
||||
}
|
||||
pub fn padPullupOnly(iopad_num: u32) void {
|
||||
sys.esp_rom_gpio_pad_pullup_only(iopad_num);
|
||||
}
|
||||
pub fn padUnhold(gpio_num: GpioNum) void {
|
||||
sys.esp_rom_gpio_pad_unhold(numToC(gpio_num));
|
||||
}
|
||||
pub fn padSetDrive(iopad_num: u32, drv: u32) void {
|
||||
sys.esp_rom_gpio_pad_set_drv(iopad_num, drv);
|
||||
}
|
||||
pub fn connectInSignal(gpio_num: GpioNum, signal_idx: u32, inv: bool) void {
|
||||
sys.esp_rom_gpio_connect_in_signal(numToC(gpio_num), signal_idx, inv);
|
||||
}
|
||||
pub fn connectOutSignal(gpio_num: GpioNum, signal_idx: u32, out_inv: bool, oen_inv: bool) void {
|
||||
sys.esp_rom_gpio_connect_out_signal(numToC(gpio_num), signal_idx, out_inv, oen_inv);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ETM (Event Task Matrix)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const ETM = struct {
|
||||
pub fn newChannel(cfg: [*c]const sys.esp_etm_channel_config_t, ret_chan: [*c]sys.esp_etm_channel_handle_t) !void {
|
||||
try errors.espCheckError(sys.esp_etm_new_channel(cfg, ret_chan));
|
||||
}
|
||||
pub fn delChannel(chan: sys.esp_etm_channel_handle_t) !void {
|
||||
try errors.espCheckError(sys.esp_etm_del_channel(chan));
|
||||
}
|
||||
pub fn channelEnable(chan: sys.esp_etm_channel_handle_t) !void {
|
||||
try errors.espCheckError(sys.esp_etm_channel_enable(chan));
|
||||
}
|
||||
pub fn channelDisable(chan: sys.esp_etm_channel_handle_t) !void {
|
||||
try errors.espCheckError(sys.esp_etm_channel_disable(chan));
|
||||
}
|
||||
pub fn channelConnect(
|
||||
chan: sys.esp_etm_channel_handle_t,
|
||||
event: sys.esp_etm_event_handle_t,
|
||||
task: sys.esp_etm_task_handle_t,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_etm_channel_connect(chan, event, task));
|
||||
}
|
||||
pub fn delEvent(event: sys.esp_etm_event_handle_t) !void {
|
||||
try errors.espCheckError(sys.esp_etm_del_event(event));
|
||||
}
|
||||
pub fn delTask(task: sys.esp_etm_task_handle_t) !void {
|
||||
try errors.espCheckError(sys.esp_etm_del_task(task));
|
||||
}
|
||||
pub fn dump(out_stream: ?*std.c.FILE) !void {
|
||||
try errors.espCheckError(sys.esp_etm_dump(out_stream));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GPIO ETM event/task binding
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn newEtmEvent(cfg: [*c]const sys.gpio_etm_event_config_t, ret_event: [*c]sys.esp_etm_event_handle_t) !void {
|
||||
try errors.espCheckError(sys.gpio_new_etm_event(cfg, ret_event));
|
||||
}
|
||||
|
||||
pub fn etmEventBindGPIO(event: sys.esp_etm_event_handle_t, gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_etm_event_bind_gpio(event, numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn newEtmTask(cfg: [*c]const sys.gpio_etm_task_config_t, ret_task: [*c]sys.esp_etm_task_handle_t) !void {
|
||||
try errors.espCheckError(sys.gpio_new_etm_task(cfg, ret_task));
|
||||
}
|
||||
|
||||
pub fn etmTaskAddGPIO(task: sys.esp_etm_task_handle_t, gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_etm_task_add_gpio(task, numToC(gpio_num)));
|
||||
}
|
||||
|
||||
pub fn etmTaskRemoveGPIO(task: sys.esp_etm_task_handle_t, gpio_num: GpioNum) !void {
|
||||
try errors.espCheckError(sys.gpio_etm_task_rm_gpio(task, numToC(gpio_num)));
|
||||
}
|
||||
326
software/zig_main/imports/heap.zig
Normal file
326
software/zig_main/imports/heap.zig
Normal file
@@ -0,0 +1,326 @@
|
||||
const sys = @import("sys");
|
||||
const std = @import("std");
|
||||
const errors = @import("error");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
// read: https://github.com/espressif/esp-idf/blob/97d95853572ab74f4769597496af9d5fe8b6bdea/components/heap/include/esp_heap_caps.h#L29-L53
|
||||
// ---------------------------------------------------------------------------
|
||||
// Caps — packed struct matching esp_heap_caps.h bit positions exactly.
|
||||
//
|
||||
// Bit layout (matches MALLOC_CAP_* defines):
|
||||
// 0 exec (only when CONFIG_HEAP_HAS_EXEC_HEAP)
|
||||
// 1 32bit
|
||||
// 2 8bit
|
||||
// 3 dma
|
||||
// 4- 9 pid2..pid7
|
||||
// 10 spiram
|
||||
// 11 internal
|
||||
// 12 default
|
||||
// 13 iram_8bit
|
||||
// 14 retention
|
||||
// 15 rtcram
|
||||
// 16 tcm
|
||||
// 17 dma_desc_ahb
|
||||
// 18 dma_desc_axi
|
||||
// 19 cache_aligned
|
||||
// 20 simd
|
||||
// 21-30 (reserved)
|
||||
// 31 invalid
|
||||
// ---------------------------------------------------------------------------
|
||||
pub const Caps = packed struct(u32) {
|
||||
exec: bool = false, // bit 0 — requires CONFIG_HEAP_HAS_EXEC_HEAP
|
||||
@"32bit": bool = false, // bit 1
|
||||
@"8bit": bool = false, // bit 2
|
||||
dma: bool = false, // bit 3
|
||||
pid2: bool = false, // bit 4
|
||||
pid3: bool = false, // bit 5
|
||||
pid4: bool = false, // bit 6
|
||||
pid5: bool = false, // bit 7
|
||||
pid6: bool = false, // bit 8
|
||||
pid7: bool = false, // bit 9
|
||||
spiram: bool = false, // bit 10
|
||||
internal: bool = false, // bit 11
|
||||
default: bool = false, // bit 12
|
||||
iram_8bit: bool = false, // bit 13
|
||||
retention: bool = false, // bit 14
|
||||
rtcram: bool = false, // bit 15
|
||||
tcm: bool = false, // bit 16
|
||||
dma_desc_ahb: bool = false, // bit 17
|
||||
dma_desc_axi: bool = false, // bit 18
|
||||
cache_aligned: bool = false, // bit 19
|
||||
simd: bool = false, // bit 20
|
||||
_reserved: u10 = 0, // bits 21-30
|
||||
invalid: bool = false, // bit 31
|
||||
|
||||
/// Cast to the raw u32 value the heap_caps_* C functions expect.
|
||||
pub fn toRaw(self: Caps) u32 {
|
||||
return @bitCast(self);
|
||||
}
|
||||
|
||||
/// Re-hydrate from a raw C bitmask (e.g. value returned by a C API).
|
||||
pub fn fromRaw(raw: u32) Caps {
|
||||
return @bitCast(raw);
|
||||
}
|
||||
|
||||
// -- Named presets matching common ESP-IDF usage patterns ---------------
|
||||
|
||||
/// General-purpose heap (equivalent to malloc/calloc).
|
||||
pub const default_caps: Caps = .{ .default = true };
|
||||
/// Internal RAM, byte-addressable.
|
||||
pub const internal_caps: Caps = .{ .internal = true, .@"8bit" = true };
|
||||
/// DMA-capable internal RAM.
|
||||
pub const dma_caps: Caps = .{ .dma = true, .@"8bit" = true, .internal = true };
|
||||
/// External SPI RAM, byte-addressable.
|
||||
pub const spiram_caps: Caps = .{ .spiram = true, .@"8bit" = true };
|
||||
/// RTC fast memory (survives deep sleep).
|
||||
pub const rtcram_caps: Caps = .{ .rtcram = true };
|
||||
/// Tightly-coupled memory.
|
||||
pub const tcm_caps: Caps = .{ .tcm = true };
|
||||
/// Executable memory (requires CONFIG_HEAP_HAS_EXEC_HEAP).
|
||||
pub const exec_caps: Caps = .{ .exec = true };
|
||||
/// Cache-line aligned memory.
|
||||
pub const cache_aligned_caps: Caps = .{ .cache_aligned = true, .default = true };
|
||||
};
|
||||
|
||||
// Verify the bit layout matches the C header at compile time.
|
||||
comptime {
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .exec = true })) == (1 << 0));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .@"32bit" = true })) == (1 << 1));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .@"8bit" = true })) == (1 << 2));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .dma = true })) == (1 << 3));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .spiram = true })) == (1 << 10));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .internal = true })) == (1 << 11));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .default = true })) == (1 << 12));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .simd = true })) == (1 << 20));
|
||||
std.debug.assert(@as(u32, @bitCast(Caps{ .invalid = true })) == (1 << 31));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HeapCapsAllocator
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const HeapCapsAllocator = struct {
|
||||
caps: Caps = Caps.default_caps,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(caps: ?Caps) Self {
|
||||
return .{ .caps = caps orelse Caps.default_caps };
|
||||
}
|
||||
|
||||
pub fn allocator(self: *Self) std.mem.Allocator {
|
||||
return .{
|
||||
.ptr = self,
|
||||
.vtable = &.{
|
||||
.alloc = alloc,
|
||||
.resize = resize,
|
||||
.remap = remap,
|
||||
.free = free,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dump(self: Self) void {
|
||||
sys.heap_caps_dump(self.caps.toRaw());
|
||||
}
|
||||
pub fn allocatedSize(_: Self, ptr: ?*anyopaque) usize {
|
||||
return sys.heap_caps_get_allocated_size(ptr);
|
||||
}
|
||||
pub fn largestFreeBlock(self: Self) usize {
|
||||
return sys.heap_caps_get_largest_free_block(self.caps.toRaw());
|
||||
}
|
||||
pub fn totalSize(self: Self) usize {
|
||||
return sys.heap_caps_get_total_size(self.caps.toRaw());
|
||||
}
|
||||
pub fn freeSize(self: Self) usize {
|
||||
return sys.heap_caps_get_free_size(self.caps.toRaw());
|
||||
}
|
||||
pub fn minimumFreeSize(self: Self) usize {
|
||||
return sys.heap_caps_get_minimum_free_size(self.caps.toRaw());
|
||||
}
|
||||
pub fn internalFreeSize(_: Self) usize {
|
||||
return sys.esp_get_free_internal_heap_size();
|
||||
}
|
||||
|
||||
fn alloc(ctx: *anyopaque, len: usize, alignment: std.mem.Alignment, _: usize) ?[*]u8 {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
return @ptrCast(sys.heap_caps_aligned_alloc(
|
||||
alignment.toByteUnits(),
|
||||
len,
|
||||
self.caps.toRaw(),
|
||||
));
|
||||
}
|
||||
|
||||
fn resize(_: *anyopaque, buf: []u8, _: std.mem.Alignment, new_len: usize, _: usize) bool {
|
||||
if (new_len <= buf.len) return true;
|
||||
if (@TypeOf(sys.heap_caps_get_allocated_size) != void) {
|
||||
if (new_len <= sys.heap_caps_get_allocated_size(buf.ptr)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn remap(ctx: *anyopaque, memory: []u8, _: std.mem.Alignment, new_len: usize, _: usize) ?[*]u8 {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
return @ptrCast(sys.heap_caps_realloc(memory.ptr, new_len, self.caps.toRaw()));
|
||||
}
|
||||
|
||||
fn free(_: *anyopaque, buf: []u8, _: std.mem.Alignment, _: usize) void {
|
||||
sys.heap_caps_free(buf.ptr);
|
||||
if (builtin.mode == .Debug) {
|
||||
if (!sys.heap_caps_check_integrity_all(true))
|
||||
@panic("heap_caps: integrity check failed after free");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MultiHeapAllocator
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const MultiHeapAllocator = struct {
|
||||
handle: sys.multi_heap_handle_t = null,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(handle: sys.multi_heap_handle_t) Self {
|
||||
return .{ .handle = handle };
|
||||
}
|
||||
|
||||
pub fn allocator(self: *Self) std.mem.Allocator {
|
||||
return .{
|
||||
.ptr = self,
|
||||
.vtable = &.{
|
||||
.alloc = alloc,
|
||||
.resize = resize,
|
||||
.remap = remap,
|
||||
.free = free,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn allocatedSize(self: Self, p: ?*anyopaque) usize {
|
||||
return sys.multi_heap_get_allocated_size(self.handle, p);
|
||||
}
|
||||
pub fn freeSize(self: Self) usize {
|
||||
return sys.multi_heap_free_size(self.handle);
|
||||
}
|
||||
pub fn minimumFreeSize(self: Self) usize {
|
||||
return sys.multi_heap_minimum_free_size(self.handle);
|
||||
}
|
||||
|
||||
fn alloc(ctx: *anyopaque, len: usize, _: std.mem.Alignment, _: usize) ?[*]u8 {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
return @ptrCast(sys.multi_heap_malloc(self.handle, len));
|
||||
}
|
||||
|
||||
fn resize(ctx: *anyopaque, buf: []u8, _: std.mem.Alignment, new_len: usize, _: usize) bool {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
if (new_len <= buf.len) return true;
|
||||
if (@TypeOf(sys.multi_heap_get_allocated_size) != void) {
|
||||
if (new_len <= sys.multi_heap_get_allocated_size(self.handle, buf.ptr))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn remap(ctx: *anyopaque, memory: []u8, _: std.mem.Alignment, new_len: usize, _: usize) ?[*]u8 {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
return @ptrCast(sys.multi_heap_realloc(self.handle, memory.ptr, new_len));
|
||||
}
|
||||
|
||||
fn free(ctx: *anyopaque, buf: []u8, _: std.mem.Alignment, _: usize) void {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
sys.multi_heap_free(self.handle, buf.ptr);
|
||||
if (builtin.mode == .Debug) {
|
||||
if (!sys.multi_heap_check(self.handle, true))
|
||||
@panic("multi_heap: integrity check failed after free");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// VPortAllocator
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const VPortAllocator = struct {
|
||||
const Self = @This();
|
||||
|
||||
pub fn init() Self {
|
||||
return .{};
|
||||
}
|
||||
|
||||
pub fn allocator(self: *Self) std.mem.Allocator {
|
||||
return .{
|
||||
.ptr = self,
|
||||
.vtable = &.{
|
||||
.alloc = alloc,
|
||||
.resize = resize,
|
||||
.remap = remap,
|
||||
.free = free,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn freeSize(_: Self) usize {
|
||||
return sys.xPortGetFreeHeapSize();
|
||||
}
|
||||
pub fn minimumFreeSize(_: Self) usize {
|
||||
return sys.xPortGetMinimumEverFreeHeapSize();
|
||||
}
|
||||
|
||||
fn alloc(_: *anyopaque, len: usize, _: std.mem.Alignment, _: usize) ?[*]u8 {
|
||||
return @ptrCast(sys.pvPortMalloc(len));
|
||||
}
|
||||
|
||||
fn resize(_: *anyopaque, buf: []u8, _: std.mem.Alignment, new_len: usize, _: usize) bool {
|
||||
return new_len <= buf.len;
|
||||
}
|
||||
|
||||
fn remap(_: *anyopaque, memory: []u8, _: std.mem.Alignment, new_len: usize, _: usize) ?[*]u8 {
|
||||
const new_ptr = sys.pvPortMalloc(new_len) orelse return null;
|
||||
@memcpy(@as([*]u8, @ptrCast(new_ptr))[0..@min(memory.len, new_len)], memory[0..@min(memory.len, new_len)]);
|
||||
sys.vPortFree(memory.ptr);
|
||||
return @ptrCast(new_ptr);
|
||||
}
|
||||
|
||||
fn free(_: *anyopaque, buf: []u8, _: std.mem.Alignment, _: usize) void {
|
||||
sys.vPortFree(buf.ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TRACE
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const TRACE = struct {
|
||||
pub fn initStandalone(record_buffer: [*c]sys.heap_trace_record_t, num_records: usize) !void {
|
||||
try errors.espCheckError(sys.heap_trace_init_standalone(record_buffer, num_records));
|
||||
}
|
||||
pub fn initTohost() !void {
|
||||
try errors.espCheckError(sys.heap_trace_init_tohost());
|
||||
}
|
||||
pub fn start(mode: sys.heap_trace_mode_t) !void {
|
||||
try errors.espCheckError(sys.heap_trace_start(mode));
|
||||
}
|
||||
pub fn stop() !void {
|
||||
try errors.espCheckError(sys.heap_trace_stop());
|
||||
}
|
||||
pub fn @"resume"() !void {
|
||||
try errors.espCheckError(sys.heap_trace_resume());
|
||||
}
|
||||
pub fn getCount() usize {
|
||||
return sys.heap_trace_get_count();
|
||||
}
|
||||
pub fn get(index: usize, record: [*c]sys.heap_trace_record_t) !void {
|
||||
try errors.espCheckError(sys.heap_trace_get(index, record));
|
||||
}
|
||||
pub fn dump() void {
|
||||
sys.heap_trace_dump();
|
||||
}
|
||||
pub fn dumpCaps(caps: Caps) void {
|
||||
sys.heap_trace_dump_caps(caps.toRaw());
|
||||
}
|
||||
pub fn summary(sum: [*c]sys.heap_trace_summary_t) !void {
|
||||
try errors.espCheckError(sys.heap_trace_summary(sum));
|
||||
}
|
||||
};
|
||||
191
software/zig_main/imports/hosted.zig
Normal file
191
software/zig_main/imports/hosted.zig
Normal file
@@ -0,0 +1,191 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
pub const MAC_LEN = 6;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Re-exported / renamed types from sys
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const EspMacType = sys.esp_mac_type_t;
|
||||
pub const EspResetReason = sys.esp_reset_reason_t;
|
||||
pub const EspEventBase = sys.esp_event_base_t;
|
||||
pub const EspHostedAppDesc = sys.esp_hosted_app_desc_t;
|
||||
pub const EspHostedMemInfo = sys.esp_hosted_mem_info_t;
|
||||
pub const EspHostedCapInfo = sys.esp_hosted_cap_info_t;
|
||||
pub const EspHostedCurrMemInfo = sys.esp_hosted_curr_mem_info_t;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Constants & enums
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const EVENT_BASE = sys.ESP_HOSTED_EVENT;
|
||||
|
||||
pub const EventId = enum(c_int) {
|
||||
cp_init = sys.ESP_HOSTED_EVENT_CP_INIT,
|
||||
cp_heartbeat = sys.ESP_HOSTED_EVENT_CP_HEARTBEAT,
|
||||
transport_failure = sys.ESP_HOSTED_EVENT_TRANSPORT_FAILURE,
|
||||
transport_up = sys.ESP_HOSTED_EVENT_TRANSPORT_UP,
|
||||
transport_down = sys.ESP_HOSTED_EVENT_TRANSPORT_DOWN,
|
||||
mem_monitor = sys.ESP_HOSTED_EVENT_MEM_MONITOR,
|
||||
_,
|
||||
};
|
||||
|
||||
pub const SlaveOtaStatus = enum(c_int) {
|
||||
activated = sys.ESP_HOSTED_SLAVE_OTA_ACTIVATED,
|
||||
completed = sys.ESP_HOSTED_SLAVE_OTA_COMPLETED,
|
||||
not_required = sys.ESP_HOSTED_SLAVE_OTA_NOT_REQUIRED,
|
||||
not_started = sys.ESP_HOSTED_SLAVE_OTA_NOT_STARTED,
|
||||
in_progress = sys.ESP_HOSTED_SLAVE_OTA_IN_PROGRESS,
|
||||
failed = sys.ESP_HOSTED_SLAVE_OTA_FAILED,
|
||||
_,
|
||||
};
|
||||
|
||||
pub const MemMonitorConfig = enum(c_uint) {
|
||||
no_change = sys.ESP_HOSTED_MEMMONITOR_NO_CHANGE,
|
||||
disable = sys.ESP_HOSTED_MEMMONITOR_DISABLE,
|
||||
enable = sys.ESP_HOSTED_MEMMONITOR_ENABLE,
|
||||
_,
|
||||
};
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Callback types (Zig-friendly)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const CustomDataCallback = *const fn (
|
||||
msg_id: u32,
|
||||
data: []const u8,
|
||||
) void;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Internal adapter state
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
var custom_data_cb: ?CustomDataCallback = null;
|
||||
|
||||
fn customDataCbAdapter(
|
||||
msg_id: u32,
|
||||
data: [*c]const u8,
|
||||
data_len: usize,
|
||||
) callconv(.C) void {
|
||||
if (custom_data_cb) |cb| {
|
||||
cb(msg_id, data[0..data_len]);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Hosted API
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const Hosted = struct {
|
||||
|
||||
// ─── Lifecycle ──────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn btControllerInit() !void {
|
||||
try errors.espCheckError(sys.esp_hosted_bt_controller_init());
|
||||
}
|
||||
|
||||
pub fn btControllerDeinit(release_mem: bool) !void {
|
||||
try errors.espCheckError(sys.esp_hosted_bt_controller_deinit(release_mem));
|
||||
}
|
||||
|
||||
pub fn btControllerEnable() !void {
|
||||
try errors.espCheckError(sys.esp_hosted_bt_controller_enable());
|
||||
}
|
||||
|
||||
pub fn btControllerDisable() !void {
|
||||
try errors.espCheckError(sys.esp_hosted_bt_controller_disable());
|
||||
}
|
||||
|
||||
// ─── MAC address management ─────────────────────────────────────────────────
|
||||
|
||||
pub fn setInterfaceMac(typ: EspMacType, mac: []const u8) !void {
|
||||
if (mac.len != MAC_LEN) return error.InvalidMacLength;
|
||||
try errors.espCheckError(sys.esp_hosted_iface_mac_addr_set(@constCast(mac.ptr), mac.len, typ));
|
||||
}
|
||||
|
||||
pub fn getInterfaceMac(typ: EspMacType) ![MAC_LEN]u8 {
|
||||
var mac: [MAC_LEN]u8 = undefined;
|
||||
try errors.espCheckError(sys.esp_hosted_iface_mac_addr_get(&mac, mac.len, typ));
|
||||
return mac;
|
||||
}
|
||||
|
||||
pub fn getInterfaceMacLen(typ: EspMacType) usize {
|
||||
return sys.esp_hosted_iface_mac_addr_len_get(typ);
|
||||
}
|
||||
|
||||
// ─── Coprocessor information ────────────────────────────────────────────────
|
||||
|
||||
pub fn getCoprocessorAppDesc() !EspHostedAppDesc {
|
||||
var desc: EspHostedAppDesc = undefined;
|
||||
try errors.espCheckError(sys.esp_hosted_get_coprocessor_app_desc(&desc));
|
||||
return desc;
|
||||
}
|
||||
|
||||
pub fn getCoprocessorFwVersion() ![3]u32 {
|
||||
var ver: [3]u32 = undefined;
|
||||
try errors.espCheckError(sys.esp_hosted_get_coprocessor_fwversion(&ver));
|
||||
return ver;
|
||||
}
|
||||
|
||||
// ─── Custom data channel ────────────────────────────────────────────────────
|
||||
|
||||
pub fn registerCustomDataCallback(cb: CustomDataCallback) !void {
|
||||
custom_data_cb = cb;
|
||||
try errors.espCheckError(sys.esp_hosted_register_custom_callback(0, customDataCbAdapter)); // msg_id currently ignored by most impls
|
||||
}
|
||||
|
||||
pub fn sendCustomData(msg_id: u32, data: []const u8) !void {
|
||||
try errors.espCheckError(sys.esp_hosted_send_custom_data(msg_id, data.ptr, data.len));
|
||||
}
|
||||
|
||||
// ─── Heartbeat ──────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn configureHeartbeat(enable: bool, duration_seconds: i32) !void {
|
||||
try errors.espCheckError(sys.esp_hosted_configure_heartbeat(enable, duration_seconds));
|
||||
}
|
||||
|
||||
// ─── Memory monitor ─────────────────────────────────────────────────────────
|
||||
|
||||
pub fn setMemoryMonitor(
|
||||
cfg: MemMonitorConfig,
|
||||
report_always: bool,
|
||||
interval_sec: u32,
|
||||
internal_thresholds: sys.esp_hosted_mem_monitor_threshold_t,
|
||||
external_thresholds: sys.esp_hosted_mem_monitor_threshold_t,
|
||||
) !EspHostedCurrMemInfo {
|
||||
const config = sys.esp_hosted_config_mem_monitor_t{
|
||||
.config = @intFromEnum(cfg),
|
||||
.report_always = report_always,
|
||||
.interval_sec = interval_sec,
|
||||
.internal_mem = internal_thresholds,
|
||||
.external_mem = external_thresholds,
|
||||
};
|
||||
|
||||
var curr: EspHostedCurrMemInfo = undefined;
|
||||
try errors.espCheckError(sys.esp_hosted_set_mem_monitor(&config, &curr));
|
||||
return curr;
|
||||
}
|
||||
|
||||
// ─── Slave OTA (very basic blocking style) ──────────────────────────────────
|
||||
|
||||
pub fn slaveOtaBegin() !void {
|
||||
try errors.espCheckError(sys.esp_hosted_slave_ota_begin());
|
||||
}
|
||||
|
||||
pub fn slaveOtaWrite(data: []const u8) !void {
|
||||
try errors.espCheckError(sys.esp_hosted_slave_ota_write(@constCast(data.ptr), @intCast(data.len)));
|
||||
}
|
||||
|
||||
pub fn slaveOtaEnd() !void {
|
||||
try errors.espCheckError(sys.esp_hosted_slave_ota_end());
|
||||
}
|
||||
|
||||
pub fn slaveOtaActivate() !void {
|
||||
try errors.espCheckError(sys.esp_hosted_slave_ota_activate());
|
||||
}
|
||||
|
||||
pub fn slaveOtaFromUrl(url: []const u8) !void {
|
||||
try errors.espCheckError(sys.esp_hosted_slave_ota(url.ptr));
|
||||
}
|
||||
};
|
||||
302
software/zig_main/imports/http.zig
Normal file
302
software/zig_main/imports/http.zig
Normal file
@@ -0,0 +1,302 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// http_parser (third-party parser bundled with ESP-IDF)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Parser = struct {
|
||||
pub const version = sys.http_parser_version;
|
||||
pub const init = sys.http_parser_init;
|
||||
pub const settingsInit = sys.http_parser_settings_init;
|
||||
pub const execute = sys.http_parser_execute;
|
||||
pub const urlInit = sys.http_parser_url_init;
|
||||
pub const parseUrl = sys.http_parser_parse_url;
|
||||
pub const pause = sys.http_parser_pause;
|
||||
};
|
||||
pub const shouldKeepAlive = sys.http_should_keep_alive;
|
||||
pub const methodStr = sys.http_method_str;
|
||||
pub const errnoName = sys.http_errno_name;
|
||||
pub const errnoDescription = sys.http_errno_description;
|
||||
pub const bodyIsFinal = sys.http_body_is_final;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HTTP server (httpd)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Server = struct {
|
||||
/// Start the HTTP server. Returns a handle on success.
|
||||
pub fn start(config: *const sys.httpd_config_t) !sys.httpd_handle_t {
|
||||
var handle: sys.httpd_handle_t = null;
|
||||
try errors.espCheckError(sys.httpd_start(&handle, config));
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// Stop and clean up the HTTP server.
|
||||
pub fn stop(handle: sys.httpd_handle_t) !void {
|
||||
return errors.espCheckError(sys.httpd_stop(handle));
|
||||
}
|
||||
|
||||
/// Register a URI handler.
|
||||
pub fn registerUri(handle: sys.httpd_handle_t, uri_handler: *const sys.httpd_uri_t) !void {
|
||||
return errors.espCheckError(sys.httpd_register_uri_handler(handle, uri_handler));
|
||||
}
|
||||
|
||||
/// Unregister a URI handler by URI string and method.
|
||||
pub fn unregisterUri(handle: sys.httpd_handle_t, uri: [*:0]const u8, method: sys.httpd_method_t) !void {
|
||||
return errors.espCheckError(sys.httpd_unregister_uri_handler(handle, uri, method));
|
||||
}
|
||||
|
||||
/// Unregister all URI handlers for a given URI string.
|
||||
pub fn unregisterAllUris(handle: sys.httpd_handle_t, uri: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_unregister_uri(handle, uri));
|
||||
}
|
||||
|
||||
pub const Session = struct {
|
||||
pub fn setReceiveOverride(hd: sys.httpd_handle_t, sockfd: c_int, recv_func: sys.httpd_recv_func_t) !void {
|
||||
return errors.espCheckError(sys.httpd_sess_set_recv_override(hd, sockfd, recv_func));
|
||||
}
|
||||
pub fn setSendOverride(hd: sys.httpd_handle_t, sockfd: c_int, send_func: sys.httpd_send_func_t) !void {
|
||||
return errors.espCheckError(sys.httpd_sess_set_send_override(hd, sockfd, send_func));
|
||||
}
|
||||
pub fn setPendingOverride(hd: sys.httpd_handle_t, sockfd: c_int, pending_func: sys.httpd_pending_func_t) !void {
|
||||
return errors.espCheckError(sys.httpd_sess_set_pending_override(hd, sockfd, pending_func));
|
||||
}
|
||||
pub fn triggerClose(handle: sys.httpd_handle_t, sockfd: c_int) !void {
|
||||
return errors.espCheckError(sys.httpd_sess_trigger_close(handle, sockfd));
|
||||
}
|
||||
pub fn updateLRUCounter(handle: sys.httpd_handle_t, sockfd: c_int) !void {
|
||||
return errors.espCheckError(sys.httpd_sess_update_lru_counter(handle, sockfd));
|
||||
}
|
||||
pub const getContext = sys.httpd_sess_get_ctx;
|
||||
pub const setContext = sys.httpd_sess_set_ctx;
|
||||
pub const getTransportContext = sys.httpd_sess_get_transport_ctx;
|
||||
pub const setTransportContext = sys.httpd_sess_set_transport_ctx;
|
||||
};
|
||||
|
||||
pub const Request = struct {
|
||||
pub fn asyncHandlerBegin(r: [*c]sys.httpd_req_t, out: [*c][*c]sys.httpd_req_t) !void {
|
||||
return errors.espCheckError(sys.httpd_req_async_handler_begin(r, out));
|
||||
}
|
||||
pub fn asyncHandlerComplete(r: [*c]sys.httpd_req_t) !void {
|
||||
return errors.espCheckError(sys.httpd_req_async_handler_complete(r));
|
||||
}
|
||||
pub const toSockfd = sys.httpd_req_to_sockfd;
|
||||
pub const receiver = sys.httpd_req_recv;
|
||||
pub const getHDRValueLen = sys.httpd_req_get_hdr_value_len;
|
||||
pub fn getHDRValueStr(r: [*c]sys.httpd_req_t, field: [*:0]const u8, val: [*:0]u8, val_size: usize) !void {
|
||||
return errors.espCheckError(sys.httpd_req_get_hdr_value_str(r, field, val, val_size));
|
||||
}
|
||||
pub const getUrlQueryLen = sys.httpd_req_get_url_query_len;
|
||||
pub fn getUrlQueryStr(r: [*c]sys.httpd_req_t, buf: [*:0]u8, buf_len: usize) !void {
|
||||
return errors.espCheckError(sys.httpd_req_get_url_query_str(r, buf, buf_len));
|
||||
}
|
||||
pub fn getCookieValue(req: [*c]sys.httpd_req_t, cookie_name: [*:0]const u8, val: [*:0]u8, val_size: [*c]usize) !void {
|
||||
return errors.espCheckError(sys.httpd_req_get_cookie_val(req, cookie_name, val, val_size));
|
||||
}
|
||||
};
|
||||
|
||||
pub fn queryKeyValue(qry: [*:0]const u8, key: [*:0]const u8, val: [*:0]u8, val_size: usize) !void {
|
||||
return errors.espCheckError(sys.httpd_query_key_value(qry, key, val, val_size));
|
||||
}
|
||||
|
||||
pub const uri_MatchWildcard = sys.httpd_uri_match_wildcard;
|
||||
|
||||
pub const Response = struct {
|
||||
/// Send a complete response with a byte-slice body.
|
||||
pub fn send(r: [*c]sys.httpd_req_t, buf: []const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send(r, buf.ptr, @intCast(buf.len)));
|
||||
}
|
||||
/// Send a response chunk with a byte-slice body.
|
||||
pub fn sendChunk(r: [*c]sys.httpd_req_t, buf: []const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send_chunk(r, buf.ptr, @intCast(buf.len)));
|
||||
}
|
||||
/// Send a null-terminated string as the complete response.
|
||||
/// Passes -1 (HTTPD_RESP_USE_STRLEN) so ESP-IDF calls strlen.
|
||||
pub fn sendStr(r: [*c]sys.httpd_req_t, str: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send(r, str, -1));
|
||||
}
|
||||
/// Send a null-terminated string as a response chunk.
|
||||
pub fn sendStrChunk(r: [*c]sys.httpd_req_t, str: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send_chunk(r, str, -1));
|
||||
}
|
||||
pub fn setStatus(r: [*c]sys.httpd_req_t, status: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_set_status(r, status));
|
||||
}
|
||||
pub fn setType(r: [*c]sys.httpd_req_t, @"type": [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_set_type(r, @"type"));
|
||||
}
|
||||
pub fn setHDR(r: [*c]sys.httpd_req_t, field: [*:0]const u8, value: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_set_hdr(r, field, value));
|
||||
}
|
||||
pub fn sendError(req: [*c]sys.httpd_req_t, @"error": sys.httpd_err_code_t, msg: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send_err(req, @"error", msg));
|
||||
}
|
||||
pub fn send404(r: [*c]sys.httpd_req_t) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send_err(r, .HTTPD_404_NOT_FOUND, null));
|
||||
}
|
||||
pub fn send408(r: [*c]sys.httpd_req_t) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send_err(r, .HTTPD_408_REQ_TIMEOUT, null));
|
||||
}
|
||||
pub fn send500(r: [*c]sys.httpd_req_t) !void {
|
||||
return errors.espCheckError(sys.httpd_resp_send_err(r, .HTTPD_500_INTERNAL_SERVER_ERROR, null));
|
||||
}
|
||||
};
|
||||
|
||||
pub const rawSend = sys.httpd_send;
|
||||
|
||||
pub const Socket = struct {
|
||||
pub const send = sys.httpd_socket_send;
|
||||
pub const receive = sys.httpd_socket_recv;
|
||||
};
|
||||
|
||||
pub const getGlobalUserContext = sys.httpd_get_global_user_ctx;
|
||||
pub const getGlobalTransportContext = sys.httpd_get_global_transport_ctx;
|
||||
pub fn getClientList(handle: sys.httpd_handle_t, fds: [*c]usize, client_fds: [*c]c_int) !void {
|
||||
return errors.espCheckError(sys.httpd_get_client_list(handle, fds, client_fds));
|
||||
}
|
||||
pub const httpd_work_fn_t = sys.httpd_work_fn_t;
|
||||
pub fn queueWork(handle: sys.httpd_handle_t, work: httpd_work_fn_t, arg: ?*anyopaque) !void {
|
||||
return errors.espCheckError(sys.httpd_queue_work(handle, work, arg));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HTTP client (esp_http_client)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Client = struct {
|
||||
handle: sys.esp_http_client_handle_t = null,
|
||||
|
||||
/// Initialise the HTTP client from a config struct. Call `deinit()` when done.
|
||||
pub fn init(config: *const sys.esp_http_client_config_t) Client {
|
||||
return .{ .handle = sys.esp_http_client_init(config) };
|
||||
}
|
||||
|
||||
/// Clean up the client handle. Must be called after use.
|
||||
pub fn deinit(self: Client) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_cleanup(self.handle));
|
||||
}
|
||||
|
||||
/// Perform a complete blocking request/response cycle.
|
||||
pub fn perform(self: Client) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_perform(self.handle));
|
||||
}
|
||||
|
||||
/// Open the connection (for streaming writes).
|
||||
/// `write_len` is Content-Length; pass 0 for requests without a body.
|
||||
pub fn open(self: Client, write_len: c_int) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_open(self.handle, write_len));
|
||||
}
|
||||
|
||||
/// Close the connection (without cleaning up the handle).
|
||||
pub fn close(self: Client) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_close(self.handle));
|
||||
}
|
||||
|
||||
/// Write request body data (call after `open()`).
|
||||
pub fn write(self: Client, data: []const u8) !c_int {
|
||||
const n = sys.esp_http_client_write(self.handle, data.ptr, @intCast(data.len));
|
||||
if (n < 0) return error.HttpWriteFailed;
|
||||
return n;
|
||||
}
|
||||
|
||||
/// Fetch (read) response headers after `open()`.
|
||||
/// Returns Content-Length, or negative on error / chunked encoding.
|
||||
pub fn fetchHeaders(self: Client) i64 {
|
||||
return sys.esp_http_client_fetch_headers(self.handle);
|
||||
}
|
||||
|
||||
/// Read response body into `buf`. Returns number of bytes read (0 = done).
|
||||
pub fn read(self: Client, buf: []u8) !c_int {
|
||||
const n = sys.esp_http_client_read(self.handle, buf.ptr, @intCast(buf.len));
|
||||
if (n < 0) return error.HttpReadFailed;
|
||||
return n;
|
||||
}
|
||||
|
||||
/// Read the entire response into `buf` (convenience wrapper).
|
||||
pub fn readResponse(self: Client, buf: []u8) !c_int {
|
||||
const n = sys.esp_http_client_read_response(self.handle, buf.ptr, @intCast(buf.len));
|
||||
if (n < 0) return error.HttpReadFailed;
|
||||
return n;
|
||||
}
|
||||
|
||||
/// HTTP status code of the last response.
|
||||
pub fn getStatusCode(self: Client) c_int {
|
||||
return sys.esp_http_client_get_status_code(self.handle);
|
||||
}
|
||||
|
||||
/// Content-Length of the last response (-1 if unknown / chunked).
|
||||
pub fn getContentLength(self: Client) i64 {
|
||||
return sys.esp_http_client_get_content_length(self.handle);
|
||||
}
|
||||
|
||||
/// Cancel the current in-progress request.
|
||||
pub fn cancelRequest(self: Client) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_cancel_request(self.handle));
|
||||
}
|
||||
|
||||
/// Setters — configure the client before calling `perform()` or `open()`.
|
||||
pub const Set = struct {
|
||||
pub fn url(client: sys.esp_http_client_handle_t, _url: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_url(client, _url));
|
||||
}
|
||||
pub fn postField(client: sys.esp_http_client_handle_t, data: []const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_post_field(client, data.ptr, @intCast(data.len)));
|
||||
}
|
||||
pub fn header(client: sys.esp_http_client_handle_t, field: [*:0]const u8, value: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_header(client, field, value));
|
||||
}
|
||||
pub fn username(client: sys.esp_http_client_handle_t, _username: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_username(client, _username));
|
||||
}
|
||||
pub fn password(client: sys.esp_http_client_handle_t, _password: [*:0]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_password(client, _password));
|
||||
}
|
||||
pub fn method(client: sys.esp_http_client_handle_t, _method: sys.esp_http_client_method_t) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_method(client, _method));
|
||||
}
|
||||
pub fn timeout(client: sys.esp_http_client_handle_t, timeout_ms: u32) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_timeout_ms(client, timeout_ms));
|
||||
}
|
||||
pub fn userData(client: sys.esp_http_client_handle_t, user_data: ?*anyopaque) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_user_data(client, user_data));
|
||||
}
|
||||
pub fn authData(client: sys.esp_http_client_handle_t, auth_data: []const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_auth_data(client, auth_data.ptr, @intCast(auth_data.len)));
|
||||
}
|
||||
pub fn authType(client: sys.esp_http_client_handle_t, auth_type: sys.esp_http_client_auth_type_t) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_authtype(client, auth_type));
|
||||
}
|
||||
pub fn redirection(client: sys.esp_http_client_handle_t) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_set_redirection(client));
|
||||
}
|
||||
};
|
||||
|
||||
/// Getters — retrieve configuration or response state.
|
||||
pub const Get = struct {
|
||||
/// Copy the current URL into a buffer.
|
||||
pub fn url(client: sys.esp_http_client_handle_t, buf: []u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_get_url(client, buf.ptr, @intCast(buf.len)));
|
||||
}
|
||||
/// Get a pointer to the POST field data (no copy).
|
||||
pub fn postField(client: sys.esp_http_client_handle_t, data: *[*c]const u8, len: *c_int) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_get_post_field(client, data, len));
|
||||
}
|
||||
/// Get the value of a request header by key (returns pointer via `value`).
|
||||
pub fn header(client: sys.esp_http_client_handle_t, key: [*:0]const u8, value: *[*c]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_get_header(client, key, value));
|
||||
}
|
||||
/// Get the configured username (returns pointer via `value`).
|
||||
pub fn username(client: sys.esp_http_client_handle_t, value: *[*c]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_get_username(client, value));
|
||||
}
|
||||
/// Get the configured password (returns pointer via `value`).
|
||||
pub fn password(client: sys.esp_http_client_handle_t, value: *[*c]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_get_password(client, value));
|
||||
}
|
||||
pub fn userData(client: sys.esp_http_client_handle_t, user_data: *?*anyopaque) !void {
|
||||
return errors.espCheckError(sys.esp_http_client_get_user_data(client, user_data));
|
||||
}
|
||||
};
|
||||
};
|
||||
44
software/zig_main/imports/i2c.zig
Normal file
44
software/zig_main/imports/i2c.zig
Normal file
@@ -0,0 +1,44 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
pub const BUS = struct {
|
||||
pub fn add(bus_config: ?*const sys.i2c_master_bus_config_t, ret_bus_handle: [*c]sys.i2c_master_bus_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2c_new_master_bus(bus_config, ret_bus_handle));
|
||||
}
|
||||
pub fn addDevice(bus_handle: sys.i2c_master_bus_handle_t, dev_config: [*c]const sys.i2c_device_config_t, ret_handle: [*c]sys.i2c_master_dev_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2c_master_bus_add_device(bus_handle, dev_config, ret_handle));
|
||||
}
|
||||
pub fn del(bus_handle: sys.i2c_master_bus_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2c_del_master_bus(bus_handle));
|
||||
}
|
||||
pub fn removeDevice(handle: sys.i2c_master_dev_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2c_master_bus_rm_device(handle));
|
||||
}
|
||||
pub fn reset(handle: sys.i2c_master_bus_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2c_master_bus_reset(handle));
|
||||
}
|
||||
};
|
||||
|
||||
/// Write bytes to an I2C device. Buffer length is taken from the slice.
|
||||
pub fn transmit(i2c_dev: sys.i2c_master_dev_handle_t, write_buffer: []const u8, xfer_timeout_ms: c_int) !void {
|
||||
return errors.espCheckError(sys.i2c_master_transmit(i2c_dev, write_buffer.ptr, write_buffer.len, xfer_timeout_ms));
|
||||
}
|
||||
|
||||
/// Write then read in a single I2C transaction (repeated-start).
|
||||
pub fn transmitReceive(i2c_dev: sys.i2c_master_dev_handle_t, write_buffer: []const u8, read_buffer: []u8, xfer_timeout_ms: c_int) !void {
|
||||
return errors.espCheckError(sys.i2c_master_transmit_receive(i2c_dev, write_buffer.ptr, write_buffer.len, read_buffer.ptr, read_buffer.len, xfer_timeout_ms));
|
||||
}
|
||||
|
||||
/// Read bytes from an I2C device. Buffer length is taken from the slice.
|
||||
pub fn receive(i2c_dev: sys.i2c_master_dev_handle_t, read_buffer: []u8, xfer_timeout_ms: c_int) !void {
|
||||
return errors.espCheckError(sys.i2c_master_receive(i2c_dev, read_buffer.ptr, read_buffer.len, xfer_timeout_ms));
|
||||
}
|
||||
pub fn probe(i2c_master: sys.i2c_master_bus_handle_t, address: u16, xfer_timeout_ms: c_int) !void {
|
||||
return try errors.espCheckError(sys.i2c_master_probe(i2c_master, address, xfer_timeout_ms));
|
||||
}
|
||||
pub fn registerEventCallbacks(i2c_dev: sys.i2c_master_dev_handle_t, cbs: [*c]const sys.i2c_master_event_callbacks_t, user_data: ?*anyopaque) !void {
|
||||
return try errors.espCheckError(sys.i2c_master_register_event_callbacks(i2c_dev, cbs, user_data));
|
||||
}
|
||||
pub fn waitAllDone(i2c_master: sys.i2c_master_bus_handle_t, timeout_ms: c_int) !void {
|
||||
return try errors.espCheckError(sys.i2c_master_wait_all_done(i2c_master, timeout_ms));
|
||||
}
|
||||
102
software/zig_main/imports/i2s.zig
Normal file
102
software/zig_main/imports/i2s.zig
Normal file
@@ -0,0 +1,102 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
pub fn newChannel(chan_cfg: [*c]const sys.i2s_chan_config_t, ret_tx_handle: [*c]sys.i2s_chan_handle_t, ret_rx_handle: [*c]sys.i2s_chan_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_new_channel(chan_cfg, ret_tx_handle, ret_rx_handle));
|
||||
}
|
||||
pub fn delChannel(handle: sys.i2s_chan_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_del_channel(handle));
|
||||
}
|
||||
pub fn channelGetInfo(handle: sys.i2s_chan_handle_t, chan_info: [*c]sys.i2s_chan_info_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_get_info(handle, chan_info));
|
||||
}
|
||||
pub fn channelEnable(handle: sys.i2s_chan_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_enable(handle));
|
||||
}
|
||||
pub fn channelDisable(handle: sys.i2s_chan_handle_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_disable(handle));
|
||||
}
|
||||
pub fn channelPreloadData(tx_handle: sys.i2s_chan_handle_t, src: ?*const anyopaque, size: usize, bytes_loaded: [*c]usize) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_preload_data(tx_handle, src, size, bytes_loaded));
|
||||
}
|
||||
pub fn channelWrite(handle: sys.i2s_chan_handle_t, src: ?*const anyopaque, size: usize, bytes_written: [*c]usize, timeout_ms: u32) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_write(handle, src, size, bytes_written, timeout_ms));
|
||||
}
|
||||
pub fn channelRead(handle: sys.i2s_chan_handle_t, dest: ?*anyopaque, size: usize, bytes_read: [*c]usize, timeout_ms: u32) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_read(handle, dest, size, bytes_read, timeout_ms));
|
||||
}
|
||||
pub fn channelRegisterEventCallback(handle: sys.i2s_chan_handle_t, callbacks: [*c]const sys.i2s_event_callbacks_t, user_data: ?*anyopaque) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_register_event_callback(handle, callbacks, user_data));
|
||||
}
|
||||
pub fn channelInitPdmRXMode(handle: sys.i2s_chan_handle_t, pdm_rx_cfg: ?*const sys.i2s_pdm_rx_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_init_pdm_rx_mode(handle, pdm_rx_cfg));
|
||||
}
|
||||
pub fn channelReconfigPdmRXClock(handle: sys.i2s_chan_handle_t, clk_cfg: [*c]const sys.i2s_pdm_rx_clk_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_reconfig_pdm_rx_clock(handle, clk_cfg));
|
||||
}
|
||||
pub fn channelReconfigPdmRXSlot(handle: sys.i2s_chan_handle_t, slot_cfg: [*c]const sys.i2s_pdm_rx_slot_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_reconfig_pdm_rx_slot(handle, slot_cfg));
|
||||
}
|
||||
pub fn channelReconfigPdmRXGPIO(handle: sys.i2s_chan_handle_t, gpio_cfg: ?*const sys.i2s_pdm_rx_gpio_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_reconfig_pdm_rx_gpio(handle, gpio_cfg));
|
||||
}
|
||||
pub fn channelInitPdmTXMode(handle: sys.i2s_chan_handle_t, pdm_tx_cfg: ?*const sys.i2s_pdm_tx_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_init_pdm_tx_mode(handle, pdm_tx_cfg));
|
||||
}
|
||||
pub fn channelReconfigPdmTXClock(handle: sys.i2s_chan_handle_t, clk_cfg: [*c]const sys.i2s_pdm_tx_clk_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_reconfig_pdm_tx_clock(handle, clk_cfg));
|
||||
}
|
||||
pub fn channelReconfigPdmTXSlot(handle: sys.i2s_chan_handle_t, slot_cfg: [*c]const sys.i2s_pdm_tx_slot_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_reconfig_pdm_tx_slot(handle, slot_cfg));
|
||||
}
|
||||
pub fn channelReconfigPdmTXGPIO(handle: sys.i2s_chan_handle_t, gpio_cfg: ?*const sys.i2s_pdm_tx_gpio_config_t) !void {
|
||||
return try errors.espCheckError(sys.i2s_channel_reconfig_pdm_tx_gpio(handle, gpio_cfg));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// STD mode (standard I2S / PCM — most common)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Initialise a channel in standard I2S mode (I2S, MSB-justified, LSB-justified, PCM).
|
||||
pub fn channelInitStdMode(handle: sys.i2s_chan_handle_t, std_cfg: *const sys.i2s_std_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_init_std_mode(handle, std_cfg));
|
||||
}
|
||||
|
||||
/// Reconfigure clock parameters of a standard-mode channel (must be disabled first).
|
||||
pub fn channelReconfigStdClock(handle: sys.i2s_chan_handle_t, clk_cfg: *const sys.i2s_std_clk_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_reconfig_std_clock(handle, clk_cfg));
|
||||
}
|
||||
|
||||
/// Reconfigure slot parameters of a standard-mode channel (must be disabled first).
|
||||
pub fn channelReconfigStdSlot(handle: sys.i2s_chan_handle_t, slot_cfg: *const sys.i2s_std_slot_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_reconfig_std_slot(handle, slot_cfg));
|
||||
}
|
||||
|
||||
/// Reconfigure GPIO pins of a standard-mode channel (must be disabled first).
|
||||
pub fn channelReconfigStdGPIO(handle: sys.i2s_chan_handle_t, gpio_cfg: *const sys.i2s_std_gpio_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_reconfig_std_gpio(handle, gpio_cfg));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TDM mode (Time-Division Multiplexed — multiple slots per frame)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Initialise a channel in TDM mode.
|
||||
pub fn channelInitTdmMode(handle: sys.i2s_chan_handle_t, tdm_cfg: *const sys.i2s_tdm_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_init_tdm_mode(handle, tdm_cfg));
|
||||
}
|
||||
|
||||
/// Reconfigure clock parameters of a TDM channel (must be disabled first).
|
||||
pub fn channelReconfigTdmClock(handle: sys.i2s_chan_handle_t, clk_cfg: *const sys.i2s_tdm_clk_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_reconfig_tdm_clock(handle, clk_cfg));
|
||||
}
|
||||
|
||||
/// Reconfigure slot parameters of a TDM channel (must be disabled first).
|
||||
pub fn channelReconfigTdmSlot(handle: sys.i2s_chan_handle_t, slot_cfg: *const sys.i2s_tdm_slot_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_reconfig_tdm_slot(handle, slot_cfg));
|
||||
}
|
||||
|
||||
/// Reconfigure GPIO pins of a TDM channel (must be disabled first).
|
||||
pub fn channelReconfigTdmGPIO(handle: sys.i2s_chan_handle_t, gpio_cfg: *const sys.i2s_tdm_gpio_config_t) !void {
|
||||
return errors.espCheckError(sys.i2s_channel_reconfig_tdm_gpio(handle, gpio_cfg));
|
||||
}
|
||||
134
software/zig_main/imports/idf.zig
Normal file
134
software/zig_main/imports/idf.zig
Normal file
@@ -0,0 +1,134 @@
|
||||
pub const bl = @import("bootloader");
|
||||
pub const bt = switch (@hasDecl(sys, "CONFIG_BT_ENABLED")) {
|
||||
true => @import("bluetooth"),
|
||||
false => @compileError("bluetooth requires CONFIG_BT_ENABLED in sdkconfig"),
|
||||
};
|
||||
pub const nimble = if (@hasDecl(sys, "CONFIG_BT_NIMBLE_ENABLED"))
|
||||
@import("nimble")
|
||||
else
|
||||
@compileError(
|
||||
\\NimBLE not enabled. Enable via:
|
||||
\\ idf.py menuconfig → Component config → Bluetooth → Host → NimBLE
|
||||
\\Then run: idf.py reconfigure
|
||||
);
|
||||
pub const crc = @import("crc");
|
||||
pub const dsp = switch (@hasDecl(sys, "HAS_ESP_DSP")) {
|
||||
true => @import("dsp"),
|
||||
false => @compileError("requires: idf.py add-dependency espressif/esp-dsp"),
|
||||
};
|
||||
pub const err = @import("error");
|
||||
pub const gpio = @import("gpio");
|
||||
pub const heap = @import("heap");
|
||||
pub const http = @import("http");
|
||||
pub const i2c = @import("i2c");
|
||||
pub const i2s = @import("i2s");
|
||||
pub const led = switch (@hasDecl(sys, "HAS_LED_STRIP")) {
|
||||
true => @import("led"),
|
||||
false => @compileError("requires: idf.py add-dependency espressif/led_strip"),
|
||||
};
|
||||
pub const log = @import("log");
|
||||
pub const lwip = @import("lwip");
|
||||
pub const mqtt = @import("mqtt");
|
||||
pub const esp_now = @import("now");
|
||||
pub const phy = @import("phy");
|
||||
pub const pulse = @import("pulse");
|
||||
pub const esp_panic = @import("panic");
|
||||
pub const rtos = @import("rtos");
|
||||
pub const nvs = @import("nvs");
|
||||
pub const partition = @import("partition");
|
||||
pub const sleep = @import("sleep");
|
||||
pub const event = @import("event");
|
||||
pub const wdt = @import("wdt");
|
||||
pub const segger = @import("segger");
|
||||
pub const spi = @import("spi");
|
||||
pub const uart = @import("uart");
|
||||
pub const ver = @import("ver");
|
||||
pub const esp_hosted = switch (@hasDecl(sys, "HAS_ESP_HOSTED")) {
|
||||
true => @import("hosted"),
|
||||
false => @compileError("requires: idf.py add-dependency espressif/esp_hosted"),
|
||||
};
|
||||
pub const wifi_remote = switch (@hasDecl(sys, "HAS_ESP_WIFI_REMOTE")) {
|
||||
true => @import("wifi_remote"),
|
||||
false => @compileError("requires: idf.py add-dependency espressif/esp_wifi_remote"),
|
||||
};
|
||||
pub const timer = @import("timer");
|
||||
pub const ledc = @import("ledc");
|
||||
pub const twai = @import("twai");
|
||||
pub const pm = @import("pm");
|
||||
pub const pthread = @import("pthread");
|
||||
pub const matter = switch (@hasDecl(sys, "HAS_ESP_MATTER")) {
|
||||
true => @import("matter"),
|
||||
false => @compileError("requires: idf.py add-dependency espressif/esp_matter"),
|
||||
};
|
||||
pub const wifi = switch (currentTarget) {
|
||||
.esp32h2, .esp32h21, .esp32h4, .esp32p4 => @compileError("Wifi requires CONFIG_ESP_WIFI_ENABLED in sdkconfig"),
|
||||
else => @import("wifi"),
|
||||
};
|
||||
pub const sys = @import("sys");
|
||||
|
||||
const Device = enum {
|
||||
esp32,
|
||||
esp32s2,
|
||||
esp32s3,
|
||||
esp32s31,
|
||||
esp32c2,
|
||||
esp32c3,
|
||||
esp32c5,
|
||||
esp32c6,
|
||||
esp32c61,
|
||||
esp32h2,
|
||||
esp32h21,
|
||||
esp32h4,
|
||||
esp32p4,
|
||||
};
|
||||
|
||||
// Convert compile-time target string to enum
|
||||
pub const currentTarget = blk: {
|
||||
const target_str = sys.CONFIG_IDF_TARGET;
|
||||
break :blk @import("std").meta.stringToEnum(Device, target_str) orelse {
|
||||
@compileError("Unknown ESP32 device target: " ++ target_str);
|
||||
};
|
||||
};
|
||||
|
||||
// Check all imports
|
||||
comptime {
|
||||
_ = sys;
|
||||
_ = bl;
|
||||
if (@hasDecl(sys, "CONFIG_BT_ENABLED")) _ = bt;
|
||||
_ = crc;
|
||||
_ = err;
|
||||
_ = gpio;
|
||||
_ = heap;
|
||||
_ = http;
|
||||
_ = i2c;
|
||||
_ = i2s;
|
||||
_ = log;
|
||||
_ = lwip;
|
||||
_ = mqtt;
|
||||
if (@hasDecl(sys, "HAS_ESP_HOSTED")) _ = esp_hosted;
|
||||
_ = esp_now;
|
||||
_ = phy;
|
||||
_ = pulse;
|
||||
_ = esp_panic;
|
||||
_ = rtos;
|
||||
if (@hasDecl(sys, "CONFIG_BT_NIMBLE_ENABLED")) _ = nimble;
|
||||
_ = nvs;
|
||||
_ = partition;
|
||||
_ = sleep;
|
||||
_ = event;
|
||||
_ = wdt;
|
||||
_ = segger;
|
||||
_ = spi;
|
||||
_ = uart;
|
||||
_ = ver;
|
||||
if (@hasDecl(sys, "CONFIG_ESP_WIFI_ENABLED")) _ = wifi;
|
||||
if (@hasDecl(sys, "HAS_ESP_WIFI_REMOTE")) _ = wifi_remote;
|
||||
if (@hasDecl(sys, "HAS_ESP_DSP")) _ = dsp;
|
||||
if (@hasDecl(sys, "HAS_LED_STRIP")) _ = led;
|
||||
_ = timer;
|
||||
_ = ledc;
|
||||
_ = twai;
|
||||
_ = pm;
|
||||
_ = pthread;
|
||||
if (@hasDecl(sys, "HAS_ESP_MATTER")) _ = matter;
|
||||
}
|
||||
119
software/zig_main/imports/led-strip.zig
Normal file
119
software/zig_main/imports/led-strip.zig
Normal file
@@ -0,0 +1,119 @@
|
||||
// requires: idf.py add-dependency espressif/led_strip
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
pub const LedModel = enum(sys.led_model_t) {
|
||||
ws2812 = sys.LED_MODEL_WS2812,
|
||||
sk6812 = sys.LED_MODEL_SK6812,
|
||||
ws2811 = sys.LED_MODEL_WS2811,
|
||||
ws2816 = sys.LED_MODEL_WS2816,
|
||||
invalid = sys.LED_MODEL_INVALID,
|
||||
};
|
||||
pub const LedStripHandle = sys.led_strip_handle_t;
|
||||
|
||||
pub const ColorComponentFormat = extern union {
|
||||
format: packed struct {
|
||||
r_pos: u2, // Position of the red channel in the color order: 0~3
|
||||
g_pos: u2, // Position of the green channel in the color order: 0~3
|
||||
b_pos: u2, // Position of the blue channel in the color order: 0~3
|
||||
w_pos: u2, // Position of the white channel in the color order: 0~3
|
||||
reserved: u19, // Reserved
|
||||
bytes_per_color: u2, // Bytes per color component: 1 or 2. If set to 0, it will fallback to 1
|
||||
num_components: u3, // Number of color components per pixel: 3 or 4. If set to 0, it will fallback to 3
|
||||
},
|
||||
format_id: u32,
|
||||
};
|
||||
pub const PixelFormat = enum(u32) {
|
||||
grb = 0x00, // default WS2812
|
||||
rgb = 0x01,
|
||||
grbw = 0x10,
|
||||
rgbw = 0x11,
|
||||
};
|
||||
pub const LedStripConfig = extern struct {
|
||||
strip_gpio_num: i32,
|
||||
max_leds: u32,
|
||||
led_model: LedModel,
|
||||
color_component_format: ColorComponentFormat,
|
||||
// flags: sys.struct_led_strip_extra_flags_29, // opaque
|
||||
flags: u32 = 0, // most users leave = 0; see component for bits
|
||||
|
||||
pub fn init(gpio: u32, count: u32, model: LedModel, format_id: u32) LedStripConfig {
|
||||
return .{
|
||||
.strip_gpio_num = @intCast(gpio),
|
||||
.max_leds = count,
|
||||
.led_model = model,
|
||||
.color_component_format = .{ .format_id = format_id },
|
||||
.flags = 0,
|
||||
};
|
||||
}
|
||||
pub fn ws2812(gpio: u32, count: u32) LedStripConfig {
|
||||
return init(gpio, count, .ws2812, @intFromEnum(PixelFormat.grb));
|
||||
}
|
||||
};
|
||||
pub const LedStripRmtConfig = struct {
|
||||
clk_src: sys.rmt_clock_source_t,
|
||||
resolution_hz: u32,
|
||||
mem_block_symbols: usize = 0, // 0 = default/auto
|
||||
// flags: sys.struct_led_strip_rmt_extra_config_33, // opaque
|
||||
flags: u32 = 0,
|
||||
|
||||
pub fn init_10mhz() LedStripRmtConfig {
|
||||
return .{
|
||||
.clk_src = sys.RMT_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 10_000_000,
|
||||
.mem_block_symbols = 0,
|
||||
.flags = 0,
|
||||
};
|
||||
}
|
||||
|
||||
// Very common setting in examples
|
||||
pub const default = init_10mhz();
|
||||
};
|
||||
pub const LedStripSpiConfig = struct {
|
||||
clk_src: sys.spi_clock_source_t,
|
||||
spi_bus: sys.spi_host_device_t,
|
||||
// flags: sys.struct_unnamed_34, // opaque
|
||||
flags: u32 = 0,
|
||||
};
|
||||
|
||||
// ───────────────────────────────────────────────
|
||||
// API functions
|
||||
|
||||
/// Creates RMT-backed LED strip
|
||||
pub fn newRmtDevice(led_config: *const LedStripConfig, rmt_config: *const LedStripRmtConfig, handle: *LedStripHandle) !LedStripHandle {
|
||||
const result = sys.led_strip_new_rmt_device(@ptrCast(led_config), @ptrCast(rmt_config), @ptrCast(handle));
|
||||
try errors.espCheckError(result);
|
||||
return handle.*;
|
||||
}
|
||||
|
||||
/// Creates SPI-backed LED strip
|
||||
pub fn newSpiDevice(led_config: *const LedStripConfig, spi_config: *const LedStripSpiConfig, handle: *LedStripHandle) !LedStripHandle {
|
||||
const result = sys.led_strip_new_spi_device(@ptrCast(led_config), @ptrCast(spi_config), @ptrCast(handle));
|
||||
try errors.espCheckError(result);
|
||||
return handle.*;
|
||||
}
|
||||
/// Set one RGB pixel
|
||||
pub fn setPixel(strip: LedStripHandle, index: u32, r: u8, g: u8, b: u8) !void {
|
||||
// The C API takes u32 — we clamp/convert for safety
|
||||
return try errors.espCheckError(sys.led_strip_set_pixel(strip, index, @as(u32, r), @as(u32, g), @as(u32, b)));
|
||||
}
|
||||
/// Variant that also sets white channel (SK6812 RGBW etc.)
|
||||
pub fn setPixelRgbw(strip: LedStripHandle, index: u32, r: u8, g: u8, b: u8, w: u8) !void {
|
||||
return try errors.espCheckError(sys.led_strip_set_pixel_rgbw(strip, index, @as(u32, r), @as(u32, g), @as(u32, b), @as(u32, w)));
|
||||
}
|
||||
/// HSV variant (convenience — internally converts to RGB)
|
||||
pub fn setPixelHsv(strip: LedStripHandle, index: u32, hue: u16, sat: u8, val: u8) !void {
|
||||
return try errors.espCheckError(sys.led_strip_set_pixel_hsv(strip, index, hue, sat, val));
|
||||
}
|
||||
/// Push all buffered pixel data to the strip
|
||||
pub fn refresh(strip: LedStripHandle) !void {
|
||||
return try errors.espCheckError(sys.led_strip_refresh(strip));
|
||||
}
|
||||
/// Turn all LEDs off
|
||||
pub fn clear(strip: LedStripHandle) !void {
|
||||
return try errors.espCheckError(sys.led_strip_clear(strip));
|
||||
}
|
||||
/// Free resources
|
||||
pub fn deinit(strip: LedStripHandle) !void {
|
||||
return try errors.espCheckError(sys.led_strip_del(strip));
|
||||
}
|
||||
218
software/zig_main/imports/ledc.zig
Normal file
218
software/zig_main/imports/ledc.zig
Normal file
@@ -0,0 +1,218 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases & enums
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const ChannelConfig = sys.ledc_channel_config_t;
|
||||
pub const TimerConfig = sys.ledc_timer_config_t;
|
||||
pub const IsrHandle = sys.ledc_isr_handle_t;
|
||||
pub const CbParam = sys.ledc_cb_param_t;
|
||||
pub const Cbs = sys.ledc_cbs_t;
|
||||
|
||||
/// Speed mode. Most modern ESP32 variants only support LOW_SPEED_MODE.
|
||||
pub const SpeedMode = enum(sys.ledc_mode_t) {
|
||||
low = sys.LEDC_LOW_SPEED_MODE,
|
||||
};
|
||||
|
||||
pub const IntrType = enum(sys.ledc_intr_type_t) {
|
||||
disable = sys.LEDC_INTR_DISABLE,
|
||||
fade_end = sys.LEDC_INTR_FADE_END,
|
||||
};
|
||||
|
||||
pub const DutyDir = enum(sys.ledc_duty_direction_t) {
|
||||
decrease = sys.LEDC_DUTY_DIR_DECREASE,
|
||||
increase = sys.LEDC_DUTY_DIR_INCREASE,
|
||||
};
|
||||
|
||||
pub const Timer = enum(sys.ledc_timer_t) {
|
||||
@"0" = sys.LEDC_TIMER_0,
|
||||
@"1" = sys.LEDC_TIMER_1,
|
||||
@"2" = sys.LEDC_TIMER_2,
|
||||
@"3" = sys.LEDC_TIMER_3,
|
||||
};
|
||||
|
||||
pub const Channel = enum(sys.ledc_channel_t) {
|
||||
@"0" = sys.LEDC_CHANNEL_0,
|
||||
@"1" = sys.LEDC_CHANNEL_1,
|
||||
@"2" = sys.LEDC_CHANNEL_2,
|
||||
@"3" = sys.LEDC_CHANNEL_3,
|
||||
@"4" = sys.LEDC_CHANNEL_4,
|
||||
@"5" = sys.LEDC_CHANNEL_5,
|
||||
};
|
||||
|
||||
pub const TimerBit = enum(sys.ledc_timer_bit_t) {
|
||||
@"1" = sys.LEDC_TIMER_1_BIT,
|
||||
@"2" = sys.LEDC_TIMER_2_BIT,
|
||||
@"3" = sys.LEDC_TIMER_3_BIT,
|
||||
@"4" = sys.LEDC_TIMER_4_BIT,
|
||||
@"5" = sys.LEDC_TIMER_5_BIT,
|
||||
@"6" = sys.LEDC_TIMER_6_BIT,
|
||||
@"7" = sys.LEDC_TIMER_7_BIT,
|
||||
@"8" = sys.LEDC_TIMER_8_BIT,
|
||||
@"9" = sys.LEDC_TIMER_9_BIT,
|
||||
@"10" = sys.LEDC_TIMER_10_BIT,
|
||||
@"11" = sys.LEDC_TIMER_11_BIT,
|
||||
@"12" = sys.LEDC_TIMER_12_BIT,
|
||||
@"13" = sys.LEDC_TIMER_13_BIT,
|
||||
@"14" = sys.LEDC_TIMER_14_BIT,
|
||||
};
|
||||
|
||||
pub const FadeMode = enum(sys.ledc_fade_mode_t) {
|
||||
no_wait = sys.LEDC_FADE_NO_WAIT,
|
||||
wait_done = sys.LEDC_FADE_WAIT_DONE,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timer configuration and control
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const TimerCtrl = struct {
|
||||
/// Configure a LEDC timer (frequency + duty resolution).
|
||||
pub fn config(cfg: *const TimerConfig) !void {
|
||||
try errors.espCheckError(sys.ledc_timer_config(cfg));
|
||||
}
|
||||
|
||||
/// Reset a LEDC timer.
|
||||
pub fn reset(mode: SpeedMode, timer: Timer) !void {
|
||||
try errors.espCheckError(sys.ledc_timer_rst(@intFromEnum(mode), @intFromEnum(timer)));
|
||||
}
|
||||
|
||||
/// Pause a LEDC timer.
|
||||
pub fn pause(mode: SpeedMode, timer: Timer) !void {
|
||||
try errors.espCheckError(sys.ledc_timer_pause(@intFromEnum(mode), @intFromEnum(timer)));
|
||||
}
|
||||
|
||||
/// Resume a paused LEDC timer.
|
||||
pub fn @"resume"(mode: SpeedMode, timer: Timer) !void {
|
||||
try errors.espCheckError(sys.ledc_timer_resume(@intFromEnum(mode), @intFromEnum(timer)));
|
||||
}
|
||||
|
||||
/// Set the frequency (Hz) of a running timer.
|
||||
pub fn setFreq(mode: SpeedMode, timer: Timer, freq_hz: u32) !void {
|
||||
try errors.espCheckError(sys.ledc_set_freq(@intFromEnum(mode), @intFromEnum(timer), freq_hz));
|
||||
}
|
||||
|
||||
/// Get the current frequency (Hz) of a timer.
|
||||
pub fn getFreq(mode: SpeedMode, timer: Timer) u32 {
|
||||
return sys.ledc_get_freq(@intFromEnum(mode), @intFromEnum(timer));
|
||||
}
|
||||
|
||||
/// Return the best duty resolution for a given source clock and target frequency.
|
||||
pub fn findSuitableDutyResolution(src_clk_freq: u32, timer_freq: u32) u32 {
|
||||
return sys.ledc_find_suitable_duty_resolution(src_clk_freq, timer_freq);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Channel configuration and control
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const ChannelCtrl = struct {
|
||||
/// Configure a LEDC output channel.
|
||||
pub fn config(cfg: *const ChannelConfig) !void {
|
||||
try errors.espCheckError(sys.ledc_channel_config(cfg));
|
||||
}
|
||||
|
||||
/// Re-assign a GPIO to an existing channel (without full reconfiguration).
|
||||
pub fn setPin(gpio_num: c_int, mode: SpeedMode, ch: Channel) !void {
|
||||
try errors.espCheckError(sys.ledc_set_pin(gpio_num, @intFromEnum(mode), @intFromEnum(ch)));
|
||||
}
|
||||
|
||||
/// Stop the channel output and set the GPIO to `idle_level` (0 or 1).
|
||||
pub fn stop(mode: SpeedMode, ch: Channel, idle_level: u32) !void {
|
||||
try errors.espCheckError(sys.ledc_stop(@intFromEnum(mode), @intFromEnum(ch), idle_level));
|
||||
}
|
||||
|
||||
/// Bind a channel to a different timer.
|
||||
pub fn bindTimer(mode: SpeedMode, ch: Channel, timer: Timer) !void {
|
||||
try errors.espCheckError(sys.ledc_bind_channel_timer(@intFromEnum(mode), @intFromEnum(ch), @intFromEnum(timer)));
|
||||
}
|
||||
|
||||
// ── Duty ──────────────────────────────────────────────────────────────
|
||||
|
||||
/// Set the duty cycle (without updating the hardware — call `updateDuty` after).
|
||||
pub fn setDuty(mode: SpeedMode, ch: Channel, duty: u32) !void {
|
||||
try errors.espCheckError(sys.ledc_set_duty(@intFromEnum(mode), @intFromEnum(ch), duty));
|
||||
}
|
||||
|
||||
/// Set duty + hpoint (without updating hardware).
|
||||
pub fn setDutyWithHpoint(mode: SpeedMode, ch: Channel, duty: u32, hpoint: u32) !void {
|
||||
try errors.espCheckError(sys.ledc_set_duty_with_hpoint(@intFromEnum(mode), @intFromEnum(ch), duty, hpoint));
|
||||
}
|
||||
|
||||
/// Latch the duty value set by `setDuty`/`setDutyWithHpoint` into hardware.
|
||||
pub fn updateDuty(mode: SpeedMode, ch: Channel) !void {
|
||||
try errors.espCheckError(sys.ledc_update_duty(@intFromEnum(mode), @intFromEnum(ch)));
|
||||
}
|
||||
|
||||
/// Set duty and immediately apply it to hardware (combines set + update).
|
||||
pub fn setDutyAndUpdate(mode: SpeedMode, ch: Channel, duty: u32, hpoint: u32) !void {
|
||||
try errors.espCheckError(sys.ledc_set_duty_and_update(@intFromEnum(mode), @intFromEnum(ch), duty, hpoint));
|
||||
}
|
||||
|
||||
/// Get the current duty cycle value.
|
||||
pub fn getDuty(mode: SpeedMode, ch: Channel) u32 {
|
||||
return sys.ledc_get_duty(@intFromEnum(mode), @intFromEnum(ch));
|
||||
}
|
||||
|
||||
/// Get the current hpoint value.
|
||||
pub fn getHpoint(mode: SpeedMode, ch: Channel) c_int {
|
||||
return sys.ledc_get_hpoint(@intFromEnum(mode), @intFromEnum(ch));
|
||||
}
|
||||
|
||||
// ── Callback ──────────────────────────────────────────────────────────
|
||||
|
||||
/// Register a callback for fade-end events on a channel.
|
||||
pub fn registerCallback(mode: SpeedMode, ch: Channel, cbs: *Cbs, user_arg: ?*anyopaque) !void {
|
||||
try errors.espCheckError(sys.ledc_cb_register(@intFromEnum(mode), @intFromEnum(ch), cbs, user_arg));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fade functions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Fade = struct {
|
||||
/// Install the fade ISR service. Call once before using any fade functions.
|
||||
/// `intr_alloc_flags`: interrupt allocation flags (e.g. ESP_INTR_FLAG_IRAM).
|
||||
pub fn install(intr_alloc_flags: c_int) !void {
|
||||
try errors.espCheckError(sys.ledc_fade_func_install(intr_alloc_flags));
|
||||
}
|
||||
|
||||
/// Uninstall the fade ISR service.
|
||||
pub fn uninstall() void {
|
||||
sys.ledc_fade_func_uninstall();
|
||||
}
|
||||
|
||||
/// Start a fade previously configured with `setFadeStep` or `setFadeTime`.
|
||||
pub fn start(mode: SpeedMode, ch: Channel, fade_mode: FadeMode) !void {
|
||||
try errors.espCheckError(sys.ledc_fade_start(@intFromEnum(mode), @intFromEnum(ch), @intFromEnum(fade_mode)));
|
||||
}
|
||||
|
||||
/// Stop an in-progress fade.
|
||||
pub fn stop(mode: SpeedMode, ch: Channel) !void {
|
||||
try errors.espCheckError(sys.ledc_fade_stop(@intFromEnum(mode), @intFromEnum(ch)));
|
||||
}
|
||||
|
||||
/// Configure a fade by time: ramp from current duty to `target_duty` in `fade_time_ms`.
|
||||
pub fn setFadeTime(mode: SpeedMode, ch: Channel, target_duty: u32, fade_time_ms: c_int) !void {
|
||||
try errors.espCheckError(sys.ledc_set_fade_with_time(@intFromEnum(mode), @intFromEnum(ch), target_duty, fade_time_ms));
|
||||
}
|
||||
|
||||
/// Configure a fade by step: ramp with a fixed `scale` increment every `cycle_num` PWM cycles.
|
||||
pub fn setFadeStep(mode: SpeedMode, ch: Channel, target_duty: u32, scale: u32, cycle_num: u32) !void {
|
||||
try errors.espCheckError(sys.ledc_set_fade_with_step(@intFromEnum(mode), @intFromEnum(ch), target_duty, scale, cycle_num));
|
||||
}
|
||||
|
||||
/// Set fade time and immediately start it.
|
||||
pub fn setFadeTimeAndStart(mode: SpeedMode, ch: Channel, target_duty: u32, fade_time_ms: u32, fade_mode: FadeMode) !void {
|
||||
try errors.espCheckError(sys.ledc_set_fade_time_and_start(@intFromEnum(mode), @intFromEnum(ch), target_duty, fade_time_ms, @intFromEnum(fade_mode)));
|
||||
}
|
||||
|
||||
/// Set fade step and immediately start it.
|
||||
pub fn setFadeStepAndStart(mode: SpeedMode, ch: Channel, target_duty: u32, scale: u32, cycle_num: u32, fade_mode: FadeMode) !void {
|
||||
try errors.espCheckError(sys.ledc_set_fade_step_and_start(@intFromEnum(mode), @intFromEnum(ch), target_duty, scale, cycle_num, @intFromEnum(fade_mode)));
|
||||
}
|
||||
};
|
||||
102
software/zig_main/imports/logger.zig
Normal file
102
software/zig_main/imports/logger.zig
Normal file
@@ -0,0 +1,102 @@
|
||||
const std = @import("std");
|
||||
const sys = @import("sys");
|
||||
|
||||
pub const default_log_scope = .espressif;
|
||||
|
||||
pub fn espLogFn(
|
||||
comptime level: std.log.Level,
|
||||
comptime scope: @TypeOf(.EnumLiteral),
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
const esp_level = comptime levelToEsp(level);
|
||||
const color = comptime levelColor(level);
|
||||
const prefix = color ++ "[" ++ comptime level.asText() ++ "] (" ++ @tagName(scope) ++ "): ";
|
||||
|
||||
var heap = std.heap.ArenaAllocator.init(std.heap.c_allocator);
|
||||
defer heap.deinit();
|
||||
|
||||
ESP_LOG(heap.allocator(), esp_level, "logging", prefix ++ format ++ LOG_RESET_COLOR ++ "\n", args);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Level mapping
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const default_level: sys.esp_log_level_t = switch (@import("builtin").mode) {
|
||||
.Debug => sys.ESP_LOG_DEBUG,
|
||||
.ReleaseSafe => sys.ESP_LOG_INFO,
|
||||
.ReleaseFast, .ReleaseSmall => sys.ESP_LOG_ERROR,
|
||||
};
|
||||
|
||||
/// Converts a Zig log level to its ESP-IDF equivalent.
|
||||
pub fn levelToEsp(comptime level: std.log.Level) sys.esp_log_level_t {
|
||||
return switch (level) {
|
||||
.err => sys.ESP_LOG_ERROR,
|
||||
.warn => sys.ESP_LOG_WARN,
|
||||
.info => sys.ESP_LOG_INFO,
|
||||
.debug => sys.ESP_LOG_DEBUG,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the ANSI color escape for a given Zig log level.
|
||||
pub fn levelColor(comptime level: std.log.Level) []const u8 {
|
||||
return switch (level) {
|
||||
.err => LOG_COLOR_E,
|
||||
.warn => LOG_COLOR_W,
|
||||
.info => LOG_COLOR_I,
|
||||
.debug => LOG_COLOR(LOG_COLOR_BLUE),
|
||||
};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Core log primitive
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn ESP_LOG(
|
||||
allocator: std.mem.Allocator,
|
||||
level: sys.esp_log_level_t,
|
||||
comptime tag: [*:0]const u8,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
if (isComptime(args)) {
|
||||
const buffer: [:0]const u8 = std.fmt.comptimePrint(fmt, args);
|
||||
sys.esp_log_write(level, tag, "%s", buffer.ptr);
|
||||
} else {
|
||||
const buffer: [:0]u8 = std.fmt.allocPrintSentinel(allocator, fmt, args, 0) catch return;
|
||||
sys.esp_log_write(level, tag, "%s", buffer.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ANSI color helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const LOG_COLOR_BLACK = "30";
|
||||
pub const LOG_COLOR_RED = "31";
|
||||
pub const LOG_COLOR_GREEN = "32";
|
||||
pub const LOG_COLOR_BROWN = "33";
|
||||
pub const LOG_COLOR_BLUE = "34";
|
||||
pub const LOG_COLOR_PURPLE = "35";
|
||||
pub const LOG_COLOR_CYAN = "36";
|
||||
|
||||
pub inline fn LOG_COLOR(comptime COLOR: []const u8) []const u8 {
|
||||
return "\x1b[0;" ++ COLOR ++ "m";
|
||||
}
|
||||
pub inline fn LOG_BOLD(comptime COLOR: []const u8) []const u8 {
|
||||
return "\x1b[1;" ++ COLOR ++ "m";
|
||||
}
|
||||
|
||||
pub const LOG_RESET_COLOR = "\x1b[0m";
|
||||
pub const LOG_COLOR_E = LOG_COLOR(LOG_COLOR_RED);
|
||||
pub const LOG_COLOR_W = LOG_COLOR(LOG_COLOR_BROWN);
|
||||
pub const LOG_COLOR_I = LOG_COLOR(LOG_COLOR_GREEN);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Internal helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
inline fn isComptime(val: anytype) bool {
|
||||
return @typeInfo(@TypeOf(.{val})).@"struct".fields[0].is_comptime;
|
||||
}
|
||||
771
software/zig_main/imports/lwip.zig
Normal file
771
software/zig_main/imports/lwip.zig
Normal file
@@ -0,0 +1,771 @@
|
||||
const std = @import("std");
|
||||
const sys = @import("sys");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// lwIP error type — maps err_t (i8) to Zig errors.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const LwipError = error{
|
||||
OutOfMemory,
|
||||
BufferError,
|
||||
Timeout,
|
||||
RoutingError,
|
||||
InProgress,
|
||||
IllegalValue,
|
||||
WouldBlock,
|
||||
AddressInUse,
|
||||
AlreadyConnecting,
|
||||
AlreadyConnected,
|
||||
NotConnected,
|
||||
InterfaceError,
|
||||
Aborted,
|
||||
Reset,
|
||||
Closed,
|
||||
IllegalArgument,
|
||||
};
|
||||
|
||||
pub fn errFromC(e: sys.err_t) LwipError!void {
|
||||
return switch (@as(sys.err_enum_t, @enumFromInt(e))) {
|
||||
.ERR_OK => {},
|
||||
.ERR_MEM => LwipError.OutOfMemory,
|
||||
.ERR_BUF => LwipError.BufferError,
|
||||
.ERR_TIMEOUT => LwipError.Timeout,
|
||||
.ERR_RTE => LwipError.RoutingError,
|
||||
.ERR_INPROGRESS => LwipError.InProgress,
|
||||
.ERR_VAL => LwipError.IllegalValue,
|
||||
.ERR_WOULDBLOCK => LwipError.WouldBlock,
|
||||
.ERR_USE => LwipError.AddressInUse,
|
||||
.ERR_ALREADY => LwipError.AlreadyConnecting,
|
||||
.ERR_ISCONN => LwipError.AlreadyConnected,
|
||||
.ERR_CONN => LwipError.NotConnected,
|
||||
.ERR_IF => LwipError.InterfaceError,
|
||||
.ERR_ABRT => LwipError.Aborted,
|
||||
.ERR_RST => LwipError.Reset,
|
||||
.ERR_CLSD => LwipError.Closed,
|
||||
.ERR_ARG => LwipError.IllegalArgument,
|
||||
};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Re-export raw types callers may need.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const RawNetif = sys.netif;
|
||||
pub const RawPbuf = sys.pbuf;
|
||||
pub const RawIp4Addr = sys.ip4_addr_t;
|
||||
pub const RawIp6Addr = sys.ip6_addr_t;
|
||||
pub const RawIpAddr = sys.ip_addr_t;
|
||||
pub const RawSockAddr = sys.sockaddr;
|
||||
pub const SockLen = sys.socklen_t;
|
||||
pub const AddrFamily = sys.sa_family_t;
|
||||
pub const MsgHdr = sys.msghdr;
|
||||
pub const AddrInfo = sys.addrinfo;
|
||||
pub const HostEnt = sys.hostent;
|
||||
pub const PollFd = sys.pollfd;
|
||||
pub const NFds = sys.nfds_t;
|
||||
pub const IoVec = sys.iovec;
|
||||
pub const InAddr = sys.in_addr;
|
||||
pub const In6Addr = sys.in6_addr;
|
||||
pub const SockAddrIn = sys.sockaddr_in;
|
||||
pub const SockAddrIn6 = sys.sockaddr_in6;
|
||||
pub const SockAddrStore = sys.sockaddr_storage;
|
||||
pub const IpMreq = sys.ip_mreq;
|
||||
pub const Ipv6Mreq = sys.ipv6_mreq;
|
||||
pub const Linger = sys.linger;
|
||||
pub const InPktInfo = sys.in_pktinfo;
|
||||
pub const IpAddrType = sys.enum_lwip_ip_addr_type;
|
||||
pub const NetifMacFilterAction = sys.enum_netif_mac_filter_action;
|
||||
pub const PbufLayer = sys.pbuf_layer;
|
||||
pub const PbufType = sys.pbuf_type;
|
||||
pub const MempType = sys.memp_t;
|
||||
pub const DnsCb = sys.dns_found_callback;
|
||||
pub const NetifExtCbFn = sys.netif_ext_callback_fn;
|
||||
pub const NetifNscReason = sys.netif_nsc_reason_t;
|
||||
pub const SysThreadCoreLock = sys.sys_thread_core_lock_t;
|
||||
pub const LwipSock = sys.lwip_sock;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IP address helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Ip4Addr = struct {
|
||||
raw: RawIp4Addr,
|
||||
|
||||
pub fn fromString(s: [*:0]const u8) ?Ip4Addr {
|
||||
var a: RawIp4Addr = undefined;
|
||||
if (sys.ip4addr_aton(s, &a) == 0) return null;
|
||||
return .{ .raw = a };
|
||||
}
|
||||
|
||||
pub fn toString(self: *const Ip4Addr, buf: []u8) ?[]const u8 {
|
||||
const p = sys.ip4addr_ntoa_r(&self.raw, buf.ptr, @intCast(buf.len));
|
||||
if (p == null) return null;
|
||||
return std.mem.sliceTo(p, 0);
|
||||
}
|
||||
|
||||
pub fn isbroadcast(self: Ip4Addr, netif: *const RawNetif) bool {
|
||||
return sys.ip4_addr_isbroadcast_u32(self.raw.addr, netif) != 0;
|
||||
}
|
||||
|
||||
pub fn netmaskValid(self: Ip4Addr) bool {
|
||||
return sys.ip4_addr_netmask_valid(self.raw.addr) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Ip6Addr = struct {
|
||||
raw: RawIp6Addr,
|
||||
|
||||
pub fn fromString(s: [*:0]const u8) ?Ip6Addr {
|
||||
var a: RawIp6Addr = undefined;
|
||||
if (sys.ip6addr_aton(s, &a) == 0) return null;
|
||||
return .{ .raw = a };
|
||||
}
|
||||
|
||||
pub fn toString(self: *const Ip6Addr, buf: []u8) ?[]const u8 {
|
||||
const p = sys.ip6addr_ntoa_r(&self.raw, buf.ptr, @intCast(buf.len));
|
||||
if (p == null) return null;
|
||||
return std.mem.sliceTo(p, 0);
|
||||
}
|
||||
};
|
||||
|
||||
pub const IpAddr = struct {
|
||||
raw: RawIpAddr,
|
||||
|
||||
pub fn fromString(s: [*:0]const u8) ?IpAddr {
|
||||
var a: RawIpAddr = undefined;
|
||||
if (sys.ipaddr_aton(s, &a) == 0) return null;
|
||||
return .{ .raw = a };
|
||||
}
|
||||
|
||||
pub fn toString(self: *const IpAddr, buf: []u8) ?[]const u8 {
|
||||
const p = sys.ipaddr_ntoa_r(&self.raw, buf.ptr, @intCast(buf.len));
|
||||
if (p == null) return null;
|
||||
return std.mem.sliceTo(p, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Socket API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A file descriptor returned by socket(). Negative values are invalid.
|
||||
pub const Socket = struct {
|
||||
fd: c_int,
|
||||
|
||||
pub fn isValid(self: Socket) bool {
|
||||
return self.fd >= 0;
|
||||
}
|
||||
|
||||
// -- Lifecycle ----------------------------------------------------------
|
||||
|
||||
pub fn create(domain: c_int, kind: c_int, protocol: c_int) !Socket {
|
||||
const fd = sys.lwip_socket(domain, kind, protocol);
|
||||
if (fd < 0) return LwipError.InterfaceError;
|
||||
return .{ .fd = fd };
|
||||
}
|
||||
|
||||
pub fn close(self: Socket) !void {
|
||||
if (sys.lwip_close(self.fd) != 0) return LwipError.InterfaceError;
|
||||
}
|
||||
|
||||
pub fn shutdown(self: Socket, how: c_int) !void {
|
||||
if (sys.lwip_shutdown(self.fd, how) != 0) return LwipError.InterfaceError;
|
||||
}
|
||||
|
||||
// -- Address binding / connection ---------------------------------------
|
||||
|
||||
pub fn bind(self: Socket, addr: *const RawSockAddr, len: SockLen) !void {
|
||||
if (sys.lwip_bind(self.fd, addr, len) != 0) return LwipError.AddressInUse;
|
||||
}
|
||||
|
||||
pub fn listen(self: Socket, backlog: c_int) !void {
|
||||
if (sys.lwip_listen(self.fd, backlog) != 0) return LwipError.InterfaceError;
|
||||
}
|
||||
|
||||
pub fn accept(self: Socket, addr: ?*RawSockAddr, len: ?*SockLen) !Socket {
|
||||
const fd = sys.lwip_accept(self.fd, addr, len);
|
||||
if (fd < 0) return LwipError.NotConnected;
|
||||
return .{ .fd = fd };
|
||||
}
|
||||
|
||||
pub fn connect(self: Socket, addr: *const RawSockAddr, len: SockLen) !void {
|
||||
if (sys.lwip_connect(self.fd, addr, len) != 0) return LwipError.NotConnected;
|
||||
}
|
||||
|
||||
// -- Send / Receive -----------------------------------------------------
|
||||
|
||||
pub fn send(self: Socket, data: []const u8, flags: c_int) !usize {
|
||||
const n = sys.lwip_send(self.fd, data.ptr, data.len, flags);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn sendTo(
|
||||
self: Socket,
|
||||
data: []const u8,
|
||||
flags: c_int,
|
||||
to: *const RawSockAddr,
|
||||
tolen: SockLen,
|
||||
) !usize {
|
||||
const n = sys.lwip_sendto(self.fd, data.ptr, data.len, flags, to, tolen);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn sendMsg(self: Socket, msg: *const MsgHdr, flags: c_int) !usize {
|
||||
const n = sys.lwip_sendmsg(self.fd, msg, flags);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn recv(self: Socket, buf: []u8, flags: c_int) !usize {
|
||||
const n = sys.lwip_recv(self.fd, buf.ptr, buf.len, flags);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn recvFrom(
|
||||
self: Socket,
|
||||
buf: []u8,
|
||||
flags: c_int,
|
||||
from: ?*RawSockAddr,
|
||||
fromlen: ?*SockLen,
|
||||
) !usize {
|
||||
const n = sys.lwip_recvfrom(self.fd, buf.ptr, buf.len, flags, from, fromlen);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn recvMsg(self: Socket, msg: *MsgHdr, flags: c_int) !usize {
|
||||
const n = sys.lwip_recvmsg(self.fd, msg, flags);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn read(self: Socket, buf: []u8) !usize {
|
||||
const n = sys.lwip_read(self.fd, buf.ptr, buf.len);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn write(self: Socket, data: []const u8) !usize {
|
||||
const n = sys.lwip_write(self.fd, data.ptr, data.len);
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn readv(self: Socket, iov: []const IoVec) !usize {
|
||||
const n = sys.lwip_readv(self.fd, iov.ptr, @intCast(iov.len));
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
pub fn writev(self: Socket, iov: []const IoVec) !usize {
|
||||
const n = sys.lwip_writev(self.fd, iov.ptr, @intCast(iov.len));
|
||||
if (n < 0) return LwipError.InterfaceError;
|
||||
return @intCast(n);
|
||||
}
|
||||
|
||||
// -- Socket options -----------------------------------------------------
|
||||
|
||||
pub fn setOpt(
|
||||
self: Socket,
|
||||
level: c_int,
|
||||
optname: c_int,
|
||||
optval: *const anyopaque,
|
||||
optlen: SockLen,
|
||||
) !void {
|
||||
if (sys.lwip_setsockopt(self.fd, level, optname, optval, optlen) != 0)
|
||||
return LwipError.IllegalArgument;
|
||||
}
|
||||
|
||||
pub fn getOpt(
|
||||
self: Socket,
|
||||
level: c_int,
|
||||
optname: c_int,
|
||||
optval: *anyopaque,
|
||||
optlen: *SockLen,
|
||||
) !void {
|
||||
if (sys.lwip_getsockopt(self.fd, level, optname, optval, optlen) != 0)
|
||||
return LwipError.IllegalArgument;
|
||||
}
|
||||
|
||||
pub fn setOptExt(
|
||||
self: *LwipSock,
|
||||
level: c_int,
|
||||
optname: c_int,
|
||||
optval: *const anyopaque,
|
||||
optlen: u32,
|
||||
) !void {
|
||||
var err_code: c_int = 0;
|
||||
if (!sys.lwip_setsockopt_impl_ext(self, level, optname, optval, optlen, &err_code))
|
||||
return LwipError.IllegalArgument;
|
||||
}
|
||||
|
||||
pub fn getOptExt(
|
||||
self: *LwipSock,
|
||||
level: c_int,
|
||||
optname: c_int,
|
||||
optval: *anyopaque,
|
||||
optlen: *u32,
|
||||
) !void {
|
||||
var err_code: c_int = 0;
|
||||
if (!sys.lwip_getsockopt_impl_ext(self, level, optname, optval, optlen, &err_code))
|
||||
return LwipError.IllegalArgument;
|
||||
}
|
||||
|
||||
// -- Name queries -------------------------------------------------------
|
||||
|
||||
pub fn getPeerName(self: Socket, addr: *RawSockAddr, len: *SockLen) !void {
|
||||
if (sys.lwip_getpeername(self.fd, addr, len) != 0) return LwipError.NotConnected;
|
||||
}
|
||||
|
||||
pub fn getSockName(self: Socket, addr: *RawSockAddr, len: *SockLen) !void {
|
||||
if (sys.lwip_getsockname(self.fd, addr, len) != 0) return LwipError.InterfaceError;
|
||||
}
|
||||
|
||||
// -- Misc ---------------------------------------------------------------
|
||||
|
||||
pub fn ioctl(self: Socket, cmd: c_long, argp: ?*anyopaque) !void {
|
||||
if (sys.lwip_ioctl(self.fd, cmd, argp) != 0) return LwipError.IllegalArgument;
|
||||
}
|
||||
|
||||
pub fn fcntl(self: Socket, cmd: c_int, val: c_int) !c_int {
|
||||
const r = sys.lwip_fcntl(self.fd, cmd, val);
|
||||
if (r < 0) return LwipError.IllegalArgument;
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
/// poll() over a set of sockets.
|
||||
pub fn poll(fds: []PollFd, timeout_ms: c_int) !c_int {
|
||||
const r = sys.lwip_poll(fds.ptr, @intCast(fds.len), timeout_ms);
|
||||
if (r < 0) return LwipError.InterfaceError;
|
||||
return r;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Address conversion
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn inetNtop(af: c_int, src: *const anyopaque, dst: []u8) ?[*:0]const u8 {
|
||||
return sys.lwip_inet_ntop(af, src, dst.ptr, @intCast(dst.len));
|
||||
}
|
||||
|
||||
pub fn inetPton(af: c_int, src: [*:0]const u8, dst: *anyopaque) bool {
|
||||
return sys.lwip_inet_pton(af, src, dst) == 1;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DNS
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Dns = struct {
|
||||
pub fn init() void {
|
||||
sys.dns_init();
|
||||
}
|
||||
|
||||
pub fn setServer(index: u8, server: *const RawIpAddr) void {
|
||||
sys.dns_setserver(index, server);
|
||||
}
|
||||
|
||||
pub fn getServer(index: u8) *const RawIpAddr {
|
||||
return sys.dns_getserver(index);
|
||||
}
|
||||
|
||||
pub fn getHostByName(
|
||||
hostname: [*:0]const u8,
|
||||
addr: *RawIpAddr,
|
||||
callback: DnsCb,
|
||||
callback_arg: ?*anyopaque,
|
||||
) !void {
|
||||
try errFromC(sys.dns_gethostbyname(hostname, addr, callback, callback_arg));
|
||||
}
|
||||
|
||||
pub fn getHostByNameAddrType(
|
||||
hostname: [*:0]const u8,
|
||||
addr: *RawIpAddr,
|
||||
callback: DnsCb,
|
||||
callback_arg: ?*anyopaque,
|
||||
addr_type: u8,
|
||||
) !void {
|
||||
try errFromC(sys.dns_gethostbyname_addrtype(hostname, addr, callback, callback_arg, addr_type));
|
||||
}
|
||||
|
||||
pub fn clearCache() void {
|
||||
sys.dns_clear_cache();
|
||||
}
|
||||
|
||||
/// Blocking getaddrinfo (uses lwip_getaddrinfo).
|
||||
pub fn getAddrInfo(
|
||||
nodename: [*:0]const u8,
|
||||
servname: [*:0]const u8,
|
||||
hints: ?*const AddrInfo,
|
||||
res: *[*c]AddrInfo,
|
||||
) !void {
|
||||
if (sys.lwip_getaddrinfo(nodename, servname, hints, res) != 0)
|
||||
return LwipError.NotConnected;
|
||||
}
|
||||
|
||||
pub fn freeAddrInfo(ai: *AddrInfo) void {
|
||||
sys.lwip_freeaddrinfo(ai);
|
||||
}
|
||||
|
||||
pub fn getHostByNameR(
|
||||
name: [*:0]const u8,
|
||||
ret: *HostEnt,
|
||||
buf: []u8,
|
||||
result: *[*c]HostEnt,
|
||||
h_errnop: *c_int,
|
||||
) !void {
|
||||
if (sys.lwip_gethostbyname_r(name, ret, buf.ptr, buf.len, result, h_errnop) != 0)
|
||||
return LwipError.NotConnected;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Network interface (netif)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Netif = struct {
|
||||
pub fn init() void {
|
||||
sys.netif_init();
|
||||
}
|
||||
|
||||
pub fn add(
|
||||
netif_ptr: *RawNetif,
|
||||
ipaddr: ?*const RawIp4Addr,
|
||||
netmask: ?*const RawIp4Addr,
|
||||
gw: ?*const RawIp4Addr,
|
||||
state: ?*anyopaque,
|
||||
init_fn: sys.netif_init_fn,
|
||||
input_fn: sys.netif_input_fn,
|
||||
) ?*RawNetif {
|
||||
return sys.netif_add(netif_ptr, ipaddr, netmask, gw, state, init_fn, input_fn);
|
||||
}
|
||||
|
||||
pub fn addNoAddr(
|
||||
netif_ptr: *RawNetif,
|
||||
state: ?*anyopaque,
|
||||
init_fn: sys.netif_init_fn,
|
||||
input_fn: sys.netif_input_fn,
|
||||
) ?*RawNetif {
|
||||
return sys.netif_add_noaddr(netif_ptr, state, init_fn, input_fn);
|
||||
}
|
||||
|
||||
pub fn remove(netif_ptr: *RawNetif) void {
|
||||
sys.netif_remove(netif_ptr);
|
||||
}
|
||||
|
||||
pub fn find(name: [*:0]const u8) ?*RawNetif {
|
||||
return sys.netif_find(name);
|
||||
}
|
||||
|
||||
pub fn setDefault(netif_ptr: *RawNetif) void {
|
||||
sys.netif_set_default(netif_ptr);
|
||||
}
|
||||
|
||||
pub fn setAddr(
|
||||
netif_ptr: *RawNetif,
|
||||
ipaddr: *const RawIp4Addr,
|
||||
netmask: *const RawIp4Addr,
|
||||
gw: *const RawIp4Addr,
|
||||
) void {
|
||||
sys.netif_set_addr(netif_ptr, ipaddr, netmask, gw);
|
||||
}
|
||||
|
||||
pub fn setUp(netif_ptr: *RawNetif) void {
|
||||
sys.netif_set_up(netif_ptr);
|
||||
}
|
||||
pub fn setDown(netif_ptr: *RawNetif) void {
|
||||
sys.netif_set_down(netif_ptr);
|
||||
}
|
||||
pub fn setLinkUp(netif_ptr: *RawNetif) void {
|
||||
sys.netif_set_link_up(netif_ptr);
|
||||
}
|
||||
pub fn setLinkDown(netif_ptr: *RawNetif) void {
|
||||
sys.netif_set_link_down(netif_ptr);
|
||||
}
|
||||
|
||||
pub fn ip6AddrSet(netif_ptr: *RawNetif, idx: i8, addr: *const RawIp6Addr) void {
|
||||
sys.netif_ip6_addr_set(netif_ptr, idx, addr);
|
||||
}
|
||||
|
||||
pub fn ip6AddrSetState(netif_ptr: *RawNetif, idx: i8, state: u8) void {
|
||||
sys.netif_ip6_addr_set_state(netif_ptr, idx, state);
|
||||
}
|
||||
|
||||
pub fn createIp6Linklocal(netif_ptr: *RawNetif, from_mac_48bit: bool) void {
|
||||
sys.netif_create_ip6_linklocal_address(netif_ptr, @intFromBool(from_mac_48bit));
|
||||
}
|
||||
|
||||
pub fn addIp6Address(netif_ptr: *RawNetif, addr: *const RawIp6Addr, chosen_idx: *i8) !void {
|
||||
try errFromC(sys.netif_add_ip6_address(netif_ptr, addr, chosen_idx));
|
||||
}
|
||||
|
||||
pub fn nameToIndex(name: [*:0]const u8) u8 {
|
||||
return sys.netif_name_to_index(name);
|
||||
}
|
||||
|
||||
pub fn getByIndex(idx: u8) ?*RawNetif {
|
||||
return sys.netif_get_by_index(idx);
|
||||
}
|
||||
|
||||
pub fn addExtCallback(cb: *sys.netif_ext_callback_t, func: NetifExtCbFn) void {
|
||||
sys.netif_add_ext_callback(cb, func);
|
||||
}
|
||||
|
||||
pub fn removeExtCallback(cb: *sys.netif_ext_callback_t) void {
|
||||
sys.netif_remove_ext_callback(cb);
|
||||
}
|
||||
|
||||
pub fn input(p: *RawPbuf, inp: *RawNetif) !void {
|
||||
try errFromC(sys.netif_input(p, inp));
|
||||
}
|
||||
|
||||
pub fn loopOutput(netif_ptr: *RawNetif, p: *RawPbuf) !void {
|
||||
try errFromC(sys.netif_loop_output(netif_ptr, p));
|
||||
}
|
||||
|
||||
pub fn poll(netif_ptr: *RawNetif) void {
|
||||
sys.netif_poll(netif_ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Packet buffer (pbuf)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Pbuf = struct {
|
||||
pub fn alloc(layer: PbufLayer, length: u16, kind: PbufType) ?*RawPbuf {
|
||||
return sys.pbuf_alloc(layer, length, kind);
|
||||
}
|
||||
|
||||
pub fn free(p: *RawPbuf) u8 {
|
||||
return sys.pbuf_free(p);
|
||||
}
|
||||
|
||||
pub fn ref(p: *RawPbuf) void {
|
||||
sys.pbuf_ref(p);
|
||||
}
|
||||
|
||||
pub fn chain(head: *RawPbuf, tail: *RawPbuf) void {
|
||||
sys.pbuf_chain(head, tail);
|
||||
}
|
||||
|
||||
pub fn cat(head: *RawPbuf, tail: *RawPbuf) void {
|
||||
sys.pbuf_cat(head, tail);
|
||||
}
|
||||
|
||||
pub fn dechain(p: *RawPbuf) ?*RawPbuf {
|
||||
return sys.pbuf_dechain(p);
|
||||
}
|
||||
|
||||
pub fn copy(dst: *RawPbuf, src: *const RawPbuf) !void {
|
||||
try errFromC(sys.pbuf_copy(dst, src));
|
||||
}
|
||||
|
||||
pub fn copyPartial(p: *const RawPbuf, dst: []u8, offset: u16) u16 {
|
||||
return sys.pbuf_copy_partial(p, dst.ptr, @intCast(dst.len), offset);
|
||||
}
|
||||
|
||||
pub fn take(buf: *RawPbuf, data: []const u8) !void {
|
||||
try errFromC(sys.pbuf_take(buf, data.ptr, @intCast(data.len)));
|
||||
}
|
||||
|
||||
pub fn coalesce(p: *RawPbuf, layer: PbufLayer) ?*RawPbuf {
|
||||
return sys.pbuf_coalesce(p, layer);
|
||||
}
|
||||
|
||||
pub fn getAt(p: *const RawPbuf, offset: u16) u8 {
|
||||
return sys.pbuf_get_at(p, offset);
|
||||
}
|
||||
|
||||
pub fn putAt(p: *RawPbuf, offset: u16, data: u8) void {
|
||||
sys.pbuf_put_at(p, offset, data);
|
||||
}
|
||||
|
||||
pub fn chainLength(p: *const RawPbuf) u16 {
|
||||
return sys.pbuf_clen(p);
|
||||
}
|
||||
|
||||
pub fn realloc(p: *RawPbuf, size: u16) void {
|
||||
sys.pbuf_realloc(p, size);
|
||||
}
|
||||
|
||||
pub fn addHeader(p: *RawPbuf, increment: usize) bool {
|
||||
return sys.pbuf_add_header(p, increment) == 0;
|
||||
}
|
||||
|
||||
pub fn removeHeader(p: *RawPbuf, size: usize) bool {
|
||||
return sys.pbuf_remove_header(p, size) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SNTP helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const SNTP = struct {
|
||||
pub fn getSyncInterval() u32 {
|
||||
return sys.sntp_get_sync_interval();
|
||||
}
|
||||
pub fn setSystemTime(sec: u32, us: u32) void {
|
||||
sys.sntp_set_system_time(sec, us);
|
||||
}
|
||||
pub fn getSystemTime(sec: *u32, us: *u32) void {
|
||||
sys.sntp_get_system_time(sec, us);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// lwIP OS abstraction (sys_arch_*)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Sys = struct {
|
||||
pub const Sem = sys.sys_sem_t;
|
||||
pub const Mutex = sys.sys_mutex_t;
|
||||
pub const Thread = sys.sys_thread_t;
|
||||
pub const Mbox = sys.sys_mbox_t;
|
||||
|
||||
pub fn init() void {
|
||||
sys.sys_init();
|
||||
}
|
||||
pub fn now() u32 {
|
||||
return sys.sys_now();
|
||||
}
|
||||
pub fn jiffies() u32 {
|
||||
return sys.sys_jiffies();
|
||||
}
|
||||
pub fn delayMs(ms: u32) void {
|
||||
sys.sys_delay_ms(ms);
|
||||
}
|
||||
|
||||
pub fn semNew(sem: *Sem, count: u8) !void {
|
||||
try errFromC(sys.sys_sem_new(sem, count));
|
||||
}
|
||||
pub fn semSignal(sem: *Sem) void {
|
||||
sys.sys_sem_signal(sem);
|
||||
}
|
||||
pub fn semWait(sem: *Sem, timeout_ms: u32) u32 {
|
||||
return sys.sys_arch_sem_wait(sem, timeout_ms);
|
||||
}
|
||||
pub fn semFree(sem: *Sem) void {
|
||||
sys.sys_sem_free(sem);
|
||||
}
|
||||
|
||||
pub fn mutexNew(m: *Mutex) !void {
|
||||
try errFromC(sys.sys_mutex_new(m));
|
||||
}
|
||||
pub fn mutexLock(m: *Mutex) void {
|
||||
sys.sys_mutex_lock(m);
|
||||
}
|
||||
pub fn mutexUnlock(m: *Mutex) void {
|
||||
sys.sys_mutex_unlock(m);
|
||||
}
|
||||
pub fn mutexFree(m: *Mutex) void {
|
||||
sys.sys_mutex_free(m);
|
||||
}
|
||||
|
||||
pub fn mboxNew(mbox: *Mbox, size: c_int) !void {
|
||||
try errFromC(sys.sys_mbox_new(mbox, size));
|
||||
}
|
||||
pub fn mboxPost(mbox: *Mbox, msg: ?*anyopaque) void {
|
||||
sys.sys_mbox_post(mbox, msg);
|
||||
}
|
||||
pub fn mboxTryPost(mbox: *Mbox, msg: ?*anyopaque) !void {
|
||||
try errFromC(sys.sys_mbox_trypost(mbox, msg));
|
||||
}
|
||||
pub fn mboxTryPostFromISR(mbox: *Mbox, msg: ?*anyopaque) !void {
|
||||
try errFromC(sys.sys_mbox_trypost_fromisr(mbox, msg));
|
||||
}
|
||||
pub fn mboxFetch(mbox: *Mbox, msg: *?*anyopaque, timeout_ms: u32) u32 {
|
||||
return sys.sys_arch_mbox_fetch(mbox, msg, timeout_ms);
|
||||
}
|
||||
pub fn mboxTryFetch(mbox: *Mbox, msg: *?*anyopaque) u32 {
|
||||
return sys.sys_arch_mbox_tryfetch(mbox, msg);
|
||||
}
|
||||
pub fn mboxFree(mbox: *Mbox) void {
|
||||
sys.sys_mbox_free(mbox);
|
||||
}
|
||||
|
||||
pub fn threadNew(
|
||||
name: [*:0]const u8,
|
||||
thread_fn: sys.lwip_thread_fn,
|
||||
arg: ?*anyopaque,
|
||||
stacksize: c_int,
|
||||
prio: c_int,
|
||||
) Thread {
|
||||
return sys.sys_thread_new(name, thread_fn, arg, stacksize, prio);
|
||||
}
|
||||
|
||||
pub fn threadSemInit() [*c]Sem {
|
||||
return sys.sys_thread_sem_init();
|
||||
}
|
||||
pub fn threadSemDeinit() void {
|
||||
sys.sys_thread_sem_deinit();
|
||||
}
|
||||
pub fn threadSemGet() [*c]Sem {
|
||||
return sys.sys_thread_sem_get();
|
||||
}
|
||||
pub fn threadTcpip(kind: SysThreadCoreLock) bool {
|
||||
return sys.sys_thread_tcpip(kind);
|
||||
}
|
||||
|
||||
pub fn archProtect() c_int {
|
||||
return sys.sys_arch_protect();
|
||||
}
|
||||
pub fn archUnprotect(pval: c_int) void {
|
||||
sys.sys_arch_unprotect(pval);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// lwIP memory pool helpers (low-level, rarely used directly)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Memp = struct {
|
||||
pub fn init() void {
|
||||
sys.memp_init();
|
||||
}
|
||||
pub fn malloc(kind: MempType) ?*anyopaque {
|
||||
return sys.memp_malloc(kind);
|
||||
}
|
||||
pub fn free(kind: MempType, mem: ?*anyopaque) void {
|
||||
sys.memp_free(kind, mem);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Byte order helpers (htons/htonl)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub inline fn htons(x: u16) u16 {
|
||||
return sys.lwip_htons(x);
|
||||
}
|
||||
pub inline fn htonl(x: u32) u32 {
|
||||
return sys.lwip_htonl(x);
|
||||
}
|
||||
pub inline fn ntohs(x: u16) u16 {
|
||||
return sys.lwip_htons(x);
|
||||
} // symmetric on all archs
|
||||
pub inline fn ntohl(x: u32) u32 {
|
||||
return sys.lwip_htonl(x);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// String helpers (lwip_str*)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn strnicmp(a: [*:0]const u8, b: [*:0]const u8, n: usize) c_int {
|
||||
return sys.lwip_strnicmp(a, b, n);
|
||||
}
|
||||
pub fn stricmp(a: [*:0]const u8, b: [*:0]const u8) c_int {
|
||||
return sys.lwip_stricmp(a, b);
|
||||
}
|
||||
pub fn strnstr(haystack: [*:0]const u8, needle: [*:0]const u8, n: usize) ?[*:0]u8 {
|
||||
return sys.lwip_strnstr(haystack, needle, n);
|
||||
}
|
||||
pub fn itoa(buf: []u8, number: c_int) void {
|
||||
sys.lwip_itoa(buf.ptr, buf.len, number);
|
||||
}
|
||||
252
software/zig_main/imports/matter.zig
Normal file
252
software/zig_main/imports/matter.zig
Normal file
@@ -0,0 +1,252 @@
|
||||
//! ESP Matter wrapper — Zig interface to the CHIP/Matter C++ SDK via matter_stubs.h.
|
||||
//!
|
||||
//! Requires the `espressif/esp_matter` managed component.
|
||||
//! To add it: `idf.py add-dependency espressif/esp_matter`
|
||||
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ── Re-export core types ─────────────────────────────────────────────────────
|
||||
|
||||
pub const Node = sys.esp_matter_node_t;
|
||||
pub const Endpoint = sys.esp_matter_endpoint_t;
|
||||
pub const Cluster = sys.esp_matter_cluster_t;
|
||||
pub const Attribute = sys.esp_matter_attribute_t;
|
||||
|
||||
/// Endpoint creation flags.
|
||||
pub const EpFlags = enum(u8) {
|
||||
none = sys.ESP_MATTER_EP_FLAG_NONE,
|
||||
destroyable = sys.ESP_MATTER_EP_FLAG_DESTROYABLE,
|
||||
bridge = sys.ESP_MATTER_EP_FLAG_BRIDGE,
|
||||
};
|
||||
|
||||
/// Attribute value types (Zig enum wrapping the C enum constants).
|
||||
pub const ValType = enum(c_uint) {
|
||||
invalid = sys.ESP_MATTER_VAL_TYPE_INVALID,
|
||||
boolean = sys.ESP_MATTER_VAL_TYPE_BOOLEAN,
|
||||
integer = sys.ESP_MATTER_VAL_TYPE_INTEGER,
|
||||
float_ = sys.ESP_MATTER_VAL_TYPE_FLOAT,
|
||||
array = sys.ESP_MATTER_VAL_TYPE_ARRAY,
|
||||
char_string = sys.ESP_MATTER_VAL_TYPE_CHAR_STRING,
|
||||
octet_string = sys.ESP_MATTER_VAL_TYPE_OCTET_STRING,
|
||||
int8 = sys.ESP_MATTER_VAL_TYPE_INT8,
|
||||
uint8 = sys.ESP_MATTER_VAL_TYPE_UINT8,
|
||||
int16 = sys.ESP_MATTER_VAL_TYPE_INT16,
|
||||
uint16 = sys.ESP_MATTER_VAL_TYPE_UINT16,
|
||||
int32 = sys.ESP_MATTER_VAL_TYPE_INT32,
|
||||
uint32 = sys.ESP_MATTER_VAL_TYPE_UINT32,
|
||||
int64 = sys.ESP_MATTER_VAL_TYPE_INT64,
|
||||
uint64 = sys.ESP_MATTER_VAL_TYPE_UINT64,
|
||||
enum8 = sys.ESP_MATTER_VAL_TYPE_ENUM8,
|
||||
bitmap8 = sys.ESP_MATTER_VAL_TYPE_BITMAP8,
|
||||
bitmap16 = sys.ESP_MATTER_VAL_TYPE_BITMAP16,
|
||||
bitmap32 = sys.ESP_MATTER_VAL_TYPE_BITMAP32,
|
||||
enum16 = sys.ESP_MATTER_VAL_TYPE_ENUM16,
|
||||
long_char_string = sys.ESP_MATTER_VAL_TYPE_LONG_CHAR_STRING,
|
||||
long_octet_string = sys.ESP_MATTER_VAL_TYPE_LONG_OCTET_STRING,
|
||||
_,
|
||||
};
|
||||
|
||||
/// Attribute value (union mirroring esp_matter_val_t).
|
||||
pub const Val = sys.esp_matter_val_t;
|
||||
|
||||
/// Attribute value with type tag.
|
||||
pub const AttrVal = sys.esp_matter_attr_val_t;
|
||||
|
||||
/// Attribute update callback type (Zig enum with named members).
|
||||
/// Use e.g. `matter.AttrCbType.ESP_MATTER_ATTR_CB_POST_UPDATE`.
|
||||
pub const AttrCbType = enum(c_uint) {
|
||||
ESP_MATTER_ATTR_CB_PRE_UPDATE = sys.ESP_MATTER_ATTR_CB_PRE_UPDATE,
|
||||
ESP_MATTER_ATTR_CB_POST_UPDATE = sys.ESP_MATTER_ATTR_CB_POST_UPDATE,
|
||||
ESP_MATTER_ATTR_CB_READ = sys.ESP_MATTER_ATTR_CB_READ,
|
||||
ESP_MATTER_ATTR_CB_WRITE = sys.ESP_MATTER_ATTR_CB_WRITE,
|
||||
_,
|
||||
};
|
||||
|
||||
/// Attribute update callback function pointer type.
|
||||
/// The callback receives a `?*AttrVal` which at the C ABI boundary is `[*c]AttrVal`.
|
||||
/// Return 0 (ESP_OK) to allow, non-zero from PRE_UPDATE to block.
|
||||
pub const AttrCallback = ?*const fn (
|
||||
AttrCbType,
|
||||
u16,
|
||||
u32,
|
||||
u32,
|
||||
?*AttrVal,
|
||||
?*anyopaque,
|
||||
) callconv(.c) c_int;
|
||||
|
||||
/// Identify cluster callback type.
|
||||
pub const IdentifyCbType = enum(c_uint) {
|
||||
ESP_MATTER_IDENTIFY_CB_START = sys.ESP_MATTER_IDENTIFY_CB_START,
|
||||
ESP_MATTER_IDENTIFY_CB_STOP = sys.ESP_MATTER_IDENTIFY_CB_STOP,
|
||||
ESP_MATTER_IDENTIFY_CB_EFFECT = sys.ESP_MATTER_IDENTIFY_CB_EFFECT,
|
||||
_,
|
||||
};
|
||||
|
||||
/// Identify cluster callback function pointer type.
|
||||
pub const IdentifyCallback = ?*const fn (IdentifyCbType, u16, u8, u8, ?*anyopaque) callconv(.c) c_int;
|
||||
|
||||
// ── Attribute value helpers ──────────────────────────────────────────────────
|
||||
|
||||
/// Convenience wrappers matching esp_matter_val_bool(), esp_matter_uint8(), etc.
|
||||
pub const val = struct {
|
||||
pub fn boolean(b: bool) AttrVal {
|
||||
return sys.esp_matter_val_bool(b);
|
||||
}
|
||||
pub fn uint8(v: u8) AttrVal {
|
||||
return sys.esp_matter_val_uint8(v);
|
||||
}
|
||||
pub fn uint16(v: u16) AttrVal {
|
||||
return sys.esp_matter_val_uint16(v);
|
||||
}
|
||||
pub fn uint32(v: u32) AttrVal {
|
||||
return sys.esp_matter_val_uint32(v);
|
||||
}
|
||||
pub fn int16(v: i16) AttrVal {
|
||||
return sys.esp_matter_val_int16(v);
|
||||
}
|
||||
pub fn nullable() AttrVal {
|
||||
return sys.esp_matter_val_nullable();
|
||||
}
|
||||
};
|
||||
|
||||
// ── Core ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Start the Matter stack (non-blocking; spawns Matter OS task).
|
||||
/// Call after building the node + endpoint + cluster data model.
|
||||
pub fn start(attr_cb: AttrCallback, identify_cb: IdentifyCallback) !void {
|
||||
// @ptrCast converts our typed function pointer to the raw sys type.
|
||||
// The types are ABI-compatible: enum(c_uint)↔c_uint, ?*T↔[*c]T.
|
||||
try errors.espCheckError(sys.esp_matter_wrapper_start(@ptrCast(attr_cb), @ptrCast(identify_cb)));
|
||||
}
|
||||
|
||||
/// Erase Matter NVS data and reboot. Non-recoverable.
|
||||
pub fn factoryReset() !void {
|
||||
try errors.espCheckError(sys.esp_matter_wrapper_factory_reset());
|
||||
}
|
||||
|
||||
/// Return true if Matter has been started.
|
||||
pub fn isStarted() bool {
|
||||
return sys.esp_matter_wrapper_is_started();
|
||||
}
|
||||
|
||||
// ── Node ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Create the root Matter node (endpoint 0, Root Node device type).
|
||||
/// attr_cb fires on every attribute read/write; identify_cb fires on Identify requests.
|
||||
/// priv_data is forwarded to both callbacks.
|
||||
pub fn nodeCreate(attr_cb: AttrCallback, identify_cb: IdentifyCallback, priv_data: ?*anyopaque) !*Node {
|
||||
const n = sys.esp_matter_wrapper_node_create(@ptrCast(attr_cb), @ptrCast(identify_cb), priv_data);
|
||||
if (n == null) return error.MatterNodeCreateFailed;
|
||||
return @ptrCast(n);
|
||||
}
|
||||
|
||||
// ── Endpoint ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Endpoint operations.
|
||||
pub const endpoint = struct {
|
||||
/// Create a generic endpoint on the node.
|
||||
pub fn create(node: *Node, flags: EpFlags, priv_data: ?*anyopaque) !*Endpoint {
|
||||
const ep = sys.esp_matter_wrapper_endpoint_create(node, @intFromEnum(flags), priv_data);
|
||||
if (ep == null) return error.MatterEndpointCreateFailed;
|
||||
return @ptrCast(ep);
|
||||
}
|
||||
|
||||
/// Destroy a destroyable endpoint.
|
||||
pub fn destroy(node: *Node, ep: *Endpoint) !void {
|
||||
try errors.espCheckError(sys.esp_matter_wrapper_endpoint_destroy(node, ep));
|
||||
}
|
||||
|
||||
/// Return the endpoint's numeric ID.
|
||||
pub fn getId(ep: *Endpoint) u16 {
|
||||
return sys.esp_matter_wrapper_endpoint_get_id(ep);
|
||||
}
|
||||
|
||||
/// Associate a Matter device type with this endpoint.
|
||||
pub fn addDeviceType(ep: *Endpoint, device_type_id: u32, version: u8) !void {
|
||||
try errors.espCheckError(
|
||||
sys.esp_matter_wrapper_endpoint_add_device_type(ep, device_type_id, version),
|
||||
);
|
||||
}
|
||||
|
||||
/// Enable a dynamically-created endpoint (only after Matter is started).
|
||||
pub fn enable(ep: *Endpoint) !void {
|
||||
try errors.espCheckError(sys.esp_matter_wrapper_endpoint_enable(ep));
|
||||
}
|
||||
|
||||
// ── Pre-built device-type helpers ─────────────────────────────────────
|
||||
|
||||
/// Add an On/Off Light endpoint (device type 0x0100).
|
||||
pub fn addOnOffLight(node: *Node, flags: EpFlags, priv_data: ?*anyopaque) !*Endpoint {
|
||||
const ep = sys.esp_matter_wrapper_add_on_off_light(node, @intFromEnum(flags), priv_data);
|
||||
if (ep == null) return error.MatterEndpointCreateFailed;
|
||||
return @ptrCast(ep);
|
||||
}
|
||||
|
||||
/// Add an On/Off Switch endpoint (device type 0x0103).
|
||||
pub fn addOnOffSwitch(node: *Node, flags: EpFlags, priv_data: ?*anyopaque) !*Endpoint {
|
||||
const ep = sys.esp_matter_wrapper_add_on_off_switch(node, @intFromEnum(flags), priv_data);
|
||||
if (ep == null) return error.MatterEndpointCreateFailed;
|
||||
return @ptrCast(ep);
|
||||
}
|
||||
|
||||
/// Add a Dimmable Light endpoint (device type 0x0101).
|
||||
pub fn addDimmableLight(node: *Node, flags: EpFlags, priv_data: ?*anyopaque) !*Endpoint {
|
||||
const ep = sys.esp_matter_wrapper_add_dimmable_light(node, @intFromEnum(flags), priv_data);
|
||||
if (ep == null) return error.MatterEndpointCreateFailed;
|
||||
return @ptrCast(ep);
|
||||
}
|
||||
|
||||
/// Add a Color Temperature Light endpoint (device type 0x010C).
|
||||
pub fn addColorTemperatureLight(node: *Node, flags: EpFlags, priv_data: ?*anyopaque) !*Endpoint {
|
||||
const ep = sys.esp_matter_wrapper_add_color_temperature_light(node, @intFromEnum(flags), priv_data);
|
||||
if (ep == null) return error.MatterEndpointCreateFailed;
|
||||
return @ptrCast(ep);
|
||||
}
|
||||
};
|
||||
|
||||
// ── Cluster ───────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Cluster operations.
|
||||
pub const cluster = struct {
|
||||
/// Cluster flag: server cluster.
|
||||
pub const FLAG_SERVER: u8 = 0x40;
|
||||
/// Cluster flag: client cluster.
|
||||
pub const FLAG_CLIENT: u8 = 0x80;
|
||||
|
||||
/// Create a cluster on an endpoint.
|
||||
pub fn create(ep: *Endpoint, cluster_id: u32, flags: u8) !*Cluster {
|
||||
const cl = sys.esp_matter_wrapper_cluster_create(ep, cluster_id, flags);
|
||||
if (cl == null) return error.MatterClusterCreateFailed;
|
||||
return @ptrCast(cl);
|
||||
}
|
||||
};
|
||||
|
||||
// ── Attribute ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Attribute operations.
|
||||
pub const attribute = struct {
|
||||
/// Create an attribute on a cluster with a default value.
|
||||
pub fn create(cl: *Cluster, attribute_id: u32, flags: u16, default_val: AttrVal) !*Attribute {
|
||||
const attr = sys.esp_matter_wrapper_attribute_create(cl, attribute_id, flags, default_val);
|
||||
if (attr == null) return error.MatterAttributeCreateFailed;
|
||||
return @ptrCast(attr);
|
||||
}
|
||||
|
||||
/// Update an attribute value (use after Matter is started).
|
||||
pub fn update(endpoint_id: u16, cluster_id: u32, attribute_id: u32, v: *AttrVal) !void {
|
||||
try errors.espCheckError(
|
||||
sys.esp_matter_wrapper_attribute_update(endpoint_id, cluster_id, attribute_id, v),
|
||||
);
|
||||
}
|
||||
|
||||
/// Get the current value of an attribute.
|
||||
pub fn getVal(attr: *Attribute, v: *AttrVal) !void {
|
||||
try errors.espCheckError(sys.esp_matter_wrapper_attribute_get_val(attr, v));
|
||||
}
|
||||
|
||||
/// Set an attribute value (use before Matter is started).
|
||||
pub fn setVal(attr: *Attribute, v: *AttrVal) !void {
|
||||
try errors.espCheckError(sys.esp_matter_wrapper_attribute_set_val(attr, v));
|
||||
}
|
||||
};
|
||||
236
software/zig_main/imports/mqtt.zig
Normal file
236
software/zig_main/imports/mqtt.zig
Normal file
@@ -0,0 +1,236 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Re-exported types
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
pub const Handle = sys.esp_mqtt_client_handle_t;
|
||||
pub const EventId = sys.esp_mqtt_event_id_t;
|
||||
pub const Event = sys.esp_mqtt_event_t;
|
||||
pub const EventHandle = sys.esp_mqtt_event_handle_t;
|
||||
pub const EventHandler = sys.esp_event_handler_t;
|
||||
pub const ClientConfig = sys.esp_mqtt_client_config_t;
|
||||
pub const Topic = sys.esp_mqtt_topic_t;
|
||||
pub const ErrorCodes = sys.esp_mqtt_error_codes_t;
|
||||
pub const ErrorType = sys.esp_mqtt_error_type_t;
|
||||
pub const Transport = sys.esp_mqtt_transport_t;
|
||||
pub const ProtocolVersion = sys.esp_mqtt_protocol_ver_t;
|
||||
pub const ConnectReturnCode = sys.esp_mqtt_connect_return_code_t;
|
||||
|
||||
// MQTT v5
|
||||
pub const Handle5 = sys.esp_mqtt5_client_handle_t;
|
||||
pub const ErrorReasonCode5 = sys.mqtt5_error_reason_code;
|
||||
pub const UserPropertyHandle5 = sys.mqtt5_user_property_handle_t;
|
||||
pub const UserPropertyItem5 = sys.esp_mqtt5_user_property_item_t;
|
||||
pub const ConnectionProperty5 = sys.esp_mqtt5_connection_property_config_t;
|
||||
pub const PublishProperty5 = sys.esp_mqtt5_publish_property_config_t;
|
||||
pub const SubscribeProperty5 = sys.esp_mqtt5_subscribe_property_config_t;
|
||||
pub const UnsubscribeProperty5 = sys.esp_mqtt5_unsubscribe_property_config_t;
|
||||
pub const DisconnectProperty5 = sys.esp_mqtt5_disconnect_property_config_t;
|
||||
pub const EventProperty5 = sys.esp_mqtt5_event_property_t;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Errors
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
pub const Error = error{
|
||||
/// publish / subscribe / enqueue returned -1 (queuing failed)
|
||||
Failed,
|
||||
/// publish returned -2 (outbox full)
|
||||
OutboxFull,
|
||||
};
|
||||
|
||||
/// Converts a message-id return value (≥0 = ok, -1 = failed, -2 = outbox full)
|
||||
/// into `!u32`.
|
||||
fn msgIdResult(rc: c_int) Error!u32 {
|
||||
return switch (rc) {
|
||||
-2 => error.OutboxFull,
|
||||
-1 => error.Failed,
|
||||
else => @intCast(rc),
|
||||
};
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Client lifecycle
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Initialise a new MQTT client. Returns `null` if allocation fails.
|
||||
pub fn init(cfg: *const ClientConfig) ?Handle {
|
||||
return sys.esp_mqtt_client_init(cfg);
|
||||
}
|
||||
|
||||
pub fn deinit(client: Handle) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_destroy(client));
|
||||
}
|
||||
|
||||
pub fn setConfig(client: Handle, cfg: *const ClientConfig) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_set_config(client, cfg));
|
||||
}
|
||||
|
||||
pub fn setUri(client: Handle, uri: [:0]const u8) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_set_uri(client, uri.ptr));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Connection control
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn start(client: Handle) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_start(client));
|
||||
}
|
||||
|
||||
pub fn stop(client: Handle) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_stop(client));
|
||||
}
|
||||
|
||||
pub fn reconnect(client: Handle) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_reconnect(client));
|
||||
}
|
||||
|
||||
pub fn disconnect(client: Handle) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_disconnect(client));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Publish
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const PublishConfig = struct {
|
||||
qos: u2 = 0,
|
||||
retain: bool = false,
|
||||
};
|
||||
|
||||
/// Publish `data` to `topic`. Returns the message-id assigned by the broker.
|
||||
pub fn publish(client: Handle, topic: [:0]const u8, data: []const u8, cfg: PublishConfig) Error!u32 {
|
||||
return msgIdResult(sys.esp_mqtt_client_publish(
|
||||
client,
|
||||
topic.ptr,
|
||||
data.ptr,
|
||||
@intCast(data.len),
|
||||
@intCast(cfg.qos),
|
||||
@intFromBool(cfg.retain),
|
||||
));
|
||||
}
|
||||
|
||||
pub const EnqueueConfig = struct {
|
||||
qos: u2 = 0,
|
||||
retain: bool = false,
|
||||
/// Store in outbox even when disconnected.
|
||||
store: bool = true,
|
||||
};
|
||||
|
||||
/// Enqueue a publish for delivery (stores in outbox when disconnected).
|
||||
pub fn enqueue(client: Handle, topic: [:0]const u8, data: []const u8, cfg: EnqueueConfig) Error!u32 {
|
||||
return msgIdResult(sys.esp_mqtt_client_enqueue(
|
||||
client,
|
||||
topic.ptr,
|
||||
data.ptr,
|
||||
@intCast(data.len),
|
||||
@intCast(cfg.qos),
|
||||
@intFromBool(cfg.retain),
|
||||
cfg.store,
|
||||
));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Subscribe / Unsubscribe
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Subscribe to a single topic filter. Returns the message-id.
|
||||
pub fn subscribe(client: Handle, topic: [:0]const u8, qos: u2) Error!u32 {
|
||||
return msgIdResult(sys.esp_mqtt_client_subscribe_single(client, topic.ptr, @intCast(qos)));
|
||||
}
|
||||
|
||||
/// Subscribe to multiple topic filters in one call.
|
||||
pub fn subscribeMultiple(client: Handle, topics: []const Topic) Error!u32 {
|
||||
return msgIdResult(sys.esp_mqtt_client_subscribe_multiple(
|
||||
client,
|
||||
topics.ptr,
|
||||
@intCast(topics.len),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn unsubscribe(client: Handle, topic: [:0]const u8) Error!u32 {
|
||||
return msgIdResult(sys.esp_mqtt_client_unsubscribe(client, topic.ptr));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Event registration
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn registerEvent(client: Handle, event: EventId, handler: EventHandler, arg: ?*anyopaque) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_register_event(client, event, handler, arg));
|
||||
}
|
||||
|
||||
pub fn unregisterEvent(client: Handle, event: EventId, handler: EventHandler) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_client_unregister_event(client, event, handler));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Diagnostics
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Returns the current outbox size in bytes.
|
||||
pub fn getOutboxSize(client: Handle) usize {
|
||||
return @intCast(sys.esp_mqtt_client_get_outbox_size(client));
|
||||
}
|
||||
|
||||
/// Dispatch a custom application event through the MQTT event loop.
|
||||
pub fn dispatchCustomEvent(client: Handle, event: *Event) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt_dispatch_custom_event(client, event));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// MQTT v5 extensions
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const v5 = struct {
|
||||
pub fn setConnectProperty(client: Handle5, prop: *const ConnectionProperty5) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_set_connect_property(client, prop));
|
||||
}
|
||||
|
||||
pub fn setPublishProperty(client: Handle5, prop: *const PublishProperty5) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_set_publish_property(client, prop));
|
||||
}
|
||||
|
||||
pub fn setSubscribeProperty(client: Handle5, prop: *const SubscribeProperty5) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_set_subscribe_property(client, prop));
|
||||
}
|
||||
|
||||
pub fn setUnsubscribeProperty(client: Handle5, prop: *const UnsubscribeProperty5) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_set_unsubscribe_property(client, prop));
|
||||
}
|
||||
|
||||
pub fn setDisconnectProperty(client: Handle5, prop: *const DisconnectProperty5) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_set_disconnect_property(client, prop));
|
||||
}
|
||||
|
||||
// ── User properties ───────────────────────────────────────────────────
|
||||
|
||||
pub fn setUserProperty(handle: *UserPropertyHandle5, items: []UserPropertyItem5) !void {
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_set_user_property(
|
||||
handle,
|
||||
items.ptr,
|
||||
@intCast(items.len),
|
||||
));
|
||||
}
|
||||
|
||||
/// Returns how many user-property items are present.
|
||||
pub fn getUserPropertyCount(handle: UserPropertyHandle5) u8 {
|
||||
return sys.esp_mqtt5_client_get_user_property_count(handle);
|
||||
}
|
||||
|
||||
/// Read user-property items into the caller-supplied slice.
|
||||
/// The slice length must be ≥ `getUserPropertyCount()`.
|
||||
pub fn getUserProperty(handle: UserPropertyHandle5, out: []UserPropertyItem5) !void {
|
||||
var count: u8 = @intCast(out.len);
|
||||
return errors.espCheckError(sys.esp_mqtt5_client_get_user_property(
|
||||
handle,
|
||||
out.ptr,
|
||||
&count,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn deleteUserProperty(handle: UserPropertyHandle5) void {
|
||||
sys.esp_mqtt5_client_delete_user_property(handle);
|
||||
}
|
||||
};
|
||||
158
software/zig_main/imports/nimble.zig
Normal file
158
software/zig_main/imports/nimble.zig
Normal file
@@ -0,0 +1,158 @@
|
||||
// NimBLE wrapper — requires CONFIG_BT_NIMBLE_ENABLED in sdkconfig.
|
||||
//
|
||||
// Enable via:
|
||||
// idf.py menuconfig → Component config → Bluetooth → NimBLE
|
||||
//
|
||||
// When enabled, re-run `idf.py reconfigure` to regenerate the bindings.
|
||||
// NimBLE symbols (ble_hs_*, ble_gap_*, ble_gatt_*) will appear in idf-sys.zig.
|
||||
const sys = @import("sys");
|
||||
|
||||
comptime {
|
||||
if (!@hasDecl(sys, "CONFIG_BT_NIMBLE_ENABLED"))
|
||||
@compileError(
|
||||
\\NimBLE is not enabled. Enable it via:
|
||||
\\ idf.py menuconfig → Component config → Bluetooth
|
||||
\\ → Host → NimBLE - BLE stack
|
||||
\\Then run: idf.py reconfigure
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// NimBLE type aliases (available only when CONFIG_BT_NIMBLE_ENABLED=y)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const HsCfg = sys.ble_hs_cfg_t;
|
||||
pub const GapConnDesc = sys.ble_gap_conn_desc;
|
||||
pub const GapAdvParams = sys.ble_gap_adv_params;
|
||||
pub const GapConnParams = sys.ble_gap_conn_params;
|
||||
pub const GattSvcDef = sys.ble_gatt_svc_def;
|
||||
pub const GattCharDef = sys.ble_gatt_chr_def;
|
||||
pub const GattDscrDef = sys.ble_gatt_dsc_def;
|
||||
pub const Uuid16 = sys.ble_uuid16_t;
|
||||
pub const Uuid128 = sys.ble_uuid128_t;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Host stack
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Host = struct {
|
||||
/// Initialize the NimBLE host stack (call before syncing).
|
||||
pub fn init() void {
|
||||
sys.nimble_port_init();
|
||||
}
|
||||
|
||||
/// Start the NimBLE host task.
|
||||
pub fn run() void {
|
||||
sys.nimble_port_run();
|
||||
}
|
||||
|
||||
/// Free task resources (call from host task on exit).
|
||||
pub fn freeThenResume() void {
|
||||
sys.nimble_port_freertos_deinit();
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GAP (Generic Access Profile)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Gap = struct {
|
||||
pub const AdvType = sys.ble_gap_adv_type;
|
||||
pub const EventCb = sys.ble_gap_event_fn;
|
||||
|
||||
pub fn setDeviceName(name: [*:0]const u8) !void {
|
||||
const rc = sys.ble_svc_gap_device_name_set(name);
|
||||
if (rc != 0) return error.NimbleError;
|
||||
}
|
||||
|
||||
pub fn advStart(
|
||||
own_addr_type: u8,
|
||||
direct_addr: ?*const sys.ble_addr_t,
|
||||
duration_ms: i32,
|
||||
params: ?*const GapAdvParams,
|
||||
cb: ?EventCb,
|
||||
cb_arg: ?*anyopaque,
|
||||
) !void {
|
||||
const rc = sys.ble_gap_adv_start(own_addr_type, direct_addr, duration_ms, params, cb, cb_arg);
|
||||
if (rc != 0) return error.NimbleError;
|
||||
}
|
||||
|
||||
pub fn advStop() !void {
|
||||
const rc = sys.ble_gap_adv_stop();
|
||||
if (rc != 0) return error.NimbleError;
|
||||
}
|
||||
|
||||
pub fn connect(
|
||||
own_addr_type: u8,
|
||||
peer_addr: *const sys.ble_addr_t,
|
||||
duration_ms: i32,
|
||||
params: ?*const GapConnParams,
|
||||
cb: EventCb,
|
||||
cb_arg: ?*anyopaque,
|
||||
) !void {
|
||||
const rc = sys.ble_gap_connect(own_addr_type, peer_addr, duration_ms, params, cb, cb_arg);
|
||||
if (rc != 0) return error.NimbleError;
|
||||
}
|
||||
|
||||
pub fn terminate(conn_handle: u16, hci_reason: u8) !void {
|
||||
const rc = sys.ble_gap_terminate(conn_handle, hci_reason);
|
||||
if (rc != 0) return error.NimbleError;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GATT Server
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const GattServer = struct {
|
||||
pub fn registerSvcs(svcs: []const GattSvcDef) !void {
|
||||
const rc = sys.ble_gatts_add_svcs(svcs.ptr);
|
||||
if (rc != 0) return error.NimbleError;
|
||||
}
|
||||
|
||||
pub fn count(svcs: []const GattSvcDef, out: *sys.ble_gatt_svc_def_iter) !void {
|
||||
_ = svcs;
|
||||
_ = out;
|
||||
}
|
||||
|
||||
pub fn notify(conn_handle: u16, attr_handle: u16) !void {
|
||||
const rc = sys.ble_gatts_notify(conn_handle, attr_handle);
|
||||
if (rc != 0) return error.NimbleError;
|
||||
}
|
||||
|
||||
pub fn indicate(conn_handle: u16, attr_handle: u16) !void {
|
||||
const rc = sys.ble_gatts_indicate(conn_handle, attr_handle);
|
||||
if (rc != 0) return error.NimbleError;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Advertising data builder (OOP API)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const AdvFields = struct {
|
||||
fields: sys.ble_hs_adv_fields = .{},
|
||||
|
||||
pub fn setFlags(self: *AdvFields, flags: u8) void {
|
||||
self.fields.flags = flags;
|
||||
self.fields.flags_is_present = 1;
|
||||
}
|
||||
|
||||
pub fn setName(self: *AdvFields, name: [*:0]const u8, complete: bool) void {
|
||||
self.fields.name = name;
|
||||
self.fields.name_len = @intCast(std.mem.len(name));
|
||||
self.fields.name_is_complete = @intFromBool(complete);
|
||||
}
|
||||
|
||||
pub fn setTxPwrLvl(self: *AdvFields, level: i8) void {
|
||||
self.fields.tx_pwr_lvl = level;
|
||||
self.fields.tx_pwr_lvl_is_present = 1;
|
||||
}
|
||||
|
||||
pub fn build(self: *AdvFields, buf: []u8, out_len: *u8) !void {
|
||||
const rc = sys.ble_hs_adv_set_fields(&self.fields, buf.ptr, out_len, @intCast(buf.len));
|
||||
if (rc != 0) return error.NimbleError;
|
||||
}
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
315
software/zig_main/imports/now.zig
Normal file
315
software/zig_main/imports/now.zig
Normal file
@@ -0,0 +1,315 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Re-exported types from sys
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const WifiInterface = sys.wifi_interface_t;
|
||||
pub const WifiPktRxCtrl = sys.wifi_pkt_rx_ctrl_t;
|
||||
pub const WifiTxInfo = sys.wifi_tx_info_t;
|
||||
pub const WifiTxRateConfig = sys.wifi_tx_rate_config_t;
|
||||
pub const WifiActionTx = sys.wifi_action_tx_t;
|
||||
pub const WifiSecondChan = sys.wifi_second_chan_t;
|
||||
pub const WifiRoc = sys.wifi_roc_t;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const MAC_LEN = 6;
|
||||
pub const LMK_LEN = 16;
|
||||
pub const PMK_LEN = 16;
|
||||
pub const OUI_LEN = 3;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Send status
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const SendStatus = enum(c_uint) {
|
||||
success = 0,
|
||||
fail = 1,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Peer info
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const PeerInfo = extern struct {
|
||||
peer_addr: [MAC_LEN]u8 = .{0} ** MAC_LEN,
|
||||
lmk: [LMK_LEN]u8 = .{0} ** LMK_LEN,
|
||||
channel: u8 = 0,
|
||||
ifidx: WifiInterface = undefined,
|
||||
encrypt: bool = false,
|
||||
priv: ?*anyopaque = null,
|
||||
};
|
||||
|
||||
pub const PeerNum = extern struct {
|
||||
total_num: c_int = 0,
|
||||
encrypt_num: c_int = 0,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Receive info (passed to the recv callback)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const RecvInfo = extern struct {
|
||||
src_addr: [*c]u8 = null,
|
||||
des_addr: [*c]u8 = null,
|
||||
rx_ctrl: ?*WifiPktRxCtrl = null,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Switch-channel / remain-on-channel action structs
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const SwitchChannel = extern struct {
|
||||
type: WifiActionTx = undefined,
|
||||
channel: u8 = 0,
|
||||
sec_channel: WifiSecondChan = undefined,
|
||||
wait_time_ms: u32 = 0,
|
||||
op_id: u8 = 0,
|
||||
dest_mac: [MAC_LEN]u8 = .{0} ** MAC_LEN,
|
||||
data_len: u16 = 0,
|
||||
_data: [0]u8 = .{},
|
||||
};
|
||||
|
||||
pub const RemainOnChannel = extern struct {
|
||||
type: WifiRoc = undefined,
|
||||
channel: u8 = 0,
|
||||
sec_channel: WifiSecondChan = undefined,
|
||||
wait_time_ms: u32 = 0,
|
||||
op_id: u8 = 0,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Callback types — use these in your application; cast to/from the C type
|
||||
// at registration time via the EspNow.registerRecvCb / registerSendCb wrappers.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const RecvCb = *const fn (
|
||||
info: *const RecvInfo,
|
||||
data: [*]const u8,
|
||||
data_len: usize,
|
||||
) void;
|
||||
|
||||
pub const SendCb = *const fn (
|
||||
tx_info: *const WifiTxInfo,
|
||||
status: SendStatus,
|
||||
) void;
|
||||
|
||||
// Internal C-ABI adapters stored when a Zig callback is registered.
|
||||
var recv_cb_zig: ?RecvCb = null;
|
||||
var send_cb_zig: ?SendCb = null;
|
||||
|
||||
fn recvCbAdapter(
|
||||
info: [*c]const sys.esp_now_recv_info_t,
|
||||
data: [*c]const u8,
|
||||
data_len: c_int,
|
||||
) callconv(.c) void {
|
||||
if (recv_cb_zig) |cb|
|
||||
cb(@ptrCast(info), data, @intCast(data_len));
|
||||
}
|
||||
|
||||
fn sendCbAdapter(
|
||||
tx_info: [*c]const sys.esp_now_send_info_t,
|
||||
status: sys.esp_now_send_status_t,
|
||||
) callconv(.c) void {
|
||||
if (send_cb_zig) |cb|
|
||||
cb(@ptrCast(tx_info), @enumFromInt(status));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// EspNow namespace
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const EspNow = struct {
|
||||
|
||||
// -- Lifecycle -----------------------------------------------------------
|
||||
|
||||
pub fn init() !void {
|
||||
try errors.espCheckError(sys.esp_now_init());
|
||||
}
|
||||
|
||||
pub fn deinit() !void {
|
||||
try errors.espCheckError(sys.esp_now_deinit());
|
||||
}
|
||||
|
||||
pub fn getVersion() !u32 {
|
||||
var ver: u32 = 0;
|
||||
try errors.espCheckError(sys.esp_now_get_version(&ver));
|
||||
return ver;
|
||||
}
|
||||
|
||||
// -- Callbacks -----------------------------------------------------------
|
||||
|
||||
/// Register a Zig-typed receive callback.
|
||||
pub fn registerRecvCb(cb: RecvCb) !void {
|
||||
recv_cb_zig = cb;
|
||||
try errors.espCheckError(sys.esp_now_register_recv_cb(recvCbAdapter));
|
||||
}
|
||||
|
||||
pub fn unregisterRecvCb() !void {
|
||||
recv_cb_zig = null;
|
||||
try errors.espCheckError(sys.esp_now_unregister_recv_cb());
|
||||
}
|
||||
|
||||
/// Register a Zig-typed send callback.
|
||||
pub fn registerSendCb(cb: SendCb) !void {
|
||||
send_cb_zig = cb;
|
||||
try errors.espCheckError(sys.esp_now_register_send_cb(sendCbAdapter));
|
||||
}
|
||||
|
||||
pub fn unregisterSendCb() !void {
|
||||
send_cb_zig = null;
|
||||
try errors.espCheckError(sys.esp_now_unregister_send_cb());
|
||||
}
|
||||
|
||||
// -- Sending -------------------------------------------------------------
|
||||
|
||||
/// Send data to a peer.
|
||||
/// `peer_addr` — 6-byte MAC or null for broadcast.
|
||||
/// `data` — payload slice (max 250 bytes).
|
||||
pub fn send(peer_addr: ?*const [MAC_LEN]u8, data: []const u8) !void {
|
||||
const addr_ptr: [*c]const u8 = if (peer_addr) |a| a else null;
|
||||
try errors.espCheckError(sys.esp_now_send(addr_ptr, data.ptr, data.len));
|
||||
}
|
||||
|
||||
// -- Peer management -----------------------------------------------------
|
||||
|
||||
pub fn addPeer(peer: *const PeerInfo) !void {
|
||||
try errors.espCheckError(sys.esp_now_add_peer(@ptrCast(peer)));
|
||||
}
|
||||
|
||||
pub fn delPeer(peer_addr: *const [MAC_LEN]u8) !void {
|
||||
try errors.espCheckError(sys.esp_now_del_peer(peer_addr));
|
||||
}
|
||||
|
||||
pub fn modPeer(peer: *const PeerInfo) !void {
|
||||
try errors.espCheckError(sys.esp_now_mod_peer(@ptrCast(peer)));
|
||||
}
|
||||
|
||||
pub fn setPeerRateConfig(
|
||||
peer_addr: *const [MAC_LEN]u8,
|
||||
config: *WifiTxRateConfig,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_now_set_peer_rate_config(peer_addr, config));
|
||||
}
|
||||
|
||||
pub fn getPeer(peer_addr: *const [MAC_LEN]u8) !PeerInfo {
|
||||
var info: PeerInfo = undefined;
|
||||
try errors.espCheckError(sys.esp_now_get_peer(peer_addr, @ptrCast(&info)));
|
||||
return info;
|
||||
}
|
||||
|
||||
/// Iterate over the peer list.
|
||||
/// Pass `from_head = true` on the first call, then `false` to continue.
|
||||
pub fn fetchPeer(from_head: bool) !PeerInfo {
|
||||
var info: PeerInfo = undefined;
|
||||
try errors.espCheckError(sys.esp_now_fetch_peer(from_head, @ptrCast(&info)));
|
||||
return info;
|
||||
}
|
||||
|
||||
pub fn isPeerExist(peer_addr: *const [MAC_LEN]u8) bool {
|
||||
return sys.esp_now_is_peer_exist(peer_addr);
|
||||
}
|
||||
|
||||
pub fn getPeerNum() !PeerNum {
|
||||
var num: PeerNum = undefined;
|
||||
try errors.espCheckError(sys.esp_now_get_peer_num(@ptrCast(&num)));
|
||||
return num;
|
||||
}
|
||||
|
||||
// -- Security ------------------------------------------------------------
|
||||
|
||||
/// Set the Primary Master Key (16 bytes).
|
||||
pub fn setPmk(pmk: *const [PMK_LEN]u8) !void {
|
||||
try errors.espCheckError(sys.esp_now_set_pmk(pmk));
|
||||
}
|
||||
|
||||
// -- Power saving --------------------------------------------------------
|
||||
|
||||
pub fn setWakeWindow(window: u16) !void {
|
||||
try errors.espCheckError(sys.esp_now_set_wake_window(window));
|
||||
}
|
||||
|
||||
// -- OUI -----------------------------------------------------------------
|
||||
|
||||
/// Set user OUI (3 bytes).
|
||||
pub fn setUserOui(oui: *[OUI_LEN]u8) !void {
|
||||
try errors.espCheckError(sys.esp_now_set_user_oui(oui));
|
||||
}
|
||||
|
||||
/// Get user OUI (3 bytes).
|
||||
pub fn getUserOui(oui: *[OUI_LEN]u8) !void {
|
||||
try errors.espCheckError(sys.esp_now_get_user_oui(oui));
|
||||
}
|
||||
|
||||
// -- Channel actions -----------------------------------------------------
|
||||
|
||||
pub fn switchChannel(config: *SwitchChannel) !void {
|
||||
try errors.espCheckError(sys.esp_now_switch_channel_tx(@ptrCast(config)));
|
||||
}
|
||||
|
||||
pub fn remainOnChannel(config: *RemainOnChannel) !void {
|
||||
try errors.espCheckError(sys.esp_now_remain_on_channel(@ptrCast(config)));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SmartConfig — bundled here because the raw bindings file included it.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const SmartconfigType = enum(c_uint) {
|
||||
esptouch = 0,
|
||||
airkiss = 1,
|
||||
esptouch_airkiss = 2,
|
||||
esptouch_v2 = 3,
|
||||
};
|
||||
|
||||
pub const SmartconfigEvent = enum(c_uint) {
|
||||
scan_done = 0,
|
||||
found_channel = 1,
|
||||
got_ssid_pswd = 2,
|
||||
send_ack_done = 3,
|
||||
};
|
||||
|
||||
pub const SmartconfigGotSsidPswd = extern struct {
|
||||
ssid: [32]u8 = .{0} ** 32,
|
||||
password: [64]u8 = .{0} ** 64,
|
||||
bssid_set: bool = false,
|
||||
bssid: [MAC_LEN]u8 = .{0} ** MAC_LEN,
|
||||
type: SmartconfigType = .esptouch,
|
||||
token: u8 = 0,
|
||||
cellphone_ip: [4]u8 = .{0} ** 4,
|
||||
};
|
||||
|
||||
pub const SmartConfig = struct {
|
||||
pub fn getVersion() [*:0]const u8 {
|
||||
return sys.esp_smartconfig_get_version();
|
||||
}
|
||||
|
||||
pub fn start(config: *const sys.smartconfig_start_config_t) !void {
|
||||
try errors.espCheckError(sys.esp_smartconfig_start(config));
|
||||
}
|
||||
|
||||
pub fn stop() !void {
|
||||
try errors.espCheckError(sys.esp_smartconfig_stop());
|
||||
}
|
||||
|
||||
pub fn setTimeout(time_s: u8) !void {
|
||||
try errors.espCheckError(sys.esp_esptouch_set_timeout(time_s));
|
||||
}
|
||||
|
||||
pub fn setType(kind: SmartconfigType) !void {
|
||||
try errors.espCheckError(sys.esp_smartconfig_set_type(@intFromEnum(kind)));
|
||||
}
|
||||
|
||||
pub fn setFastMode(enable: bool) !void {
|
||||
try errors.espCheckError(sys.esp_smartconfig_fast_mode(enable));
|
||||
}
|
||||
|
||||
pub fn getRvdData(buf: []u8) !void {
|
||||
try errors.espCheckError(sys.esp_smartconfig_get_rvd_data(buf.ptr, @intCast(buf.len)));
|
||||
}
|
||||
};
|
||||
292
software/zig_main/imports/nvs.zig
Normal file
292
software/zig_main/imports/nvs.zig
Normal file
@@ -0,0 +1,292 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Handle = sys.nvs_handle_t;
|
||||
pub const Iterator = sys.nvs_iterator_t;
|
||||
pub const EntryInfo = sys.nvs_entry_info_t;
|
||||
pub const Stats = sys.nvs_stats_t;
|
||||
|
||||
pub const OpenMode = enum(sys.nvs_open_mode_t) {
|
||||
read_only = sys.NVS_READONLY,
|
||||
read_write = sys.NVS_READWRITE,
|
||||
};
|
||||
|
||||
pub const Type = enum(sys.nvs_type_t) {
|
||||
u8 = sys.NVS_TYPE_U8,
|
||||
i8 = sys.NVS_TYPE_I8,
|
||||
u16 = sys.NVS_TYPE_U16,
|
||||
i16 = sys.NVS_TYPE_I16,
|
||||
u32 = sys.NVS_TYPE_U32,
|
||||
i32 = sys.NVS_TYPE_I32,
|
||||
u64 = sys.NVS_TYPE_U64,
|
||||
i64 = sys.NVS_TYPE_I64,
|
||||
str = sys.NVS_TYPE_STR,
|
||||
blob = sys.NVS_TYPE_BLOB,
|
||||
any = sys.NVS_TYPE_ANY,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Flash initialisation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Initialise NVS flash with the default "nvs" partition.
|
||||
/// Call once at boot before opening any handles.
|
||||
pub fn flashInit() !void {
|
||||
try errors.espCheckError(sys.nvs_flash_init());
|
||||
}
|
||||
|
||||
/// Initialise NVS flash on an explicitly named partition.
|
||||
pub fn flashInitPartition(partition_label: [*:0]const u8) !void {
|
||||
try errors.espCheckError(sys.nvs_flash_init_partition(partition_label));
|
||||
}
|
||||
|
||||
/// Initialise NVS flash, erasing the default partition if it has no free pages
|
||||
/// or if an incompatible version was found. This is the pattern recommended by
|
||||
/// Espressif for robust boot-time NVS initialisation.
|
||||
pub fn flashInitOrErase() !void {
|
||||
const rc = sys.nvs_flash_init();
|
||||
if (rc == sys.ESP_ERR_NVS_NO_FREE_PAGES or rc == sys.ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
try errors.espCheckError(sys.nvs_flash_erase());
|
||||
try errors.espCheckError(sys.nvs_flash_init());
|
||||
} else {
|
||||
try errors.espCheckError(rc);
|
||||
}
|
||||
}
|
||||
|
||||
/// De-initialise the default NVS partition.
|
||||
pub fn flashDeinit() !void {
|
||||
try errors.espCheckError(sys.nvs_flash_deinit());
|
||||
}
|
||||
|
||||
/// De-initialise a named NVS partition.
|
||||
pub fn flashDeinitPartition(partition_label: [*:0]const u8) !void {
|
||||
try errors.espCheckError(sys.nvs_flash_deinit_partition(partition_label));
|
||||
}
|
||||
|
||||
/// Erase all data in the default NVS partition (loses all stored values).
|
||||
pub fn flashErase() !void {
|
||||
try errors.espCheckError(sys.nvs_flash_erase());
|
||||
}
|
||||
|
||||
/// Erase all data in a named NVS partition.
|
||||
pub fn flashErasePartition(part_name: [*:0]const u8) !void {
|
||||
try errors.espCheckError(sys.nvs_flash_erase_partition(part_name));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handle open / close
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Open an NVS namespace in the default "nvs" partition.
|
||||
pub fn open(namespace: [*:0]const u8, mode: OpenMode) !Handle {
|
||||
var handle: Handle = 0;
|
||||
try errors.espCheckError(sys.nvs_open(namespace, @intFromEnum(mode), &handle));
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// Open an NVS namespace on a specific partition.
|
||||
pub fn openFromPartition(part_name: [*:0]const u8, namespace: [*:0]const u8, mode: OpenMode) !Handle {
|
||||
var handle: Handle = 0;
|
||||
try errors.espCheckError(sys.nvs_open_from_partition(part_name, namespace, @intFromEnum(mode), &handle));
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// Close an NVS handle. Must be called to release resources.
|
||||
pub fn close(handle: Handle) void {
|
||||
sys.nvs_close(handle);
|
||||
}
|
||||
|
||||
/// Commit pending writes. Must be called after set operations to persist data.
|
||||
pub fn commit(handle: Handle) !void {
|
||||
try errors.espCheckError(sys.nvs_commit(handle));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Write (set) operations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn setI8(handle: Handle, key: [*:0]const u8, value: i8) !void {
|
||||
try errors.espCheckError(sys.nvs_set_i8(handle, key, value));
|
||||
}
|
||||
|
||||
pub fn setU8(handle: Handle, key: [*:0]const u8, value: u8) !void {
|
||||
try errors.espCheckError(sys.nvs_set_u8(handle, key, value));
|
||||
}
|
||||
|
||||
pub fn setI16(handle: Handle, key: [*:0]const u8, value: i16) !void {
|
||||
try errors.espCheckError(sys.nvs_set_i16(handle, key, value));
|
||||
}
|
||||
|
||||
pub fn setU16(handle: Handle, key: [*:0]const u8, value: u16) !void {
|
||||
try errors.espCheckError(sys.nvs_set_u16(handle, key, value));
|
||||
}
|
||||
|
||||
pub fn setI32(handle: Handle, key: [*:0]const u8, value: i32) !void {
|
||||
try errors.espCheckError(sys.nvs_set_i32(handle, key, value));
|
||||
}
|
||||
|
||||
pub fn setU32(handle: Handle, key: [*:0]const u8, value: u32) !void {
|
||||
try errors.espCheckError(sys.nvs_set_u32(handle, key, value));
|
||||
}
|
||||
|
||||
pub fn setI64(handle: Handle, key: [*:0]const u8, value: i64) !void {
|
||||
try errors.espCheckError(sys.nvs_set_i64(handle, key, value));
|
||||
}
|
||||
|
||||
pub fn setU64(handle: Handle, key: [*:0]const u8, value: u64) !void {
|
||||
try errors.espCheckError(sys.nvs_set_u64(handle, key, value));
|
||||
}
|
||||
|
||||
/// Store a null-terminated string. The key limit is 15 characters.
|
||||
pub fn setStr(handle: Handle, key: [*:0]const u8, value: [*:0]const u8) !void {
|
||||
try errors.espCheckError(sys.nvs_set_str(handle, key, value));
|
||||
}
|
||||
|
||||
/// Store a binary blob of arbitrary length.
|
||||
pub fn setBlob(handle: Handle, key: [*:0]const u8, value: []const u8) !void {
|
||||
try errors.espCheckError(sys.nvs_set_blob(handle, key, value.ptr, value.len));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Read (get) operations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn getI8(handle: Handle, key: [*:0]const u8) !i8 {
|
||||
var v: i8 = 0;
|
||||
try errors.espCheckError(sys.nvs_get_i8(handle, key, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn getU8(handle: Handle, key: [*:0]const u8) !u8 {
|
||||
var v: u8 = 0;
|
||||
try errors.espCheckError(sys.nvs_get_u8(handle, key, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn getI16(handle: Handle, key: [*:0]const u8) !i16 {
|
||||
var v: i16 = 0;
|
||||
try errors.espCheckError(sys.nvs_get_i16(handle, key, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn getU16(handle: Handle, key: [*:0]const u8) !u16 {
|
||||
var v: u16 = 0;
|
||||
try errors.espCheckError(sys.nvs_get_u16(handle, key, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn getI32(handle: Handle, key: [*:0]const u8) !i32 {
|
||||
var v: i32 = 0;
|
||||
try errors.espCheckError(sys.nvs_get_i32(handle, key, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn getU32(handle: Handle, key: [*:0]const u8) !u32 {
|
||||
var v: u32 = 0;
|
||||
try errors.espCheckError(sys.nvs_get_u32(handle, key, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn getI64(handle: Handle, key: [*:0]const u8) !i64 {
|
||||
var v: i64 = 0;
|
||||
try errors.espCheckError(sys.nvs_get_i64(handle, key, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn getU64(handle: Handle, key: [*:0]const u8) !u64 {
|
||||
var v: u64 = 0;
|
||||
try errors.espCheckError(sys.nvs_get_u64(handle, key, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Read a string value.
|
||||
/// Pass `buf = null` with `buf_len` pointing to 0 first to query the required size.
|
||||
/// Then allocate `buf_len` bytes and call again.
|
||||
pub fn getStr(handle: Handle, key: [*:0]const u8, buf: ?[*]u8, buf_len: *usize) !void {
|
||||
try errors.espCheckError(sys.nvs_get_str(handle, key, buf, buf_len));
|
||||
}
|
||||
|
||||
/// Read a blob value.
|
||||
/// Pass `buf = null` with `buf_len` pointing to 0 first to query the required size.
|
||||
pub fn getBlob(handle: Handle, key: [*:0]const u8, buf: ?*anyopaque, buf_len: *usize) !void {
|
||||
try errors.espCheckError(sys.nvs_get_blob(handle, key, buf, buf_len));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Key management
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Returns the type stored for `key`, or an error if the key does not exist.
|
||||
pub fn findKey(handle: Handle, key: [*:0]const u8) !Type {
|
||||
var t: sys.nvs_type_t = 0;
|
||||
try errors.espCheckError(sys.nvs_find_key(handle, key, &t));
|
||||
return @enumFromInt(t);
|
||||
}
|
||||
|
||||
/// Erase a single key from the namespace.
|
||||
pub fn eraseKey(handle: Handle, key: [*:0]const u8) !void {
|
||||
try errors.espCheckError(sys.nvs_erase_key(handle, key));
|
||||
}
|
||||
|
||||
/// Erase all keys in the namespace.
|
||||
pub fn eraseAll(handle: Handle) !void {
|
||||
try errors.espCheckError(sys.nvs_erase_all(handle));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Statistics
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Get storage statistics for a partition (pass `null` for the default "nvs" partition).
|
||||
pub fn getStats(part_name: ?[*:0]const u8, stats: *Stats) !void {
|
||||
try errors.espCheckError(sys.nvs_get_stats(part_name, stats));
|
||||
}
|
||||
|
||||
/// Get the number of used entries for an open handle's namespace.
|
||||
pub fn getUsedEntryCount(handle: Handle) !usize {
|
||||
var count: usize = 0;
|
||||
try errors.espCheckError(sys.nvs_get_used_entry_count(handle, &count));
|
||||
return count;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Iteration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Find an iterator over entries matching the given partition, namespace, and type.
|
||||
/// Release with `releaseIterator` when done.
|
||||
pub fn entryFind(
|
||||
part_name: ?[*:0]const u8,
|
||||
namespace: ?[*:0]const u8,
|
||||
nvs_type: Type,
|
||||
) !Iterator {
|
||||
var it: Iterator = null;
|
||||
try errors.espCheckError(sys.nvs_entry_find(part_name, namespace, @intFromEnum(nvs_type), &it));
|
||||
return it;
|
||||
}
|
||||
|
||||
/// Find an iterator over entries in an open handle matching the given type.
|
||||
pub fn entryFindInHandle(handle: Handle, nvs_type: Type) !Iterator {
|
||||
var it: Iterator = null;
|
||||
try errors.espCheckError(sys.nvs_entry_find_in_handle(handle, @intFromEnum(nvs_type), &it));
|
||||
return it;
|
||||
}
|
||||
|
||||
/// Advance the iterator to the next entry. Returns error when no more entries.
|
||||
pub fn entryNext(it: *Iterator) !void {
|
||||
try errors.espCheckError(sys.nvs_entry_next(it));
|
||||
}
|
||||
|
||||
/// Fill `info` with details about the current iterator position.
|
||||
pub fn entryInfo(it: Iterator, info: *EntryInfo) !void {
|
||||
try errors.espCheckError(sys.nvs_entry_info(it, info));
|
||||
}
|
||||
|
||||
/// Release an iterator obtained with `entryFind`.
|
||||
pub fn releaseIterator(it: Iterator) void {
|
||||
sys.nvs_release_iterator(it);
|
||||
}
|
||||
21
software/zig_main/imports/panic.zig
Normal file
21
software/zig_main/imports/panic.zig
Normal file
@@ -0,0 +1,21 @@
|
||||
const sys = @import("sys");
|
||||
const log = @import("log");
|
||||
|
||||
/// panic handler for esp-idf
|
||||
pub fn panic(msg: []const u8, stack_trace: ?*@import("std").builtin.StackTrace, _: ?usize) noreturn {
|
||||
sys.esp_log_write(log.default_level, "PANIC", "[%lu ms] PANIC: %.*s\n", sys.esp_log_timestamp(), msg.len, msg.ptr);
|
||||
|
||||
// try print stack trace if available
|
||||
if (stack_trace) |st| {
|
||||
var i: usize = st.index;
|
||||
if (i > st.instruction_addresses.len) i = st.instruction_addresses.len;
|
||||
var idx: usize = 0;
|
||||
while (idx < i) : (idx += 1) {
|
||||
sys.esp_log_write(log.default_level, "PANIC", " #%u: 0x%08lx\n", idx, st.instruction_addresses[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
asm volatile ("" ::: .{ .memory = true });
|
||||
}
|
||||
}
|
||||
171
software/zig_main/imports/partition.zig
Normal file
171
software/zig_main/imports/partition.zig
Normal file
@@ -0,0 +1,171 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Partition = sys.esp_partition_t;
|
||||
pub const Iterator = sys.esp_partition_iterator_t;
|
||||
pub const MmapHandle = sys.esp_partition_mmap_handle_t;
|
||||
|
||||
pub const Type = enum(sys.esp_partition_type_t) {
|
||||
app = sys.ESP_PARTITION_TYPE_APP,
|
||||
data = sys.ESP_PARTITION_TYPE_DATA,
|
||||
bootloader = sys.ESP_PARTITION_TYPE_BOOTLOADER,
|
||||
partition_table = sys.ESP_PARTITION_TYPE_PARTITION_TABLE,
|
||||
any = sys.ESP_PARTITION_TYPE_ANY,
|
||||
};
|
||||
|
||||
pub const Subtype = enum(sys.esp_partition_subtype_t) {
|
||||
// APP subtypes
|
||||
app_factory = sys.ESP_PARTITION_SUBTYPE_APP_FACTORY,
|
||||
app_ota_0 = sys.ESP_PARTITION_SUBTYPE_APP_OTA_0,
|
||||
app_ota_1 = sys.ESP_PARTITION_SUBTYPE_APP_OTA_1,
|
||||
app_ota_2 = sys.ESP_PARTITION_SUBTYPE_APP_OTA_2,
|
||||
app_ota_3 = sys.ESP_PARTITION_SUBTYPE_APP_OTA_3,
|
||||
app_test = sys.ESP_PARTITION_SUBTYPE_APP_TEST,
|
||||
// DATA subtypes
|
||||
data_ota = sys.ESP_PARTITION_SUBTYPE_DATA_OTA,
|
||||
data_phy = sys.ESP_PARTITION_SUBTYPE_DATA_PHY,
|
||||
data_nvs = sys.ESP_PARTITION_SUBTYPE_DATA_NVS,
|
||||
data_coredump = sys.ESP_PARTITION_SUBTYPE_DATA_COREDUMP,
|
||||
data_nvs_keys = sys.ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS,
|
||||
data_fat = sys.ESP_PARTITION_SUBTYPE_DATA_FAT,
|
||||
data_spiffs = sys.ESP_PARTITION_SUBTYPE_DATA_SPIFFS,
|
||||
// Catch-all
|
||||
any = sys.ESP_PARTITION_SUBTYPE_ANY,
|
||||
};
|
||||
|
||||
pub const MmapMemory = enum(sys.esp_partition_mmap_memory_t) {
|
||||
data = sys.ESP_PARTITION_MMAP_DATA,
|
||||
inst = sys.ESP_PARTITION_MMAP_INST,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Find
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Return an iterator over all partitions that match `type`, `subtype`, and
|
||||
/// optional `label`. Release with `iteratorRelease` when done.
|
||||
pub fn find(part_type: Type, subtype: Subtype, label: ?[*:0]const u8) Iterator {
|
||||
return sys.esp_partition_find(
|
||||
@intFromEnum(part_type),
|
||||
@intFromEnum(subtype),
|
||||
label,
|
||||
);
|
||||
}
|
||||
|
||||
/// Return a pointer to the first matching partition, or null if none found.
|
||||
pub fn findFirst(part_type: Type, subtype: Subtype, label: ?[*:0]const u8) ?*const Partition {
|
||||
return sys.esp_partition_find_first(
|
||||
@intFromEnum(part_type),
|
||||
@intFromEnum(subtype),
|
||||
label,
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Iterator
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Return the partition pointed to by the iterator, or null at end.
|
||||
pub fn iteratorGet(it: Iterator) ?*const Partition {
|
||||
return sys.esp_partition_get(it);
|
||||
}
|
||||
|
||||
/// Advance the iterator by one step. Returns null when exhausted.
|
||||
pub fn iteratorNext(it: Iterator) Iterator {
|
||||
return sys.esp_partition_next(it);
|
||||
}
|
||||
|
||||
/// Release an iterator obtained via `find`.
|
||||
pub fn iteratorRelease(it: Iterator) void {
|
||||
sys.esp_partition_iterator_release(it);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Read / Write / Erase
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Read `size` bytes from `partition` at `src_offset` into `dst`.
|
||||
pub fn read(partition: *const Partition, src_offset: usize, dst: []u8) !void {
|
||||
try errors.espCheckError(sys.esp_partition_read(partition, src_offset, dst.ptr, dst.len));
|
||||
}
|
||||
|
||||
/// Write `src` bytes to `partition` at `dst_offset`.
|
||||
/// The region must have been erased first.
|
||||
pub fn write(partition: *const Partition, dst_offset: usize, src: []const u8) !void {
|
||||
try errors.espCheckError(sys.esp_partition_write(partition, dst_offset, src.ptr, src.len));
|
||||
}
|
||||
|
||||
/// Read without decryption (raw flash content).
|
||||
pub fn readRaw(partition: *const Partition, src_offset: usize, dst: []u8) !void {
|
||||
try errors.espCheckError(sys.esp_partition_read_raw(partition, src_offset, dst.ptr, dst.len));
|
||||
}
|
||||
|
||||
/// Write without encryption (raw flash write).
|
||||
pub fn writeRaw(partition: *const Partition, dst_offset: usize, src: []const u8) !void {
|
||||
try errors.espCheckError(sys.esp_partition_write_raw(partition, dst_offset, src.ptr, src.len));
|
||||
}
|
||||
|
||||
/// Erase `size` bytes starting at `offset`.
|
||||
/// Both `offset` and `size` must be aligned to 4096 bytes (the sector size).
|
||||
pub fn eraseRange(partition: *const Partition, offset: usize, size: usize) !void {
|
||||
try errors.espCheckError(sys.esp_partition_erase_range(partition, offset, size));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Memory-map
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Memory-map a region of a partition into the CPU's address space.
|
||||
/// Returns `(ptr, handle)` — call `munmap(handle)` when done.
|
||||
pub fn mmap(
|
||||
partition: *const Partition,
|
||||
offset: usize,
|
||||
size: usize,
|
||||
memory: MmapMemory,
|
||||
) !struct { ptr: *const anyopaque, handle: MmapHandle } {
|
||||
var out_ptr: ?*const anyopaque = null;
|
||||
var out_handle: MmapHandle = 0;
|
||||
try errors.espCheckError(sys.esp_partition_mmap(
|
||||
partition,
|
||||
offset,
|
||||
size,
|
||||
@intFromEnum(memory),
|
||||
&out_ptr,
|
||||
&out_handle,
|
||||
));
|
||||
return .{ .ptr = out_ptr.?, .handle = out_handle };
|
||||
}
|
||||
|
||||
/// Unmap a previously mapped partition region.
|
||||
pub fn munmap(handle: MmapHandle) void {
|
||||
sys.esp_partition_munmap(handle);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Utilities
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Fill `sha_256` (must be 32 bytes) with the SHA-256 of the partition contents.
|
||||
pub fn getSha256(partition: *const Partition, sha_256: *[32]u8) !void {
|
||||
try errors.espCheckError(sys.esp_partition_get_sha256(partition, sha_256));
|
||||
}
|
||||
|
||||
/// Returns true if two partitions have identical content.
|
||||
pub fn checkIdentity(p1: *const Partition, p2: *const Partition) bool {
|
||||
return sys.esp_partition_check_identity(p1, p2);
|
||||
}
|
||||
|
||||
/// Copy `size` bytes from `src_part` (at `src_offset`) to `dest_part` (at `dest_offset`).
|
||||
pub fn copy(
|
||||
dest_part: *const Partition,
|
||||
dest_offset: u32,
|
||||
src_part: *const Partition,
|
||||
src_offset: u32,
|
||||
size: usize,
|
||||
) !void {
|
||||
try errors.espCheckError(sys.esp_partition_copy(dest_part, dest_offset, src_part, src_offset, size));
|
||||
}
|
||||
71
software/zig_main/imports/pcnt.zig
Normal file
71
software/zig_main/imports/pcnt.zig
Normal file
@@ -0,0 +1,71 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
pub const PulseCounter = struct {
|
||||
pub const watchEventData_t = sys.pcnt_watch_event_data_t;
|
||||
pub const eventCallbacks_t = sys.pcnt_event_callbacks_t;
|
||||
pub const glitchFilterConfig_t = sys.pcnt_glitch_filter_config_t;
|
||||
|
||||
pub const Unit = struct {
|
||||
pub const config_t = sys.pcnt_unit_config_t;
|
||||
pub const handle_t = sys.pcnt_unit_handle_t;
|
||||
|
||||
pub fn init(config: ?*const sys.pcnt_unit_config_t, unit: ?*sys.pcnt_unit_handle_t) !void {
|
||||
return try errors.espCheckError(sys.pcnt_new_unit(config, unit));
|
||||
}
|
||||
pub fn del(unit: sys.pcnt_unit_handle_t) !void {
|
||||
return try errors.espCheckError(sys.pcnt_del_unit(unit));
|
||||
}
|
||||
pub fn enable(unit: sys.pcnt_unit_handle_t) !void {
|
||||
return try errors.espCheckError(sys.pcnt_unit_enable(unit));
|
||||
}
|
||||
pub fn disable(unit: sys.pcnt_unit_handle_t) !void {
|
||||
return try errors.espCheckError(sys.pcnt_unit_disable(unit));
|
||||
}
|
||||
pub fn start(unit: sys.pcnt_unit_handle_t) !void {
|
||||
return try errors.espCheckError(sys.pcnt_unit_start(unit));
|
||||
}
|
||||
pub fn stop(unit: sys.pcnt_unit_handle_t) !void {
|
||||
return try errors.espCheckError(sys.pcnt_unit_stop(unit));
|
||||
}
|
||||
pub fn clear(unit: sys.pcnt_unit_handle_t) !void {
|
||||
return try errors.espCheckError(sys.pcnt_unit_clear_count(unit));
|
||||
}
|
||||
pub fn setGlitchFilter(unit: sys.pcnt_unit_handle_t, config: ?*const sys.pcnt_glitch_filter_config_t) !void {
|
||||
return try errors.espCheckError(sys.pcnt_unit_set_glitch_filter(unit, config));
|
||||
}
|
||||
pub fn getCount(unit: sys.pcnt_unit_handle_t, value: ?*c_int) !void {
|
||||
return try errors.espCheckError(sys.pcnt_unit_get_count(unit, value));
|
||||
}
|
||||
pub fn registerEventCallbacks(unit: sys.pcnt_unit_handle_t, cbs: ?*const sys.pcnt_event_callbacks_t, user_data: ?*anyopaque) !void {
|
||||
return try errors.espCheckError(sys.pcnt_unit_register_event_callbacks(unit, cbs, user_data));
|
||||
}
|
||||
pub fn addWatchPoint(unit: sys.pcnt_unit_handle_t, watch_point: c_int) !void {
|
||||
return try errors.espCheckError(sys.pcnt_unit_add_watch_point(unit, watch_point));
|
||||
}
|
||||
pub fn removeWatchPoint(unit: sys.pcnt_unit_handle_t, watch_point: c_int) !void {
|
||||
return try errors.espCheckError(sys.pcnt_unit_remove_watch_point(unit, watch_point));
|
||||
}
|
||||
};
|
||||
pub const Channel = struct {
|
||||
pub const handle_t = sys.pcnt_channel_handle_t;
|
||||
pub const config_t = sys.pcnt_chan_config_t;
|
||||
pub const edgeAction_t = sys.pcnt_channel_edge_action_t;
|
||||
pub const level_t = sys.pcnt_channel_level_t;
|
||||
|
||||
pub fn init(unit: sys.pcnt_unit_handle_t, config: ?*const sys.pcnt_chan_config_t, chan: ?*sys.pcnt_channel_handle_t) !void {
|
||||
return try errors.espCheckError(sys.pcnt_new_channel(unit, config, chan));
|
||||
}
|
||||
pub fn del(chan: sys.pcnt_channel_handle_t) !void {
|
||||
return try errors.espCheckError(sys.pcnt_del_channel(chan));
|
||||
}
|
||||
const set = struct {
|
||||
pub fn edgeAction(chan: sys.pcnt_channel_handle_t, pos_act: sys.pcnt_channel_edge_action_t, neg_act: sys.pcnt_channel_edge_action_t) !void {
|
||||
return try errors.espCheckError(sys.pcnt_channel_set_edge_action(chan, pos_act, neg_act));
|
||||
}
|
||||
pub fn levelAction(chan: sys.pcnt_channel_handle_t, high_act: sys.pcnt_channel_level_action_t, low_act: sys.pcnt_channel_level_action_t) !void {
|
||||
return try errors.espCheckError(sys.pcnt_channel_set_edge_action(chan, high_act, low_act));
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
46
software/zig_main/imports/phy.zig
Normal file
46
software/zig_main/imports/phy.zig
Normal file
@@ -0,0 +1,46 @@
|
||||
const sys = @import("sys");
|
||||
|
||||
pub const RF = struct {
|
||||
pub fn config(conf: u8) void {
|
||||
sys.esp_phy_rftest_config(conf);
|
||||
}
|
||||
pub fn init() void {
|
||||
sys.esp_phy_rftest_init();
|
||||
}
|
||||
};
|
||||
pub fn txContinEn(contin_en: bool) void {
|
||||
sys.esp_phy_tx_contin_en(contin_en);
|
||||
}
|
||||
pub fn cbw40mEn(en: bool) void {
|
||||
sys.esp_phy_cbw40m_en(en);
|
||||
}
|
||||
pub fn testStartStop(value: u8) void {
|
||||
sys.esp_phy_test_start_stop(value);
|
||||
}
|
||||
pub const WIFI = struct {
|
||||
pub fn tx(chan: u32, rate: sys.esp_phy_wifi_rate_t, backoff: i8, length_byte: u32, packet_delay: u32, packet_num: u32) void {
|
||||
sys.esp_phy_wifi_tx(chan, rate, backoff, length_byte, packet_delay, packet_num);
|
||||
}
|
||||
pub fn rx(chan: u32, rate: sys.esp_phy_wifi_rate_t) void {
|
||||
sys.esp_phy_wifi_rx(chan, rate);
|
||||
}
|
||||
pub fn txTone(start: u32, chan: u32, backoff: u32) void {
|
||||
sys.esp_phy_wifi_tx_tone(start, chan, backoff);
|
||||
}
|
||||
};
|
||||
pub const BLE = struct {
|
||||
pub fn tx(txpwr: u32, chan: u32, len: u32, data_type: sys.esp_phy_ble_type_t, syncw: u32, rate: sys.esp_phy_ble_rate_t, tx_num_in: u32) void {
|
||||
sys.esp_phy_ble_tx(txpwr, chan, len, data_type, syncw, rate, tx_num_in);
|
||||
}
|
||||
pub fn rx(chan: u32, syncw: u32, rate: sys.esp_phy_ble_rate_t) void {
|
||||
sys.esp_phy_ble_rx(chan, syncw, rate);
|
||||
}
|
||||
};
|
||||
pub const BT = struct {
|
||||
pub fn txTone(start: u32, chan: u32, power: u32) void {
|
||||
sys.esp_phy_bt_tx_tone(start, chan, power);
|
||||
}
|
||||
};
|
||||
pub fn getRXResult(rx_result: [*c]sys.esp_phy_rx_result_t) void {
|
||||
sys.esp_phy_get_rx_result(rx_result);
|
||||
}
|
||||
68
software/zig_main/imports/pm.zig
Normal file
68
software/zig_main/imports/pm.zig
Normal file
@@ -0,0 +1,68 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases & enums
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Unified power management configuration.
|
||||
/// Fields: `max_freq_mhz`, `min_freq_mhz`, `light_sleep_enable`.
|
||||
pub const Config = sys.esp_pm_config_t;
|
||||
|
||||
pub const LockHandle = sys.esp_pm_lock_handle_t;
|
||||
|
||||
/// Type of power management lock.
|
||||
/// - `cpu_freq_max`: CPU runs at its maximum configured frequency while held.
|
||||
/// - `apb_freq_max`: APB bus runs at maximum frequency while held.
|
||||
/// - `no_light_sleep`: Prevents light sleep while held.
|
||||
pub const LockType = enum(sys.esp_pm_lock_type_t) {
|
||||
cpu_freq_max = sys.ESP_PM_CPU_FREQ_MAX,
|
||||
apb_freq_max = sys.ESP_PM_APB_FREQ_MAX,
|
||||
no_light_sleep = sys.ESP_PM_NO_LIGHT_SLEEP,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// System-wide configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Apply a power management configuration.
|
||||
/// `config` is a pointer to an `esp_pm_config_t` struct.
|
||||
pub fn configure(config: *const Config) !void {
|
||||
try errors.espCheckError(sys.esp_pm_configure(config));
|
||||
}
|
||||
|
||||
/// Retrieve the current power management configuration.
|
||||
pub fn getConfiguration(config: *Config) !void {
|
||||
try errors.espCheckError(sys.esp_pm_get_configuration(config));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Power management locks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Lock = struct {
|
||||
/// Create a new PM lock.
|
||||
/// `lock_type`: the resource to hold.
|
||||
/// `arg`: only used for `cpu_freq_max`/`apb_freq_max` (target MHz); pass 0 otherwise.
|
||||
/// `name`: optional human-readable name for `esp_pm_dump_locks`.
|
||||
pub fn create(lock_type: LockType, arg: c_int, name: [*:0]const u8) !LockHandle {
|
||||
var handle: LockHandle = null;
|
||||
try errors.espCheckError(sys.esp_pm_lock_create(@intFromEnum(lock_type), arg, name, &handle));
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// Acquire a PM lock. Nesting is supported — matched `release` calls are required.
|
||||
pub fn acquire(handle: LockHandle) !void {
|
||||
try errors.espCheckError(sys.esp_pm_lock_acquire(handle));
|
||||
}
|
||||
|
||||
/// Release a previously acquired PM lock.
|
||||
pub fn release(handle: LockHandle) !void {
|
||||
try errors.espCheckError(sys.esp_pm_lock_release(handle));
|
||||
}
|
||||
|
||||
/// Delete a PM lock. The lock must not be held when deleted.
|
||||
pub fn delete(handle: LockHandle) !void {
|
||||
try errors.espCheckError(sys.esp_pm_lock_delete(handle));
|
||||
}
|
||||
};
|
||||
233
software/zig_main/imports/pthread.zig
Normal file
233
software/zig_main/imports/pthread.zig
Normal file
@@ -0,0 +1,233 @@
|
||||
// Zig wrappers for ESP-IDF's POSIX pthread implementation (backed by FreeRTOS).
|
||||
//
|
||||
// Symbols come from idf-sys.zig (generated by `zig translate-c include/stubs.h`
|
||||
// with ESP_IDF_COMP_PTHREAD_ENABLED defined, which pulls in pthread.h and
|
||||
// esp_pthread.h).
|
||||
//
|
||||
// Error handling:
|
||||
// POSIX pthread_* functions return 0 on success, errno on failure → `check()`.
|
||||
// ESP-IDF esp_pthread_* functions return esp_err_t → `errors.espCheckError()`.
|
||||
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Error helper for POSIX-style return codes
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Error = error{PthreadError};
|
||||
|
||||
fn check(rc: c_int) Error!void {
|
||||
if (rc != 0) return error.PthreadError;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases (from idf-sys)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Thread_t = sys.pthread_t;
|
||||
pub const Attr_t = sys.pthread_attr_t;
|
||||
pub const Mutex_t = sys.pthread_mutex_t;
|
||||
pub const MutexAttr_t = sys.pthread_mutexattr_t;
|
||||
pub const Cond_t = sys.pthread_cond_t;
|
||||
pub const CondAttr_t = sys.pthread_condattr_t;
|
||||
pub const Key_t = sys.pthread_key_t;
|
||||
pub const Once_t = sys.pthread_once_t;
|
||||
pub const SchedParam = sys.sched_param;
|
||||
pub const Timespec = sys.timespec;
|
||||
|
||||
/// ESP-IDF-specific pthread configuration (stack size, priority, core pinning, etc.).
|
||||
pub const EspCfg = sys.esp_pthread_cfg_t;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Thread — lifecycle & identity
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Thread = struct {
|
||||
/// Create a thread with optional attributes. Returns the thread ID.
|
||||
pub fn create(
|
||||
attr: ?*const Attr_t,
|
||||
start_fn: *const fn (?*anyopaque) callconv(.c) ?*anyopaque,
|
||||
arg: ?*anyopaque,
|
||||
) !Thread_t {
|
||||
var tid: Thread_t = undefined;
|
||||
try check(sys.pthread_create(&tid, attr, start_fn, arg));
|
||||
return tid;
|
||||
}
|
||||
|
||||
/// Wait for a thread to terminate; returns its exit value.
|
||||
pub fn join(tid: Thread_t) !?*anyopaque {
|
||||
var value: ?*anyopaque = null;
|
||||
try check(sys.pthread_join(tid, &value));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Detach a thread so its resources are freed automatically on exit.
|
||||
pub fn detach(tid: Thread_t) !void {
|
||||
try check(sys.pthread_detach(tid));
|
||||
}
|
||||
|
||||
/// Terminate the calling thread with `retval`.
|
||||
pub fn exit(retval: ?*anyopaque) noreturn {
|
||||
sys.pthread_exit(retval);
|
||||
unreachable;
|
||||
}
|
||||
|
||||
/// Return the thread ID of the calling thread.
|
||||
pub fn self() Thread_t {
|
||||
return sys.pthread_self();
|
||||
}
|
||||
|
||||
/// Return `true` if two thread IDs refer to the same thread.
|
||||
pub fn equal(t1: Thread_t, t2: Thread_t) bool {
|
||||
return sys.pthread_equal(t1, t2) != 0;
|
||||
}
|
||||
|
||||
/// Yield the processor to another ready thread.
|
||||
pub fn yield() void {
|
||||
sys.pthread_yield();
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Thread attributes
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Attr = struct {
|
||||
pub fn init(attr: *Attr_t) !void {
|
||||
try check(sys.pthread_attr_init(attr));
|
||||
}
|
||||
pub fn deinit(attr: *Attr_t) !void {
|
||||
try check(sys.pthread_attr_destroy(attr));
|
||||
}
|
||||
pub fn setStackSize(attr: *Attr_t, size: usize) !void {
|
||||
try check(sys.pthread_attr_setstacksize(attr, size));
|
||||
}
|
||||
pub fn getStackSize(attr: *const Attr_t) !usize {
|
||||
var sz: usize = 0;
|
||||
try check(sys.pthread_attr_getstacksize(attr, &sz));
|
||||
return sz;
|
||||
}
|
||||
pub fn setDetachState(attr: *Attr_t, state: c_int) !void {
|
||||
try check(sys.pthread_attr_setdetachstate(attr, state));
|
||||
}
|
||||
pub fn getDetachState(attr: *const Attr_t) !c_int {
|
||||
var s: c_int = 0;
|
||||
try check(sys.pthread_attr_getdetachstate(attr, &s));
|
||||
return s;
|
||||
}
|
||||
pub fn setSchedParam(attr: *Attr_t, param: *const SchedParam) !void {
|
||||
try check(sys.pthread_attr_setschedparam(attr, param));
|
||||
}
|
||||
pub fn getSchedParam(attr: *const Attr_t) !SchedParam {
|
||||
var p: SchedParam = .{};
|
||||
try check(sys.pthread_attr_getschedparam(attr, &p));
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mutex
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Mutex = struct {
|
||||
pub fn init(mutex: *Mutex_t, attr: ?*const MutexAttr_t) !void {
|
||||
try check(sys.pthread_mutex_init(mutex, attr));
|
||||
}
|
||||
pub fn deinit(mutex: *Mutex_t) !void {
|
||||
try check(sys.pthread_mutex_destroy(mutex));
|
||||
}
|
||||
pub fn lock(mutex: *Mutex_t) !void {
|
||||
try check(sys.pthread_mutex_lock(mutex));
|
||||
}
|
||||
pub fn tryLock(mutex: *Mutex_t) !void {
|
||||
try check(sys.pthread_mutex_trylock(mutex));
|
||||
}
|
||||
pub fn unlock(mutex: *Mutex_t) !void {
|
||||
try check(sys.pthread_mutex_unlock(mutex));
|
||||
}
|
||||
pub fn timedLock(mutex: *Mutex_t, timeout: *const Timespec) !void {
|
||||
try check(sys.pthread_mutex_timedlock(mutex, timeout));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Condition variable
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Cond = struct {
|
||||
pub fn init(cond: *Cond_t, attr: ?*const CondAttr_t) !void {
|
||||
try check(sys.pthread_cond_init(cond, attr));
|
||||
}
|
||||
pub fn deinit(cond: *Cond_t) !void {
|
||||
try check(sys.pthread_cond_destroy(cond));
|
||||
}
|
||||
pub fn wait(cond: *Cond_t, mutex: *Mutex_t) !void {
|
||||
try check(sys.pthread_cond_wait(cond, mutex));
|
||||
}
|
||||
pub fn timedWait(cond: *Cond_t, mutex: *Mutex_t, abstime: *const Timespec) !void {
|
||||
try check(sys.pthread_cond_timedwait(cond, mutex, abstime));
|
||||
}
|
||||
pub fn signal(cond: *Cond_t) !void {
|
||||
try check(sys.pthread_cond_signal(cond));
|
||||
}
|
||||
pub fn broadcast(cond: *Cond_t) !void {
|
||||
try check(sys.pthread_cond_broadcast(cond));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Thread-local storage (TLS) keys
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Key = struct {
|
||||
pub fn create(destructor: ?*const fn (?*anyopaque) callconv(.c) void) !Key_t {
|
||||
var key: Key_t = undefined;
|
||||
try check(sys.pthread_key_create(&key, destructor));
|
||||
return key;
|
||||
}
|
||||
pub fn delete(key: Key_t) !void {
|
||||
try check(sys.pthread_key_delete(key));
|
||||
}
|
||||
pub fn setSpecific(key: Key_t, value: ?*const anyopaque) !void {
|
||||
try check(sys.pthread_setspecific(key, value));
|
||||
}
|
||||
pub fn getSpecific(key: Key_t) ?*anyopaque {
|
||||
return sys.pthread_getspecific(key);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Once-only initialisation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn once(once_ctrl: *Once_t, init_fn: *const fn () callconv(.c) void) !void {
|
||||
try check(sys.pthread_once(once_ctrl, init_fn));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ESP-IDF pthread extensions (esp_pthread.h)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const EspThread = struct {
|
||||
/// Return a default ESP pthread configuration populated from menuconfig values.
|
||||
pub fn getDefaultConfig() EspCfg {
|
||||
return sys.esp_pthread_get_default_config();
|
||||
}
|
||||
|
||||
/// Set per-thread creation parameters for the next `pthread_create()` call.
|
||||
/// Overrides stack size, priority, core affinity, and thread name.
|
||||
pub fn setConfig(cfg: *const EspCfg) !void {
|
||||
try errors.espCheckError(sys.esp_pthread_set_cfg(cfg));
|
||||
}
|
||||
|
||||
/// Retrieve the currently active ESP pthread configuration.
|
||||
pub fn getConfig(cfg: *EspCfg) !void {
|
||||
try errors.espCheckError(sys.esp_pthread_get_cfg(cfg));
|
||||
}
|
||||
|
||||
/// Initialise the ESP pthread library (called internally by ESP-IDF startup).
|
||||
pub fn init() !void {
|
||||
try errors.espCheckError(sys.esp_pthread_init());
|
||||
}
|
||||
};
|
||||
734
software/zig_main/imports/rtos.zig
Normal file
734
software/zig_main/imports/rtos.zig
Normal file
@@ -0,0 +1,734 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases — use these instead of raw C types at call sites.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const TaskHandle = sys.TaskHandle_t;
|
||||
pub const QueueHandle = sys.QueueHandle_t;
|
||||
pub const SemaphoreHandle = sys.QueueHandle_t;
|
||||
pub const TimerHandle = sys.TimerHandle_t;
|
||||
pub const EventGroupHandle = sys.EventGroupHandle_t;
|
||||
pub const StreamBufferHandle = sys.StreamBufferHandle_t;
|
||||
pub const RingbufHandle = sys.RingbufHandle_t;
|
||||
pub const TickType = sys.TickType_t;
|
||||
pub const BaseType = sys.BaseType_t;
|
||||
pub const UBaseType = sys.UBaseType_t;
|
||||
|
||||
pub const portMAX_DELAY = sys.portMAX_DELAY;
|
||||
pub const portTICK_PERIOD_MS = sys.portTICK_PERIOD_MS;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tick conversion helpers.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Convert milliseconds to ticks (truncating, matching FreeRTOS pdMS_TO_TICKS).
|
||||
pub inline fn msToTicks(ms: TickType) TickType {
|
||||
return @divTrunc(ms * sys.configTICK_RATE_HZ, 1000);
|
||||
}
|
||||
|
||||
/// Convert ticks to milliseconds (truncating).
|
||||
pub inline fn ticksToMs(ticks: TickType) TickType {
|
||||
return @divTrunc(ticks * 1000, sys.configTICK_RATE_HZ);
|
||||
}
|
||||
|
||||
/// Returns true if a FreeRTOS BaseType_t return value indicates success.
|
||||
inline fn pdOK(ret: BaseType) bool {
|
||||
return ret == sys.pdTRUE;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Task
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Task = struct {
|
||||
pub const Handle = TaskHandle;
|
||||
pub const Function = sys.TaskFunction_t;
|
||||
|
||||
/// Create a task pinned to a specific core (pass `tskNO_AFFINITY` for any core).
|
||||
pub fn createPinnedToCore(
|
||||
task_fn: Function,
|
||||
name: [*:0]const u8,
|
||||
stack_size: u32,
|
||||
param: ?*anyopaque,
|
||||
priority: UBaseType,
|
||||
core_id: BaseType,
|
||||
) !Handle {
|
||||
var handle: Handle = null;
|
||||
if (!pdOK(sys.xTaskCreatePinnedToCore(task_fn, name, stack_size, param, priority, &handle, core_id)))
|
||||
return error.TaskCreateFailed;
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// Create a task with no core affinity.
|
||||
pub fn create(
|
||||
task_fn: Function,
|
||||
name: [*:0]const u8,
|
||||
stack_size: u32,
|
||||
param: ?*anyopaque,
|
||||
priority: UBaseType,
|
||||
) !Handle {
|
||||
var handle: Handle = null;
|
||||
if (!pdOK(sys.xTaskCreate(task_fn, name, stack_size, param, priority, &handle)))
|
||||
return error.TaskCreateFailed;
|
||||
return handle;
|
||||
}
|
||||
|
||||
pub fn createStaticPinnedToCore(
|
||||
task_fn: Function,
|
||||
name: [*:0]const u8,
|
||||
stack_size: u32,
|
||||
param: ?*anyopaque,
|
||||
priority: UBaseType,
|
||||
stack_buf: [*]sys.StackType_t,
|
||||
task_buf: *sys.StaticTask_t,
|
||||
core_id: BaseType,
|
||||
) Handle {
|
||||
return sys.xTaskCreateStaticPinnedToCore(task_fn, name, stack_size, param, priority, stack_buf, task_buf, core_id);
|
||||
}
|
||||
|
||||
pub fn delete(handle: ?Handle) void {
|
||||
sys.vTaskDelete(handle);
|
||||
}
|
||||
|
||||
pub fn delay(ticks: TickType) void {
|
||||
sys.vTaskDelay(ticks);
|
||||
}
|
||||
|
||||
pub fn delayMs(ms: TickType) void {
|
||||
sys.vTaskDelay(msToTicks(ms));
|
||||
}
|
||||
|
||||
pub fn delayUntil(prev_wake: *TickType, increment: TickType) bool {
|
||||
return pdOK(sys.xTaskDelayUntil(prev_wake, increment));
|
||||
}
|
||||
|
||||
pub fn @"suspend"(handle: ?Handle) void {
|
||||
sys.vTaskSuspend(handle);
|
||||
}
|
||||
|
||||
pub fn @"resume"(handle: Handle) void {
|
||||
sys.vTaskResume(handle);
|
||||
}
|
||||
|
||||
pub fn resumeFromISR(handle: Handle) bool {
|
||||
return pdOK(sys.xTaskResumeFromISR(handle));
|
||||
}
|
||||
|
||||
pub fn priorityGet(handle: ?Handle) UBaseType {
|
||||
return sys.uxTaskPriorityGet(handle);
|
||||
}
|
||||
|
||||
pub fn prioritySet(handle: ?Handle, priority: UBaseType) void {
|
||||
sys.vTaskPrioritySet(handle, priority);
|
||||
}
|
||||
|
||||
pub fn getName(handle: ?Handle) [*:0]const u8 {
|
||||
return sys.pcTaskGetName(handle);
|
||||
}
|
||||
|
||||
pub fn getHandle(name: [*:0]const u8) ?Handle {
|
||||
return sys.xTaskGetHandle(name);
|
||||
}
|
||||
|
||||
pub fn getCurrent() Handle {
|
||||
return sys.xTaskGetCurrentTaskHandle();
|
||||
}
|
||||
|
||||
pub fn getTickCount() TickType {
|
||||
return sys.xTaskGetTickCount();
|
||||
}
|
||||
|
||||
pub fn getTickCountFromISR() TickType {
|
||||
return sys.xTaskGetTickCountFromISR();
|
||||
}
|
||||
|
||||
pub fn getNumberOfTasks() UBaseType {
|
||||
return sys.uxTaskGetNumberOfTasks();
|
||||
}
|
||||
|
||||
pub fn getStackHighWaterMark(handle: ?Handle) UBaseType {
|
||||
return sys.uxTaskGetStackHighWaterMark(handle);
|
||||
}
|
||||
|
||||
pub fn getSchedulerState() BaseType {
|
||||
return sys.xTaskGetSchedulerState();
|
||||
}
|
||||
|
||||
pub fn abortDelay(handle: Handle) bool {
|
||||
return pdOK(sys.xTaskAbortDelay(handle));
|
||||
}
|
||||
|
||||
pub fn startScheduler() void {
|
||||
sys.vTaskStartScheduler();
|
||||
}
|
||||
|
||||
pub fn endScheduler() void {
|
||||
sys.vTaskEndScheduler();
|
||||
}
|
||||
|
||||
pub fn suspendAll() void {
|
||||
sys.vTaskSuspendAll();
|
||||
}
|
||||
|
||||
pub fn resumeAll() bool {
|
||||
return pdOK(sys.xTaskResumeAll());
|
||||
}
|
||||
|
||||
pub fn setThreadLocalStoragePointer(handle: ?Handle, index: BaseType, value: ?*anyopaque) void {
|
||||
sys.vTaskSetThreadLocalStoragePointer(handle, index, value);
|
||||
}
|
||||
|
||||
pub fn getThreadLocalStoragePointer(handle: ?Handle, index: BaseType) ?*anyopaque {
|
||||
return sys.pvTaskGetThreadLocalStoragePointer(handle, index);
|
||||
}
|
||||
|
||||
/// Send a direct-to-task notification.
|
||||
pub fn notify(
|
||||
handle: Handle,
|
||||
value: u32,
|
||||
action: sys.eNotifyAction,
|
||||
index: UBaseType,
|
||||
) !void {
|
||||
if (!pdOK(sys.xTaskGenericNotify(handle, index, value, action, null)))
|
||||
return error.TaskNotifyFailed;
|
||||
}
|
||||
|
||||
pub fn notifyWait(
|
||||
index: UBaseType,
|
||||
clear_on_entry: u32,
|
||||
clear_on_exit: u32,
|
||||
out_value: ?*u32,
|
||||
ticks_to_wait: TickType,
|
||||
) bool {
|
||||
return pdOK(sys.xTaskGenericNotifyWait(index, clear_on_entry, clear_on_exit, out_value, ticks_to_wait));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Queue
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Queue = struct {
|
||||
pub const Handle = QueueHandle;
|
||||
|
||||
pub fn create(length: UBaseType, item_size: UBaseType) !Handle {
|
||||
const h = sys.xQueueGenericCreate(length, item_size, sys.queueQUEUE_TYPE_BASE);
|
||||
return h orelse error.QueueCreateFailed;
|
||||
}
|
||||
|
||||
pub fn createStatic(
|
||||
length: UBaseType,
|
||||
item_size: UBaseType,
|
||||
storage: [*]u8,
|
||||
buf: *sys.StaticQueue_t,
|
||||
) Handle {
|
||||
return sys.xQueueGenericCreateStatic(length, item_size, storage, buf, sys.queueQUEUE_TYPE_BASE);
|
||||
}
|
||||
|
||||
pub fn delete(handle: Handle) void {
|
||||
sys.vQueueDelete(handle);
|
||||
}
|
||||
|
||||
pub fn reset(handle: Handle) bool {
|
||||
return pdOK(sys.xQueueGenericReset(handle, 0));
|
||||
}
|
||||
|
||||
pub fn send(handle: Handle, item: *const anyopaque, ticks_to_wait: TickType) bool {
|
||||
return pdOK(sys.xQueueGenericSend(handle, item, ticks_to_wait, sys.queueSEND_TO_BACK));
|
||||
}
|
||||
|
||||
pub fn sendToFront(handle: Handle, item: *const anyopaque, ticks_to_wait: TickType) bool {
|
||||
return pdOK(sys.xQueueGenericSend(handle, item, ticks_to_wait, sys.queueSEND_TO_FRONT));
|
||||
}
|
||||
|
||||
pub fn overwrite(handle: Handle, item: *const anyopaque) void {
|
||||
_ = sys.xQueueGenericSend(handle, item, 0, sys.queueOVERWRITE);
|
||||
}
|
||||
|
||||
pub fn sendFromISR(handle: Handle, item: *const anyopaque, higher_prio_woken: *BaseType) bool {
|
||||
return pdOK(sys.xQueueGenericSendFromISR(handle, item, higher_prio_woken, sys.queueSEND_TO_BACK));
|
||||
}
|
||||
|
||||
pub fn overwriteFromISR(handle: Handle, item: *const anyopaque, higher_prio_woken: *BaseType) void {
|
||||
_ = sys.xQueueGenericSendFromISR(handle, item, higher_prio_woken, sys.queueOVERWRITE);
|
||||
}
|
||||
|
||||
pub fn receive(handle: Handle, out: *anyopaque, ticks_to_wait: TickType) bool {
|
||||
return pdOK(sys.xQueueReceive(handle, out, ticks_to_wait));
|
||||
}
|
||||
|
||||
pub fn receiveFromISR(handle: Handle, out: *anyopaque, higher_prio_woken: *BaseType) bool {
|
||||
return pdOK(sys.xQueueReceiveFromISR(handle, out, higher_prio_woken));
|
||||
}
|
||||
|
||||
pub fn peek(handle: Handle, out: *anyopaque, ticks_to_wait: TickType) bool {
|
||||
return pdOK(sys.xQueuePeek(handle, out, ticks_to_wait));
|
||||
}
|
||||
|
||||
pub fn messagesWaiting(handle: Handle) UBaseType {
|
||||
return sys.uxQueueMessagesWaiting(handle);
|
||||
}
|
||||
|
||||
pub fn spacesAvailable(handle: Handle) UBaseType {
|
||||
return sys.uxQueueSpacesAvailable(handle);
|
||||
}
|
||||
|
||||
pub fn isEmptyFromISR(handle: Handle) bool {
|
||||
return pdOK(sys.xQueueIsQueueEmptyFromISR(handle));
|
||||
}
|
||||
|
||||
pub fn isFullFromISR(handle: Handle) bool {
|
||||
return pdOK(sys.xQueueIsQueueFullFromISR(handle));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Semaphore
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Semaphore = struct {
|
||||
pub const Handle = SemaphoreHandle;
|
||||
|
||||
// Queue type constants (from FreeRTOS queue.h).
|
||||
const QUEUE_TYPE_BINARY_SEMAPHORE = sys.queueQUEUE_TYPE_BINARY_SEMAPHORE;
|
||||
const QUEUE_TYPE_MUTEX = sys.queueQUEUE_TYPE_MUTEX;
|
||||
const QUEUE_TYPE_RECURSIVE_MUTEX = sys.queueQUEUE_TYPE_RECURSIVE_MUTEX;
|
||||
const QUEUE_TYPE_COUNTING_SEMAPHORE = sys.queueQUEUE_TYPE_COUNTING_SEMAPHORE;
|
||||
|
||||
pub fn createBinary() !Handle {
|
||||
const h = sys.xQueueGenericCreate(1, 0, QUEUE_TYPE_BINARY_SEMAPHORE);
|
||||
return h orelse error.SemaphoreCreateFailed;
|
||||
}
|
||||
|
||||
pub fn createBinaryStatic(buf: *sys.StaticQueue_t) Handle {
|
||||
return sys.xQueueGenericCreateStatic(1, 0, null, buf, QUEUE_TYPE_BINARY_SEMAPHORE);
|
||||
}
|
||||
|
||||
pub fn createMutex() !Handle {
|
||||
const h = sys.xQueueCreateMutex(QUEUE_TYPE_MUTEX);
|
||||
return h orelse error.SemaphoreCreateFailed;
|
||||
}
|
||||
|
||||
pub fn createMutexStatic(buf: *sys.StaticQueue_t) Handle {
|
||||
return sys.xQueueCreateMutexStatic(QUEUE_TYPE_MUTEX, buf);
|
||||
}
|
||||
|
||||
pub fn createRecursiveMutex() !Handle {
|
||||
const h = sys.xQueueCreateMutex(QUEUE_TYPE_RECURSIVE_MUTEX);
|
||||
return h orelse error.SemaphoreCreateFailed;
|
||||
}
|
||||
|
||||
pub fn createRecursiveMutexStatic(buf: *sys.StaticQueue_t) Handle {
|
||||
return sys.xQueueCreateMutexStatic(QUEUE_TYPE_RECURSIVE_MUTEX, buf);
|
||||
}
|
||||
|
||||
pub fn createCounting(max_count: UBaseType, initial_count: UBaseType) !Handle {
|
||||
const h = sys.xQueueCreateCountingSemaphore(max_count, initial_count);
|
||||
return h orelse error.SemaphoreCreateFailed;
|
||||
}
|
||||
|
||||
pub fn createCountingStatic(max_count: UBaseType, initial_count: UBaseType, buf: *sys.StaticQueue_t) Handle {
|
||||
return sys.xQueueCreateCountingSemaphoreStatic(max_count, initial_count, buf);
|
||||
}
|
||||
|
||||
pub fn delete(handle: Handle) void {
|
||||
sys.vQueueDelete(handle);
|
||||
}
|
||||
|
||||
pub fn take(handle: Handle, ticks_to_wait: TickType) bool {
|
||||
return pdOK(sys.xQueueSemaphoreTake(handle, ticks_to_wait));
|
||||
}
|
||||
|
||||
pub fn takeRecursive(handle: Handle, ticks_to_wait: TickType) bool {
|
||||
return pdOK(sys.xQueueTakeMutexRecursive(handle, ticks_to_wait));
|
||||
}
|
||||
|
||||
pub fn takeFromISR(handle: Handle, higher_prio_woken: *BaseType) bool {
|
||||
return pdOK(sys.xQueueReceiveFromISR(handle, null, higher_prio_woken));
|
||||
}
|
||||
|
||||
pub fn give(handle: Handle) bool {
|
||||
return pdOK(sys.xQueueGenericSend(handle, null, 0, sys.queueSEND_TO_BACK));
|
||||
}
|
||||
|
||||
pub fn giveRecursive(handle: Handle) bool {
|
||||
return pdOK(sys.xQueueGiveMutexRecursive(handle));
|
||||
}
|
||||
|
||||
pub fn giveFromISR(handle: Handle, higher_prio_woken: *BaseType) bool {
|
||||
return pdOK(sys.xQueueGiveFromISR(handle, higher_prio_woken));
|
||||
}
|
||||
|
||||
pub fn getMutexHolder(handle: Handle) ?TaskHandle {
|
||||
return sys.xQueueGetMutexHolder(handle);
|
||||
}
|
||||
|
||||
pub fn getMutexHolderFromISR(handle: Handle) ?TaskHandle {
|
||||
return sys.xQueueGetMutexHolderFromISR(handle);
|
||||
}
|
||||
|
||||
pub fn getCount(handle: Handle) UBaseType {
|
||||
return sys.uxQueueMessagesWaiting(handle);
|
||||
}
|
||||
|
||||
pub fn getCountFromISR(handle: Handle) UBaseType {
|
||||
return sys.uxQueueMessagesWaitingFromISR(handle);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timer
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Timer = struct {
|
||||
pub const Handle = TimerHandle;
|
||||
pub const Callback = sys.TimerCallbackFunction_t;
|
||||
|
||||
pub fn create(
|
||||
name: [*:0]const u8,
|
||||
period: TickType,
|
||||
auto_reload: bool,
|
||||
id: ?*anyopaque,
|
||||
callback: Callback,
|
||||
) !Handle {
|
||||
const h = sys.xTimerCreate(name, period, @intFromBool(auto_reload), id, callback);
|
||||
return h orelse error.TimerCreateFailed;
|
||||
}
|
||||
|
||||
pub fn createStatic(
|
||||
name: [*:0]const u8,
|
||||
period: TickType,
|
||||
auto_reload: bool,
|
||||
id: ?*anyopaque,
|
||||
callback: Callback,
|
||||
buf: *sys.StaticTimer_t,
|
||||
) Handle {
|
||||
return sys.xTimerCreateStatic(name, period, @intFromBool(auto_reload), id, callback, buf);
|
||||
}
|
||||
|
||||
pub fn start(handle: Handle, ticks_to_wait: TickType) bool {
|
||||
return pdOK(sys.xTimerGenericCommandFromTask(handle, sys.tmrCOMMAND_START, sys.xTaskGetTickCount(), null, ticks_to_wait));
|
||||
}
|
||||
|
||||
pub fn stop(handle: Handle, ticks_to_wait: TickType) bool {
|
||||
return pdOK(sys.xTimerGenericCommandFromTask(handle, sys.tmrCOMMAND_STOP, 0, null, ticks_to_wait));
|
||||
}
|
||||
|
||||
pub fn reset(handle: Handle, ticks_to_wait: TickType) bool {
|
||||
return pdOK(sys.xTimerGenericCommandFromTask(handle, sys.tmrCOMMAND_RESET, sys.xTaskGetTickCount(), null, ticks_to_wait));
|
||||
}
|
||||
|
||||
pub fn changePeriod(handle: Handle, new_period: TickType, ticks_to_wait: TickType) bool {
|
||||
return pdOK(sys.xTimerGenericCommandFromTask(handle, sys.tmrCOMMAND_CHANGE_PERIOD, new_period, null, ticks_to_wait));
|
||||
}
|
||||
|
||||
pub fn delete(handle: Handle, ticks_to_wait: TickType) bool {
|
||||
return pdOK(sys.xTimerGenericCommandFromTask(handle, sys.tmrCOMMAND_DELETE, 0, null, ticks_to_wait));
|
||||
}
|
||||
|
||||
pub fn isActive(handle: Handle) bool {
|
||||
return pdOK(sys.xTimerIsTimerActive(handle));
|
||||
}
|
||||
|
||||
pub fn getName(handle: Handle) [*:0]const u8 {
|
||||
return sys.pcTimerGetName(handle);
|
||||
}
|
||||
|
||||
pub fn getPeriod(handle: Handle) TickType {
|
||||
return sys.xTimerGetPeriod(handle);
|
||||
}
|
||||
|
||||
pub fn getID(handle: Handle) ?*anyopaque {
|
||||
return sys.pvTimerGetTimerID(handle);
|
||||
}
|
||||
|
||||
pub fn setID(handle: Handle, id: ?*anyopaque) void {
|
||||
sys.vTimerSetTimerID(handle, id);
|
||||
}
|
||||
|
||||
pub fn setReloadMode(handle: Handle, auto_reload: bool) void {
|
||||
sys.vTimerSetReloadMode(handle, @intFromBool(auto_reload));
|
||||
}
|
||||
|
||||
pub fn pendFunctionCall(
|
||||
callback: sys.PendedFunction_t,
|
||||
param1: ?*anyopaque,
|
||||
param2: u32,
|
||||
ticks_to_wait: TickType,
|
||||
) bool {
|
||||
return pdOK(sys.xTimerPendFunctionCall(callback, param1, param2, ticks_to_wait));
|
||||
}
|
||||
|
||||
pub fn pendFunctionCallFromISR(
|
||||
callback: sys.PendedFunction_t,
|
||||
param1: ?*anyopaque,
|
||||
param2: u32,
|
||||
higher_prio_woken: *BaseType,
|
||||
) bool {
|
||||
return pdOK(sys.xTimerPendFunctionCallFromISR(callback, param1, param2, higher_prio_woken));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// EventGroup
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const EventGroup = struct {
|
||||
pub const Handle = EventGroupHandle;
|
||||
pub const Bits = sys.EventBits_t;
|
||||
|
||||
pub fn create() !Handle {
|
||||
const h = sys.xEventGroupCreate();
|
||||
return h orelse error.EventGroupCreateFailed;
|
||||
}
|
||||
|
||||
pub fn createStatic(buf: *sys.StaticEventGroup_t) Handle {
|
||||
return sys.xEventGroupCreateStatic(buf);
|
||||
}
|
||||
|
||||
pub fn delete(handle: Handle) void {
|
||||
sys.vEventGroupDelete(handle);
|
||||
}
|
||||
|
||||
pub fn waitBits(
|
||||
handle: Handle,
|
||||
bits_to_wait: Bits,
|
||||
clear_on_exit: bool,
|
||||
wait_for_all: bool,
|
||||
ticks_to_wait: TickType,
|
||||
) Bits {
|
||||
return sys.xEventGroupWaitBits(
|
||||
handle,
|
||||
bits_to_wait,
|
||||
@intFromBool(clear_on_exit),
|
||||
@intFromBool(wait_for_all),
|
||||
ticks_to_wait,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setBits(handle: Handle, bits: Bits) Bits {
|
||||
return sys.xEventGroupSetBits(handle, bits);
|
||||
}
|
||||
|
||||
pub fn clearBits(handle: Handle, bits: Bits) Bits {
|
||||
return sys.xEventGroupClearBits(handle, bits);
|
||||
}
|
||||
|
||||
pub fn getBitsFromISR(handle: Handle) Bits {
|
||||
return sys.xEventGroupGetBitsFromISR(handle);
|
||||
}
|
||||
|
||||
pub fn sync(
|
||||
handle: Handle,
|
||||
bits_to_set: Bits,
|
||||
bits_to_wait: Bits,
|
||||
ticks_to_wait: TickType,
|
||||
) Bits {
|
||||
return sys.xEventGroupSync(handle, bits_to_set, bits_to_wait, ticks_to_wait);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// StreamBuffer
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const StreamBuffer = struct {
|
||||
pub const Handle = StreamBufferHandle;
|
||||
|
||||
pub fn create(size: usize, trigger_level: usize) !Handle {
|
||||
const h = sys.xStreamBufferGenericCreate(size, trigger_level, 0, null, null);
|
||||
return h orelse error.StreamBufferCreateFailed;
|
||||
}
|
||||
|
||||
pub fn createStatic(
|
||||
size: usize,
|
||||
trigger_level: usize,
|
||||
storage: [*]u8,
|
||||
buf: *sys.StaticStreamBuffer_t,
|
||||
) Handle {
|
||||
return sys.xStreamBufferGenericCreateStatic(size, trigger_level, 0, storage, buf, null, null);
|
||||
}
|
||||
|
||||
pub fn delete(handle: Handle) void {
|
||||
sys.vStreamBufferDelete(handle);
|
||||
}
|
||||
|
||||
pub fn send(handle: Handle, data: []const u8, ticks_to_wait: TickType) usize {
|
||||
return sys.xStreamBufferSend(handle, data.ptr, data.len, ticks_to_wait);
|
||||
}
|
||||
|
||||
pub fn sendFromISR(handle: Handle, data: []const u8, higher_prio_woken: *BaseType) usize {
|
||||
return sys.xStreamBufferSendFromISR(handle, data.ptr, data.len, higher_prio_woken);
|
||||
}
|
||||
|
||||
pub fn receive(handle: Handle, buf: []u8, ticks_to_wait: TickType) usize {
|
||||
return sys.xStreamBufferReceive(handle, buf.ptr, buf.len, ticks_to_wait);
|
||||
}
|
||||
|
||||
pub fn receiveFromISR(handle: Handle, buf: []u8, higher_prio_woken: *BaseType) usize {
|
||||
return sys.xStreamBufferReceiveFromISR(handle, buf.ptr, buf.len, higher_prio_woken);
|
||||
}
|
||||
|
||||
pub fn reset(handle: Handle) bool {
|
||||
return pdOK(sys.xStreamBufferReset(handle));
|
||||
}
|
||||
|
||||
pub fn isFull(handle: Handle) bool {
|
||||
return pdOK(sys.xStreamBufferIsFull(handle));
|
||||
}
|
||||
|
||||
pub fn isEmpty(handle: Handle) bool {
|
||||
return pdOK(sys.xStreamBufferIsEmpty(handle));
|
||||
}
|
||||
|
||||
pub fn spacesAvailable(handle: Handle) usize {
|
||||
return sys.xStreamBufferSpacesAvailable(handle);
|
||||
}
|
||||
|
||||
pub fn bytesAvailable(handle: Handle) usize {
|
||||
return sys.xStreamBufferBytesAvailable(handle);
|
||||
}
|
||||
|
||||
pub fn setTriggerLevel(handle: Handle, trigger_level: usize) bool {
|
||||
return pdOK(sys.xStreamBufferSetTriggerLevel(handle, trigger_level));
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MessageBuffer (thin wrapper over StreamBuffer with a 4-byte length header).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const MessageBuffer = struct {
|
||||
pub const Handle = StreamBufferHandle;
|
||||
|
||||
pub fn create(size: usize) !Handle {
|
||||
const h = sys.xStreamBufferGenericCreate(size, 0, 1, null, null);
|
||||
return h orelse error.MessageBufferCreateFailed;
|
||||
}
|
||||
|
||||
pub fn delete(handle: Handle) void {
|
||||
sys.vStreamBufferDelete(handle);
|
||||
}
|
||||
|
||||
pub fn send(handle: Handle, data: []const u8, ticks_to_wait: TickType) usize {
|
||||
return sys.xStreamBufferSend(handle, data.ptr, data.len, ticks_to_wait);
|
||||
}
|
||||
|
||||
pub fn receive(handle: Handle, buf: []u8, ticks_to_wait: TickType) usize {
|
||||
return sys.xStreamBufferReceive(handle, buf.ptr, buf.len, ticks_to_wait);
|
||||
}
|
||||
|
||||
pub fn nextMessageLength(handle: Handle) usize {
|
||||
return sys.xStreamBufferNextMessageLengthBytes(handle);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// RingBuffer (ESP-IDF extension)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const RingBuffer = struct {
|
||||
pub const Handle = RingbufHandle;
|
||||
pub const Type = sys.RingbufferType_t;
|
||||
|
||||
pub fn create(size: usize, kind: Type) !Handle {
|
||||
const h = sys.xRingbufferCreate(size, kind);
|
||||
return h orelse error.RingbufferCreateFailed;
|
||||
}
|
||||
|
||||
pub fn createNoSplit(item_size: usize, item_count: usize) !Handle {
|
||||
const h = sys.xRingbufferCreateNoSplit(item_size, item_count);
|
||||
return h orelse error.RingbufferCreateFailed;
|
||||
}
|
||||
|
||||
pub fn createWithCaps(size: usize, kind: Type, caps: u32) !Handle {
|
||||
const h = sys.xRingbufferCreateWithCaps(size, kind, caps);
|
||||
return h orelse error.RingbufferCreateFailed;
|
||||
}
|
||||
|
||||
pub fn delete(handle: Handle) void {
|
||||
sys.vRingbufferDelete(handle);
|
||||
}
|
||||
|
||||
pub fn deleteWithCaps(handle: Handle) void {
|
||||
sys.vRingbufferDeleteWithCaps(handle);
|
||||
}
|
||||
|
||||
pub fn send(handle: Handle, data: []const u8, ticks_to_wait: TickType) bool {
|
||||
return pdOK(sys.xRingbufferSend(handle, data.ptr, data.len, ticks_to_wait));
|
||||
}
|
||||
|
||||
pub fn sendFromISR(handle: Handle, data: []const u8, higher_prio_woken: *BaseType) bool {
|
||||
return pdOK(sys.xRingbufferSendFromISR(handle, data.ptr, data.len, higher_prio_woken));
|
||||
}
|
||||
|
||||
pub fn receive(handle: Handle, out_size: *usize, ticks_to_wait: TickType) ?[]u8 {
|
||||
var size: usize = 0;
|
||||
const ptr = sys.xRingbufferReceive(handle, &size, ticks_to_wait);
|
||||
if (ptr == null) return null;
|
||||
out_size.* = size;
|
||||
return @as([*]u8, @ptrCast(ptr))[0..size];
|
||||
}
|
||||
|
||||
pub fn receiveFromISR(handle: Handle, out_size: *usize) ?[]u8 {
|
||||
var size: usize = 0;
|
||||
const ptr = sys.xRingbufferReceiveFromISR(handle, &size);
|
||||
if (ptr == null) return null;
|
||||
out_size.* = size;
|
||||
return @as([*]u8, @ptrCast(ptr))[0..size];
|
||||
}
|
||||
|
||||
pub fn returnItem(handle: Handle, item: *anyopaque) void {
|
||||
sys.vRingbufferReturnItem(handle, item);
|
||||
}
|
||||
|
||||
pub fn returnItemFromISR(handle: Handle, item: *anyopaque, higher_prio_woken: *BaseType) void {
|
||||
sys.vRingbufferReturnItemFromISR(handle, item, higher_prio_woken);
|
||||
}
|
||||
|
||||
pub fn getMaxItemSize(handle: Handle) usize {
|
||||
return sys.xRingbufferGetMaxItemSize(handle);
|
||||
}
|
||||
|
||||
pub fn getCurFreeSize(handle: Handle) usize {
|
||||
return sys.xRingbufferGetCurFreeSize(handle);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Hook (idle/tick callbacks)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Hook = struct {
|
||||
pub const Register = struct {
|
||||
pub fn idleForCPU(cb: sys.esp_freertos_idle_cb_t, cpu_id: UBaseType) !void {
|
||||
try errors.espCheckError(sys.esp_register_freertos_idle_hook_for_cpu(cb, cpu_id));
|
||||
}
|
||||
pub fn idle(cb: sys.esp_freertos_idle_cb_t) !void {
|
||||
try errors.espCheckError(sys.esp_register_freertos_idle_hook(cb));
|
||||
}
|
||||
pub fn tickForCPU(cb: sys.esp_freertos_tick_cb_t, cpu_id: UBaseType) !void {
|
||||
try errors.espCheckError(sys.esp_register_freertos_tick_hook_for_cpu(cb, cpu_id));
|
||||
}
|
||||
pub fn tick(cb: sys.esp_freertos_tick_cb_t) !void {
|
||||
try errors.espCheckError(sys.esp_register_freertos_tick_hook(cb));
|
||||
}
|
||||
};
|
||||
|
||||
pub const Deregister = struct {
|
||||
pub fn idleForCPU(cb: sys.esp_freertos_idle_cb_t, cpu_id: UBaseType) void {
|
||||
sys.esp_deregister_freertos_idle_hook_for_cpu(cb, cpu_id);
|
||||
}
|
||||
pub fn idle(cb: sys.esp_freertos_idle_cb_t) void {
|
||||
sys.esp_deregister_freertos_idle_hook(cb);
|
||||
}
|
||||
pub fn tickForCPU(cb: sys.esp_freertos_tick_cb_t, cpu_id: UBaseType) void {
|
||||
sys.esp_deregister_freertos_tick_hook_for_cpu(cb, cpu_id);
|
||||
}
|
||||
pub fn tick(cb: sys.esp_freertos_tick_cb_t) void {
|
||||
sys.esp_deregister_freertos_tick_hook(cb);
|
||||
}
|
||||
};
|
||||
};
|
||||
219
software/zig_main/imports/segger.zig
Normal file
219
software/zig_main/imports/segger.zig
Normal file
@@ -0,0 +1,219 @@
|
||||
const sys = @import("sys");
|
||||
|
||||
/// SEGGER RTT channel flags
|
||||
pub const Flags = enum(c_uint) {
|
||||
no_block_skip = 0,
|
||||
no_block_trim = 1,
|
||||
block_if_fifo_full = 2,
|
||||
_,
|
||||
};
|
||||
|
||||
/// Errors returned by RTT operations
|
||||
pub const Error = error{
|
||||
/// Buffer index out of range or channel not configured
|
||||
InvalidBuffer,
|
||||
/// Write was skipped (no-block mode, FIFO full)
|
||||
WriteSkipped,
|
||||
};
|
||||
|
||||
fn rttResult(rc: c_int) Error!void {
|
||||
if (rc < 0) return error.InvalidBuffer;
|
||||
}
|
||||
|
||||
pub const RTT = struct {
|
||||
// -------------------------------------------------------------------------
|
||||
// Init
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
pub fn init() void {
|
||||
sys.SEGGER_RTT_Init();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Buffer allocation / configuration
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// Allocate an additional up-buffer (target → host).
|
||||
/// Returns the buffer index or `error.InvalidBuffer`.
|
||||
pub fn allocUpBuffer(name: [:0]const u8, buf: []u8, flags: Flags) Error!c_uint {
|
||||
const rc = sys.SEGGER_RTT_AllocUpBuffer(name.ptr, buf.ptr, @intCast(buf.len), @intFromEnum(flags));
|
||||
if (rc < 0) return error.InvalidBuffer;
|
||||
return @intCast(rc);
|
||||
}
|
||||
|
||||
/// Allocate an additional down-buffer (host → target).
|
||||
pub fn allocDownBuffer(name: [:0]const u8, buf: []u8, flags: Flags) Error!c_uint {
|
||||
const rc = sys.SEGGER_RTT_AllocDownBuffer(name.ptr, buf.ptr, @intCast(buf.len), @intFromEnum(flags));
|
||||
if (rc < 0) return error.InvalidBuffer;
|
||||
return @intCast(rc);
|
||||
}
|
||||
|
||||
/// Reconfigure an existing up-buffer.
|
||||
pub fn configUpBuffer(idx: c_uint, name: [:0]const u8, buf: []u8, flags: Flags) Error!void {
|
||||
return rttResult(sys.SEGGER_RTT_ConfigUpBuffer(idx, name.ptr, buf.ptr, @intCast(buf.len), @intFromEnum(flags)));
|
||||
}
|
||||
|
||||
/// Reconfigure an existing down-buffer.
|
||||
pub fn configDownBuffer(idx: c_uint, name: [:0]const u8, buf: []u8, flags: Flags) Error!void {
|
||||
return rttResult(sys.SEGGER_RTT_ConfigDownBuffer(idx, name.ptr, buf.ptr, @intCast(buf.len), @intFromEnum(flags)));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Rename / re-flag existing channels
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
pub fn setNameUpBuffer(idx: c_uint, name: [:0]const u8) Error!void {
|
||||
return rttResult(sys.SEGGER_RTT_SetNameUpBuffer(idx, name.ptr));
|
||||
}
|
||||
|
||||
pub fn setNameDownBuffer(idx: c_uint, name: [:0]const u8) Error!void {
|
||||
return rttResult(sys.SEGGER_RTT_SetNameDownBuffer(idx, name.ptr));
|
||||
}
|
||||
|
||||
pub fn setFlagsUpBuffer(idx: c_uint, flags: Flags) Error!void {
|
||||
return rttResult(sys.SEGGER_RTT_SetFlagsUpBuffer(idx, @intFromEnum(flags)));
|
||||
}
|
||||
|
||||
pub fn setFlagsDownBuffer(idx: c_uint, flags: Flags) Error!void {
|
||||
return rttResult(sys.SEGGER_RTT_SetFlagsDownBuffer(idx, @intFromEnum(flags)));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Write (target → host)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// Write bytes to an up-buffer. Returns the number of bytes actually written.
|
||||
pub fn write(idx: c_uint, data: []const u8) c_uint {
|
||||
return sys.SEGGER_RTT_Write(idx, data.ptr, @intCast(data.len));
|
||||
}
|
||||
|
||||
/// Write bytes without locking (call only from within a lock / ISR context).
|
||||
pub fn writeNoLock(idx: c_uint, data: []const u8) c_uint {
|
||||
return sys.SEGGER_RTT_WriteNoLock(idx, data.ptr, @intCast(data.len));
|
||||
}
|
||||
|
||||
/// Write bytes, overwriting old data if the FIFO is full (no-lock variant).
|
||||
pub fn writeWithOverwriteNoLock(idx: c_uint, data: []const u8) void {
|
||||
sys.SEGGER_RTT_WriteWithOverwriteNoLock(idx, data.ptr, @intCast(data.len));
|
||||
}
|
||||
|
||||
/// Write a single character to an up-buffer. Returns 1 on success, 0 if skipped.
|
||||
pub fn putChar(idx: c_uint, c: u8) bool {
|
||||
return sys.SEGGER_RTT_PutChar(idx, c) != 0;
|
||||
}
|
||||
|
||||
/// Write a single character; skip (don't wait) if full.
|
||||
pub fn putCharSkip(idx: c_uint, c: u8) bool {
|
||||
return sys.SEGGER_RTT_PutCharSkip(idx, c) != 0;
|
||||
}
|
||||
|
||||
/// Write a null-terminated string to an up-buffer.
|
||||
pub fn writeString(idx: c_uint, s: [:0]const u8) c_uint {
|
||||
return sys.SEGGER_RTT_WriteString(idx, s.ptr);
|
||||
}
|
||||
|
||||
/// Formatted print to an up-buffer (wraps `SEGGER_RTT_printf`).
|
||||
pub const printf = sys.SEGGER_RTT_printf;
|
||||
|
||||
/// Formatted print using a `va_list`.
|
||||
pub fn vprintf(idx: c_uint, fmt: [:0]const u8, args: [*c]sys.va_list) Error!void {
|
||||
return rttResult(sys.SEGGER_RTT_vprintf(idx, fmt.ptr, args));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Read (host → target)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// Read bytes from a down-buffer into `out`.
|
||||
/// Returns the number of bytes actually read.
|
||||
pub fn read(idx: c_uint, out: []u8) c_uint {
|
||||
return sys.SEGGER_RTT_Read(idx, out.ptr, @intCast(out.len));
|
||||
}
|
||||
|
||||
/// Read without locking — use only from a locked / ISR context.
|
||||
pub fn readNoLock(idx: c_uint, out: []u8) c_uint {
|
||||
return sys.SEGGER_RTT_ReadNoLock(idx, out.ptr, @intCast(out.len));
|
||||
}
|
||||
|
||||
/// Read from an up-buffer (host-side mirror; rarely needed on target).
|
||||
pub fn readUpBuffer(idx: c_uint, out: []u8) c_uint {
|
||||
return sys.SEGGER_RTT_ReadUpBuffer(idx, out.ptr, @intCast(out.len));
|
||||
}
|
||||
|
||||
pub fn readUpBufferNoLock(idx: c_uint, out: []u8) c_uint {
|
||||
return sys.SEGGER_RTT_ReadUpBufferNoLock(idx, out.ptr, @intCast(out.len));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Keyboard / single-char helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// Read one byte from down-buffer 0. Returns -1 if no data available.
|
||||
pub fn getKey() ?u8 {
|
||||
const rc = sys.SEGGER_RTT_GetKey();
|
||||
return if (rc < 0) null else @intCast(rc);
|
||||
}
|
||||
|
||||
/// Block until a byte is available on down-buffer 0.
|
||||
pub fn waitKey() u8 {
|
||||
return @intCast(sys.SEGGER_RTT_WaitKey());
|
||||
}
|
||||
|
||||
/// Returns `true` if at least one byte is available in the given down-buffer.
|
||||
pub fn hasData(idx: c_uint) bool {
|
||||
return sys.SEGGER_RTT_HasData(idx) != 0;
|
||||
}
|
||||
|
||||
/// Returns `true` if at least one byte is available in down-buffer 0.
|
||||
pub fn hasKey() bool {
|
||||
return sys.SEGGER_RTT_HasKey() != 0;
|
||||
}
|
||||
|
||||
/// Returns `true` if the given up-buffer has unread data (host hasn't read it yet).
|
||||
pub fn hasDataUp(idx: c_uint) bool {
|
||||
return sys.SEGGER_RTT_HasDataUp(idx) != 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Space / occupancy queries
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// Free space available for writing in an up-buffer (bytes).
|
||||
pub fn getAvailWriteSpace(idx: c_uint) c_uint {
|
||||
return sys.SEGGER_RTT_GetAvailWriteSpace(idx);
|
||||
}
|
||||
|
||||
/// Bytes currently pending in an up-buffer (not yet consumed by host).
|
||||
pub fn getBytesInBuffer(idx: c_uint) c_uint {
|
||||
return sys.SEGGER_RTT_GetBytesInBuffer(idx);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Multi-terminal helpers (virtual terminals over a single up-buffer)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// Select the active virtual terminal for subsequent writes on up-buffer 0.
|
||||
pub fn setTerminal(id: u8) Error!void {
|
||||
return rttResult(sys.SEGGER_RTT_SetTerminal(id));
|
||||
}
|
||||
|
||||
/// Write a null-terminated string directly to a specific virtual terminal.
|
||||
pub fn terminalOut(id: u8, s: [:0]const u8) Error!void {
|
||||
return rttResult(sys.SEGGER_RTT_TerminalOut(id, s.ptr));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// ESP-IDF specific flush helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// Flush the RTT up-buffer without locking.
|
||||
/// `min_sz`: minimum bytes to flush; `tmo`: timeout in ms.
|
||||
pub fn espFlushNoLock(min_sz: usize, tmo_ms: usize) void {
|
||||
sys.SEGGER_RTT_ESP_FlushNoLock(@intCast(min_sz), @intCast(tmo_ms));
|
||||
}
|
||||
|
||||
/// Flush the RTT up-buffer (with locking).
|
||||
pub fn espFlush(min_sz: usize, tmo_ms: usize) void {
|
||||
sys.SEGGER_RTT_ESP_Flush(@intCast(min_sz), @intCast(tmo_ms));
|
||||
}
|
||||
};
|
||||
152
software/zig_main/imports/sleep.zig
Normal file
152
software/zig_main/imports/sleep.zig
Normal file
@@ -0,0 +1,152 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Wakeup cause
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const WakeupCause = enum(sys.esp_sleep_wakeup_cause_t) {
|
||||
undefined_cause = sys.ESP_SLEEP_WAKEUP_UNDEFINED,
|
||||
all = sys.ESP_SLEEP_WAKEUP_ALL,
|
||||
ext0 = sys.ESP_SLEEP_WAKEUP_EXT0,
|
||||
ext1 = sys.ESP_SLEEP_WAKEUP_EXT1,
|
||||
timer = sys.ESP_SLEEP_WAKEUP_TIMER,
|
||||
touchpad = sys.ESP_SLEEP_WAKEUP_TOUCHPAD,
|
||||
ulp = sys.ESP_SLEEP_WAKEUP_ULP,
|
||||
gpio = sys.ESP_SLEEP_WAKEUP_GPIO,
|
||||
uart = sys.ESP_SLEEP_WAKEUP_UART,
|
||||
wifi = sys.ESP_SLEEP_WAKEUP_WIFI,
|
||||
bt = sys.ESP_SLEEP_WAKEUP_BT,
|
||||
_,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Power domain configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const PdDomain = enum(sys.esp_sleep_pd_domain_t) {
|
||||
rtc_periph = sys.ESP_PD_DOMAIN_RTC_PERIPH,
|
||||
rtc_slow_mem = sys.ESP_PD_DOMAIN_RTC_SLOW_MEM,
|
||||
rtc_fast_mem = sys.ESP_PD_DOMAIN_RTC_FAST_MEM,
|
||||
xtal = sys.ESP_PD_DOMAIN_XTAL,
|
||||
rc_fast = sys.ESP_PD_DOMAIN_RC_FAST,
|
||||
vddsdio = sys.ESP_PD_DOMAIN_VDDSDIO,
|
||||
modem = sys.ESP_PD_DOMAIN_MODEM,
|
||||
max = sys.ESP_PD_DOMAIN_MAX,
|
||||
};
|
||||
|
||||
pub const PdOption = enum(sys.esp_sleep_pd_option_t) {
|
||||
off = sys.ESP_PD_OPTION_OFF,
|
||||
on = sys.ESP_PD_OPTION_ON,
|
||||
auto = sys.ESP_PD_OPTION_AUTO,
|
||||
};
|
||||
|
||||
/// Configure the power state of a peripheral domain during sleep.
|
||||
pub fn pdConfig(domain: PdDomain, option: PdOption) !void {
|
||||
try errors.espCheckError(sys.esp_sleep_pd_config(@intFromEnum(domain), @intFromEnum(option)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Wakeup source configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Enable wakeup by timer after `time_in_us` microseconds.
|
||||
pub fn enableTimerWakeup(time_in_us: u64) !void {
|
||||
try errors.espCheckError(sys.esp_sleep_enable_timer_wakeup(time_in_us));
|
||||
}
|
||||
|
||||
/// Enable wakeup from a GPIO level change (light sleep only on most targets).
|
||||
pub fn enableGpioWakeup() !void {
|
||||
try errors.espCheckError(sys.esp_sleep_enable_gpio_wakeup());
|
||||
}
|
||||
|
||||
/// Enable wakeup by external signal on one RTC IO pin (ESP32 / ESP32-S* only).
|
||||
pub fn enableExt0Wakeup(gpio_num: sys.gpio_num_t, level: c_int) !void {
|
||||
try errors.espCheckError(sys.esp_sleep_enable_ext0_wakeup(gpio_num, level));
|
||||
}
|
||||
|
||||
/// Enable wakeup by any/all of the given RTC IO pins.
|
||||
pub fn enableExt1Wakeup(io_mask: u64, level_mode: sys.esp_sleep_ext1_wakeup_mode_t) !void {
|
||||
try errors.espCheckError(sys.esp_sleep_enable_ext1_wakeup(io_mask, level_mode));
|
||||
}
|
||||
|
||||
/// Enable wakeup from UART activity.
|
||||
pub fn enableUartWakeup(uart_num: c_int) !void {
|
||||
try errors.espCheckError(sys.esp_sleep_enable_uart_wakeup(uart_num));
|
||||
}
|
||||
|
||||
/// Enable Bluetooth as a wakeup source.
|
||||
pub fn enableBtWakeup() !void {
|
||||
try errors.espCheckError(sys.esp_sleep_enable_bt_wakeup());
|
||||
}
|
||||
|
||||
/// Enable WiFi as a wakeup source.
|
||||
pub fn enableWifiWakeup() !void {
|
||||
try errors.espCheckError(sys.esp_sleep_enable_wifi_wakeup());
|
||||
}
|
||||
|
||||
/// Enable ULP co-processor as a wakeup source.
|
||||
pub fn enableUlpWakeup() !void {
|
||||
try errors.espCheckError(sys.esp_sleep_enable_ulp_wakeup());
|
||||
}
|
||||
|
||||
/// Disable a wakeup source.
|
||||
pub fn disableWakeupSource(source: sys.esp_sleep_source_t) !void {
|
||||
try errors.espCheckError(sys.esp_sleep_disable_wakeup_source(source));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Sleep entry points
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Enter deep sleep immediately. Does not return.
|
||||
/// Configure wakeup sources before calling.
|
||||
pub fn deepSleepStart() noreturn {
|
||||
sys.esp_deep_sleep_start();
|
||||
unreachable;
|
||||
}
|
||||
|
||||
/// Enter deep sleep for exactly `time_in_us` microseconds.
|
||||
/// Convenience shortcut — sets timer wakeup and enters deep sleep. Does not return.
|
||||
pub fn deepSleep(time_in_us: u64) noreturn {
|
||||
sys.esp_deep_sleep(time_in_us);
|
||||
unreachable;
|
||||
}
|
||||
|
||||
/// Try to enter deep sleep. Returns an error if not all conditions are met.
|
||||
pub fn deepSleepTryToStart() !void {
|
||||
try errors.espCheckError(sys.esp_deep_sleep_try_to_start());
|
||||
}
|
||||
|
||||
/// Enter light sleep. Returns when a wakeup event is triggered.
|
||||
pub fn lightSleepStart() !void {
|
||||
try errors.espCheckError(sys.esp_light_sleep_start());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Wakeup cause query
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Get the reason the chip was last woken from sleep.
|
||||
pub fn getWakeupCause() WakeupCause {
|
||||
return @enumFromInt(sys.esp_sleep_get_wakeup_cause());
|
||||
}
|
||||
|
||||
/// Returns true if the wakeup cause matches `cause`.
|
||||
pub fn wasWokenBy(cause: WakeupCause) bool {
|
||||
return getWakeupCause() == cause;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Deep sleep hooks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Register a callback to run just before deep sleep entry.
|
||||
pub fn registerDeepSleepHook(cb: sys.esp_deep_sleep_cb_t) !void {
|
||||
try errors.espCheckError(sys.esp_deep_sleep_register_hook(cb));
|
||||
}
|
||||
|
||||
/// Unregister a previously registered deep sleep hook.
|
||||
pub fn deregisterDeepSleepHook(cb: sys.esp_deep_sleep_cb_t) void {
|
||||
sys.esp_deep_sleep_deregister_hook(cb);
|
||||
}
|
||||
110
software/zig_main/imports/spi.zig
Normal file
110
software/zig_main/imports/spi.zig
Normal file
@@ -0,0 +1,110 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases — callers can use these instead of the raw sys.* types.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Host = sys.spi_host_device_t;
|
||||
pub const Device = sys.spi_device_handle_t;
|
||||
pub const Transaction = sys.spi_transaction_t;
|
||||
pub const ExtTransaction = sys.spi_transaction_ext_t;
|
||||
pub const DmaChan = sys.spi_dma_chan_t;
|
||||
pub const BusConfig = sys.spi_bus_config_t;
|
||||
pub const DeviceConfig = sys.spi_device_interface_config_t;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Bus management
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub fn busInitialize(host_id: sys.spi_host_device_t, bus_config: [*c]const sys.spi_bus_config_t, dma_chan: sys.spi_dma_chan_t) !void {
|
||||
return try errors.espCheckError(sys.spi_bus_initialize(host_id, bus_config, dma_chan));
|
||||
}
|
||||
pub fn busFree(host_id: sys.spi_host_device_t) !void {
|
||||
return try errors.espCheckError(sys.spi_bus_free(host_id));
|
||||
}
|
||||
pub fn busAddDevice(host_id: sys.spi_host_device_t, dev_config: [*c]const sys.spi_device_interface_config_t, handle: [*c]sys.spi_device_handle_t) !void {
|
||||
return try errors.espCheckError(sys.spi_bus_add_device(host_id, dev_config, handle));
|
||||
}
|
||||
pub fn busRemoveDevice(handle: sys.spi_device_handle_t) !void {
|
||||
return try errors.espCheckError(sys.spi_bus_remove_device(handle));
|
||||
}
|
||||
pub fn deviceQueueTrans(handle: sys.spi_device_handle_t, trans_desc: [*c]sys.spi_transaction_t, ticks_to_wait: sys.TickType_t) !void {
|
||||
return try errors.espCheckError(sys.spi_device_queue_trans(handle, trans_desc, ticks_to_wait));
|
||||
}
|
||||
pub fn deviceGetTransResult(handle: sys.spi_device_handle_t, trans_desc: [*c][*c]sys.spi_transaction_t, ticks_to_wait: sys.TickType_t) !void {
|
||||
return try errors.espCheckError(sys.spi_device_get_trans_result(handle, trans_desc, ticks_to_wait));
|
||||
}
|
||||
pub fn deviceTransmit(handle: sys.spi_device_handle_t, trans_desc: [*c]sys.spi_transaction_t) !void {
|
||||
return try errors.espCheckError(sys.spi_device_transmit(handle, trans_desc));
|
||||
}
|
||||
pub fn devicePollingStart(handle: sys.spi_device_handle_t, trans_desc: [*c]sys.spi_transaction_t, ticks_to_wait: sys.TickType_t) !void {
|
||||
return try errors.espCheckError(sys.spi_device_polling_start(handle, trans_desc, ticks_to_wait));
|
||||
}
|
||||
pub fn devicePollingEnd(handle: sys.spi_device_handle_t, ticks_to_wait: sys.TickType_t) !void {
|
||||
return try errors.espCheckError(sys.spi_device_polling_end(handle, ticks_to_wait));
|
||||
}
|
||||
pub fn devicePollingTransmit(handle: sys.spi_device_handle_t, trans_desc: [*c]sys.spi_transaction_t) !void {
|
||||
return try errors.espCheckError(sys.spi_device_polling_transmit(handle, trans_desc));
|
||||
}
|
||||
pub fn deviceAcquireBus(device: sys.spi_device_handle_t, wait: sys.TickType_t) !void {
|
||||
return try errors.espCheckError(sys.spi_device_acquire_bus(device, wait));
|
||||
}
|
||||
pub fn deviceReleaseBus(dev: sys.spi_device_handle_t) void {
|
||||
sys.spi_device_release_bus(dev);
|
||||
}
|
||||
/// Returns the actual clock frequency used by the device in kHz.
|
||||
pub fn deviceGetActualFreq(handle: sys.spi_device_handle_t) !c_int {
|
||||
var freq_khz: c_int = 0;
|
||||
try errors.espCheckError(sys.spi_device_get_actual_freq(handle, &freq_khz));
|
||||
return freq_khz;
|
||||
}
|
||||
pub fn getActualClock(fapb: c_int, hz: c_int, duty_cycle: c_int) c_int {
|
||||
return sys.spi_get_actual_clock(fapb, hz, duty_cycle);
|
||||
}
|
||||
pub fn getTiming(gpio_is_used: bool, input_delay_ns: c_int, eff_clk: c_int, dummy_o: [*c]c_int, cycles_remain_o: [*c]c_int) void {
|
||||
sys.spi_get_timing(gpio_is_used, input_delay_ns, eff_clk, dummy_o, cycles_remain_o);
|
||||
}
|
||||
pub fn getFreqLimit(gpio_is_used: bool, input_delay_ns: c_int) c_int {
|
||||
return sys.spi_get_freq_limit(gpio_is_used, input_delay_ns);
|
||||
}
|
||||
/// Returns the maximum allowed transaction length in bytes for the given bus.
|
||||
pub fn busGetMaxTransactionLen(host_id: sys.spi_host_device_t) !usize {
|
||||
var max_bytes: usize = 0;
|
||||
try errors.espCheckError(sys.spi_bus_get_max_transaction_len(host_id, &max_bytes));
|
||||
return max_bytes;
|
||||
}
|
||||
|
||||
pub const SDSPI = struct {
|
||||
pub const Host = struct {
|
||||
pub fn init() !void {
|
||||
return try errors.espCheckError(sys.sdspi_host_init());
|
||||
}
|
||||
pub fn initDevice(dev_config: [*c]const sys.sdspi_device_config_t, out_handle: [*c]sys.sdspi_dev_handle_t) !void {
|
||||
return try errors.espCheckError(sys.sdspi_host_init_device(dev_config, out_handle));
|
||||
}
|
||||
pub fn removeDevice(handle: sys.sdspi_dev_handle_t) !void {
|
||||
return try errors.espCheckError(sys.sdspi_host_remove_device(handle));
|
||||
}
|
||||
pub fn doTransaction(handle: sys.sdspi_dev_handle_t, cmdinfo: [*c]sys.sdmmc_command_t) !void {
|
||||
return try errors.espCheckError(sys.sdspi_host_do_transaction(handle, cmdinfo));
|
||||
}
|
||||
pub fn setCardClk(host: sys.sdspi_dev_handle_t, freq_khz: u32) !void {
|
||||
return try errors.espCheckError(sys.sdspi_host_set_card_clk(host, freq_khz));
|
||||
}
|
||||
pub fn getRealFreq(handle: sys.sdspi_dev_handle_t, real_freq_khz: [*c]c_int) !void {
|
||||
return try errors.espCheckError(sys.sdspi_host_get_real_freq(handle, real_freq_khz));
|
||||
}
|
||||
pub fn deinit() !void {
|
||||
return try errors.espCheckError(sys.sdspi_host_deinit());
|
||||
}
|
||||
pub const IO = struct {
|
||||
pub fn intEnable(handle: sys.sdspi_dev_handle_t) !void {
|
||||
return try errors.espCheckError(sys.sdspi_host_io_int_enable(handle));
|
||||
}
|
||||
pub fn intWait(handle: sys.sdspi_dev_handle_t, timeout_ticks: sys.TickType_t) !void {
|
||||
return try errors.espCheckError(sys.sdspi_host_io_int_wait(handle, timeout_ticks));
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
96
software/zig_main/imports/timer.zig
Normal file
96
software/zig_main/imports/timer.zig
Normal file
@@ -0,0 +1,96 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Handle = sys.esp_timer_handle_t;
|
||||
pub const Callback = sys.esp_timer_cb_t;
|
||||
pub const CreateArgs = sys.esp_timer_create_args_t;
|
||||
|
||||
/// Dispatch method: callback runs in a dedicated high-priority task (TASK)
|
||||
/// or directly from the ISR (ISR — use only for very short callbacks).
|
||||
pub const Dispatch = enum(sys.esp_timer_dispatch_t) {
|
||||
task = sys.ESP_TIMER_TASK,
|
||||
max = sys.ESP_TIMER_MAX,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// High-resolution timer (esp_timer)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Timer = struct {
|
||||
// ── Lifecycle ────────────────────────────────────────────────────────────
|
||||
|
||||
/// Create a new timer. `args.callback` must be non-null.
|
||||
/// Returns the handle; caller owns it and must call `delete()` when done.
|
||||
pub fn create(args: *const CreateArgs) !Handle {
|
||||
var handle: Handle = null;
|
||||
try errors.espCheckError(sys.esp_timer_create(args, &handle));
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// Start a one-shot timer that fires after `timeout_us` microseconds.
|
||||
pub fn startOnce(handle: Handle, timeout_us: u64) !void {
|
||||
try errors.espCheckError(sys.esp_timer_start_once(handle, timeout_us));
|
||||
}
|
||||
|
||||
/// Start a periodic timer that fires every `period_us` microseconds.
|
||||
pub fn startPeriodic(handle: Handle, period_us: u64) !void {
|
||||
try errors.espCheckError(sys.esp_timer_start_periodic(handle, period_us));
|
||||
}
|
||||
|
||||
/// Restart a running timer with a new `timeout_us`.
|
||||
/// Works for both one-shot and periodic timers.
|
||||
pub fn restart(handle: Handle, timeout_us: u64) !void {
|
||||
try errors.espCheckError(sys.esp_timer_restart(handle, timeout_us));
|
||||
}
|
||||
|
||||
/// Stop a running timer. Does not delete it.
|
||||
pub fn stop(handle: Handle) !void {
|
||||
try errors.espCheckError(sys.esp_timer_stop(handle));
|
||||
}
|
||||
|
||||
/// Delete a timer. The timer must be stopped first.
|
||||
pub fn delete(handle: Handle) !void {
|
||||
try errors.espCheckError(sys.esp_timer_delete(handle));
|
||||
}
|
||||
|
||||
// ── Query ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Return current time in microseconds since boot.
|
||||
/// This never returns an error.
|
||||
pub fn getTime() i64 {
|
||||
return sys.esp_timer_get_time();
|
||||
}
|
||||
|
||||
/// Return the expiry time (µs since boot) of the next pending alarm.
|
||||
pub fn getNextAlarm() i64 {
|
||||
return sys.esp_timer_get_next_alarm();
|
||||
}
|
||||
|
||||
/// Return the expiry time of the next alarm that can wake the system.
|
||||
pub fn getNextAlarmForWakeUp() i64 {
|
||||
return sys.esp_timer_get_next_alarm_for_wake_up();
|
||||
}
|
||||
|
||||
/// Return the period (µs) of a periodic timer.
|
||||
pub fn getPeriod(handle: Handle) !u64 {
|
||||
var period: u64 = 0;
|
||||
try errors.espCheckError(sys.esp_timer_get_period(handle, &period));
|
||||
return period;
|
||||
}
|
||||
|
||||
/// Return the expiry time (µs since boot) for a one-shot timer.
|
||||
pub fn getExpiryTime(handle: Handle) !u64 {
|
||||
var expiry: u64 = 0;
|
||||
try errors.espCheckError(sys.esp_timer_get_expiry_time(handle, &expiry));
|
||||
return expiry;
|
||||
}
|
||||
|
||||
/// Returns `true` if the timer is currently active (started, not yet fired / periodic).
|
||||
pub fn isActive(handle: Handle) bool {
|
||||
return sys.esp_timer_is_active(handle);
|
||||
}
|
||||
};
|
||||
175
software/zig_main/imports/twai.zig
Normal file
175
software/zig_main/imports/twai.zig
Normal file
@@ -0,0 +1,175 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Handle = sys.twai_handle_t;
|
||||
pub const Message = sys.twai_message_t;
|
||||
pub const FilterConfig = sys.twai_filter_config_t;
|
||||
pub const TimingConfig = sys.twai_timing_config_t;
|
||||
pub const GeneralConfig = sys.twai_general_config_t;
|
||||
pub const StatusInfo = sys.twai_status_info_t;
|
||||
pub const FrameHeader = sys.twai_frame_header_t;
|
||||
|
||||
/// TWAI operating mode.
|
||||
pub const Mode = enum(sys.twai_mode_t) {
|
||||
normal = sys.TWAI_MODE_NORMAL,
|
||||
no_ack = sys.TWAI_MODE_NO_ACK,
|
||||
listen_only = sys.TWAI_MODE_LISTEN_ONLY,
|
||||
};
|
||||
|
||||
/// Bus error state.
|
||||
pub const ErrorState = enum(sys.twai_error_state_t) {
|
||||
active = sys.TWAI_ERROR_ACTIVE,
|
||||
warning = sys.TWAI_ERROR_WARNING,
|
||||
passive = sys.TWAI_ERROR_PASSIVE,
|
||||
bus_off = sys.TWAI_ERROR_BUS_OFF,
|
||||
};
|
||||
|
||||
/// Driver state.
|
||||
pub const State = enum(sys.twai_state_t) {
|
||||
stopped = sys.TWAI_STATE_STOPPED,
|
||||
running = sys.TWAI_STATE_RUNNING,
|
||||
bus_off = sys.TWAI_STATE_BUS_OFF,
|
||||
recovering = sys.TWAI_STATE_RECOVERING,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Alert flag constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Alert = struct {
|
||||
pub const tx_idle: u32 = sys.TWAI_ALERT_TX_IDLE;
|
||||
pub const tx_success: u32 = sys.TWAI_ALERT_TX_SUCCESS;
|
||||
pub const rx_data: u32 = sys.TWAI_ALERT_RX_DATA;
|
||||
pub const below_err_warn: u32 = sys.TWAI_ALERT_BELOW_ERR_WARN;
|
||||
pub const err_active: u32 = sys.TWAI_ALERT_ERR_ACTIVE;
|
||||
pub const recovery_in_progress: u32 = sys.TWAI_ALERT_RECOVERY_IN_PROGRESS;
|
||||
pub const bus_recovered: u32 = sys.TWAI_ALERT_BUS_RECOVERED;
|
||||
pub const arb_lost: u32 = sys.TWAI_ALERT_ARB_LOST;
|
||||
pub const above_err_warn: u32 = sys.TWAI_ALERT_ABOVE_ERR_WARN;
|
||||
pub const bus_error: u32 = sys.TWAI_ALERT_BUS_ERROR;
|
||||
pub const tx_failed: u32 = sys.TWAI_ALERT_TX_FAILED;
|
||||
pub const rx_queue_full: u32 = sys.TWAI_ALERT_RX_QUEUE_FULL;
|
||||
pub const err_pass: u32 = sys.TWAI_ALERT_ERR_PASS;
|
||||
pub const bus_off: u32 = sys.TWAI_ALERT_BUS_OFF;
|
||||
pub const rx_fifo_overrun: u32 = sys.TWAI_ALERT_RX_FIFO_OVERRUN;
|
||||
pub const tx_retried: u32 = sys.TWAI_ALERT_TX_RETRIED;
|
||||
pub const periph_reset: u32 = sys.TWAI_ALERT_PERIPH_RESET;
|
||||
pub const all: u32 = sys.TWAI_ALERT_ALL;
|
||||
pub const none: u32 = sys.TWAI_ALERT_NONE;
|
||||
pub const and_log: u32 = sys.TWAI_ALERT_AND_LOG;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Message flag constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const MsgFlag = struct {
|
||||
pub const none: u32 = sys.TWAI_MSG_FLAG_NONE;
|
||||
pub const extd: u32 = sys.TWAI_MSG_FLAG_EXTD;
|
||||
pub const rtr: u32 = sys.TWAI_MSG_FLAG_RTR;
|
||||
pub const ss: u32 = sys.TWAI_MSG_FLAG_SS;
|
||||
pub const self: u32 = sys.TWAI_MSG_FLAG_SELF;
|
||||
pub const dlc_non_comp: u32 = sys.TWAI_MSG_FLAG_DLC_NON_COMP;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Driver — v2 handle-based API (preferred)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Driver = struct {
|
||||
// ── Lifecycle ────────────────────────────────────────────────────────────
|
||||
|
||||
/// Install the TWAI driver and return a handle.
|
||||
/// Use `GPIO_NUM_NC` (-1) for unused clkout/bus_off pins.
|
||||
pub fn install(
|
||||
g_config: *const GeneralConfig,
|
||||
t_config: *const TimingConfig,
|
||||
f_config: *const FilterConfig,
|
||||
) !Handle {
|
||||
var handle: Handle = null;
|
||||
try errors.espCheckError(sys.twai_driver_install_v2(g_config, t_config, f_config, &handle));
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// Uninstall a TWAI driver instance and release resources.
|
||||
pub fn uninstall(handle: Handle) !void {
|
||||
try errors.espCheckError(sys.twai_driver_uninstall_v2(handle));
|
||||
}
|
||||
|
||||
/// Start the TWAI driver (enter running state).
|
||||
pub fn start(handle: Handle) !void {
|
||||
try errors.espCheckError(sys.twai_start_v2(handle));
|
||||
}
|
||||
|
||||
/// Stop the TWAI driver (enter stopped state).
|
||||
pub fn stop(handle: Handle) !void {
|
||||
try errors.espCheckError(sys.twai_stop_v2(handle));
|
||||
}
|
||||
|
||||
// ── Transmit / Receive ───────────────────────────────────────────────────
|
||||
|
||||
/// Queue a message for transmission. `ticks_to_wait` is the FreeRTOS
|
||||
/// tick timeout (use `sys.portMAX_DELAY` to block indefinitely).
|
||||
pub fn transmit(handle: Handle, msg: *const Message, ticks_to_wait: sys.TickType_t) !void {
|
||||
try errors.espCheckError(sys.twai_transmit_v2(handle, msg, ticks_to_wait));
|
||||
}
|
||||
|
||||
/// Receive a message. Blocks until a message is available or timeout.
|
||||
pub fn receive(handle: Handle, ticks_to_wait: sys.TickType_t) !Message {
|
||||
var msg: Message = std.mem.zeroes(Message);
|
||||
try errors.espCheckError(sys.twai_receive_v2(handle, &msg, ticks_to_wait));
|
||||
return msg;
|
||||
}
|
||||
|
||||
// ── Alerts ───────────────────────────────────────────────────────────────
|
||||
|
||||
/// Block until one of the enabled alerts fires (or timeout).
|
||||
/// Returns bitmask of triggered alerts.
|
||||
pub fn readAlerts(handle: Handle, ticks_to_wait: sys.TickType_t) !u32 {
|
||||
var alerts: u32 = 0;
|
||||
try errors.espCheckError(sys.twai_read_alerts_v2(handle, &alerts, ticks_to_wait));
|
||||
return alerts;
|
||||
}
|
||||
|
||||
/// Change the set of enabled alerts at runtime.
|
||||
/// Returns the alerts that were pending at the time of reconfiguration.
|
||||
pub fn reconfigureAlerts(handle: Handle, alerts_enabled: u32) !u32 {
|
||||
var current: u32 = 0;
|
||||
try errors.espCheckError(sys.twai_reconfigure_alerts_v2(handle, alerts_enabled, ¤t));
|
||||
return current;
|
||||
}
|
||||
|
||||
// ── Recovery ──────────────────────────────────────────────────────────────
|
||||
|
||||
/// Initiate bus-off recovery. The driver must be in `bus_off` state.
|
||||
pub fn initiateRecovery(handle: Handle) !void {
|
||||
try errors.espCheckError(sys.twai_initiate_recovery_v2(handle));
|
||||
}
|
||||
|
||||
// ── Status ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Get the current driver and bus status.
|
||||
pub fn getStatus(handle: Handle) !StatusInfo {
|
||||
var info: StatusInfo = std.mem.zeroes(StatusInfo);
|
||||
try errors.espCheckError(sys.twai_get_status_info_v2(handle, &info));
|
||||
return info;
|
||||
}
|
||||
|
||||
// ── Queue management ─────────────────────────────────────────────────────
|
||||
|
||||
/// Discard all messages pending in the transmit queue.
|
||||
pub fn clearTxQueue(handle: Handle) !void {
|
||||
try errors.espCheckError(sys.twai_clear_transmit_queue_v2(handle));
|
||||
}
|
||||
|
||||
/// Discard all messages in the receive queue.
|
||||
pub fn clearRxQueue(handle: Handle) !void {
|
||||
try errors.espCheckError(sys.twai_clear_receive_queue_v2(handle));
|
||||
}
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
370
software/zig_main/imports/uart.zig
Normal file
370
software/zig_main/imports/uart.zig
Normal file
@@ -0,0 +1,370 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Re-export ESP-IDF types so callers only need to @import("uart")
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
pub const Port = sys.uart_port_t;
|
||||
pub const WordLength = sys.uart_word_length_t;
|
||||
pub const StopBits = sys.uart_stop_bits_t;
|
||||
pub const Parity = sys.uart_parity_t;
|
||||
pub const HWFlowCtrl = sys.uart_hw_flowcontrol_t;
|
||||
pub const Mode = sys.uart_mode_t;
|
||||
pub const Sclk = sys.uart_sclk_t;
|
||||
pub const Config = sys.uart_config_t;
|
||||
pub const IntrConfig = sys.uart_intr_config_t;
|
||||
pub const QueueHandle = sys.QueueHandle_t;
|
||||
pub const TickType = sys.TickType_t;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Pin assignment sentinel — mirrors UART_PIN_NO_CHANGE from esp-idf
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
pub const pin_no_change: c_int = -1;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Driver lifecycle
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const DriverConfig = struct {
|
||||
rx_buffer_size: usize,
|
||||
tx_buffer_size: usize = 0,
|
||||
/// Pass null to disable event queue.
|
||||
queue_size: c_int = 0,
|
||||
uart_queue: ?*QueueHandle = null,
|
||||
intr_alloc_flags: c_int = 0,
|
||||
};
|
||||
|
||||
pub fn driverInstall(port: Port, cfg: DriverConfig) !void {
|
||||
return errors.espCheckError(sys.uart_driver_install(
|
||||
port,
|
||||
@intCast(cfg.rx_buffer_size),
|
||||
@intCast(cfg.tx_buffer_size),
|
||||
cfg.queue_size,
|
||||
cfg.uart_queue orelse null,
|
||||
cfg.intr_alloc_flags,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn driverDelete(port: Port) !void {
|
||||
return errors.espCheckError(sys.uart_driver_delete(port));
|
||||
}
|
||||
|
||||
pub fn isDriverInstalled(port: Port) bool {
|
||||
return sys.uart_is_driver_installed(port);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Parameter configuration (word length, stop bits, parity, baud rate …)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn paramConfig(port: Port, cfg: *const Config) !void {
|
||||
return errors.espCheckError(sys.uart_param_config(port, cfg));
|
||||
}
|
||||
|
||||
pub fn setWordLength(port: Port, data_bit: WordLength) !void {
|
||||
return errors.espCheckError(sys.uart_set_word_length(port, data_bit));
|
||||
}
|
||||
|
||||
pub fn getWordLength(port: Port) !WordLength {
|
||||
var v: WordLength = undefined;
|
||||
try errors.espCheckError(sys.uart_get_word_length(port, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn setStopBits(port: Port, stop_bits: StopBits) !void {
|
||||
return errors.espCheckError(sys.uart_set_stop_bits(port, stop_bits));
|
||||
}
|
||||
|
||||
pub fn getStopBits(port: Port) !StopBits {
|
||||
var v: StopBits = undefined;
|
||||
try errors.espCheckError(sys.uart_get_stop_bits(port, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn setParity(port: Port, parity: Parity) !void {
|
||||
return errors.espCheckError(sys.uart_set_parity(port, parity));
|
||||
}
|
||||
|
||||
pub fn getParity(port: Port) !Parity {
|
||||
var v: Parity = undefined;
|
||||
try errors.espCheckError(sys.uart_get_parity(port, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn setBaudrate(port: Port, baudrate: u32) !void {
|
||||
return errors.espCheckError(sys.uart_set_baudrate(port, baudrate));
|
||||
}
|
||||
|
||||
pub fn getBaudrate(port: Port) !u32 {
|
||||
var v: u32 = 0;
|
||||
try errors.espCheckError(sys.uart_get_baudrate(port, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn getSclkFreq(sclk: Sclk) !u32 {
|
||||
var v: u32 = 0;
|
||||
try errors.espCheckError(sys.uart_get_sclk_freq(sclk, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn setLineInverse(port: Port, inverse_mask: u32) !void {
|
||||
return errors.espCheckError(sys.uart_set_line_inverse(port, inverse_mask));
|
||||
}
|
||||
|
||||
pub fn setMode(port: Port, mode: Mode) !void {
|
||||
return errors.espCheckError(sys.uart_set_mode(port, mode));
|
||||
}
|
||||
|
||||
pub fn setLoopBack(port: Port, enable: bool) !void {
|
||||
return errors.espCheckError(sys.uart_set_loop_back(port, enable));
|
||||
}
|
||||
|
||||
pub fn setAlwaysRxTimeout(port: Port, enable: bool) void {
|
||||
sys.uart_set_always_rx_timeout(port, enable);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Pin mapping
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const PinConfig = struct {
|
||||
tx: c_int = pin_no_change,
|
||||
rx: c_int = pin_no_change,
|
||||
rts: c_int = pin_no_change,
|
||||
cts: c_int = pin_no_change,
|
||||
};
|
||||
|
||||
pub fn setPin(port: Port, pins: PinConfig) !void {
|
||||
return errors.espCheckError(sys.uart_set_pin(port, pins.tx, pins.rx, pins.rts, pins.cts));
|
||||
}
|
||||
|
||||
/// Drive RTS line manually (level: true = high, false = low).
|
||||
pub fn setRTS(port: Port, level: bool) !void {
|
||||
return errors.espCheckError(sys.uart_set_rts(port, @intFromBool(level)));
|
||||
}
|
||||
|
||||
/// Drive DTR line manually.
|
||||
pub fn setDTR(port: Port, level: bool) !void {
|
||||
return errors.espCheckError(sys.uart_set_dtr(port, @intFromBool(level)));
|
||||
}
|
||||
|
||||
pub fn setTXIdleNum(port: Port, idle_num: u16) !void {
|
||||
return errors.espCheckError(sys.uart_set_tx_idle_num(port, idle_num));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Flow control
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn setHWFlowCtrl(port: Port, flow_ctrl: HWFlowCtrl, rx_thresh: u8) !void {
|
||||
return errors.espCheckError(sys.uart_set_hw_flow_ctrl(port, flow_ctrl, rx_thresh));
|
||||
}
|
||||
|
||||
pub fn getHWFlowCtrl(port: Port) !HWFlowCtrl {
|
||||
var v: HWFlowCtrl = undefined;
|
||||
try errors.espCheckError(sys.uart_get_hw_flow_ctrl(port, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn setSWFlowCtrl(port: Port, enable: bool, rx_thresh_xon: u8, rx_thresh_xoff: u8) !void {
|
||||
return errors.espCheckError(sys.uart_set_sw_flow_ctrl(port, enable, rx_thresh_xon, rx_thresh_xoff));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Interrupt configuration
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn intrConfig(port: Port, cfg: *const IntrConfig) !void {
|
||||
return errors.espCheckError(sys.uart_intr_config(port, cfg));
|
||||
}
|
||||
|
||||
pub fn clearIntrStatus(port: Port, clr_mask: u32) !void {
|
||||
return errors.espCheckError(sys.uart_clear_intr_status(port, clr_mask));
|
||||
}
|
||||
|
||||
pub fn enableIntrMask(port: Port, mask: u32) !void {
|
||||
return errors.espCheckError(sys.uart_enable_intr_mask(port, mask));
|
||||
}
|
||||
|
||||
pub fn disableIntrMask(port: Port, mask: u32) !void {
|
||||
return errors.espCheckError(sys.uart_disable_intr_mask(port, mask));
|
||||
}
|
||||
|
||||
pub fn enableRXIntr(port: Port) !void {
|
||||
return errors.espCheckError(sys.uart_enable_rx_intr(port));
|
||||
}
|
||||
|
||||
pub fn disableRXIntr(port: Port) !void {
|
||||
return errors.espCheckError(sys.uart_disable_rx_intr(port));
|
||||
}
|
||||
|
||||
pub fn enableTXIntr(port: Port, enable: bool, thresh: c_int) !void {
|
||||
return errors.espCheckError(sys.uart_enable_tx_intr(port, @intFromBool(enable), thresh));
|
||||
}
|
||||
|
||||
pub fn disableTXIntr(port: Port) !void {
|
||||
return errors.espCheckError(sys.uart_disable_tx_intr(port));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// RX / TX thresholds and timeouts
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn setRXFullThreshold(port: Port, threshold: c_int) !void {
|
||||
return errors.espCheckError(sys.uart_set_rx_full_threshold(port, threshold));
|
||||
}
|
||||
|
||||
pub fn setTXEmptyThreshold(port: Port, threshold: c_int) !void {
|
||||
return errors.espCheckError(sys.uart_set_tx_empty_threshold(port, threshold));
|
||||
}
|
||||
|
||||
pub fn setRXTimeout(port: Port, tout_thresh: u8) !void {
|
||||
return errors.espCheckError(sys.uart_set_rx_timeout(port, tout_thresh));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Write
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Transmit raw bytes directly (no ring buffer).
|
||||
/// Returns the number of bytes pushed into the TX FIFO.
|
||||
pub fn txChars(port: Port, data: []const u8) usize {
|
||||
const rc = sys.uart_tx_chars(port, data.ptr, @intCast(data.len));
|
||||
return if (rc < 0) 0 else @intCast(rc);
|
||||
}
|
||||
|
||||
/// Write bytes through the TX ring buffer (if installed) or directly.
|
||||
/// Returns number of bytes written, or error on failure.
|
||||
pub fn writeBytes(port: Port, data: []const u8) !usize {
|
||||
const rc = sys.uart_write_bytes(port, data.ptr, data.len);
|
||||
if (rc < 0) return error.UartWriteFailed;
|
||||
return @intCast(rc);
|
||||
}
|
||||
|
||||
/// Like `writeBytes` but appends a serial break signal of `brk_len` bit-periods.
|
||||
pub fn writeBytesWithBreak(port: Port, data: []const u8, brk_len: c_int) !usize {
|
||||
const rc = sys.uart_write_bytes_with_break(port, data.ptr, data.len, brk_len);
|
||||
if (rc < 0) return error.UartWriteFailed;
|
||||
return @intCast(rc);
|
||||
}
|
||||
|
||||
pub fn waitTXDone(port: Port, ticks_to_wait: TickType) !void {
|
||||
return errors.espCheckError(sys.uart_wait_tx_done(port, ticks_to_wait));
|
||||
}
|
||||
|
||||
pub fn waitTXIdlePolling(port: Port) !void {
|
||||
return errors.espCheckError(sys.uart_wait_tx_idle_polling(port));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Read
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Read up to `buf.len` bytes from the RX ring buffer.
|
||||
/// Returns the number of bytes actually read, or error on failure.
|
||||
pub fn readBytes(port: Port, buf: []u8, ticks_to_wait: TickType) !usize {
|
||||
const rc = sys.uart_read_bytes(port, buf.ptr, @intCast(buf.len), ticks_to_wait);
|
||||
if (rc < 0) return error.UartReadFailed;
|
||||
return @intCast(rc);
|
||||
}
|
||||
|
||||
/// Returns the number of bytes waiting in the RX ring buffer.
|
||||
pub fn getBufferedDataLen(port: Port) !usize {
|
||||
var v: usize = 0;
|
||||
try errors.espCheckError(sys.uart_get_buffered_data_len(port, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Returns free space in the TX ring buffer (bytes).
|
||||
pub fn getTXBufferFreeSize(port: Port) !usize {
|
||||
var v: usize = 0;
|
||||
try errors.espCheckError(sys.uart_get_tx_buffer_free_size(port, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Flush
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Flush TX FIFO — waits until all bytes are sent.
|
||||
pub fn flush(port: Port) !void {
|
||||
return errors.espCheckError(sys.uart_flush(port));
|
||||
}
|
||||
|
||||
/// Flush (discard) RX ring buffer contents.
|
||||
pub fn flushInput(port: Port) !void {
|
||||
return errors.espCheckError(sys.uart_flush_input(port));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Pattern detection
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const PatternConfig = struct {
|
||||
/// Character to detect (e.g. '+' for AT commands).
|
||||
chr: u8,
|
||||
/// Number of consecutive pattern characters required.
|
||||
chr_num: u8,
|
||||
/// Maximum gap between characters (in baud ticks).
|
||||
chr_tout: c_int,
|
||||
/// Idle time after pattern (baud ticks).
|
||||
post_idle: c_int,
|
||||
/// Idle time before pattern (baud ticks).
|
||||
pre_idle: c_int,
|
||||
};
|
||||
|
||||
pub fn enablePatternDetBaudIntr(port: Port, cfg: PatternConfig) !void {
|
||||
return errors.espCheckError(sys.uart_enable_pattern_det_baud_intr(
|
||||
port,
|
||||
cfg.chr,
|
||||
cfg.chr_num,
|
||||
cfg.chr_tout,
|
||||
cfg.post_idle,
|
||||
cfg.pre_idle,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn disablePatternDetIntr(port: Port) !void {
|
||||
return errors.espCheckError(sys.uart_disable_pattern_det_intr(port));
|
||||
}
|
||||
|
||||
/// Pop the oldest pattern position from the queue.
|
||||
/// Returns `null` if the queue is empty or an error occurred.
|
||||
pub fn patternPopPos(port: Port) ?usize {
|
||||
const rc = sys.uart_pattern_pop_pos(port);
|
||||
return if (rc < 0) null else @intCast(rc);
|
||||
}
|
||||
|
||||
/// Peek at the oldest pattern position without removing it.
|
||||
pub fn patternGetPos(port: Port) ?usize {
|
||||
const rc = sys.uart_pattern_get_pos(port);
|
||||
return if (rc < 0) null else @intCast(rc);
|
||||
}
|
||||
|
||||
pub fn patternQueueReset(port: Port, queue_length: c_int) !void {
|
||||
return errors.espCheckError(sys.uart_pattern_queue_reset(port, queue_length));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// RS-485 collision detection
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn getCollisionFlag(port: Port) !bool {
|
||||
var v: bool = false;
|
||||
try errors.espCheckError(sys.uart_get_collision_flag(port, &v));
|
||||
return v;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Light-sleep wakeup
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn setWakeupThreshold(port: Port, threshold: c_int) !void {
|
||||
return errors.espCheckError(sys.uart_set_wakeup_threshold(port, threshold));
|
||||
}
|
||||
|
||||
pub fn getWakeupThreshold(port: Port) !c_int {
|
||||
var v: c_int = 0;
|
||||
try errors.espCheckError(sys.uart_get_wakeup_threshold(port, &v));
|
||||
return v;
|
||||
}
|
||||
50
software/zig_main/imports/version.zig
Normal file
50
software/zig_main/imports/version.zig
Normal file
@@ -0,0 +1,50 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const Version = struct {
|
||||
major: u32 = 0,
|
||||
minor: u32 = 0,
|
||||
patch: u32 = 0,
|
||||
|
||||
pub fn get() Version {
|
||||
var final_version: Version = .{};
|
||||
const idf_version = std.mem.span(@import("sys").esp_get_idf_version());
|
||||
|
||||
if (!std.mem.startsWith(u8, idf_version, "v"))
|
||||
return final_version;
|
||||
|
||||
// Strip any pre-release suffix (e.g. "v5.3.0-beta1" → "v5.3.0").
|
||||
var strip = std.mem.splitScalar(u8, idf_version, '-');
|
||||
var it = std.mem.tokenizeScalar(u8, strip.first(), '.');
|
||||
|
||||
// Use a positional index (0=major, 1=minor, 2=patch) so that
|
||||
// version components with value 0 are parsed correctly.
|
||||
var field: u32 = 0;
|
||||
while (it.next()) |token| {
|
||||
const raw = if (std.mem.startsWith(u8, token, "v")) token[1..] else token;
|
||||
const digit = std.fmt.parseUnsigned(u32, raw, 10) catch break;
|
||||
switch (field) {
|
||||
0 => final_version.major = digit,
|
||||
1 => final_version.minor = digit,
|
||||
2 => final_version.patch = digit,
|
||||
else => break,
|
||||
}
|
||||
field += 1;
|
||||
}
|
||||
|
||||
return final_version;
|
||||
}
|
||||
|
||||
pub fn toString(self: Version, allocator: std.mem.Allocator) []const u8 {
|
||||
const idf_version = std.mem.span(@import("sys").esp_get_idf_version());
|
||||
|
||||
// Return the raw string for non-release builds (e.g. commit hashes).
|
||||
if (!std.mem.startsWith(u8, idf_version, "v"))
|
||||
return idf_version;
|
||||
|
||||
return std.fmt.allocPrint(allocator, "v{d}.{d}.{d}", .{
|
||||
self.major,
|
||||
self.minor,
|
||||
self.patch,
|
||||
}) catch |err| @panic(@errorName(err));
|
||||
}
|
||||
};
|
||||
85
software/zig_main/imports/wdt.zig
Normal file
85
software/zig_main/imports/wdt.zig
Normal file
@@ -0,0 +1,85 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type aliases
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub const Config = sys.esp_task_wdt_config_t;
|
||||
pub const UserHandle = sys.esp_task_wdt_user_handle_t;
|
||||
pub const TaskHandle = sys.TaskHandle_t;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Watchdog initialisation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Initialise the Task Watchdog Timer.
|
||||
///
|
||||
/// `config.timeout_ms` — watchdog timeout in milliseconds
|
||||
/// `config.idle_core_mask` — bitmask of idle tasks to subscribe (0 = none)
|
||||
/// `config.trigger_panic` — whether to trigger a panic on timeout
|
||||
pub fn init(config: *const Config) !void {
|
||||
try errors.espCheckError(sys.esp_task_wdt_init(config));
|
||||
}
|
||||
|
||||
/// Reconfigure a running watchdog (does not reset the timer).
|
||||
pub fn reconfigure(config: *const Config) !void {
|
||||
try errors.espCheckError(sys.esp_task_wdt_reconfigure(config));
|
||||
}
|
||||
|
||||
/// De-initialise the Task Watchdog Timer.
|
||||
pub fn deinit() !void {
|
||||
try errors.espCheckError(sys.esp_task_wdt_deinit());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Task subscription
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Subscribe a FreeRTOS task to the watchdog. Pass `null` for the calling task.
|
||||
pub fn add(task_handle: TaskHandle) !void {
|
||||
try errors.espCheckError(sys.esp_task_wdt_add(task_handle));
|
||||
}
|
||||
|
||||
/// Subscribe a named user (not a FreeRTOS task) to the watchdog.
|
||||
/// Returns a `UserHandle` for resetting and unsubscribing.
|
||||
pub fn addUser(user_name: [*:0]const u8) !UserHandle {
|
||||
var handle: UserHandle = null;
|
||||
try errors.espCheckError(sys.esp_task_wdt_add_user(user_name, &handle));
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// Unsubscribe a FreeRTOS task from the watchdog.
|
||||
pub fn delete(task_handle: TaskHandle) !void {
|
||||
try errors.espCheckError(sys.esp_task_wdt_delete(task_handle));
|
||||
}
|
||||
|
||||
/// Unsubscribe a user registered with `addUser`.
|
||||
pub fn deleteUser(user_handle: UserHandle) !void {
|
||||
try errors.espCheckError(sys.esp_task_wdt_delete_user(user_handle));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Watchdog reset (feed)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Reset (feed) the watchdog for the calling task.
|
||||
/// Call at least once per watchdog period to prevent a timeout.
|
||||
pub fn reset() !void {
|
||||
try errors.espCheckError(sys.esp_task_wdt_reset());
|
||||
}
|
||||
|
||||
/// Reset the watchdog for a named user registered with `addUser`.
|
||||
pub fn resetUser(user_handle: UserHandle) !void {
|
||||
try errors.espCheckError(sys.esp_task_wdt_reset_user(user_handle));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Status query
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Returns `ESP_OK` if `task_handle` is subscribed and has recently reset,
|
||||
/// or an error if it is overdue or not subscribed.
|
||||
pub fn status(task_handle: TaskHandle) !void {
|
||||
try errors.espCheckError(sys.esp_task_wdt_status(task_handle));
|
||||
}
|
||||
429
software/zig_main/imports/wifi.zig
Normal file
429
software/zig_main/imports/wifi.zig
Normal file
@@ -0,0 +1,429 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
pub const WIFI_ENABLE_ENTERPRISE = 1 << 7;
|
||||
|
||||
pub const CONFIG_FEATURE_WPA3_SAE_BIT = 1 << 0;
|
||||
pub const CONFIG_FEATURE_CACHE_TX_BUF_BIT = 1 << 1;
|
||||
pub const CONFIG_FEATURE_FTM_INITIATOR_BIT = 1 << 2;
|
||||
pub const CONFIG_FEATURE_FTM_RESPONDER_BIT = 1 << 3;
|
||||
pub const CONFIG_FEATURE_GCMP_BIT = 1 << 4;
|
||||
pub const CONFIG_FEATURE_GMAC_BIT = 1 << 5;
|
||||
pub const CONFIG_FEATURE_11R_BIT = 1 << 6;
|
||||
pub const CONFIG_FEATURE_WIFI_ENT_BIT = 1 << 7;
|
||||
|
||||
pub const WIFI_FEATURE_CAPS =
|
||||
CONFIG_FEATURE_WPA3_SAE_BIT |
|
||||
CONFIG_FEATURE_CACHE_TX_BUF_BIT |
|
||||
CONFIG_FEATURE_FTM_INITIATOR_BIT |
|
||||
CONFIG_FEATURE_FTM_RESPONDER_BIT |
|
||||
CONFIG_FEATURE_GCMP_BIT |
|
||||
CONFIG_FEATURE_GMAC_BIT |
|
||||
CONFIG_FEATURE_11R_BIT |
|
||||
WIFI_ENABLE_ENTERPRISE;
|
||||
|
||||
pub const wifi_mode_t = enum(sys.wifi_mode_t) {
|
||||
WIFI_MODE_NULL = sys.WIFI_MODE_NULL,
|
||||
WIFI_MODE_STA = sys.WIFI_MODE_STA,
|
||||
WIFI_MODE_AP = sys.WIFI_MODE_AP,
|
||||
WIFI_MODE_APSTA = sys.WIFI_MODE_APSTA,
|
||||
WIFI_MODE_NAN = sys.WIFI_MODE_NAN,
|
||||
WIFI_MODE_MAX = sys.WIFI_MODE_MAX,
|
||||
};
|
||||
pub const wifi_interface_t = enum(sys.wifi_interface_t) {
|
||||
WIFI_IF_STA = sys.WIFI_IF_STA,
|
||||
WIFI_IF_AP = sys.WIFI_IF_AP,
|
||||
WIFI_IF_NAN = sys.WIFI_IF_NAN,
|
||||
WIFI_IF_MAX = sys.WIFI_IF_MAX,
|
||||
};
|
||||
pub const wifi_country_policy_t = enum(sys.wifi_country_policy_t) {
|
||||
WIFI_COUNTRY_POLICY_AUTO = sys.WIFI_COUNTRY_POLICY_AUTO,
|
||||
WIFI_COUNTRY_POLICY_MANUAL = sys.WIFI_COUNTRY_POLICY_MANUAL,
|
||||
};
|
||||
|
||||
pub fn init(config: *const sys.wifi_init_config_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_init(config));
|
||||
}
|
||||
pub fn setDefaultWifiStationHandlers() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_default_wifi_sta_handlers());
|
||||
}
|
||||
pub fn setDefaultWifiAPHandlers() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_default_wifi_ap_handlers());
|
||||
}
|
||||
pub fn setDefaultWifiNANHandlers() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_default_wifi_nan_handlers());
|
||||
}
|
||||
pub fn clearDefaultWifiDriverHandlers(esp_netif: ?*anyopaque) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_clear_default_wifi_driver_and_handlers(esp_netif));
|
||||
}
|
||||
pub fn deinit() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_deinit());
|
||||
}
|
||||
pub fn setMode(mode: wifi_mode_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_mode(@intFromEnum(mode)));
|
||||
}
|
||||
pub fn getMode(mode: [*]wifi_mode_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_mode(mode));
|
||||
}
|
||||
pub fn start() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_start());
|
||||
}
|
||||
pub fn stop() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_stop());
|
||||
}
|
||||
pub fn restore() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_restore());
|
||||
}
|
||||
pub fn connect() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_connect());
|
||||
}
|
||||
pub fn disconnect() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_disconnect());
|
||||
}
|
||||
pub fn clearFastConnect() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_clear_fast_connect());
|
||||
}
|
||||
pub const Scan = struct {
|
||||
pub fn start(config: [*c]const sys.wifi_scan_config_t, block: bool) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_scan_start(config, block));
|
||||
}
|
||||
pub fn stop() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_scan_stop());
|
||||
}
|
||||
pub fn getAPNum(number: [*c]u16) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_scan_get_ap_num(number));
|
||||
}
|
||||
pub fn getAPRecords(number: [*c]u16, ap_records: ?*sys.wifi_ap_record_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_scan_get_ap_records(number, ap_records));
|
||||
}
|
||||
pub fn getAPRecord(ap_record: ?*sys.wifi_ap_record_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_scan_get_ap_record(ap_record));
|
||||
}
|
||||
};
|
||||
pub const PowerSave = struct {
|
||||
pub fn set(@"type": sys.wifi_ps_type_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_ps(@"type"));
|
||||
}
|
||||
pub fn get(@"type": [*c]sys.wifi_ps_type_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_ps(@"type"));
|
||||
}
|
||||
};
|
||||
pub const Protocol = struct {
|
||||
pub fn set(ifx: wifi_interface_t, protocol_bitmap: u8) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_protocol(ifx, protocol_bitmap));
|
||||
}
|
||||
pub fn get(ifx: wifi_interface_t, protocol_bitmap: [*:0]u8) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_protocol(ifx, protocol_bitmap));
|
||||
}
|
||||
};
|
||||
pub const Bandwidth = struct {
|
||||
pub fn set(ifx: wifi_interface_t, bw: sys.wifi_bandwidth_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_bandwidth(ifx, bw));
|
||||
}
|
||||
pub fn get(ifx: wifi_interface_t, bw: [*c]sys.wifi_bandwidth_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_bandwidth(ifx, bw));
|
||||
}
|
||||
};
|
||||
pub const Channel = struct {
|
||||
pub fn set(primary: u8, second: sys.wifi_second_chan_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_channel(primary, second));
|
||||
}
|
||||
pub fn get(primary: [*c]u8, second: [*c]sys.wifi_second_chan_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_channel(primary, second));
|
||||
}
|
||||
};
|
||||
pub const Country = struct {
|
||||
pub fn set(country: [*c]const sys.wifi_country_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_country(country));
|
||||
}
|
||||
pub fn get(country: [*c]sys.wifi_country_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_country(country));
|
||||
}
|
||||
pub fn setCode(country: [*:0]const u8, ieee80211d_enabled: bool) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_country_code(country, ieee80211d_enabled));
|
||||
}
|
||||
pub fn getCode(country: [*:0]u8) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_country_code(country));
|
||||
}
|
||||
};
|
||||
pub const MAC = struct {
|
||||
pub fn set(ifx: wifi_interface_t, mac: [*:0]const u8) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_mac(ifx, mac));
|
||||
}
|
||||
pub fn get(ifx: wifi_interface_t, mac: [*:0]u8) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_mac(ifx, mac));
|
||||
}
|
||||
};
|
||||
pub const Promiscuous = struct {
|
||||
pub const promiscuous_callback_type = sys.wifi_promiscuous_cb_t;
|
||||
pub fn setRXCallback(cb: sys.wifi_promiscuous_cb_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_promiscuous_rx_cb(cb));
|
||||
}
|
||||
pub fn set(en: bool) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_promiscuous(en));
|
||||
}
|
||||
pub fn get(en: [*c]bool) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_promiscuous(en));
|
||||
}
|
||||
pub fn setFilter(filter: [*c]const sys.wifi_promiscuous_filter_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_promiscuous_filter(filter));
|
||||
}
|
||||
pub fn getFilter(filter: [*c]sys.wifi_promiscuous_filter_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_promiscuous_filter(filter));
|
||||
}
|
||||
pub fn setCtrlFilter(filter: [*c]const sys.wifi_promiscuous_filter_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_promiscuous_ctrl_filter(filter));
|
||||
}
|
||||
pub fn getCtrlFilter(filter: [*c]sys.wifi_promiscuous_filter_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_promiscuous_ctrl_filter(filter));
|
||||
}
|
||||
};
|
||||
pub const wifiConfig = sys.wifi_config_t;
|
||||
pub fn setConfig(interface: wifi_interface_t, conf: ?*sys.wifi_config_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_config(@intFromEnum(interface), conf));
|
||||
}
|
||||
pub fn getConfig(interface: wifi_interface_t, conf: ?*sys.wifi_config_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_config(interface, conf));
|
||||
}
|
||||
pub fn setStorage(storage: sys.wifi_storage_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_storage(storage));
|
||||
}
|
||||
pub const Vendor = struct {
|
||||
pub const vendor_ie_callback_id_type = sys.wifi_vendor_ie_id_t;
|
||||
pub fn setIE(enable: bool, @"type": sys.wifi_vendor_ie_type_t, idx: sys.wifi_vendor_ie_id_t, vnd_ie: ?*const anyopaque) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_vendor_ie(enable, @"type", idx, vnd_ie));
|
||||
}
|
||||
pub fn setIECallback(callback: sys.esp_vendor_ie_cb_t, ctx: ?*anyopaque) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_vendor_ie_cb(callback, ctx));
|
||||
}
|
||||
};
|
||||
pub fn setMaxTXPower(power: i8) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_max_tx_power(power));
|
||||
}
|
||||
pub fn getMaxTXPower(power: [*c]i8) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_max_tx_power(power));
|
||||
}
|
||||
pub fn setEventMask(mask: u32) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_event_mask(mask));
|
||||
}
|
||||
pub fn getEventMask(mask: [*c]u32) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_event_mask(mask));
|
||||
}
|
||||
pub fn p80211TX(ifx: wifi_interface_t, buffer: ?*const anyopaque, len: c_int, en_sys_seq: bool) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_80211_tx(ifx, buffer, len, en_sys_seq));
|
||||
}
|
||||
pub const csi_callback_type = sys.wifi_csi_cb_t;
|
||||
pub fn setCsiRXCallback(callback: csi_callback_type, ctx: ?*anyopaque) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_csi_rx_cb(callback, ctx));
|
||||
}
|
||||
pub fn setCsiConfig(config: ?*const csi_callback_type) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_csi_config(config));
|
||||
}
|
||||
pub fn setCsi(en: bool) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_csi(en));
|
||||
}
|
||||
pub fn setAntGPIO(config: [*c]const sys.wifi_ant_gpio_config_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_ant_gpio(config));
|
||||
}
|
||||
pub fn getAntGPIO(config: [*c]sys.wifi_ant_gpio_config_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_ant_gpio(config));
|
||||
}
|
||||
pub fn setAnt(config: ?*const sys.wifi_ant_config_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_ant(config));
|
||||
}
|
||||
pub fn getAnt(config: ?*sys.wifi_ant_config_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_ant(config));
|
||||
}
|
||||
pub fn getTsfTime(interface: wifi_interface_t) i64 {
|
||||
return sys.esp_wifi_get_tsf_time(interface);
|
||||
}
|
||||
pub fn setInactiveTime(ifx: wifi_interface_t, sec: u16) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_inactive_time(ifx, sec));
|
||||
}
|
||||
pub fn getInactiveTime(ifx: wifi_interface_t, sec: [*c]u16) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_get_inactive_time(ifx, sec));
|
||||
}
|
||||
pub fn statisDump(modules: u32) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_statis_dump(modules));
|
||||
}
|
||||
pub fn setRssiThreshold(rssi: i32) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_rssi_threshold(rssi));
|
||||
}
|
||||
pub const FTM = struct {
|
||||
pub fn initiateSession(cfg: [*c]sys.wifi_ftm_initiator_cfg_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_ftm_initiate_session(cfg));
|
||||
}
|
||||
pub fn endSession() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_ftm_end_session());
|
||||
}
|
||||
pub fn respSetOffset(offset_cm: i16) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_ftm_resp_set_offset(offset_cm));
|
||||
}
|
||||
};
|
||||
pub fn config11bRate(ifx: wifi_interface_t, disable: bool) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_config_11b_rate(ifx, disable));
|
||||
}
|
||||
pub fn connectionlessModuleSetWakeInterval(wake_interval: u16) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_connectionless_module_set_wake_interval(wake_interval));
|
||||
}
|
||||
pub fn forceWakeupAcquire() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_force_wakeup_acquire());
|
||||
}
|
||||
pub fn forceWakeupRelease() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_force_wakeup_release());
|
||||
}
|
||||
pub fn config80211TXRate(ifx: wifi_interface_t, rate: sys.wifi_phy_rate_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_config_80211_tx_rate(ifx, rate));
|
||||
}
|
||||
pub fn disablePMFConfig(ifx: wifi_interface_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_disable_pmf_config(ifx));
|
||||
}
|
||||
pub fn setDynCS(enabled: bool) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_set_dynamic_cs(enabled));
|
||||
}
|
||||
pub const Station = struct {
|
||||
pub fn getAPInfo(ap_info: ?*sys.wifi_ap_record_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_sta_get_ap_info(ap_info));
|
||||
}
|
||||
pub fn getRssi(rssi: [*c]c_int) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_sta_get_rssi(rssi));
|
||||
}
|
||||
pub fn getAid(aid: [*c]u16) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_sta_get_aid(aid));
|
||||
}
|
||||
pub fn getNegotiatedPHYMode(phymode: [*c]sys.wifi_phy_mode_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_sta_get_negotiated_phymode(phymode));
|
||||
}
|
||||
pub fn deauth(aid: u16) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_deauth_sta(aid));
|
||||
}
|
||||
pub const AP = struct {
|
||||
pub fn getList(sta: ?*sys.wifi_sta_list_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_ap_get_sta_list(sta));
|
||||
}
|
||||
pub fn getAid(mac: [*:0]const u8, aid: [*c]u16) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_ap_get_sta_aid(mac, aid));
|
||||
}
|
||||
pub fn clearList() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_clear_ap_list());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub const PowerDomain = struct {
|
||||
pub fn On() void {
|
||||
sys.esp_wifi_power_domain_on();
|
||||
}
|
||||
pub fn Off() void {
|
||||
sys.esp_wifi_power_domain_off();
|
||||
}
|
||||
};
|
||||
|
||||
pub const Enterprise = struct {
|
||||
pub const Station = struct {
|
||||
pub fn enable() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_sta_enterprise_enable());
|
||||
}
|
||||
pub fn disable() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_sta_enterprise_disable());
|
||||
}
|
||||
};
|
||||
pub const Client = struct {
|
||||
pub fn setIdentity(identity: [*:0]const u8, len: c_int) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_set_identity(identity, len));
|
||||
}
|
||||
pub const clearIdentity = sys.esp_eap_client_clear_identity;
|
||||
pub fn setUsername(username: [*:0]const u8, len: c_int) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_set_username(username, len));
|
||||
}
|
||||
pub const clearUsername = sys.esp_eap_client_clear_username;
|
||||
pub fn setPassword(password: [*:0]const u8, len: c_int) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_set_password(password, len));
|
||||
}
|
||||
pub const clearPassword = sys.esp_eap_client_clear_new_password;
|
||||
pub fn setNewPassword(new_password: [*:0]const u8, len: c_int) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_set_new_password(new_password, len));
|
||||
}
|
||||
pub const clearNewPassword = sys.esp_eap_client_clear_new_password;
|
||||
pub fn setCACertificate(ca_cert: [*:0]const u8, ca_cert_len: c_int) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_set_ca_cert(ca_cert, ca_cert_len));
|
||||
}
|
||||
pub const clearCACertificate = sys.esp_eap_client_clear_ca_cert;
|
||||
pub fn setCertificateKey(client_cert: [*:0]const u8, client_cert_len: c_int, private_key: [*:0]const u8, private_key_len: c_int, private_key_password: [*:0]const u8, private_key_passwd_len: c_int) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_set_certificate_and_key(client_cert, client_cert_len, private_key, private_key_len, private_key_password, private_key_passwd_len));
|
||||
}
|
||||
pub const clearCertificateKey = sys.esp_eap_client_clear_certificate_and_key;
|
||||
pub fn setDisableTimeCheck(disable: bool) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_set_disable_time_check(disable));
|
||||
}
|
||||
pub fn getDisableTimeCheck(disable: [*c]bool) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_get_disable_time_check(disable));
|
||||
}
|
||||
pub fn setTTLSPhase2Method(@"type": sys.esp_eap_ttls_phase2_types) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_set_ttls_phase2_method(@"type"));
|
||||
}
|
||||
pub fn setSuiteb192bitCertification(enable: bool) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_set_suiteb_192bit_certification(enable));
|
||||
}
|
||||
pub fn setPACFile(pac_file: [*:0]const u8, pac_file_len: c_int) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_set_pac_file(pac_file, pac_file_len));
|
||||
}
|
||||
pub fn setFastParams(config: sys.esp_eap_fast_config) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_set_fast_params(config));
|
||||
}
|
||||
pub fn useDefaultCertificateBundle(use_default_bundle: bool) !void {
|
||||
return try errors.espCheckError(sys.esp_eap_client_use_default_cert_bundle(use_default_bundle));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub const Internal = struct {
|
||||
// STUBS
|
||||
pub fn setStationIp() !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_internal_set_sta_ip());
|
||||
}
|
||||
pub fn registryNetstackBufCallback(ref: sys.wifi_netstack_buf_ref_cb_t, free: sys.wifi_netstack_buf_free_cb_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_internal_reg_netstack_buf_cb(ref, free));
|
||||
}
|
||||
pub fn freeRXBuffer(buffer: ?*anyopaque) !void {
|
||||
if (buffer) |b|
|
||||
return try errors.espCheckError(sys.esp_wifi_internal_free_rx_buffer(b));
|
||||
}
|
||||
pub fn txBuffer(ifx: wifi_interface_t, buffer: ?*anyopaque, len: u16) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_internal_tx(ifx, buffer, len));
|
||||
}
|
||||
pub fn registryTXCallBack(ifx: wifi_interface_t, @"fn": sys.wifi_rxcb_t) !void {
|
||||
return try errors.espCheckError(sys.esp_wifi_internal_reg_rxcb(ifx, @"fn"));
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init_config_default() sys.wifi_init_config_t {
|
||||
return sys.wifi_init_config_t{
|
||||
.osi_funcs = &sys.g_wifi_osi_funcs,
|
||||
.wpa_crypto_funcs = sys.g_wifi_default_wpa_crypto_funcs,
|
||||
.static_rx_buf_num = sys.CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM,
|
||||
.dynamic_rx_buf_num = sys.CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM,
|
||||
.tx_buf_type = sys.CONFIG_ESP_WIFI_TX_BUFFER_TYPE,
|
||||
.static_tx_buf_num = sys.WIFI_STATIC_TX_BUFFER_NUM,
|
||||
.dynamic_tx_buf_num = sys.WIFI_DYNAMIC_TX_BUFFER_NUM,
|
||||
.rx_mgmt_buf_type = sys.CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF,
|
||||
.rx_mgmt_buf_num = sys.WIFI_RX_MGMT_BUF_NUM_DEF,
|
||||
.cache_tx_buf_num = sys.WIFI_CACHE_TX_BUFFER_NUM,
|
||||
.csi_enable = sys.WIFI_CSI_ENABLED,
|
||||
.ampdu_rx_enable = sys.WIFI_AMPDU_RX_ENABLED,
|
||||
.ampdu_tx_enable = sys.WIFI_AMPDU_TX_ENABLED,
|
||||
.amsdu_tx_enable = sys.WIFI_AMSDU_TX_ENABLED,
|
||||
.nvs_enable = sys.WIFI_NVS_ENABLED,
|
||||
.nano_enable = sys.WIFI_NANO_FORMAT_ENABLED,
|
||||
.rx_ba_win = sys.WIFI_DEFAULT_RX_BA_WIN,
|
||||
.wifi_task_core_id = sys.WIFI_TASK_CORE_ID,
|
||||
.beacon_max_len = sys.WIFI_SOFTAP_BEACON_MAX_LEN,
|
||||
.mgmt_sbuf_num = sys.WIFI_MGMT_SBUF_NUM,
|
||||
.feature_caps = sys.WIFI_FEATURE_CAPS,
|
||||
.sta_disconnected_pm = sys.WIFI_STA_DISCONNECTED_PM_ENABLED != 0,
|
||||
.espnow_max_encrypt_num = sys.CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM,
|
||||
.tx_hetb_queue_num = sys.WIFI_TX_HETB_QUEUE_NUM,
|
||||
.dump_hesigb_enable = sys.WIFI_DUMP_HESIGB_ENABLED != 0,
|
||||
.magic = sys.WIFI_INIT_CONFIG_MAGIC,
|
||||
};
|
||||
}
|
||||
149
software/zig_main/imports/wifi_remote.zig
Normal file
149
software/zig_main/imports/wifi_remote.zig
Normal file
@@ -0,0 +1,149 @@
|
||||
const sys = @import("sys");
|
||||
const errors = @import("error");
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Re-exported types
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const WifiInterface = sys.wifi_interface_t;
|
||||
pub const WifiMode = sys.wifi_mode_t;
|
||||
pub const WifiPsType = sys.wifi_ps_type_t;
|
||||
pub const WifiBandwidth = sys.wifi_bandwidth_t;
|
||||
pub const WifiSecondChan = sys.wifi_second_chan_t;
|
||||
pub const WifiCountry = sys.wifi_country_t;
|
||||
pub const WifiStorage = sys.wifi_storage_t;
|
||||
pub const WifiPhyRate = sys.wifi_phy_rate_t;
|
||||
pub const WifiBand = sys.wifi_band_t;
|
||||
pub const WifiPhyMode = sys.wifi_phy_mode_t;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Remote Wi-Fi API
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
pub const WifiRemote = struct {
|
||||
|
||||
// ─── Lifecycle ──────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn init(config: *const sys.wifi_init_config_t) !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_init(config));
|
||||
}
|
||||
|
||||
pub fn deinit() !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_deinit());
|
||||
}
|
||||
|
||||
pub fn start() !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_start());
|
||||
}
|
||||
|
||||
pub fn stop() !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_stop());
|
||||
}
|
||||
|
||||
pub fn restore() !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_restore());
|
||||
}
|
||||
|
||||
// ─── Mode ───────────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn setMode(mode: WifiMode) !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_set_mode(mode));
|
||||
}
|
||||
|
||||
pub fn getMode() !WifiMode {
|
||||
var m: WifiMode = undefined;
|
||||
try errors.espCheckError(sys.esp_wifi_remote_get_mode(&m));
|
||||
return m;
|
||||
}
|
||||
|
||||
// ─── Connect / Disconnect ──────────────────────────────────────────────────
|
||||
|
||||
pub fn connect() !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_connect());
|
||||
}
|
||||
|
||||
pub fn disconnect() !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_disconnect());
|
||||
}
|
||||
|
||||
// ─── Scan ───────────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn scanStart(config: ?*const sys.wifi_scan_config_t, block: bool) !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_scan_start(config, block));
|
||||
}
|
||||
|
||||
pub fn scanStop() !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_scan_stop());
|
||||
}
|
||||
|
||||
pub fn getApNum() !u16 {
|
||||
var n: u16 = 0;
|
||||
try errors.espCheckError(sys.esp_wifi_remote_scan_get_ap_num(&n));
|
||||
return n;
|
||||
}
|
||||
|
||||
pub fn getApRecords(records: []sys.wifi_ap_record_t) !u16 {
|
||||
var num = @as(u16, @intCast(records.len));
|
||||
try errors.espCheckError(sys.esp_wifi_remote_scan_get_ap_records(&num, records.ptr));
|
||||
return num;
|
||||
}
|
||||
|
||||
// ─── Station info ───────────────────────────────────────────────────────────
|
||||
|
||||
pub fn staGetApInfo() !sys.wifi_ap_record_t {
|
||||
var info: sys.wifi_ap_record_t = undefined;
|
||||
try errors.espCheckError(sys.esp_wifi_remote_sta_get_ap_info(&info));
|
||||
return info;
|
||||
}
|
||||
|
||||
pub fn staGetRssi() !i32 {
|
||||
var rssi: c_int = 0;
|
||||
try errors.espCheckError(sys.esp_wifi_remote_sta_get_rssi(&rssi));
|
||||
return @intCast(rssi);
|
||||
}
|
||||
|
||||
// ─── Configuration ──────────────────────────────────────────────────────────
|
||||
|
||||
pub fn setConfig(iface: WifiInterface, conf: *const sys.wifi_config_t) !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_set_config(iface, @constCast(conf)));
|
||||
}
|
||||
|
||||
pub fn getConfig(iface: WifiInterface) !sys.wifi_config_t {
|
||||
var conf: sys.wifi_config_t = undefined;
|
||||
try errors.espCheckError(sys.esp_wifi_remote_get_config(iface, &conf));
|
||||
return conf;
|
||||
}
|
||||
|
||||
pub fn setCountry(country: *const WifiCountry) !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_set_country(country));
|
||||
}
|
||||
|
||||
pub fn setMac(iface: WifiInterface, mac: []const u8) !void {
|
||||
if (mac.len != 6) return error.InvalidMacLength;
|
||||
try errors.espCheckError(sys.esp_wifi_remote_set_mac(iface, mac.ptr));
|
||||
}
|
||||
|
||||
pub fn getMac(iface: WifiInterface) ![6]u8 {
|
||||
var mac: [6]u8 = undefined;
|
||||
try errors.espCheckError(sys.esp_wifi_remote_get_mac(iface, &mac));
|
||||
return mac;
|
||||
}
|
||||
|
||||
// ─── Promiscuous ────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn setPromiscuous(enable: bool) !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_set_promiscuous(enable));
|
||||
}
|
||||
|
||||
pub fn setPromiscuousRxCb(cb: sys.wifi_promiscuous_cb_t) !void {
|
||||
try errors.espCheckError(sys.esp_wifi_remote_set_promiscuous_rx_cb(cb));
|
||||
}
|
||||
|
||||
pub fn createDefaultStaNetif() !*sys.esp_netif_t {
|
||||
return sys.esp_wifi_remote_create_default_sta() orelse error.FailedToCreateNetif;
|
||||
}
|
||||
|
||||
pub fn createDefaultApNetif() !*sys.esp_netif_t {
|
||||
return sys.esp_wifi_remote_create_default_ap() orelse error.FailedToCreateNetif;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user