SolarOS

SolarOS 4.6.8 manual · hardware

Expansion Ports

SolarOS treats an expansion port as a board-described collection of resources, not as one fixed connector standard. A board may expose individual GPIO pins, named I2C, SPI, or UART buses, or free pins that can be routed to an approved spare peripheral host at runtime.

Use expansion layout for the physical connector arrangement, and use expansion status and gpio list on the running device for the authoritative resource view. The layout overlays live pin policy and claims; the available resources depend on the board and the compiled firmware flavor. Boards with multiple named headers can be filtered, for example with expansion layout J1.

Resource Model

TermMeaningOwnership and lifetime
Connector pinA signal physically present on an expansion header or breakout. Physical presence does not make a pin safe for runtime control.Described by the board profile.
Runtime GPIOA connector pin approved for direct gpio and 1-Wire use, and for adc or pwm where the board tables allow it.Claimed while a service or attached device uses it.
Board-defined busA named bus with fixed pins, such as i2c0 or spi0.Registered at boot and cannot be removed. A named UART can still be detached and reattached.
Runtime busA named bus routed onto approved free pins and a spare hardware host.It can be removed when idle. UART controller and pin claims follow attach/detach; other bus signals remain claimed for the descriptor lifetime.
Expansion driverCode that knows how to initialize and operate a supported external device.Listed by expansion drivers; availability is package- and capability-filtered.
Attached deviceA named driver instance bound to buses, addresses, chip-selects, or GPIO roles.Acquires resource leases on attach and releases them on detach.

Board pin policy has three levels:

PolicyDirect GPIORuntime bus routingTypical use
FreeYesYesUncommitted expansion pin.
ReleasableNoYes, after its current service releases it.UART or another default board role.
FixedNoNoBoot straps, flash/PSRAM, display, storage, USB, controls, or other board hardware.

This policy is separate from physical connector membership. For example, a strapping pin may appear on a header and in the physical connector description while remaining blocked from runtime use.

Board Resources

GPIO, ADC, and PWM

BoardPhysical expansion signalsRuntime GPIO and PWMRuntime ADCConnector restrictions
Waveshare ESP32-S3-RLCD-4.2GPIO0-GPIO3, GPIO13, GPIO14, GPIO17-GPIO20, GPIO43, GPIO44GPIO1-GPIO3, GPIO17GPIO1-GPIO3, GPIO17GPIO0 is BOOT; GPIO13/GPIO14 are I2C; GPIO18 is KEY; GPIO19/GPIO20 are native USB; GPIO43/GPIO44 belong to uart0 by default.
Elecrow CrowPanel ESP32-S3 4.2-inch E-paperGPIO3, GPIO8, GPIO9, GPIO14-GPIO21, GPIO38GPIO8, GPIO9, GPIO14-GPIO21, GPIO38GPIO8, GPIO9, GPIO14-GPIO20GPIO3 is physically exposed but blocked as a strapping pin.
ESP32-S3-DevKitC-1-N16R8ESP32-S3 signals broken out on the DevKitC headersGPIO1, GPIO2, GPIO4-GPIO7, GPIO10, GPIO14-GPIO18, GPIO21, GPIO39-GPIO42, GPIO47GPIO1, GPIO2, GPIO4-GPIO7, GPIO10, GPIO14-GPIO18GPIO0 is BOOT/KEY; GPIO3/GPIO45/GPIO46 are other strapping pins; GPIO19/GPIO20 are native USB; GPIO35-GPIO37 are Octal PSRAM; GPIO38/GPIO48 are reserved for either RGB LED revision; GPIO43/GPIO44 are uart0.
ODROID-GOExternal IO GPIO4 and GPIO15GPIO4, GPIO15NoneBoth pins are also the allowed external chip-select slots on the shared VSPI bus.
ESP32-WROVER v3.0GPIO0-GPIO5, GPIO12-GPIO15, GPIO18, GPIO19, GPIO21-GPIO23, GPIO25-GPIO27, GPIO32-GPIO36, GPIO39GPIO4, GPIO5, GPIO13, GPIO18, GPIO19, GPIO21-GPIO23, GPIO26, GPIO27, GPIO32-GPIO36, GPIO39; PWM excludes input-only GPIO34-GPIO36 and GPIO39GPIO32-GPIO36, GPIO39GPIO0 is BOOT/KEY; GPIO1/GPIO3 are CH340 uart0; GPIO2/GPIO14/GPIO15 are SDMMC; GPIO25 is PAL; GPIO5 is a strapping pin; GPIO34-GPIO36/GPIO39 are input-only.

