Skip to content

Conversation

@TuEmb
Copy link
Contributor

@TuEmb TuEmb commented Dec 21, 2025

related issue #2902
This PR includes:

  • Add wifi feature for Omi
  • Add a warning for SD card full storage.
  • Sync audio file over Wifi using TCP protocol (pause the mic)

@TuEmb TuEmb force-pushed the TuEmb/sync_over_wifi branch from c35d2ca to 6587336 Compare December 21, 2025 07:32
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces WiFi functionality for audio data synchronization, which is a significant feature. The implementation includes a new WiFi state machine, configuration via BLE, and data transfer over UDP. While the overall structure is good, I've found several critical and high-severity issues that must be addressed. These include a buffer overflow vulnerability, race conditions, incorrect state handling, and long blocking calls that can affect system stability and responsiveness. Please review the detailed comments for suggestions on how to resolve these issues.

I am having trouble creating individual review comments. Click here to see my feedback.

omi/firmware/omi/src/lib/core/storage.c (276-301)

critical

There's a critical buffer overflow vulnerability here. The lengths ssid_len, pwd_len, and ip_len are read from the input buffer but are not validated against the sizes of the destination buffers (ssid, pwd, ip) before calling memcpy. A malicious client could provide a large length value, causing a buffer overflow and potentially leading to arbitrary code execution. You should add checks to ensure the lengths do not exceed the buffer sizes minus one (for the null terminator).

            if (ssid_len == 0 || ssid_len >= sizeof(ssid) || idx + ssid_len > len) {
                LOG_WRN("SSID length invalid: ssid_len=%d, idx=%d, len=%d", ssid_len, idx, len);
                result_buffer[0] = 3; break;
            }
            char ssid[33] = {0};
            memcpy(ssid, &((const uint8_t *)buf)[idx], ssid_len);
            idx += ssid_len;
            LOG_INF("WIFI_SETUP: ssid='%s', idx=%d", ssid, idx);

            uint8_t pwd_len = ((const uint8_t *)buf)[idx++];
            LOG_INF("WIFI_SETUP: pwd_len=%d, idx=%d, len=%d", pwd_len, idx, len);
            if (pwd_len > 0 && (pwd_len >= sizeof(pwd) || idx + pwd_len > len)) {
                LOG_WRN("PWD length invalid: pwd_len=%d, idx=%d, len=%d", pwd_len, idx, len);
                result_buffer[0] = 4; break;
            }
            char pwd[65] = {0};
            if (pwd_len > 0) memcpy(pwd, &((const uint8_t *)buf)[idx], pwd_len);
            idx += pwd_len;
            LOG_INF("WIFI_SETUP: pwd='%s', idx=%d", pwd, idx);

            uint8_t ip_len = ((const uint8_t *)buf)[idx++];
            LOG_INF("WIFI_SETUP: ip_len=%d, idx=%d, len=%d", ip_len, idx, len);
            if (ip_len == 0 || ip_len >= sizeof(ip) || idx + ip_len > len) {
                LOG_WRN("IP length invalid: ip_len=%d, idx=%d, len=%d", ip_len, idx, len);
                result_buffer[0] = 5; break;
            }

omi/firmware/omi/src/wifi.c (373-389)

critical

This function is critically flawed. It's called when a disconnect event occurs, but it fails to update the connection status by calling wifi_set_connected(false). This will cause the state machine to incorrectly believe it's still connected, leading to failures in reconnection logic. Additionally, it unconditionally sets WIFI_FLAG_DISCONNECT_REQUESTED, which is incorrect for unsolicited disconnections.

static void handle_wifi_disconnect_result(struct net_mgmt_event_callback *cb, struct net_if *iface)
{
	if (!wifi_is_connected()) {
		return;
	}

	const struct wifi_status *status = (const struct wifi_status *)cb->info;
	if (status && status->status) {
		LOG_WRN("Disconnect status: %d", status->status);
	}

	LOG_WRN("WiFi disconnected, close UDP socket if any");
	wifi_set_connected(false);
	udp_close_socket();
	set_wifi_state(WIFI_STATE_ON);
}

