kodeOS internals

Settings screen

The control centre that comes down over the face, the Settings page and the pages behind it, and the curtain that carries both.

What it is

The settings screen is a control centre. It switches the radios, shows the battery, turns the board off and sets the brightness and the volume. Three circles stand in the top row (Wi-Fi, Bluetooth and Settings). Under Wi-Fi stands the battery, under Settings stands Turn off, and the brightness level and the volume level run under them all.

  Wi-Fi     Bluetooth     Settings
  Battery                 Turn off
  [ brightness level ------------ ]
  [ volume level --------------- ]

It comes down over the face like a curtain. Open it with the pad's up, TOP held or a finger dragging down from anywhere on the face. Close it with BOT or a swipe up, and it goes back to the face.

A circle that has more to say opens a page over the screen. Settings opens one flat list: the radios, the battery, the time and its zone, the settings that change where they stand, the two supplies of the expansion header, Self-test and About. The battery circle opens the Battery page alone. Every page grows out of the middle of the glass and goes back into it.

How it is connected

flowchart LR
power["AO_Power<br/>POWER_EVT_SIG"] --> gui["AO_Gui<br/>gui_cc_paint()"]
radio["AO_Radio<br/>WIFI_STATE_SIG<br/>BLE_STATE_SIG"] --> gui
display["AO_Display<br/>DISPLAY_STATE_SIG"] --> gui
gui --> cc["control centre<br/>circles, levels"]
cc -- "tap or TOP" --> act["gui_settings_act()"]
cc -- "Settings, the battery, or a hold" --> page["a page<br/>on the sheet"]
class power ao
class radio ao
class display ao
class gui ao
What colours the circles

Gui_settings is a child of Gui_running, like the face and Apps, so the tick, the touch feed and the flash-write takeover work here with no extra code. kgui_screen_settings.c builds the screen from the kit. The composition follows kshell.

The controls

PartWhereWhat it shows
Three circlesy 54 (KGUI_CC_Y0), x 43, 161 and 279, each 88 pxWi-Fi, Bluetooth and Settings (a gear)
Batteryy 172 (KGUI_CC_Y1), x 43, under Wi-FiA circle in the off grey filled to the charge with a liquid whose surface is a wave
Turn offy 172, x 279, under SettingsA circle in the off grey with a red ring and a red power glyph
Brightness levelx 18, y 290 (KGUI_CC_LEVEL_Y), 374 x 72 pxA capsule whose Light Grey fill grows from 72 px to the whole width, with a sun glyph in four steps and the figure
Volume levelx 18, y 378 (KGUI_CC_VOLUME_Y), 374 x 72 pxThe same capsule, with a speaker glyph in four steps: struck through at 0 %, then one, two and three waves

The cursor and what a control does

The pad's left and right walk the top row and stop at its ends, and on the second row they go from the battery to Turn off and back. Down from Wi-Fi goes to the battery, down from Settings goes to Turn off, down from Bluetooth, the battery or Turn off goes to the brightness level, and down from the brightness level goes to the volume level. Up is the same walk back: from the volume to the brightness, from the brightness to Turn off, from Turn off to Settings and from the battery to Wi-Fi. Nothing wraps. On a level, left and right step it by 5 % (GUI_LEVEL_STEP).

A control acts on the release of TOP, or on a tap. gui_settings_act() is the one function that acts:

ControlTOP released, or a tapTOP held, or a finger held 0.6 s
Wi-FiSwitches the radio on or offOpens the Wi-Fi page
BluetoothSwitches the radio on or offOpens the Bluetooth page
SettingsOpens the Settings pageNothing
BatteryOpens the Battery page alone over the circlesNothing
Turn offAsks "Turn off?"Nothing
A levelNothingNothing

The pages

The Settings circle opens a page titled Settings, one flat list (GUI_SHEET_MORE, named more by gui_info, filled by gui_more_fill()). Each row is a disc, a name and a value. Some rows open a page, and the others change where they stand: TOP or a tap steps the value, and nothing opens.

RowValueWhat TOP or a tap does
Wi-FiThe state, or the network the board is onOpens the Wi-Fi page
BluetoothThe stateOpens the Bluetooth page
BatteryThe charge and the stateOpens the Battery page
TimeThe local time, hh:mm, following the clock while the list is openOpens the Time page
Time zoneThe zoneOpens the list of zones
Sleep afterThe kody_sleep timeSteps to the next time
Kody's sceneThe scene's English name, or AutoSteps to the next scene
VibrationOff, Light or Normal (haptic_ui)Steps to the next
While chargingStays awake or Sleeps (usb_awake), a switchFlips it
Header 3.3 VOn or off, a switchAsks AO_Radio to switch the header's 3.3 V supply (exp_power_request()), answered by EXP_POWER_SIG. Not stored
Header 5 VOn or off, a switch. Battery only while a cable is inAsks AO_Power for the 5 V boost (power_ctrl_request(POWER_CTRL_BOOST)), answered by POWER_CTRL_SIG. Not stored. Refused with a thud, before asking, while a cable is in
Self-testLast run: pass, Last run: <n> to look at, or Never runOpens the Self-test page; it does not run until you start it. TBD: the row comes back before About on the branch fix/kgui-selftest-and-pack, not yet merged
AboutThe serialOpens About

One switch of each supply runs at a time. A refusal is a thud and a log line. The brightness and the volume are on the control centre, and the auto-lock time is set with config key="lock". The Kody page has no row in this list; its pack updates with assets_update (internals/gui/kody-pack).

The battery circle opens the same Battery page alone over the circles (bat_from_cc), and the way back from it is the control centre.

A page is a sheet in kshell's dress: Wi-Fi, Bluetooth, Settings, Screen, Battery, About, Time zone, Auto-lock, Self-test and Kody. The page has no header disc and no subtitle. Its name stands centred at y 30 in Outfit Bold 28, and its body runs from y 68 to y 458. Wi-Fi and Bluetooth have a header instead of a name: a disc of 56 px with the radio's glyph, Kody Blue while the radio is on and dark grey while off, then the radio's name and a grey line under it. Disc and words are measured and centred together at y 26 (KGUI_PAGE_HDR_Y), a name too long for 240 px ends in dots, and their body starts at y 100 (KGUI_PAGE_HDR_BODY_Y).

What each page holds is on internals/gui/sheets.

The factory reset

About ends with a red row, Factory reset, under Update, with the line Erase apps and settings. TOP released or a tap on it opens a question. Nothing is asked of the board until the question is answered. The reset is the one the console's factory_reset runs (internals/command-set) and the one a unit that passes the E5 test runs before it switches itself off (sdk/kaos/test-dot). From the settings screen it ends in a restart, so the welcome app greets the next boot.

The question is a system card on the frame of Turn off (reset in gui_info). It shows a red chip glyph, the title Factory reset?, the line Apps and settings go, three lines on what else goes and what stays, and one red Reset pill:

So do networks and paired devices. The welcome app, the name and files stay.

Like every system card it ignores all input for 700 ms after it begins to rise. Then:

The wait is a locked card (resetting). The edge turns in red, the title reads Factory reset, the line Erasing and the detail The Kode restarts into the welcome app. There is nothing to press.

When the reset works AO_Boot restarts the board and the wait stays on the glass until it does. When it does not, the card buzzes, the edge is cut, the second line reads Not done and the third says why. The card is not locked any more, and BOT or HOME closes it. It has no pill.

The third lineWhy
The welcome app is not on this KodeThe welcome app is neither installed nor on the card. Nothing was erased
Busy: try again in a momentAO_AppMgr is writing, a reset or a restart is already on its way, or the request could not be posted. Nothing was changed
Stopped at <step>A step failed: find, install, apps, link, wipe, nvs or request. What the earlier steps erased stays erased

gui_info prints reset: asked= answered= step= status= while the wait is up. kgui_reset_why() writes the third line from the answer of AO_Boot (FactoryResetRspEvt).

The curtain

The settings screen does not slide in as a screen load. It comes down over the face, and goes up again, as a curtain: two still pictures on a screen of its own. AO_Gui places the pictures, and nothing else is drawn while the curtain moves. The page that moves is a kui_drag_t of the kit (sdk/kui). The face is a curtain of the same make, and it comes down over Apps and over this screen (internals/gui/face).

The pictures. A curtain has a picture below and a picture of the panel:

A black dim grows from nothing to 40 % (KGUI_CURTAIN_DIM_OPA) between the two as the panel comes down, and fades as it goes back up. The face curtain has no dim.

Before a picture is taken, the entry work that needs only what AO_Gui holds runs: the circles are painted, the ring stands on its control, the time is painted. The reads and the requests stay with the landing, so no circle changes colour at the landing. AO_Gui also asks the owners once, when it comes up, for the states the screen shows. The copy of a screen comes from the front frame buffer of the panel. When a picture cannot be taken, because PSRAM refused a buffer or the render failed, the door goes through with a cut and the log has a warning.

The motion. The page is a number from 0 (the panel is above the screen) to 502 (it covers the screen). A door aims it at the other end with a critically damped spring of 0.25 s, and a finger holds it. The spring starts at the finger's speed, or from rest for a door, and lands exactly on its end at most 320 ms after it started. The tick evaluates the spring from the clock, so the motion does not depend on how often it is stepped. It places the pictures about 30 times a second, every second refresh of the panel.

The doors.

Only a drag follows the finger. The other doors start the spring from rest. A settings door that arrives while a curtain moves is dropped, and the log says door dropped: the curtain is still moving. Over the details or a card Settings does not open: a drag down from the top edge of a card is the card's own, and it closes the card.

The auto-lock brings the face down over the settings screen: the face is the panel and this screen is the picture below.

Following the finger. The recognizer claims a drag down on the face, or a drag up on the settings screen, 12 px from the landing. At the claim AO_Gui tells LVGL to wait for the lift, takes the pictures, and grabs the page at the point of the claim, closed for a drag that opens and open for a drag that closes, so the curtain makes no jump. Every later sample feeds the page, which follows the finger with some resistance at the start.

While the finger follows and letting go would open the curtain, a cue shows: the line "Release for Settings" in the look of the hint. It moves with the panel and goes when letting go would take the curtain back. A spring has no finger, so it has no cue.

The release rule. When the finger lifts, the page opens or goes back:

The speed is the one over the last 80 ms of samples. The log says gesture: release at <p>% <v> px/s -> open|back (settings).

The state changes on landing. While the curtain moves, the state of AO_Gui is the screen where the door started. When the spring lands, the tick posts GUI_CURTAIN_LAND_SIG, because a tick cannot transition, and the state answers. If the page landed on the far side the door is taken and the screen it leads to is loaded with a cut in the same step. If it went back to where it started, the screen it started from is loaded back and the state stays.

Catching the curtain. A press on a curtain that settles catches it where the spring has it. The page freezes, the pictures are put there at once, and the log says gesture: caught the curtain at <p>%. A drag grabs the page from there with no resistance. A lift with no drag lets the page go on to the end it was going to. A press on a curtain that a finger holds, or that has landed and waits to be taken, is blocked.

A button finishing it. A press of a button, a hold, home, a card that opens, and the doors to the face and Apps do not wait for a spring. Every state a curtain can start from lands a moving curtain at once, on the end it was going to, and the state it lands in handles the event. The release of a button passes through without finishing it, and a curtain that starts disarms an armed press of a side button.

Measured on the bench board 6c:0a, a settings curtain takes 356 to 380 ms over 11 frames, and every frame lands within two or three refreshes of the panel.

Entry, exit and the tick

On entry, gui_screen_enter() loads the screen with a cut, since the curtain has shown it. The entry also:

  1. Sets the cursor where it was left, because set_sel outlives the screen.
  2. Paints the control centre.
  3. Posts five requests: display_state_request(), a read of config key="vol" for AO_Audio, wifi_state_request(), channel_state_request() and ble_state_request().

Battery is not among the requests, because AO_Gui already holds the last publish.

The tick has three extra jobs here:

Nothing polls a radio for a screen nobody looks at.

Four subscriptions feed the circles. Gui_running handles them and not Gui_settings, so a view opens with current values rather than the last visit's.

EventSetsUsed by
POWER_EVT_SIG (every 2 s)soc, bat_state, vbat_mv, current_maBattery page
POWER_FIELD_SIGtemp_c, temp_knownBattery page
WIFI_STATE_SIGwifi_on, wifi_linked, wifi_rssi, wifi_ssidWi-Fi circle and page
BLE_STATE_SIGble_on, adv_on, ble_console, ble_bondsBluetooth circle and page
DISPLAY_STATE_SIGbright, through gui_seed_bright()The brightness level
RADIO_STATE_SIGc5_fw[], c5_fw_knownAbout's Radio row
STORAGE_INFO_SIGsd_mounted, sd_card_mb, sd_knownAbout's card row
WIFI_SCAN_*, WIFI_SAVED_*scan_ssid[], scan_rssi[], scan_auth[], saved_ssid[]Wi-Fi page

Specifications

The page rail

The numbers of the Settings page and of every page, all named defines of kgui_screens.h.

ThingValue
Titley 30 (KGUI_PAGE_TITLE_Y), Outfit Bold 28
Bodyy 68 to 458 (KGUI_PAGE_BODY_Y, KGUI_PAGE_BODY_H 390)
Row92 px tall, 12 px gap (KGUI_PAGE_RAIL_ROW_H, _GAP)
Disc56 px at x 30 (KGUI_PAGE_RAIL_DISC), glyph 30 px
Name and valueFrom x 104
Capsule366 px wide at x 22 (KGUI_PAGE_RAIL_CAPSULE_W)
Focusy 215 (KGUI_PAGE_RAIL_FOCUS_Y); not on Wi-Fi and Bluetooth
Pinned switchWi-Fi and Bluetooth: one row and its gap, 104 px, kept at the body's top (KGUI_PAGE_PIN_H)
FadeFrom y 99 over 50 px at the top, from y 430 over 70 px at the foot
Page growth out of the middle160 ms (GUI_GROW_IN_MS)
Page back into the middle180 ms (GUI_GROW_OUT_MS)
Page slide, when the pictures fail180 ms (GUI_PAGE_ANIM_MS)
Radio page headerDisc 56 px at y 26, 16 px to the words, words 240 px at most, body from y 100 (KGUI_PAGE_HDR_*)

The curtain

ThingValue
Page travel0 to 502
SpringCritically damped, 0.25 s, lands within 320 ms
Dim0 to 40 % (KGUI_CURTAIN_DIM_OPA), the settings curtain only
Release800 px/s flings; otherwise half decides
Claim12 px from the landing

Gotchas

Example code

Read a control's state and open its page. Each step ends with something you see:

  1. On the face, press the pad's up button. The settings screen comes down with the ring on Wi-Fi and the label row reading its state.
  2. Look at the Bluetooth circle. It is blue and pulses once a second while the board advertises, and grey when Bluetooth is off.
  3. Press the pad's down button twice. The ring goes to the battery and then to the brightness level. Press the pad's right button and the level grows by 5 %.
  4. Press the pad's up button twice. The ring walks to Turn off and then to Settings. Press TOP. The Settings page grows out of the middle with the ring on Wi-Fi. Walk down to Vibration and press TOP: its value steps to the next, and nothing opens.
  5. Press BOT to go back to the control centre, then swipe up anywhere to return to the face.

In the SDK