Power and ground pins are physical wiring resources and are not managed by the SolarOS pin-claim system. Check the board schematic and the external module's voltage and current requirements before connecting it.

Named and Runtime Buses

BoardBoard-defined busesRuntime-routable busesNotes
Waveshare ESP32-S3-RLCD-4.2i2c0: SDA GPIO13, SCL GPIO14; uart0: TX GPIO43, RX GPIO44I2C on i2c1, SPI on spi3, UART on uart1/uart2, or 1-Wire, using approved free pinsThere is no fixed expansion SPI bus. The internal display SPI pins are not expansion pins.
Elecrow CrowPanel ESP32-S3 4.2-inch E-paperuart0: TX GPIO43, RX GPIO44I2C on i2c0/i2c1, SPI on spi3, UART on uart1/uart2, or named 1-Wire, using approved free pinsSPI3 is shared with microSD and is available for a runtime expansion bus only while the SD card is unmounted. The SSD1683 stays on its dedicated internal SPI2 host.
ESP32-S3-DevKitC-1-N16R8i2c0: SDA GPIO8, SCL GPIO9; spi0: SCK GPIO12, MISO GPIO13, MOSI GPIO11, CS GPIO4/GPIO10/GPIO5/GPIO6/GPIO7; uart0: TX GPIO43, RX GPIO44I2C on i2c1, SPI on spi3, UART on uart1/uart2, or 1-Wire, using approved free pinsThe board-defined spi0 is the normal expansion SPI bus.
ODROID-GOspi0: SCK GPIO18, MISO GPIO19, MOSI GPIO23, CS GPIO15/GPIO4; uart0: TX GPIO1, RX GPIO3UART on uart1/uart2, or named 1-Wire, using approved free pinsVSPI is shared with onboard TFT and SD devices; external devices use their own allowed CS slot.
ESP32-WROVER v3.0uart0: TX GPIO1, RX GPIO3I2C, SPI on spi2/spi3, UART on uart1/uart2, or 1-Wire, using free output-capable GPIO4, GPIO5, GPIO13, GPIO18, GPIO19, GPIO21-GPIO23, GPIO26, GPIO27, GPIO32, or GPIO33The rear SD slot uses the dedicated one-bit SDMMC host. GPIO34-GPIO36 and GPIO39 are available only for input signals and ADC.

I2C and SPI buses accept shared logical leases. UART and registered 1-Wire bus instances are exclusive. Registered 1-Wire buses appear in expansion status and can be addressed by name. Bus names are unique across protocols.

I2C, SPI, UART, and 1-Wire buses can be created at runtime. Runtime hardware buses require an unused board-approved controller or host; all signal pins must be approved by the board's runtime pin policy. Every named UART has an explicit attached state. Attaching reserves its controller and pins; the hardware driver still starts lazily on the first consumer claim and stops after the final claim. Detaching an idle UART releases the controller and pins but preserves its name and configuration. Runtime UART descriptors may additionally be removed; board-defined UART descriptors cannot. The direct numeric form of the onewire command remains available without creating a named expansion bus.

Typical Workflow

Start by inspecting the live resource map and compiled drivers:

expansion layout
expansion status
gpio list
expansion drivers
expansion scan

If the device can use a board-defined bus, attach it directly. The device name is chosen by the user and becomes the lease owner:

expansion attach ssd1306 oled0 i2c=i2c0 addr=0x3c
expansion devices
display test oled0
expansion detach oled0

Runtime I2C and 1-Wire buses use the same lifecycle:

expansion bus create i2c i2c1 port=i2c1 sda=gpio14 scl=gpio15 speed=100000
i2c scan i2c1
expansion bus remove i2c1

expansion bus create onewire onewire0 pin=gpio16
onewire scan onewire0
expansion bus remove onewire0

expansion bus create ps2 ps2kbd clock=gpio17 data=gpio18
job start ps2-keyboard ps2kbd
job stop ps2-keyboard
expansion bus remove ps2kbd

expansion bus create uart uart1 port=uart1 tx=gpio14 rx=gpio15 baud=115200
uart status uart1
uart write uart1 AT
expansion bus detach uart1
expansion bus attach uart1
expansion bus remove uart1

On the Waveshare board, uart0 owns the releasable GPIO43/GPIO44 pair while it is attached. From a display or other non-uart0 shell, detach it before reusing those pins and attach it again after the temporary bus is removed:

