Hardware¶
Kube32 runs on ESP32-S3 microcontrollers with 8 MB of flash. One firmware image boots both cluster roles:
| Role | Reference board | PSRAM | Console |
|---|---|---|---|
| Control plane | Generic ESP32-S3 devkit | 2 MB, quad SPI | Native USB-Serial/JTAG, and UART0 on GPIO 43/44 |
| Worker | M5Stack Cardputer | None | Native USB-Serial/JTAG only. GPIO 43 drives the speaker |
A board with PSRAM defaults to the control plane. A board without PSRAM defaults to a worker. Override the role in NVS with K32_CMD:ROLE:control-plane|worker|auto.
Board profiles and labels¶
Select the peripheral profile with K32_CMD:BOARD:cardputer|generic:
| Profile | Labels | Allocatable peripherals | Default bus pins |
|---|---|---|---|
generic |
kube32.io/board: generic |
33 GPIO, 10 ADC, 2 I2C, 2 UART | I2C0 on GPIO 8/9, UART1 on GPIO 17/18 |
cardputer |
kube32.io/board: cardputer |
11 GPIO, 3 ADC, 1 I2C, 1 UART | I2C0 or UART1 on Grove port GPIO 2/1 |
Control plane nodes also carry the node-role.kubernetes.io/control-plane label, and worker nodes carry node-role.kubernetes.io/worker.
Cardputer pins¶
| Function | GPIO |
|---|---|
| ST7789 LCD, 240x135, on SPI2 | MOSI 35, SCLK 36, CS 37, DC 34, RST 33, backlight 38 |
| NS4168 I2S speaker | BCLK 41, DATA 42, LRCK 43 |
| Key matrix | address lines 8, 9, 11; row inputs 13, 15, 3, 4, 5, 6, 7 |
| microSD on SPI3, 20 MHz | MOSI 14, MISO 39, SCK 40, CS 12 |
Memory budget¶
The firmware reserves contiguous pod memory at boot before Wi-Fi initializes:
| Board type | Boot reservation | Pod allocation |
|---|---|---|
| With PSRAM | One 1,284 KiB PSRAM block, 64 KiB shared interpreter stack in PSRAM | Serves one large pod requesting at least half the block, up to 1,280 KiB of linear memory. Allocations over 4 KB and interpreter structures use remaining PSRAM. Serializes all pod calls on the 64 KiB stack |
| Without PSRAM | 80 KiB internal RAM pool block holding the 28 KiB shared interpreter stack, 24 KiB system heap reserve, 1 KiB log ring per pod | Serves all pod allocations from the pool block first, falling back to the general heap when full. The kubelet admits a pod only when its estimated footprint fits in the pool block. Serializes all pod calls on the 28 KiB stack (app_main pods require PSRAM). The kubelet refuses API responses over 16 KB |
Without PSRAM, network buffers come from internal RAM. The Wi-Fi station uses a 576-byte MTU, ~650 bytes per frame, with 12 dynamic RX and 12 dynamic TX buffers. Each socket holds up to 4 unread and 2 out-of-order segments. The pod HTTP server queues one connection, keeps two idle, and returns 503 when free internal heap is under 10 KB.
A pod's module loads only into pieces of the pool block left free by earlier pods. When a module fails to load twice for memory, the kubelet stops pods with a smaller memory limit once so the module loads first, then restarts them; a pod never stops for a smaller one.
Check free internal RAM and PSRAM on the serial console with K32_CMD:STATUS.
Partition table¶
Layout in partitions.csv for 8 MB flash:
| Name | Offset | Size | Use |
|---|---|---|---|
nvs |
0x9000 |
32 KB | Node configuration, Wi-Fi, role, board profile, name, API address, token, TLS credentials |
otadata |
0x11000 |
8 KB | Selects the active boot slot and tracks the confirmation state of an updated image |
phy_init |
0x13000 |
4 KB | Radio calibration |
ota_0 |
0x20000 |
3.25 MB | App slot |
ota_1 |
0x360000 |
3.25 MB | App slot: a firmware update writes to the inactive slot |
journal |
0x6A0000 |
1 MB | Raw object store journal (data, subtype 0x40), used when no microSD card is mounted |
storage |
0x7A0000 |
384 KB | FAT filesystem with wear levelling at /data, used when no microSD card is mounted |
Configuration notes¶
Settings in platformio.ini and sdkconfig.defaults:
- Target CPU clock is 240 MHz. Flash runs at 80 MHz, written with
--flash-mode diobyscripts/flash.sh. - PSRAM is configured for 80 MHz quad SPI via
CONFIG_SPIRAM_MODE_QUAD=yandCONFIG_SPIRAM_IGNORE_NOTFOUND=yso the same image boots on boards without PSRAM.
Limitations¶
- Boards with octal PSRAM require
CONFIG_SPIRAM_MODE_OCT=yinsdkconfig.defaults. - Pods cannot claim strapping, USB, flash/PSRAM, or board profile peripheral pins. See Capability firewall.