Using the embedded swift example here as my starting point and incorporating the code from the esp-idf installation examples/wifi/scan/
, I can successfully scan the APs in my local environment. My problem is accessing the results! Here is my code:
@_cdecl("app_main")
func app_main() {
print("Hello from Swift on ESP32-C6!")
var ret = nvs_flash_init()
if ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND {
ESP_ERROR_CHECK(nvs_flash_erase())
ret = nvs_flash_init()
}
ESP_ERROR_CHECK(ret)
ESP_ERROR_CHECK(esp_netif_init())
var cfg = get_config()
ESP_ERROR_CHECK(esp_wifi_init(&cfg))
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA))
ESP_ERROR_CHECK(esp_wifi_start())
var scanListSize: UInt16 = 32
var ap_count: UInt16 = 0
var ap_info = UnsafeMutablePointer<wifi_ap_record_t>.allocate(capacity: Int(scanListSize))
ESP_ERROR_CHECK(esp_netif_init())
ESP_ERROR_CHECK(esp_event_loop_create_default())
let _ = esp_netif_create_default_wifi_sta()
// assert(sta_netif)
while true {
scanListSize = 32
esp_wifi_scan_start(nil, true)
print("Max AP scanListSize ap_info can hold = \(scanListSize)")
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count))
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&scanListSize, ap_info))
print("Total APs scanned = \(ap_count), actual AP scanListSize ap_info holds = \(scanListSize)" )
for i in 0..<Int(scanListSize) {
let pointer = I * 47
let record: wifi_ap_record_t = ap_info[pointer]
print("\(record.ssid.0),\(record.ssid.1),\(record.ssid.2),\(record.ssid.3),\(record.ssid.4),")
}
}
}
// The ESP_ERROR_CHECK macro is unavailable
func ESP_ERROR_CHECK(_ errno: esp_err_t, file: String = #file, line: Int = #line) {
if errno == ESP_OK { return }
print("\(errno) at line \(line) in \(file)")
}
The structure of wifi_ap_record_t
is:
typedef struct {
uint8_t bssid[6]; /**< MAC address of AP */
uint8_t ssid[32]; /**< SSID of AP */
uint8_t primary; /**< channel of AP */
wifi_second_chan_t second; /**< second channel of AP */
int8_t rssi; /**< signal strength of AP */
wifi_auth_mode_t authmode; /**< authmode of AP */
uint32_t low_rate_enable:1; /**< bit: 0 flag to identify if low rate is enabled or not */
uint32_t reserved:31; /**< bit: 1..31 reserved */
} wifi_ap_record_t;
The code above returns a reasonable value for the number of APs discovered and prints out the first five values of the SSID field. It runs for extended periods without crashing, but the second and subsequent runs don't appear to return as good data as the first.
The SSID field is a 32-element tuple of UInt8
s. In other situations (e.g. decoding similar data from libgpiod on a raspberry pi, I use Mirror
to convert tuples to arrays (and onward to String if required). However, Mirror
is unavailable in embedded swift. Trying to go via Data(bytes:)
also fails because Data
isn't available either - although it doesn't explicitly say this like it does with Mirror
, it just says it cannot find it in scope. Accessing tuple elements has to be literal, i.e. you can't do record.ssid.n
, so I next switched to trying to convert individual bytes to Character
using let ch0 = record.ssid.0 as? Character
but this gives nil
when appended to a String and cannot print value in embedded Swift
if printed directly.
I've got two questions:
- Is the way I am allocating the buffer the best or even the correct way?
- Is there an easy way to get the data from the SSID field into, say, a String?