expansion bus detach uart0
expansion bus create uart uart1 port=uart1 tx=gpio43 rx=gpio44
expansion bus remove uart1
expansion bus attach uart0

Detaching the port that carries the current shell fails as busy, so the shell cannot disconnect itself accidentally.

On a board with an approved available SPI host, create a bus before attaching the device. Creating the bus claims its controller, SCLK, MOSI, and optional MISO immediately. Each cs= option declares an allowed chip-select pin but leaves it available until a device or one-shot transfer selects it. That user claims both the GPIO and logical chip-select slot, preventing GPIO or SPI users from driving it concurrently:

expansion bus create spi spi1 host=spi3 sclk=gpio1 mosi=gpio2 miso=gpio3 cs=gpio17
expansion attach rfm69 radio0 spi=spi1 cs=gpio17
expansion detach radio0
expansion bus remove spi1

On the Elecrow CrowPanel, run disk umount before creating the runtime SPI3 bus. Remove that bus before using disk mount to make SPI3 available to microSD again.

The spi command addresses board-defined and runtime buses by name. This makes the same transfer tools available for spi0, spi1, or any other registered SPI bus:

spi status
spi status spi1
spi xfer spi1 gpio17 0 1m 0x9f 0 0 0
spi read spi1 gpio17 0 1m 4 0xff
spi write spi1 gpio17 0 1m 0xaa 0x55

The bus name and chip-select are always explicit. Transfers temporarily claim the selected chip-select and lease the bus, so they fail cleanly when an attached device already owns that chip-select.

The i2c command also accepts a named bus. Omitting it retains the i2c0 shortcut used by existing scripts:

i2c status i2c0
i2c scan i2c0
i2c probe i2c0 0x3c
i2c read i2c0 0x50 0x00 8
i2c write i2c0 0x50 0x00 0xaa 0x55

Omit miso or use miso=none for output-only peripherals. A runtime bus can only use a host and pins approved by the board profile. It cannot take fixed display, storage, I2C, USB, or strapping pins. A bus cannot be detached or removed while it has device leases, and board-defined buses can never be removed. expansion bus detach preserves the named descriptor and works for every runtime bus plus board buses whose owned pins are marked releasable. Fixed-pin board buses reject detach.

Drivers and Bindings

Run expansion drivers on the device to see the exact compiled set.

DriverDeviceRequired bindingsResult after attach
manualResource-only profileAny valid bus, address, chip-select, GPIO, ADC, or PWM bindingsClaims resources without initializing hardware.
rfm69HopeRF RFM69W/CW packet radiospi=<bus> cs=<pin>; optional irq=<pin> reset=<pin>Registers a packet-radio target with PA0 power from -18 through 13 dBm.
rfm69hHopeRF RFM69HW/HCW high-power packet radiospi=<bus> cs=<pin>; optional irq=<pin> reset=<pin>Registers a packet-radio target with PA_BOOST power from -2 through 20 dBm.
rfm95HopeRF RFM95W multimode radiospi=<bus> cs=<pin>; optional irq=<pin> reset=<pin>Registers an FSK/GFSK/MSK/GMSK/OOK/LoRa target for the radio command.
pcd854484x48 SPI LCDspi=<bus> cs=<pin> dc=<pin> reset=<pin>Registers an auxiliary display target.
ssd1306128x64 I2C OLEDi2c=<bus> addr=<address>Registers an auxiliary display target.
sh1106128x64 I2C OLED with SH1106 addressingi2c=<bus> addr=<address>Registers an auxiliary display target with the two-column offset.
neopixelWS2812/NeoPixel GRB stripdata=<pin> count=<1..256>Claims the data GPIO and registers a named strip for the neopixel command and script API.

Manual profiles are useful when another app or workflow operates the hardware but SolarOS still needs to prevent conflicting claims:

expansion attach manual radio0 spi0 cs=gpio10 irq=gpio4 reset=gpio5
expansion attach manual sensor0 i2c0 addr=0x40
expansion detach radio0

Binding names may be explicit (spi=spi0, i2c=i2c0) or, where unambiguous, supplied as positional bus names. ce= aliases cs= and rst= aliases reset= for common module labels.

Select the RFM69 driver from the module variant, not from the requested power. The rfm69h driver uses PA1 through 13 dBm, PA1+PA2 through 17 dBm, and applies the datasheet high-power OCP/TestPA settings only during 18-20 dBm transmit. Those settings are restored before standby, receive, or sleep. Both module families require 3.3 V power and a band-appropriate antenna.

WS2812/NeoPixel strip

