The alp console shell
The console shell is an on-target interactive shell that ships as SDK
infrastructure. Setting CONFIG_ALP_SDK_CONSOLE=y registers the whole alp
command tree on the Zephyr shell at link time, so an application gets a
Linux-like diagnostic console on the SoM's UART — board, gpio, i2c,
adc, pwm, mem, clk, reboot, and companion — without registering a
single command itself.
:::note Two different alps
This is not the alp host CLI. That one runs on your
developer machine (alp build / flash / run). This one runs on the
board, typed into the serial console after boot (uart:~$ alp board). They
share a name and a philosophy — one front door per surface — but live on
opposite ends of the cable.
:::
Enabling it
# prj.conf
CONFIG_ALP_SDK_CONSOLE=y
The command tree is registered from src/zephyr/console/alp_console.c when the
symbol is on; main() does nothing to opt in. Read-only by default — the
write-capable verbs (mem wr, gpio write, companion gpio write) are gated
behind a separate switch:
CONFIG_ALP_SDK_CONSOLE_UNSAFE=y # unlocks write verbs — leave OFF in field builds
Command tree
Type alp and press Tab to explore. The core commands are backed by the
same portable SDK surfaces the C API uses:
| Command | Does | Backed by |
|---|---|---|
alp board | SoM identity (rev, serial, part) | <alp/hw_info.h> + on-module EEPROM |
alp gpio … | read / (unsafe) write pins | <alp/peripheral.h> GPIO |
alp i2c … | probe / read I²C devices | <alp/peripheral.h> I²C |
alp adc … | sample a channel | <alp/adc.h> |
alp pwm … | drive a channel | <alp/pwm.h> |
alp mem … | read / (unsafe) write memory | direct MMIO |
alp clk | clock-tree summary | SoC clock control |
alp reboot | reset the SoM | sys reboot |
alp companion … | talk to the companion coprocessor | see below |
alp companion — the coprocessor bridge
Every Alp SoM pairs the main SoC with a companion coprocessor; alp companion
is the console door to it. What the sub-commands are depends on the SoM:
| SoM | Companion | Sub-commands |
|---|---|---|
| E1M-AEN801 (Alif E8) | CC3501E Wi-Fi/BLE | ver, ping, wifi scan, wifi connect, ble enable, ble scan |
| E1M-X V2N | GD32 supervisor MCU | ver, ping, gpio … |
uart:~$ alp companion wifi scan
5 AP(s):
MyNetwork ch5 -43 dBm wpa3
Guest-2.4GHz ch3 -59 dBm wpa2
IoT-Hub ch6 -84 dBm open
uart:~$ alp companion wifi connect "MyNetwork" hunter2 wpa3
connecting to "MyNetwork" (wpa3)...
connected rssi=-44 dBm
ip 192.0.2.74
uart:~$ alp companion ble enable
BLE controller + NimBLE host up
uart:~$ alp companion ble scan
10 device(s):
66:c6:d2:91:31:26 -91 dBm ET-2870 Series
26:2a:8d:21:9d:4d -50 dBm (no name)
wifi connect takes an SSID plus an optional passphrase (no passphrase ⇒ open;
a trailing wpa3 token forces WPA3-SAE) and prints the post-join RSSI and DHCP
IP. wifi scan decodes each AP's security (open / wpa2 / wpa3) from the
CC3501E SecurityInfo.
:::caution Bench ordering
On real silicon, run ble enable from a clean boot (or before a wifi scan).
A Wi-Fi scan immediately followed by ble enable can return -4 — two
back-to-back heavy radio ops desync the CS-less inter-chip bridge. A firmware
fix is tracked.
:::
Binding the companion from your app
On the Alif SoMs there is no companion singleton, so the application opens its
CC3501E and registers the handle once — that is the sole app-facing hook in
<alp/console.h>:
#include <alp/console.h>
cc3501e_t *cc = /* your cc3501e_init(...) handle */;
alp_console_companion_set(cc); // now `alp companion …` reaches Wi-Fi/BLE
On the V2N family the GD32 supervisor is a managed singleton inside the SDK, so
this call is a no-op — alp companion reaches the GD32 automatically. Passing
NULL unbinds. alp_console_companion_set is [ABI-EXPERIMENTAL] (new in
v0.9.0), tracking the experimental CC3501E companion surface it depends on.
Reference example
examples/peripheral-io/alp-console is a single slot0 application that brings
the console up on the UART, binds the companion, and breathes an RGB status LED
in a background thread — one app demonstrating a Linux-like shell, live
Wi-Fi/BLE, and a "board is alive" indicator. main() registers no commands; it
only binds the companion and spawns the LED thread. It builds and runs
everywhere, including native_sim (no companion — the non-companion commands
still work).