If you want multiple SSIDs, roaming, daily neighbor scanning and auto channel selection, etc, but don't like to spend hours tinkering with your equipment beyond the physical setup, then Ubiquiti UniFi equipment is great.
I stopped recommending UniFi around 2020 (several of their best engineers had left, and they made some dumb choices), but IMO they're back to being a decent choice. And I appreciate that they're become a one-stop solution for all home/SOHO as well as mid size enterprise IT needs.
By lucky chance, while he set up usteer, he modified DTIM to 3 thus fixing the fast transition roaming, which doesn't work well on default openwrt because of DTIM. Especially Apple devices really hate DTIM=2 (they need the extra off-time given by DTIM to properly scan the other channels).
On iOS, equal channel with correct ESS will switch liberally. On Android 14+ with Broadcom chip it will start conservative, then switch liberally after the first poor signal switch-over event, up until disconnection.
Android (Pixel/Moto) will never switch (even with K/V) on large network activity, only VoIP/video call. It depends on vendor implementation. [0] I use "dp.logcatapp" log reader while roaming, "com.android.location.fused" can be used to show score and current load.
Samsung is known to push protocol support early: 802.11r in 2013, 802.11w 2015, some models do not use Android's default connectivity manager.
To add, WPA3 with 802.11r is known to have issues on Apple hardware before 2021 on all iOS versions, many Android devices, especially smart TVs don't support it, will not connect or are unreliable (protected beacon frame), can be searched in buried report results at OpenWrt forum mega threads and Ubiquity. WPA2+FT and forced MFP with a long password is a safe alternative. 802.11r use PMK push on WPA3 compared to WPA2, which was known to be problematic on older hardware.
802.11K/V is more suitable for campus and load balancing, tuning it based on RSSI and station metrics is very difficult, enterprise hardware rely on network traffic and air time.
[0] https://source.android.com/docs/core/connect/wifi-network-se...
What difference does the presence of legacy devices make? Is the intent to isolate them from modern devices from a network perspective? Then create a separate SSID on both 2.4 and 5 GHz for modern devices.
I can't think of any legitimate reason for split SSIDs anymore. Linux clients used to be pretty bad at preferring 5 over 2.4 GHz if RSSIs were both excellent but 2.4 was slightly better, but I haven't seen that in years.
Great write up, good information to share. This really is such an important next step for many people's wifi and it's documentation is pretty so-so.
With 802.11r on, things would disconnect for 60+ seconds before reconnecting. It was a constant frustration of "arrrrrrrggghhhh fucking connect damnit I'm standing a meter in front of the AP can't you fucking see it fuck fuck fuck just connect, it's right THERE, connect NOW, arghhh" and then it would completely disconnect (no wifi found) and then reconnect a minute later.
With 802.11r off things just roam smoothly. I guess the people who inventned the tech didn't test it thoroughly enough.
I haven't had luck with the roaming extensions; when I run them, some of my devices won't connect or won't stay connected and it's a pain to monitor. I guess I could run a different SSID with roaming enhancement, but effort.
I need my TV to rapidly switch APs in very heavy load wide area networks with thousands of devices while I'm cruising through the venue with my motorized couch and entertainment system.
Now I want to actually build that for GPN24 next week. Wouldn't use AndroidTV for that though.
Not sure if they finally got around to making the BSSID selection algorithm a bit smarter or whether all my access points just support active steering at this point, but I haven't seen this in the past couple of years.
Running Omada on my Windows Server was painful (doesn’t really run properly as a service, software updates are a chore), but since I moved it to run on Proxmox using a super simple LXC image (I maybe got terminology wrong here) it’s been very nice.
Supposedly I should have excellent roaming between the APs, but I’m not sure how to check. Certainly, walking from one end of the house the other while on a Teams or WhatsApp call on my phone has maybe only a super minimal amount of time that I might not hear the other person (sub second for sure, if at all), but mostly I don’t notice.
Not sure if roaming is actually the fix for this problem. For whatever reason my Ring cameras just love connecting to the worst and most far away AP in my house.
* https://www.omadanetworks.com/us/business-networking/omada-r...
I had to solve a similar issue for some crap IoT lights that would join the incorrect AP after a power cut every time.
> https://community.ui.com/questions/Lock-Client-to-Specific-A...
That can mean that the portable wifi speaker-widget (which itself doesn't need much bandwidth) might go from working fine on the back deck or well-enough about anywhere else in the yard, to not working at all outside.
May 26th 2026 · 6 min read · #cudy #hardware #homelab #networking #openwrt #usteer #wifi
A few months after writing up the Cudy AX3000 units and moving the house over to OpenWRT, I ended up revisiting the one bit I had deliberately waved away as “good enough”: roaming.
A real house, with a mix of phones, tablets, laptops and a few stubborn IoT things that insist on staying in 2016, has… issues. But they’re not always obvious, and given we’d both upgraded the 5GHz band and changed the locations of the access points, it took a while to figure out where the new rough spots were.
If you’re just tuning in, I have a hard split between a legacy 2.4GHz network and the modern 5GHz one. I already had client-managed roaming and basic handoff guidance, but now I added
usteer, 802.11k neighbour reports (becausehostapdwas not cooperating), and things are now pretty much perfect.
The long version is below, with anonymised data and enough detail for future me to remember why I did this.
[
](https://taoofmac.com/space/blog/2026/05/26/1730#why-i-did-not-merge-the-ssids)
The obvious advice for roaming is “use one SSID everywhere”, and that is often correct if you’re running Wi-Fi in an office, a public venue, or generally somewhere where you don’t have (or care about) legacy devices. It is also not what I did, because the 2.4GHz side needs to remain friendly to older and slightly terrible IoT devices, which means WPA2 compatibility and a conservative setup.
The 5GHz side is where the more modern clients live, and despite losing 5GHz access for a couple of things, I was happy to move it to WPA3. So this is what things look like from a high level:
[
](https://taoofmac.com/space/blog/2026/05/26/1730#user-feedback)
However, I got a few complaints that when moving about the house, iPhones, iPads and MacBooks would not switch to another AP. Since our flat is wrapped around a couple of elevator shafts and there are a few spots (like the kitchen) where tiling, pipes and tiny RF nuisances like fridges were prevalent, that sort of tended to happen a lot–and Apple devices are notorious for being opinionated about that base station they want to stick to.
The baseline seemed fine. All four APs had 802.11r/k/v-related options enabled. Fast Transition was also demonstrably happening–the AP logs had auth_alg=ft entries that showed fast transition was happening, I had installed wpad-mbedtls for “mesh” support, but roaming clearly needed to be improved.
And my setup meant it had to be improved within each band/SSID, not across bands. Cross-band roaming is the client’s job, and many clients are not especially good at it.
[
usteer](https://taoofmac.com/space/blog/2026/05/26/1730#adding)
But two things stood out:
rrm_nr_list was empty on every radio. In other words, even though 802.11k was enabled, hostapd was not exposing neighbour reports to clients, so… no real way to steer anything.So I installed usteer and its LuCI companion package on all four APs, enabled it, and left the initial configuration at defaults:
opkg update opkg install usteer luci-app-usteer /etc/init.d/usteer enable /etc/init.d/usteer restart
The default configuration is minimal: LAN gossip, syslog enabled, IPv6 disabled for the daemon (because, for reasons, I don’t trust our current ISP router to do anything reliably except act as an ONT), and a moderate debug level. That was enough for all APs to see one another and exchange client data, which is exactly what I wanted.
However, the 802.11k neighbour list wasn’t being populated. After poking through the OpenWRT forums, I realized the missing piece was static-neighbor-reports, which is one of those tiny OpenWRT packages that does exactly what it says and nothing more.
Each AP can generate its own 802.11k neighbour report element via:
ubus call hostapd.<iface> rrm_nr_get_own
But clients only get useful neighbour lists if each AP is told about the other APs. So I generated per-band lists and installed them per AP:
opkg install static-neighbor-reports /etc/init.d/static-neighbor-reports enable /etc/init.d/static-neighbor-reports restart
The important detail is that the reports are band-specific: 2.4GHz radios only advertise 2.4GHz peers, and 5GHz radios only advertise 5GHz peers. No cross-band mixing, because the two networks intentionally have different SSIDs and security settings.
After that, every AP had three neighbours per radio, usteer had AP/client state, and hostapd has explicit 802.11k neighbour data to hand to clients that ask for it.
[
](https://taoofmac.com/space/blog/2026/05/26/1730#what-changed)
The first comparison is a little boring, but useful. Here is the 2.4GHz SNR before and after the change (this, like the other charts here, was generated from Graphite data):
2.4GHz SNR over the week
2.4GHz SNR: pre-rollout vs latest
There is no miracle here. 2.4GHz remains 2.4GHz–crowded, noisy, full of junk devices and crowded by all my neighbors. Two of the APs improved or stayed roughly level, two got worse in the sampling window, and I have zero expectations about ever clearing this kind of congestion without moving to the countryside.
The 5GHz side is more encouraging, even if you do need to know when we were near which AP at what time when you look at active bitrates:
5GHz bitrate over the week
The interesting part, though, is that at least between two APs, there was a noticeable shift in usage–which seems to reflect where clients should be registered in practice:
5GHz bitrate: pre-rollout vs latest
But the best sanity check is the sticky-client view, because that is what started this in the first place:
Sticky-client check
The number of merely weak clients did not disappear–one extra client fell below -75dBm in the later sample–but the very weak clients went away. That is the bit I care about: the previous -90dBm-ish sticky associations were gone in the later check, which seems to indicate clients are not getting hung up on their previous AP and are indeed roaming.
[
](https://taoofmac.com/space/blog/2026/05/26/1730#caveats)
A single sample is not science, and Wi-Fi is a swamp of client decisions, radio noise and domestic entropy. I also saw one new Fast Transition log entry after the rollout:
FT: Missing required pairwise in pull response from a peer AP
That happened once in the latest check. It is not enough to call the setup broken, but it is worth watching–especially because SAE and FT have enough moving parts that I would rather trust logs than assumptions.
[
](https://taoofmac.com/space/blog/2026/05/26/1730#going-forward)
I will be keeping an eye on this over the next few weeks… somehow. I got an LLM to do the Graphite queries and chart scripting for me, and ain’t nobody got time to build dashboards only I would look at, but the metrics aren’t going to go away and the stable config lives in my local Gitea instance now, so there’s really no excuse not to do a spot check in a few months.
But I really like my Cudy APs. No cloud controller, no meshing, no mobile app and no secret sauce. Just OpenWRT, collectd/Graphite, and the odd ssh session to check configs.
That is still the main thing I like about this setup: when it gets weird, it gets weird in ways I can inspect.