omi/firmware/omi/src/wifi.c (886-891)

critical

This function blocks the calling thread by busy-waiting in a while loop for the WiFi state to become WIFI_STATE_OFF. The shutdown process itself includes a 5-second sleep (k_msleep(5000)) in handle_wifi_shutdown, meaning this function will block for at least 5 seconds. This is a critical issue that can lead to an unresponsive system or watchdog timer expirations. The wifi_turn_off function should be asynchronous, or the shutdown logic should be redesigned to avoid long blocking calls.

omi/firmware/omi/src/lib/core/storage.c (340)

high

The bt_gatt_notify call is using &storage_service.attrs[1], which corresponds to the storage_write_uuid characteristic. However, this handler is for the storage_wifi_uuid characteristic. You should send notifications on the correct characteristic, which is storage_wifi_uuid at index 5 of the storage_service_attr array. Using the wrong attribute will send notifications to clients subscribed to the wrong characteristic. This also applies to other bt_gatt_notify calls in this function.

    bt_gatt_notify(conn, &storage_service.attrs[5], &result_buffer, 1);

omi/firmware/omi/src/main.c (314)

high

There is a 10-second blocking sleep (k_sleep(K_SECONDS(10))) during startup before initializing WiFi. This significantly delays the device's readiness without any explanation. If this delay is to wait for hardware to stabilize, it should be replaced with a more robust, event-driven mechanism, such as waiting for a device-ready signal. Unexplained long sleeps in initialization are fragile and hurt user experience.

omi/firmware/omi/src/wifi.c (53)

high

The global variable current_wifi_state is accessed from multiple threads (the WiFi thread and any thread calling the public API) without any synchronization. This can lead to race conditions. For example, a check-then-act sequence in wifi_turn_on is not atomic. You should use an atomic_t variable and atomic operations (atomic_set, atomic_get) to ensure thread-safe access to the WiFi state.

static atomic_t current_wifi_state = ATOMIC_INIT(WIFI_STATE_OFF);

omi/firmware/omi/src/wifi.c (86-89)

high

The global variables for WiFi credentials and server address (wifi_ssid, wifi_password, tcp_server_addr, tcp_server_port) are written in setup_wifi_credentials and setup_udp_server (called from a BLE callback thread) and read in wifi_connect (called from the WiFi thread) without any synchronization. This is a race condition that could lead to using partially updated or corrupted credentials. You should protect access to these variables with a mutex.

omi/firmware/omi/src/wifi.c (238-267)

high

This while loop is a busy-wait. It will spin, consuming CPU cycles, until DHCP is bound or the timeout is reached. This is inefficient. You should use k_sem_take with a timeout to wait for the dhcp_bound_sem semaphore, which is a much more efficient way to wait for an event.

static int wifi_wait_for_dhcp(int32_t timeout_ms)
{
	struct net_if *iface = net_if_get_wifi_sta();

	if (atomic_get(&dhcp_bound)) {
		return 0;
	}

	/* Make sure we don't consume a stale give from previous connections */
	k_sem_reset(&dhcp_bound_sem);

	if (k_sem_take(&dhcp_bound_sem, K_MSEC(timeout_ms)) != 0) {
		/* Timeout occurred, check final state */
		if (!atomic_get(&dhcp_bound) && !ipv4_ready(iface)) {
			return -ETIMEDOUT;
		}
	}

	return 0;
}

omi/firmware/omi/src/wifi.c (631)

high

Using a long, blocking k_msleep(5000) to wait for WiFi disconnection is unreliable and inefficient. It blocks the WiFi thread for 5 seconds, preventing it from doing other work. A better approach would be to request disconnection and then wait for the NET_EVENT_WIFI_DISCONNECT_RESULT event, for example by using a semaphore. This would make the shutdown process faster and more robust.

omi/firmware/omi/src/wifi.c (1142-1144)

high

