Table of contents
switch-wifi: Separating network measurements from the UI
switch-wifi is a Switch network dashboard with a Borealis interface. It separates portable metric calculations from the console application so that host tests can exercise the core without requiring the Switch toolchain.
- Languages
- 3Code breakdown
- Documented components
- 2Explore modules
- Source documents
- 2Read the instructions
Repository languagesC++ 86.58%3
Shares calculated from GitHub Linguist bytes.
| Language | % | Bytes |
|---|---|---|
| C++ | 61,640 | |
| CMake | 5,914 | |
| Python | 3,639 |
1. Overview
1.1. Why it exists
Native C++ network configuration tool interfacing directly with Switch horizon OS wlan services.
1.2. Best fit
Fast headless Wi-Fi network credential provisioning on Switch.
2. Use cases
Nintendo Switch homebrew app that shows Wi-Fi state, benchmarks the HTTPS path and reads Bluetooth status.
- Check the Wi-Fi link
- Switch ownerHorizon NIFM / BTM
The Dashboard tab reads link type, SSID, IPv4, gateway, DNS and 0-3 signal bars from NIFM through NetworkService, with RSSI from legacy wlan:inf only where firmware exposes it.
- Benchmark the connection
- Switch ownerHTTPS speed server
SpeedTestEngine runs 8 HTTPS latency probes, then a 25 MiB download and a 10 MiB zero-filled upload with libcurl against speed.cloudflare.com by default, reporting median, P95 and jitter.
- Review saved networks
- Switch ownerHorizon NIFM / BTM
The Networks tab lists basic saved-profile metadata and Horizon's allowed channels, with channel-overlap math from core/metrics; credentials are never read for display.
- Scan Bluetooth state
- Switch ownerHorizon NIFM / BTM
BluetoothService reads BTM radio state and device counts, and runs a bounded passive BLE scan that returns at most ten unique addresses.
- Export a diagnostics report
- Switch owner
The About tab writes sdmc:/switch-wifi-diagnostics.txt with the app version, compatibility target, network details and Bluetooth counts, without credentials.
2.1. Scenario: Compare two router channels on a Switch
- Build build-switch/switch-wifi.nro in the devkitpro/devkita64 container and check it with python3 scripts/verify_nro.py.
- Copy the NRO to the SD card and launch it from the homebrew menu.
- Open the Dashboard tab to confirm the Switch is on Wi-Fi and note the signal bars.
- Run the Benchmark tab and read median latency, P95, jitter, download and upload speed.
- Change the router channel, run the benchmark again and compare the history graphs.
- Export sdmc:/switch-wifi-diagnostics.txt from the About tab to keep the result.
Files readREADME.mddocs/ARCHITECTURE.mddocs/BENCHMARKING.mdinclude/switch_wifi/network/speed_test.hppsrc/ui/main_tabs.cppscripts/verify_nro.py
3. Getting started
These commands come from the repository documentation. They were not run during this review.
git clone --recursive https://github.com/antoinebou12/switch-wifi.git
cd switch-wifipython3 scripts/verify_nro.py build-switch/switch-wifi.nrocmake -S . -B build-switch -DPLATFORM_SWITCH=ON -DUSE_GLFW=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build-switch --target switch-wifi.nro --paralleldocker run --rm \
-v "$PWD:/work" -w /work \
devkitpro/devkita64:latest \
bash -lc 'cmake -S . -B build-switch \
-DPLATFORM_SWITCH=ON \
-DUSE_GLFW=ON \
-DCMAKE_BUILD_TYPE=Release && \
cmake --build build-switch --target switch-wifi.nro --parallel'4. Repository references
Excerpts from the repository’s guides, in their original language. Links open the inspected source revision.
Prerequisites and first run
4.1. Native devkitPro installation
README.md
Configuration and usage
4.2. Wi-Fi environment
README.md
- Basic saved-network metadata without requesting full saved credential profiles.
- Authentication/encryption labels.
- Horizon allowed-channel query on HOS 6.0.0+.
- 2.4 GHz and 5 GHz channel/frequency helpers.
- Tested channel-overlap and contention-proxy math.
- Nearby AP scan kept behind a hardware-verification boundary until opaque Horizon AP records can be decoded safely across firmware versions.
- RF noise floor and SNR shown as unavailable rather than inferred from unrelated values.
Development and contributions
4.3. Unit tests
README.md
The platform-independent metric, history, channel and benchmark-statistics code is tested on the host. The suite is split into:
- switch_wifi_metrics_tests;
- switch_wifi_history_tests;
- switch_wifi_benchmark_tests. Sanitizer build:
4.4. Development rules
CONTRIBUTING.md
- Keep Horizon/libnx IPC out of the Borealis UI layer.
- Add a host-side test for calculations and parsers whenever possible.
- Gate firmware-dependent behavior explicitly.
- Do not add code that retrieves Wi-Fi passphrases just to display profile information.
- Do not call a derived AP-overlap value “noise floor”, “SNR”, or “channel utilization” unless the underlying metric is actually measured.
- Keep Bluetooth diagnostics read-only.
5. Features and components 2
- Portable metric core
- CMake creates switch_wifi_core from src/core/metrics.cpp. Host test targets link this library separately from Borealis. Tests cover metrics, history and benchmark behavior, with optional sanitizers.
- Console integration
- The application target collects the src modules and links Borealis, libnx platform libraries and libcurl. Network activity and the UI therefore depend on platform services, while the numeric core can stay independent.
6. Architecture
Expand a component to follow its connections, or explore the full diagram.
Network samples
Portable metrics core
History and benchmark results
Borealis dashboard
Host unit tests
Mermaid Interactive diagram
System overview of the components and connections verified in the repository.