Use a runtime-safe expansion GPIO for DIN. The driver uses an ESP32 RMT transmit channel and supports up to 256 GRB pixels per attached strip:

5V supply + -> strip 5V       supply GND -> strip GND and SolarOS board GND
GPIO1 -> level shifter -> strip DIN

expansion attach neopixel pixels0 data=gpio1 count=8
neopixel set pixels0 0 32 0 0
neopixel fill pixels0 0 0 16
neopixel clear pixels0
expansion detach pixels0

Use an external supply sized for the strip; full-white WS2812 pixels can draw roughly 60 mA each. Do not power a multi-pixel strip from a board GPIO. A 3.3 V data signal may work with short wiring when the strip supply is low enough, but a 3.3-to-5 V logic-level shifter is the reliable arrangement. Put the usual bulk capacitor across the strip supply and a small series resistor near DIN.

Wiring Examples

PCD8544 on ESP32-S3-DevKitC-1

VCC -> 3V3        GND -> GND
CLK/SCLK -> GPIO12
DIN/MOSI -> GPIO11
CE/CS -> GPIO10   DC -> GPIO4   RST -> GPIO5

expansion attach pcd8544 lcd0 spi=spi0 cs=gpio10 dc=gpio4 reset=gpio5
display test lcd0

Wire a module backlight according to the module board and use suitable current limiting when connecting it to 3V3.

RFM95W on ESP32-S3-DevKitC-1

The RFM95W is a 3.3 V device. Connect an antenna suitable for the module band before transmitting.

VCC -> 3V3        GND -> GND
SCK -> GPIO12     MISO -> GPIO13
MOSI -> GPIO11    NSS/CS -> GPIO4
RESET -> GPIO5

expansion attach rfm95 radio0 spi=spi0 cs=gpio4 reset=gpio5
radio status radio0

The module and driver support FSK, GFSK, MSK, GMSK, OOK, and LoRa. The default LoRa profile is 868 MHz, 125 kHz bandwidth, SF7, coding rate 4/5, CRC enabled, explicit headers, sync word 0x12, and 13 dBm transmit power. The matching built-in profile applies those settings as one operation:

radio profile apply radio0 lora-eu868
radio send radio0 "hello from SolarOS"
radio recv radio0 5000

The built-in meshcore-eu868 profile is specifically for MeshCore companion operation in the EU868 region: 869.618 MHz, 62.5 kHz, SF8, coding rate 4/8, private sync word 0x12, CRC, variable length, and 14 dBm. MeshCore always requires an explicit profile:

job start meshcore radio0 meshcore-eu868

gfsk-eu868 and ook-eu868 are also built in. Change both ends of a link to the same profile before exchanging packets. A custom set of settings can be captured in one of eight persistent NVS user profiles:

radio profile apply radio0 gfsk-eu868
radio config radio0 bitrate 9600
radio profile save radio0 gfsk-9600
radio profile show gfsk-9600

Applying a profile leaves the radio in standby and rolls back the complete configuration and prior state if the driver rejects it. User profiles preserve every common radio setting, including addressing. Built-in profiles are read-only. Selecting MSK or GMSK sets the deviation to one quarter of the bitrate, giving the required modulation index of 0.5. GFSK and GMSK enable Gaussian shaping with BT=1.0.

FSK-family and OOK packet payloads are limited to 64 bytes by the modem FIFO; LoRa payloads may contain up to 255 bytes. Fixed length zero selects the FSK/OOK unlimited FIFO-stream mode used by services such as POCSAG.

The driver polls the radio status registers, so DIO0/IRQ is optional. An IRQ binding can still be reserved for future interrupt-driven operation.

SSD1306 or SH1106 on Waveshare ESP32-S3-RLCD-4.2

VCC -> 3V3        GND -> GND
SDA -> GPIO13     SCL -> GPIO14

i2c scan i2c0
expansion attach ssd1306 oled0 i2c=i2c0 addr=0x3c
display test oled0

Common modules answer at 0x3c or 0x3d. If the image is shifted two pixels left with two uninitialized columns on the right, reattach it as SH1106:

expansion detach oled0
expansion attach sh1106 oled0 i2c=i2c0 addr=0x3c
display test oled0

After an auxiliary display is attached, it can also host a shell session:

session create shell oled0

Quick reference

Inspect runtime-safe pins and buses before attaching hardware. Use the io application or the gpio, adc, pwm, i2c, spi, uart, bus, and expansion commands as documented here. Resource ownership prevents two drivers, jobs, or sessions from claiming the same hardware concurrently.