On a UDP sendto error, this code immediately closes the UDP socket and requests a WiFi disconnect. This is too aggressive. Many sendto errors are transient (e.g., buffer full) or related to the remote peer, not the local WiFi link. Tearing down the entire WiFi connection will cause significant disruption. It would be more robust to handle errors like EHOSTUNREACH or ECONNREFUSED by simply logging them and maybe pausing UDP traffic for a short period, without disconnecting from the WiFi network.

omi/firmware/omi/src/wifi.h (31)

high

The function register_wifi_ready is declared as static, which limits its scope to the current translation unit. Declaring a static function in a public header file is incorrect and will lead to compiler warnings or errors if the header is included in multiple source files. This function should be removed from the header and kept private to wifi.c.

@TuEmb TuEmb marked this pull request as ready for review December 21, 2025 10:04
@TuEmb
Copy link
Contributor Author

TuEmb commented Dec 22, 2025

1. Configure WiFi & TCP Server (WIFI_SETUP)

Command: 0x01
UUID: 30295783-4301-EABD-2904-2849ADFEAE43

Payload format: <cmd> <ssid_len> <ssid> <pwd_len> <pwd> <ip_len> <ip> <port (2bytes)>
Script to generate: parse_wifi_setup.py

  • cmd: 0x01
  • ssid_len: SSID length (1 byte)
  • ssid: SSID string (ssid_len bytes)
  • pwd_len: Password length (1 byte)
  • pwd: Password string (pwd_len bytes)
  • ip_len: Server IP length (1 byte)
  • ip: Server IP string (ip_len bytes, e.g. "192.168.1.100")
  • port_hi, port_lo: TCP server port (2 bytes, big endian)

Example:
SSID: "mywifi", Password: "12345678", IP: "192.168.1.100", Port: 12345

Response: [0x00] on success, other error codes for invalid format.

2. Enable WiFi (WIFI_START)

Command: 0x02
UUID: 30295783-4301-EABD-2904-2849ADFEAE43
Payload: [0x02]
Description: Turns on the WiFi module and prepares TCP connection for data transfer.
Response: [0x00] on success.

3. Disable WiFi (WIFI_SHUTDOWN)

Command: 0x03
UUID: 30295783-4301-EABD-2904-2849ADFEAE43
Payload: [0x03]
Description: Turns off the WiFi module and resumes microphone operation.
Response: [0x00] on success.

@TuEmb
Copy link
Contributor Author

TuEmb commented Dec 22, 2025

image

@TuEmb
Copy link
Contributor Author

TuEmb commented Dec 22, 2025

Now we can reach ~100KBps in TCP protocol

image

@TuEmb
Copy link
Contributor Author

TuEmb commented Dec 23, 2025

Flow to make an WIFI connection for Omi device

image

@TuEmb
Copy link
Contributor Author

TuEmb commented Dec 25, 2025

Tested with some scenarios:

  1. turn off the wifi hotspot then turn on --> device can reconnect to the wifi and continue to syncup
image
  1. Close the TCP server then open again --> device can reconnect to the wifi and continue to syncup
image
  1. connect/disconnect bluetooth while syncup --> device still keeps syncup over wifi
image

@TuEmb TuEmb force-pushed the TuEmb/sync_over_wifi branch from 30d76f6 to cf5e767 Compare December 27, 2025 11:48
@TuEmb TuEmb force-pushed the TuEmb/sync_over_wifi branch 2 times, most recently from ebf5cdd to 58f2468 Compare December 30, 2025 04:29
@TuEmb
Copy link
Contributor Author

TuEmb commented Jan 1, 2026

Wifi sync speed:

image

@TuEmb TuEmb requested a review from beastoin January 1, 2026 07:52
@TuEmb
Copy link
Contributor Author

TuEmb commented Jan 1, 2026

lifetime in offline mode - without wifi feature adding ~7 hours and 10 minutes

[07:10:34.663,482] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:35.541,290] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:36.356,903] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:36.983,581] <inf> battery: Median ADC raw (after discarding 1st of 51 total): 2564
[07:10:36.983,642] <inf> battery: ADC mV at pin (after conversion): 1126, charging: false
[07:10:36.983,642] <inf> battery: Battery voltage (mV): 3587
Battery at 3587 mV (capacity 1%)
[07:10:37.253,875] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:38.070,648] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:38.350,555] <inf> sd_card: [SD_WORK] fs_sync triggered after 22000 bytes

