BB.NSK.Network (bb_nsk v0.1.2)

Copy Markdown View Source

The robot's own access point, and the credentials for joining somebody else's.

A robot that has never been told about a network brings one up itself, and serves the whole web interface on it. So an unconfigured robot is a robot you can drive — join its network, open the address the panel is showing, and the drive page is there. Setting it up to join your own network is one page of that same interface rather than a separate mode it has to be talked into.

One radio, so one mode at a time

The RTL8723DU advertises both station and access point, but not at once, so giving the robot a network of its own takes the access point away. That makes a way back mandatory rather than a nicety, which is what BB.NSK.Network.Monitor is for: a station configuration that never reaches a connection falls back to the access point, so a mistyped passphrase costs a minute rather than a reflash.

Nothing is stored

The SSID and the passphrase are derived from the OTP application and the board's serial number. They survive a reboot without being written anywhere, they differ between two robots on the same bench — which matters in a room full of them — and they can be recovered from a robot that has forgotten everything else.

The passphrase is a hash rather than the serial number's own digits, because the serial is printed on things. It is drawn from an alphabet with no 0, 1, i, l or o in it, since somebody has to read it off a 400x300 e-paper panel and type it into a phone.

Where the credentials live

VintageNet persists what it is told to /root/vintage_net, and a persisted configuration beats the one compiled into the firmware — so joining a network outlives mix upload. It does not outlive a complete burn, which wipes the application partition, so a freshly flashed robot is an access point again.

The access point itself is applied with persist: false. It is the state the robot falls back to rather than a state it is ever put into, so persisting it would let a bad afternoon become the permanent configuration.

Summary

Functions

The configuration for the robot's own access point.

The networks the last scan found, strongest first.

Where the robot answers on its own network.

Whether vintage_net is here to be driven.

The network the robot is set up to join, or nil when it isn't set up for one.

Whether the interface has reached a network, rather than merely being set up for one.

Forget the configured network and return to the access point.

The interface all of this configures.

Join a network, and remember it.

Which of the two the robot is currently set up for.

Which mode a particular vintage_net configuration describes.

The passphrase for the robot's own network.

Ask the radio to look for networks.

The name of the robot's own network.

Bring up the robot's own access point.

The configuration for joining somebody else's network.

The address to open once you have joined the robot's network.

Functions

access_point()

@spec access_point() :: map()

The configuration for the robot's own access point.

access_points()

@spec access_points() :: [map()]

The networks the last scan found, strongest first.

address()

@spec address() :: :inet.ip4_address()

Where the robot answers on its own network.

available?()

@spec available?() :: boolean()

Whether vintage_net is here to be driven.

False on the host, where the setup page is built and looked at but no radio exists, so everything that reaches for it says so rather than raising UndefinedFunctionError into a LiveView.

configured_ssid()

@spec configured_ssid() :: String.t() | nil

The network the robot is set up to join, or nil when it isn't set up for one.

connected?()

@spec connected?() :: boolean()

Whether the interface has reached a network, rather than merely being set up for one.

forget()

@spec forget() :: :ok | {:error, term()}

Forget the configured network and return to the access point.

interface()

@spec interface() :: String.t()

The interface all of this configures.

join(ssid, passphrase)

@spec join(String.t(), String.t()) :: :ok | {:error, term()}

Join a network, and remember it.

The access point goes away as this takes effect, so whoever called this over it will lose their connection whether or not the new credentials are any good.

mode()

@spec mode() :: :access_point | :station | :unconfigured

Which of the two the robot is currently set up for.

:unconfigured is a robot that has never been told anything, which is the state a freshly burned one is in until BB.NSK.Network.Monitor brings the access point up.

A VintageNetWiFi interface with no networks counts as unconfigured, which is what it is: an interface that has been declared and never told what to join. mix nerves.new writes exactly that — {"wlan0", %{type: VintageNetWiFi}} — into config/target.exs, and reading it as :station costs a freshly burned robot a full connection timeout of doing nothing before it gives up on a network it was never told about and brings up its own.

mode(arg1)

@spec mode(map() | nil) :: :access_point | :station | :unconfigured

Which mode a particular vintage_net configuration describes.

Split out from mode/0 so it can be reasoned about without a radio.

passphrase()

@spec passphrase() :: String.t()

The passphrase for the robot's own network.

Derived from the serial number, so it is stable across reboots and different on every robot, and shown on the panel because that is the only place anybody can read it from.

scan()

@spec scan() :: :ok | {:error, term()}

Ask the radio to look for networks.

Results don't come back from this — they arrive asynchronously and are read with access_points/0.

Scanning works while the robot is its own access point, confirmed on hardware 2026-09-17. That is not a given: one radio is doing both jobs, and a chip that refused would have been within its rights. Nothing above this depends on it anyway — the setup page takes a typed SSID, because a hidden network never appears in a scan whatever the radio can do.

ssid()

@spec ssid() :: String.t()

The name of the robot's own network.

The robot's name, then enough of the serial number to tell two robots apart — which a workshop full of them very much needs.

The name comes from BB.NSK.name/0, which bb_nsk.install sets to the host application. It cannot be derived here: this module's own OTP application is :bb_nsk, so every robot on the bench would advertise the same one.

start_access_point()

@spec start_access_point() :: :ok | {:error, term()}

Bring up the robot's own access point.

Deliberately not persisted — see the moduledoc.

station(ssid, passphrase)

@spec station(String.t(), String.t()) :: {:ok, map()} | {:error, term()}

The configuration for joining somebody else's network.

An empty passphrase means an open network, which plenty of cafes and a few workshops still are.

url()

@spec url() :: String.t()

The address to open once you have joined the robot's network.