//! Device identity derived from the factory MAC address.
//!
//! The id is the lowercase hex of the last 3 bytes of the WiFi-STA MAC (6
//! chars, e.g. `0a1b2c`), so every unit is self-identifying without needing
//! anything flashed per-device.
/// Maximum SSID length in bytes (IEEE 802.11).
///
/// Worth enforcing: since esp-idf-svc 0.52 / heapless 0.9 the SSID goes into a
/// fixed-capacity string via `try_into()`, so an over-long name is a runtime
/// error at AP start rather than a silent truncation.
pub const SSID_MAX_LEN: usize = 32;
/// Format the 6-character lowercase-hex id from a MAC address.
pub fn format_id(mac: [u8; 6]) -> String {
format!("{:02x}{:02x}{:02x}", mac[3], mac[4], mac[5])
}
/// SSID advertised while in provisioning mode, e.g. `setup_ssid("scan-setup",
/// "0a1b2c")` -> `scan-setup-0a1b2c`.
///
/// Truncated to [`SSID_MAX_LEN`] on a UTF-8 boundary if the prefix is long.
pub fn setup_ssid(prefix: &str, id: &str) -> String {
let mut ssid = format!("{prefix}-{id}");
if ssid.len() > SSID_MAX_LEN {
let mut cut = SSID_MAX_LEN;
while cut > 0 && !ssid.is_char_boundary(cut) {
cut -= 1;
}
ssid.truncate(cut);
}
ssid
}
/// Read the factory WiFi-STA MAC and format it as a device id.
#[cfg(target_os = "espidf")]
pub fn device_id() -> String {
use esp_idf_svc::sys::{esp_mac_type_t_ESP_MAC_WIFI_STA, esp_read_mac};
let mut mac = [0u8; 6];
// SAFETY: `esp_read_mac` writes exactly 6 bytes into the provided buffer.
unsafe {
esp_read_mac(mac.as_mut_ptr(), esp_mac_type_t_ESP_MAC_WIFI_STA);
}
format_id(mac)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn formats_last_three_bytes() {
assert_eq!(format_id([0xde, 0xad, 0xbe, 0x0a, 0x1b, 0x2c]), "0a1b2c");
}
#[test]
fn pads_single_hex_digits() {
assert_eq!(format_id([0, 0, 0, 0x01, 0x02, 0x03]), "010203");
}
#[test]
fn builds_setup_ssid() {
assert_eq!(setup_ssid("scan-setup", "0a1b2c"), "scan-setup-0a1b2c");
}
#[test]
fn truncates_over_long_ssid() {
let ssid = setup_ssid(&"x".repeat(40), "0a1b2c");
assert_eq!(ssid.len(), SSID_MAX_LEN);
}
#[test]
fn truncation_respects_utf8_boundaries() {
// 'é' is 2 bytes; cutting at 32 must not split it.
let ssid = setup_ssid(&"é".repeat(20), "0a1b2c");
assert!(ssid.len() <= SSID_MAX_LEN);
assert!(std::str::from_utf8(ssid.as_bytes()).is_ok());
}
}
neiam
/neiam-portal
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
2.5 KiB · text
History
b85db9e