[07:10:38.963,592] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:39.839,813] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:40.655,181] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:41.469,451] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:42.369,812] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:42.664,703] <inf> sd_card: [SD_WORK] fs_sync triggered after 22000 bytes

[07:10:43.266,510] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:44.146,057] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:44.972,839] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:45.867,797] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:46.668,487] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
[07:10:46.967,193] <inf> sd_card: [SD_WORK] fs_sync triggered after 22000 bytes

[07:10:46.984,985] <inf> battery: Median ADC raw (after discarding 1st of 51 total): 2489
[07:10:46.985,015] <inf> battery: ADC mV at pin (after conversion): 1093, charging: false
[07:10:46.985,015] <inf> battery: Battery voltage (mV): 3482
Battery at 3482 mV (capacity 1%)
[07:10:46.985,076] <wrn> transport: Battery critical level reached (3482 mV). Initiating shutdown.
[07:10:47.005,249] <inf> haptic: Playing haptic for 100 ms
[07:10:47.465,637] <inf> sd_card: [SD_WORK] WRITE_BATCH_COUNT reached. Flushing batch write.
metal: warning:   tx_vq: freeing non-empty virtqueue

metal: warning:   rx_vq: freeing non-empty virtqueue

[07:10:48.615,631] <inf> mic: Microphone stopped
[07:10:49.269,592] <inf> sd_card: Disk unmounted.

lifetime in offline mode - with wifi feature adding ~7 hours and 8 minutes
image

@beastoin
Copy link
Collaborator

beastoin commented Jan 1, 2026

lgtm @TuEmb

@beastoin beastoin merged commit 61a2832 into BasedHardware:main Jan 1, 2026
@TuEmb TuEmb deleted the TuEmb/sync_over_wifi branch January 1, 2026 09:48
@beastoin
Copy link
Collaborator

beastoin commented Jan 3, 2026

--
1/ Wi-Fi sync isn't working for me.
=> i used a random ssid/password but not the personal hotspot (so case closed).
=> but please enhance the UX to let the device test the connection every single time before syncing via wifi.

2/ Can we automatically create the SSID and password without asking the user?
=> cannot so case closed
=> but if we have the chance to do it on any platform (android?), please do it.

3/ I’m not sure if the current sync mode is Wi-Fi or BLE. It might be better to let users choose whether they want to use BLE or Wi-Fi for the next sync, or at least allow them to switch.
=> please improve the UI/UX. i don't think users need to know about wifi or ble; it should just be about fast transfers or normal.

4/ Can we display the speed after 5 seconds of syncing and recalculate it every 5 seconds? I think it would improve the UX since waiting too long for the first bytes might lead users to think our sync is slow.

5/ Is BLE sync now 17KB/s? I remember it being 30KB/s last time. Just double-checking.

6/ After a while, the app shows "Device disconnected," and the device's LED is still blue, but I cannot find it using either Omi or NRF Connect. I turned the device off by pressing the button, but now I cannot turn it on again. So, red flag.

7/ I also cannot find a way to update my SSID/password for my hotspot. If allowing users to enter it manually is the only way, we need to let users edit it. It would be great if we could run some tests to let users know that their hotspot is good to go.
=> i can find it now (device settings) but please improve the UX.
=> make sure everything is good (firmware feature, wifi module on the device, the hotspot connection) before starting to transfer via wifi.

8/ On the firmware side, please also handle cases where the hotspot credentials are incorrect to protect the device.
=> and again, verifying is a must; make sure everything is good (firmware feature, wifi module on the device, the hotspot connection) before starting to transfer via wifi.

(updates: add 6, 7, 8)

app: testflight 1.0.517 (553)
firmware: 1.0.14 build 2 (built from main, attached)
ssid: one, password: two

@beastoin
Copy link
Collaborator

beastoin commented Jan 3, 2026

#3934

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants