kodeOS internals

Dock console

Use the kodeOS console through the magnetic charging base as a second USB serial port, with no reset on open, and what it costs in RAM and image size.

What it is

The dock console is the console of a Kode Dot reached through the charging base instead of the USB-C cable. It has the same commands, the same grammar, the same answers and the same payloads as the cable. A computer connected to the base sees a USB serial port.

The base is passive. It wires the four lines of its own USB-C socket (VBUS, D+, D-, GND) to four magnetic pads, so it is a cable with magnets. The Kode Dot has a second USB circuit next to the one behind its USB-C socket, and the pads are wired to it. The computer therefore sees a second USB connection that offers a CDC-ACM serial port.

Opening this port does not reset the board. The USB-Serial/JTAG peripheral behind the USB-C socket resets the P4 on every attach. A CDC device that ignores DTR and RTS does not. A session over the base starts with the board as it is: its uptime, its radio link and its screen do not change.

How it is connected

flowchart LR
subgraph board["On the Kode Dot"]
  usbc["USB-C socket<br/>GPIO24, GPIO25"] --> sj["USB-Serial/JTAG"]
  pads["magnetic pads<br/>GPIO26, GPIO27"] --> otg["OTG full-speed PHY<br/>TinyUSB CDC"]
  sj --> rd["khal reader task"]
  otg --> rd
  rd --> log["AO_Log"]
  log --> con["AO_Console"]
end
class usbc,pads,sj,otg chip
class log,con ao
Two wired ports, one console
Pad on the Kode DotWhat it carries
+5 VVBUS of the base
GNDGND of the base
GPIO26 and GPIO27D+ and D- of the base, the data pair of the second full-speed USB PHY of the P4

The USB-C socket uses another pair, GPIO24 and GPIO25 (KBSP_PIN_USB_USJ_DM and _DP), which belongs to the USB-Serial/JTAG.

Above khal the two wired ports are one port. Both feed one reader task and one line editor, and lines from both carry the source KODE_CONSOLE_SRC_SERIAL. Every console write goes to both ports. The rules that make two ports into one console are short functions in khal_console_esp.c:

FileWhat it holds
khal/console/src/khal_console_esp.cThe dock port, the read from two ports, the raw-mode latch and the bounded write
khal/KconfigKHAL_CONSOLE_DOCK and KHAL_CONSOLE_DOCK_TX_WAIT_MS
khal/idf_component.ymlThe esp_tinyusb dependency, fetched only when the option is on
sdkconfig.defaults of kodeOSTurns the option on and sets the size of the CDC FIFOs

AO_Log owns the console device, and this port is part of it. The option exists on the ESP32-P4 only. Its default is off in the SDK and on in kodeOS, because the port takes the OTG controller, which an app might want for another job such as a USB drive.

Specifications

What the computer sees

FieldValue
VID:PID303a:4001. The vendor id is Espressif's. The product id is the esp_tinyusb default for a device with only a CDC class
ManufacturerKode
ProductDot. macOS shows "Kode Dot"
SerialThe printed serial of the board (khal_identity_pretty()), or empty when the board does not know it. The board does not invent one
InterfaceKode Dot console
Cable port, for comparison303a:1001, "USB JTAG/serial debug unit"

Two boards on one computer are two ports, and each port names its board. Any reset of the P4 closes the port, since the controller stops. When kodeOS has started, the board enumerates again.

What it costs

Measured on a Kode Dot with the face on screen, Bluetooth advertising and the card mounted.

WhatWith the option
Image33,696 bytes more
Static internal RAM10,862 bytes in the two TinyUSB libraries: 9,559 in .bss and 1,303 in .data. The two CDC FIFOs of 4 KB are part of it
TasksOne more: the TinyUSB task, stack 4,096 bytes, priority 5, core 1
Internal pool15,524 bytes less, at rest and under load. ps reads int_free=134651 int_max=131072 on the face
Reader wake-upEvery 5 ms while the console is idle, instead of every 50 ms

A firmware with the option off neither downloads nor compiles the stack: idf_component.yml gives esp_tinyusb the rule $CONFIG{KHAL_CONSOLE_DOCK} == True.

Building it into a firmware

kodeOS sets four lines in sdkconfig.defaults:

CONFIG_KHAL_CONSOLE_DOCK=y
CONFIG_TINYUSB_CDC_ENABLED=y
CONFIG_TINYUSB_CDC_RX_BUFSIZE=4096
CONFIG_TINYUSB_CDC_TX_BUFSIZE=4096

The FIFOs default to 512 bytes. 4 KB each holds a whole 4 KB ack window in each direction, so a chunk never waits for the FIFO.

An app built from the SDK has the option off and shows no port on the base unless it turns it on. An app needs only the first two lines: the 512-byte FIFOs carry its lines and logs, and the two 4 KB ones would cost it about 7 KB of internal RAM for installs and file transfers it does not take. To turn it on:

  1. Add CONFIG_KHAL_CONSOLE_DOCK=y and CONFIG_TINYUSB_CDC_ENABLED=y to the sdkconfig.defaults of the app.
  2. Delete the generated sdkconfig.
  3. Build.

The first pass reports the TinyUSB options as unknown symbols, which is normal: they exist only after khal has made the component manager fetch esp_tinyusb, and the next pass applies them.

Gotchas

Example code

Use the console through the base.

    1. Unplug the USB-C cable.
    2. List the serial ports of the computer.
    3. Put the Kode Dot on the base and plug the base into the computer. List the ports again. The new one is the dock port.
    4. Run python3 tools/kcli.py -p <dock port> cmd 'ping'. It prints pong and ok, and the screen of the Kode Dot does not restart.
    5. Run python3 tools/kcli.py -p <dock port> screenshot -o shot.png. The payload commands work as they do on the cable, install dst=c5 included.

In the SDK