flowchart LR
n0["Network samples"]
n1["Portable metrics core"]
n2["History and benchmark results"]
n3["Borealis dashboard"]
n4["Host unit tests"]
n0 --> n1
n1 --> n2
n2 --> n3
n4 --> n1
Text version of the diagram
- Network samples → Portable metrics core
- Portable metrics core → History and benchmark results
- History and benchmark results → Borealis dashboard
- Host unit tests → Portable metrics core
Follow one path through the code
Start with the core library target and its tests, then inspect the network module feeding its samples. An HTTPS throughput measurement includes the chosen remote server and network path; it is not a direct reading of Wi-Fi radio capacity.
7. Technical dossier
Built from the source at commit 8ff883f (30 files). Each item comes from a pattern match in the code, so open the linked file before relying on it.
7.1. Layers and modules
Folders are sorted into layers by name, from the user interface down to data access. Because the sorting uses names only, the diagram says nothing about calls between layers.
Mermaid Layered view
flowchart TB
subgraph L_presentation["Presentation"]
direction TB
presentation_0["switch_wifi/ui<br/>3 files"]
presentation_1["ui<br/>3 files"]
end
subgraph L_interface["Entry points and API"]
direction TB
interface_0["src<br/>1 file"]
end
subgraph L_application["Application services"]
direction TB
application_0["scripts<br/>3 files"]
application_1["bluetooth<br/>1 file"]
application_2["switch_wifi/bluetooth<br/>1 file"]
end
subgraph L_domain["Domain and business rules"]
direction TB
domain_0["core<br/>1 file"]
domain_1["switch_wifi/core<br/>1 file"]
end
subgraph L_infrastructure["Data and infrastructure"]
direction TB
infrastructure_0["network<br/>1 file"]
infrastructure_1["switch_wifi/network<br/>1 file"]
end
subgraph L_core["Core source"]
direction TB
core_0["switch_wifi<br/>1 file"]
end
subgraph L_tests["Tests"]
direction TB
tests_0["tests<br/>3 files"]
tests_1["network<br/>1 file"]
tests_2["switch_wifi<br/>1 file"]
end
subgraph L_hosting["Build, CI and hosting"]
direction TB
hosting_0[".github<br/>8 files"]
hosting_1["(root)<br/>1 file"]
hosting_2["library<br/>1 file"]
end
L_presentation ~~~ L_interface
L_interface ~~~ L_application
L_application ~~~ L_domain
L_domain ~~~ L_infrastructure
L_infrastructure ~~~ L_core
L_core ~~~ L_tests
L_core ~~~ L_hosting
| Module | Layer | Files | Types | Extensions |
|---|---|---|---|---|
scripts | Application services | 3 | 0 | .py |
switch_wifi/ui | Presentation | 3 | 3 | .hpp |
ui | Presentation | 3 | 6 | .cpp |
bluetooth | Application services | 1 | 0 | .cpp |
core | Domain and business rules | 1 | 0 | .cpp |
network | Data and infrastructure | 1 | 1 | .cpp |
src | Entry points and API | 1 | 0 | .cpp |
switch_wifi | Core source | 1 | 0 | .hpp |
switch_wifi/bluetooth | Application services | 1 | 1 | .hpp |
switch_wifi/core | Domain and business rules | 1 | 10 | .hpp |
switch_wifi/network | Data and infrastructure | 1 | 1 | .hpp |
7.2. C4 model
From system context to code declarations. Each level reflects the available source evidence.
UML 1 · System context
The widest C4 view: who uses the project and which outside systems and data stores it relies on. Rendered with uml-mcp.
PlantUML source
@startuml
!include <C4/C4_Context>
LAYOUT_LEFT_RIGHT()
System(SYS, "switch-wifi", "switch-wifi is a Switch network dashboard with a Borealis interface")
@enduml
UML 2 · Containers
One level closer: the parts that run separately inside the project, their main language and how calls flow between them. Rendered with uml-mcp.
PlantUML source
@startuml
!include <C4/C4_Container>
LAYOUT_LEFT_RIGHT()
System_Boundary(SYS, "switch-wifi") {
Container(APP, "switch-wifi", "C++")
}
@enduml
UML 3 · Components and layers
Modules grouped by source-layer classification. Boundaries show organization, not runtime isolation or call relationships. Rendered with uml-mcp.
PlantUML source
@startuml
!include <C4/C4_Component>
LAYOUT_TOP_DOWN()
System_Boundary(SYS, "switch-wifi") {
Boundary(L0, "Presentation", "layer") {
Component(M0_0, "switch_wifi/ui", "C++", "3 files")
Component(M0_1, "ui", "C++", "3 files")
}
Boundary(L1, "Entry points and API", "layer") {
Component(M1_0, "src", "C++", "1 files")
}
Boundary(L2, "Application services", "layer") {
Component(M2_0, "scripts", "Python", "3 files")
Component(M2_1, "bluetooth", "C++", "1 files")
Component(M2_2, "switch_wifi/bluetooth", "C++", "1 files")
}
Boundary(L3, "Domain and business rules", "layer") {
Component(M3_0, "core", "C++", "1 files")
Component(M3_1, "switch_wifi/core", "C++", "1 files")
}
Boundary(L4, "Data and infrastructure", "layer") {
Component(M4_0, "network", "C++", "1 files")
Component(M4_1, "switch_wifi/network", "C++", "1 files")
}
Boundary(L5, "Core source", "layer") {
Component(M5_0, "switch_wifi", "C++", "1 files")
}
Boundary(L6, "Tests", "layer") {
Component(M6_0, "tests", "C++", "3 files")
Component(M6_1, "network", "C++", "1 files")
Component(M6_2, "switch_wifi", "C++", "1 files")
}
Boundary(L7, "Build, CI and hosting", "layer") {
Component(M7_0, ".github", ".yml, .txt, .md", "8 files")
Component(M7_1, "(root)", ".txt", "1 files")
Component(M7_2, "library", ".txt", "1 files")
}
}
@enduml
UML 4 · Code
Selected declarations, fields and methods grouped by source file. No inheritance or call edges are inferred. Rendered with uml-mcp.
PlantUML source
@startuml
skinparam backgroundColor transparent
skinparam shadowing false
skinparam roundCorner 10
skinparam defaultFontName Arial
skinparam defaultFontSize 13
skinparam ArrowColor #4e6070
skinparam ArrowFontColor #3d4f5f
skinparam titleFontSize 14
skinparam actorBorderColor #006a62
skinparam actorBackgroundColor #e9f3f4
skinparam usecaseBorderColor #006a62
skinparam usecaseBackgroundColor #e9f3f4
skinparam componentBorderColor #006a62
skinparam componentBackgroundColor #e9f3f4
skinparam interfaceBorderColor #006a62
skinparam interfaceBackgroundColor #ffffff
skinparam nodeBorderColor #4e6070
skinparam nodeBackgroundColor #f4f8fb
skinparam databaseBorderColor #006a62
skinparam databaseBackgroundColor #ffffff
skinparam cloudBorderColor #4e6070
skinparam cloudBackgroundColor #f0f5fa
skinparam artifactBorderColor #006a62
skinparam artifactBackgroundColor #ffffff
skinparam rectangleBorderColor #b0c2d3
skinparam activityBorderColor #006a62
skinparam activityBackgroundColor #e9f3f4
skinparam partitionBorderColor #b0c2d3
skinparam stereotypeCBackgroundColor #ffffff
top to bottom direction
hide empty members
package "switch-wifi" {
package "include/switch_wifi/core/metrics.hpp" as F0 {
class "RingHistory" as T0_0 {
push()
clear()
size()
capacity()
empty()
values()
}
class "ConnectionSnapshot" as T0_1 {
std
ip
}
class "IpConfig" as T0_2 {
std
ip
}
class "BluetoothDevice" as T0_3 {
std
}
class "BluetoothScanResult" as T0_4 {
std
}
class "BluetoothSnapshot" as T0_5 {
std
}
class "LatencyStats" as T0_6 {
std
}
class "NearbyWifiNetwork" as T0_7 {
std
}
class "SavedNetwork" as T0_8 {
std
}
class "SpeedSample" as T0_9 {
std
}
}
package "src/network/network_service.cpp" as F1 {
class "NifmBasicProfileWire" as T1_0 {
uuid
networkName
profileType
connectionType
ssidLen
ssid
authentication
encryption
}
}
}
@enduml
7.3. Public interface
Outside code can use everything listed here. The project also declares 22 public types.
Entry points
src/main.cppentry file
Public headers 8
include/switch_wifi/bluetooth/bluetooth_service.hpp include/switch_wifi/core/metrics.hpp include/switch_wifi/network/network_service.hpp include/switch_wifi/network/speed_test.hpp include/switch_wifi/ui/main_tabs.hpp include/switch_wifi/ui/signal_shader.hpp include/switch_wifi/ui/sparkline.hpp include/switch_wifi/version.hpp
7.4. Private interface
These names are internal by the language’s conventions: a leading underscore, private, static or pub(crate).
- anonymous namespace
- 3
namespace { }src/bluetooth/bluetooth_service.cpp:13namespace { }src/network/network_service.cpp:12namespace { }src/ui/main_tabs.cpp:23
7.5. Services
Service 2
NetworkServiceshutdown, snapshot, savedNetworks and 2 moreBluetoothServicesnapshot, passiveScan
7.6. Entities and data model 10
Mermaid Entity class diagram
classDiagram
direction LR
class E0["NifmBasicProfileWire"] {
+Uuid uuid
+char networkName
+u8 profileType
+u8 connectionType
+u8 ssidLen
+char ssid
}
class E1["ConnectionSnapshot"] {
+:string std
+IpConfig ip
}
class E2["IpConfig"] {
+:string std
+IpConfig ip
}
class E3["BluetoothDevice"] {
+:string std
}
class E4["BluetoothScanResult"] {
+:string std
}
class E5["BluetoothSnapshot"] {
+:string std
}
class E6["LatencyStats"] {
+:vector std
}
class E7["NearbyWifiNetwork"] {
+:string std
}
class E8["SavedNetwork"] {
+:size_t std
}
class E9["SpeedSample"] {
+:string std
}
E1 --> E2 : ip
E6 --> E3 : std
An arrow links a field to the entity its type names.
| Entity | Kind | First fields |
|---|---|---|
NifmBasicProfileWire | Data struct | uuid, networkName, profileType, connectionType, ssidLen, ssid, authentication, encryption |
ConnectionSnapshot | Data struct | std, ip |
IpConfig | Data struct | std, ip |
BluetoothDevice | Data struct | std |
BluetoothScanResult | Data struct | std |
BluetoothSnapshot | Data struct | std |
LatencyStats | Data struct | std |
NearbyWifiNetwork | Data struct | std |
Show 2 more entities
SavedNetwork | Data struct | std |
|---|---|---|
SpeedSample | Data struct | std |
7.7. Business logic
Once UI code and the services and entities above are set aside, these classes have the most methods.
RingHistoryDomain and business rules, 6 methodspush, clear, size, capacity, empty, values
7.8. External integrations
HTTP clients
7.9. Stack and dependencies
External dependencies
2 declared packages (2 runtime, 0 development) in CMakeLists.txt.
Package table 2
| Package | Version | Scope | Purpose | Registry |
|---|---|---|---|---|
Threads | — | runtime | — | C/C++ (CMake) |
PkgConfig | — | runtime | — | C/C++ (CMake) |
7.10. CI/CD pipeline
3 GitHub Actions workflows, 8 jobs. Each box is a job; double-bordered boxes publish or deploy, and arrows follow needs.
Mermaid Pipeline flow
flowchart LR
subgraph wf0["CI"]
direction LR
wf0_host_tests["Host tests<br/>ubuntu-latest"]
wf0_sanitizers["ASan + UBSan<br/>ubuntu-latest"]
wf0_switch_nro["NRO<br/>ubuntu-latest"]
wf0_switch_nro_libnx_master[["NRO (current libnx master)<br/>ubuntu-latest"]]
end
subgraph wf1["Publish dist NRO"]
direction LR
wf1_build_and_publish["Build and publish dist/switch-wifi.nro<br/>ubuntu-latest"]
end
subgraph wf2["Release"]
direction LR
wf2_validate[["Validate release<br/>ubuntu-latest"]]
wf2_build_nro[["Build release NRO<br/>ubuntu-latest"]]
wf2_publish[["Publish GitHub Release<br/>ubuntu-latest"]]
end
t0(["push · pull request · manual run"])
t1(["push · manual run"])
d0[("GitHub Releases")]
t0 --> wf0_host_tests
t0 --> wf0_sanitizers
t0 --> wf0_switch_nro
t0 --> wf0_switch_nro_libnx_master
t1 --> wf1_build_and_publish
t1 --> wf2_validate
wf2_validate --> wf2_build_nro
wf2_validate --> wf2_publish
wf2_build_nro --> wf2_publish
wf2_publish -.-> d0
| Workflow | Triggers | Jobs | Publishes to |
|---|---|---|---|
CI.github/workflows/ci.yml | push, pull request, manual run |
| — |
Publish dist NRO.github/workflows/publish-dist.yml | push, manual run |
| — |
Release.github/workflows/release.yml | push, manual run |
| GitHub Releases |
7.11. Deployment
Mermaid Deployment diagram
flowchart LR
repo[("GitHub repository")]
ci[["GitHub Actions<br/>3 workflows"]]
pub_GitHub_Releases[("GitHub Releases")]
repo -->|"push or pull request"| ci
ci -->|"publishes"| pub_GitHub_Releases
Where it runs
Published to
7.12. Security
Supply chain
7.13. Testing
- Test files
- 5
- Source files
- 17
- Unit tests
- 4
- Benchmark tests
- 1
No measured coverage report in the analyzed sources.
Where the tests are
tests 3 include/switch_wifi/network 1 src/network 1
Continuous integration
UML CI pipeline
The main GitHub Actions workflow runs these steps. Jobs that run in parallel sit side by side. Rendered with uml-mcp.
PlantUML source
@startuml
skinparam backgroundColor transparent
skinparam shadowing false
skinparam roundCorner 10
skinparam defaultFontName Arial
skinparam defaultFontSize 13
skinparam ArrowColor #4e6070
skinparam ArrowFontColor #3d4f5f
skinparam titleFontSize 14
skinparam actorBorderColor #006a62
skinparam actorBackgroundColor #e9f3f4
skinparam usecaseBorderColor #006a62
skinparam usecaseBackgroundColor #e9f3f4
skinparam componentBorderColor #006a62
skinparam componentBackgroundColor #e9f3f4
skinparam interfaceBorderColor #006a62
skinparam interfaceBackgroundColor #ffffff
skinparam nodeBorderColor #4e6070
skinparam nodeBackgroundColor #f4f8fb
skinparam databaseBorderColor #006a62
skinparam databaseBackgroundColor #ffffff
skinparam cloudBorderColor #4e6070
skinparam cloudBackgroundColor #f0f5fa
skinparam artifactBorderColor #006a62
skinparam artifactBackgroundColor #ffffff
skinparam rectangleBorderColor #b0c2d3
skinparam activityBorderColor #006a62
skinparam activityBackgroundColor #e9f3f4
skinparam partitionBorderColor #b0c2d3
skinparam stereotypeCBackgroundColor #ffffff
title CI
start
fork
partition "Host tests" {
:actions/checkout;
:Configure;
:Build;
:Unit tests;
}
fork again
partition "ASan + UBSan" {
:actions/checkout;
:Configure sanitizers;
:Build;
:Unit tests;
}
fork again
partition "NRO" {
:stable-image;
:latest-image;
:Checkout with Borealis;
:Toolchain inventory;
:Configure Switch build;
:Build NRO;
:Verify NRO structure;
:Build checksum;
}
fork again
partition "NRO (current libnx master)" {
:Checkout with Borealis;
:Build current libnx master;
:Configure Switch build;
:Build and verify NRO;
:Upload current-libnx NRO;
}
end fork
stop
@enduml
Nothing found for: Database, Monitoring.
8. Deployment and operations
The ports, storage, and startup relationships below come from the repository configuration. depends_on records startup order, not service readiness.
9. Types and file structure
27 declared types, with 12 in the base-relationship overview.
Type diagram
classDiagram
direction TB
class tec54ffc64505["BluetoothService"] {
snapshot()
passiveScan()
}
class t17625c2744a3["IpConfig"] {
<<struct>>
}
class t7988c53361f7["ConnectionSnapshot"] {
<<struct>>
}
class t4a309fd00cd7["SavedNetwork"] {
<<struct>>
}
class tc583422c7f0f["NearbyWifiNetwork"] {
<<struct>>
}
class tff9ef4d0d29e["LatencyStats"] {
<<struct>>
}
class t2488d1629c51["SpeedSample"] {
<<struct>>
}
class t60189b23c9ad["BluetoothDevice"] {
<<struct>>
}
class t28065228e336["BluetoothSnapshot"] {
<<struct>>
}
class t6f2d7c0133a5["BluetoothScanResult"] {
<<struct>>
}
class tcd996a2f6785["RingHistory"] {
RingHistory()
push()
clear()
size()
}
class tba04fe8b8282["NetworkService"] {
initialize()
shutdown()
snapshot()
savedNetworks()
}
Unresolved bases remain labelled; full signatures are not reconstructed.
10. Reference
25 / 25 eligible files read (51 in the tree); 0 oversized, 0 failed reads and 3 syntax diagnostics.
What the scan covers
Known dependency/build folders, credential files and binaries are excluded. The scan covers READMEs, recognized manifests and parser-supported source files.
Linguist statistics cover the whole repository and may include other languages.
12. Files to read 3
13. Glossary
Repository
The versioned source files, configuration, and history of a project.
README
The repository’s entry document, usually covering its purpose and initial setup.
Dependency
A library, tool, or service that another component needs to build or run.
HTTP
A request-and-response protocol used by browsers and web services. A method, path, headers, and optional body describe a request.
Published port
A host port mapped to a service’s container port. Publishing a port can expose that service beyond its container network.
Build
The steps that turn source files and dependencies into a runnable program or distributable artifact.
14. References
- antoinebou12. (2026, November 5). switch-wifi. [Software repository] github.com.
- antoinebou12. (2026, November 5). switch-wifi: README. [Repository documentation] github.com.
- antoinebou12. (2026, November 5). CMakeLists.txt. [Source file] github.com.
- antoinebou12. (2026, November 5). src/core/metrics.cpp. [Source file] github.com.
- antoinebou12. (2026, November 5). src/main.cpp. [Source file] github.com.
- Boucher, A. (2026, October 23). switch-wifi: Separating network measurements from the UI. [Project article] antoineboucher.info.
Sources without a publication date show the date they were last checked, November 5, 2026. File links retain the inspected revision where available.