Repository navigation
Porting and Build Systems
MicroCS is plain C99 with no dependencies. Wherever your C compiles, MicroCS compiles. This page explains how to add it to a project (any build system), which port to use, and how to support a new chip.
| Full (compiler on device) | Runtime only (precompiled images) | Low-RAM profile | |
|---|---|---|---|
| Flash | ~213 KB | ~168 KB | ~144 KB |
VM heap after mcs_new
|
~25 KB | ~25 KB | ~13 KB |
| Heap while compiling a 2.5 KB script | ~90 KB peak | — | — |
| C stack for the VM task | 3–8 KB | 3–8 KB | 3 KB |
Flash numbers are Cortex-M, -Os, including newlib + libm (~49 KB). Details:
PERFORMANCE.md · small parts: LOW_RESOURCE.md.
Sources: src/*.c (core) + modules/*/*.c (optional: fs, hal, sched, shell, runtime) +
ports/<port>/mcs_port_<port>.c (optional). Include paths: include/ and ports/<port>/.
Modules that cannot build on a target compile to nothing (e.g. the POSIX filesystem on an MCU).
MICROCS_DIR := third_party/MicroCS
MICROCS_PORT := stm32 # optional
include $(MICROCS_DIR)/microcs.mk
C_SOURCES += $(MICROCS_SRCS)
C_INCLUDES += $(MICROCS_INCS)
C_DEFS += $(MICROCS_DEFS) # set MICROCS_PROFILE := lowram to use a profilePut mcs_user_config.h in one of your C_INCLUDES folders (it is found automatically), or
name it with MICROCS_CONFIG := app/my_config.h. MICROCS_CORE_ONLY := 1 builds only the
language core.
set(MICROCS_PORT stm32) # stm32 | rp2 | esp32 | template | "" (none)
set(MICROCS_PROFILE embedded) # auto | min | tiny | mcu | embedded | lowram | linux | full | "" (default)
add_subdirectory(third_party/MicroCS)
target_link_libraries(${PROJECT_NAME} microcs)With MICROCS_PORT stm32 the library links the generated stm32cubemx target, so it sees
your HAL configuration; with rp2 it links the hardware_* libraries of the pico-sdk.
A mcs_user_config.h next to your top-level CMakeLists.txt (or in its include/, src/ or
main/ folder) is found automatically; MICROCS_CONFIG=path/to/my_config.h names another
file, MICROCS_CONFIG=NONE ignores it. MICROCS_MODULES=OFF builds only the core. On the host it also builds the mcs CLI.
Clone into components/MicroCS (or add it with the IDF component manager — idf_component.yml
is included). The same CMakeLists.txt detects ESP-IDF and registers a component with the
ESP32 port. In your main/CMakeLists.txt nothing is needed (IDF links all components) — see
ports/esp32/example. Options: idf.py menuconfig → MicroCS
(profile, filesystem, ws2812) and a mcs_user_config.h in the project folder or main/
(or Project config header in menuconfig) for everything else.
Add the repo as a module (west.yml project or -DZEPHYR_EXTRA_MODULES=path/to/MicroCS)
and enable it in prj.conf:
CONFIG_MICROCS=y
zephyr/module.yml, zephyr/CMakeLists.txt and zephyr/Kconfig do the rest. See
ports/zephyr/example. A mcs_user_config.h in the application
folder (or its include/ / src/) is used automatically; CONFIG_MICROCS_USER_CONFIG_FILE
names another one.
[env:board]
lib_deps = https://github.com/amir1387aht/MicroCSlibrary.json + tools/pio_build.py select the port from the framework: espidf → ESP32
port, arduino → Arduino port, stm32cube → STM32 port, zephyr → Zephyr port. Put
mcs_user_config.h in the project's include/ folder; build_flags = -D… still work.
python3 tools/make_arduino.py # → dist/arduino/MicroCS/ and dist/arduino/MicroCS-1.14.0.zipSketch → Include Library → Add .ZIP Library, then open File → Examples → MicroCS.
The Arduino IDE has no per-library -D flags, so options live in the packaged
src/mcs_user_config.h: edit it there, or build the package with
--config my_config.h, --profile lowram, --define MCS_ENABLE_LINQ=0.
Add the folders src, modules, ports/<port> as source folders and include,
ports/<port> as include paths, plus the folder with your mcs_user_config.h. No special
compiler flags, no extensions; GCC, Clang,
ARMCC 6, IAR and MSVC (host) are fine. Recommended: -Os (or -O2 for speed).
All options are #defines with defaults in include/mcs_config.h.
Set them in a project header mcs_user_config.h — copy the template
config/mcs_user_config.h, which sets every option, including
the port options, to its default value, and edit what you change — or with -D…, which wins
over the header. Ready-made profiles in include/profiles/: auto, min, tiny, mcu,
lowram, embedded, linux; python3 tools/gen_config.py --profile embedded -o mcs_user_config.h writes the header with that profile's values (or, without a header,
-DMCS_PROFILE=MCS_PROFILE_EMBEDDED).
Where the header goes for each build system and the precedence rules:
CONFIGURATION.md.
A port maps the C# peripheral API to the vendor's own SDK. It includes the SDK's headers
from your project — CubeMX handles, ESP-IDF drivers, pico-sdk hardware_*, Zephyr device
drivers — so it follows your chip variant, pin muxing and clocks. MicroCS never pokes
registers behind the SDK's back.
| Port | SDK | Chips | Console helper | Docs |
|---|---|---|---|---|
ports/stm32 |
STM32Cube HAL (CubeMX) | C0, F0–F7, G0, G4, H5, H7, L0–L5, U5, WB, WL | mcs_stm32_console(&board, uart) |
README |
ports/esp32 |
ESP-IDF 5.0+ | ESP32, S2, S3, C3, C6, H2, P4 |
mcs_esp32_console() (UART0 + USB) / _usb() / _uart(n, baud)
|
README |
ports/rp2 |
pico-sdk 1.5 / 2.x | RP2040, RP2350 (Arm and RISC-V) | mcs_rp2_console_stdio() |
README |
ports/zephyr |
Zephyr 3.6+ (CI: 4.1) | every board with devicetree support |
mcs_zephyr_console() (UART or USB CDC ACM), files: mcs_zephyr_fs_mount()
|
README |
ports/arduino |
Arduino API | ESP32, RP2040, SAMD, nRF52, STM32duino, Teensy, Renesas |
mcs_arduino_console(&Serial), files: MCS_ARDUINO_FS(LittleFS, …)
|
README |
ports/cortex-m |
none (bare metal) | Cortex-M0/M4/M33 reference | UART registers | below |
ports/unix |
POSIX | Linux, macOS | stdin/stdout | the mcs CLI |
Every port provides mcs_<port>_hal_init(&hal, &cfg), mcs_<port>_ticks, mcs_<port>_delay
and a console transport, which is all mcs_runtime needs.
| Hook | Bare metal | FreeRTOS | Zephyr | ESP-IDF | pico-sdk |
|---|---|---|---|---|---|
realloc_fn |
mcs_pool_realloc on a static buffer (recommended everywhere) |
same | same | same | same |
write_fn |
UART TX | UART driver |
printk / UART API |
fwrite(stdout) / uart_write_bytes
|
stdio_put_string |
ticks_fn |
SysTick ms | xTaskGetTickCount() * portTICK_PERIOD_MS |
k_uptime_get_32() |
esp_timer_get_time() / 1000 |
to_ms_since_boot(get_absolute_time()) |
delay_fn |
busy wait / WFI
|
vTaskDelay(pdMS_TO_TICKS(ms)) |
k_msleep(ms) |
vTaskDelay |
sleep_ms |
hook_fn |
feed watchdog, poll break flag | taskYIELD() |
k_yield() |
taskYIELD() |
— |
Run each VM in one task/thread — a VM is not thread-safe, but several VMs can run in
different tasks. mcs_request_abort(vm) may be called from an ISR or another task to stop a
script at the next safe point. Peripheral events from ISRs go through mcs_hal_post().
- Copy
ports/template/mcs_port_template.c. - Fill the console (
read/write),ticks,delayand themcs_hal_tmembers your chip has (GPIO first — everything else is optional; see HAL.md). - Post interrupts with
mcs_hal_post(MCS_HAL_EV_GPIO, pin, level)and timer ticks withMCS_HAL_EV_TIMER. - Optional (HAL API v3):
gpio_configure,spi_configure… so scripts can set pins, pulls, drive strength and filters with configuration objects, and anoptionstable for settings only your chip has (PERIPHERALS.md). ReturnMCS_HAL_ENOTSUPaftermcs_hal_error("why")for anything the hardware cannot do — never ignore a field. Background transfers:dma_alloc,dma_copy,spi_transfer_async… postingMCS_HAL_EV_DMAwhen done; returnMCS_HAL_ENOTSUPfor one request and the library runs it synchronously (DMA.md). A bus opened withthree_wiregets exactly one oftx/rxperspi_transfer. - Optional: persistent files — fill the template's flash port (
mcs_flash_port_t: region size, erase unit, program unit andread/write/eraseof the chip's flash;mcs_intflash_init()makes themcs_flash_t) and build withMICROCS_FS=tinyfs(built in, a few KB of internal flash is enough),littlefsoryaffs2(FILESYSTEM.md). STM32, RP2040/RP2350, ESP32 and Zephyr already have theirs. Drivers — fill the template'sws2812_writebackend forLedStrip(Servouses the HAL PWM, nothing to add), or add your own driver (DRIVERS.md). - Optional: networking — fill an
mcs_net_t(include/mcs_net.h: Wi-Fi, ESP-NOW, SNTP, mDNS, ping, TLS; leave the restNULL) and callmcs_net_set()before the HAL library opens; with lwIP / BSD socketsTcpClient,UdpClientandHttpwork as they are (NETWORK.md). Bluetooth LE — fill anmcs_ble_t(include/mcs_ble.h), callmcs_ble_set()the same way and build withMCS_ENABLE_BLE=1(BLE.md). - Run
examples/hardware/*.cson the board — they are the acceptance tests for a port. - Send a pull request: add a
tools/check_ports.sh-style compile check if the SDK headers are freely downloadable.
A minimal firmware without any SDK: startup.c (vector table, .data/.bss, stack painting,
FPU enable), cm.ld, board.c (UART + tick drivers for a minimal SoC + newlib stubs) and
main.c (pool heap, VM, RAM FS, scheduler, demo image, optional shell). Targets (make cm):
m0-runtime, m0-lowram, m0-node (48 KB RAM), m4-full, m33-full, m33-shell.
make cm-check executes them instruction by instruction and requires the output to be
byte-identical to the host interpreter — this is how the code generation, ABI, alignment,
soft/hard float paths and the memory budgets are checked for each core. To use it on a real
chip, replace board.c with your UART/SysTick code and adapt cm.ld (or use one of the
vendor ports above, which already do this).
tools/check_ports.sh compiles a port against the real vendor headers without hardware:
tools/check_ports.sh stm32 f4 STM32F446xx cortex-m4 # downloads the Cube HAL headers into build/sdk
tools/check_ports.sh stm32 h7 STM32H743xx cortex-m7Each run compiles the port in 8 configurations in parallel (default, without the ws2812 /
servo drivers, without drivers, the auto profile, and the internal flash with LittleFS, YAFFS2
and TinyFS). CI runs it for 18 popular STM32 boards across 12 families (Blue Pill, Black Pill
F401/F411, F4 Discovery, Nucleo F0/F4/F7/G0/G4/H5/H7/L0/L4/U5/WB) as parallel jobs with the ST
headers cached, builds the RP2 example for pico and pico2, and builds
the ESP-IDF example for ESP32, S3, C3 and C6 (.github/workflows/ci.yml).
Generated from the MicroCS repository - edit the files there, not in the wiki.
Start here
Step-by-step guides
- Guides
- Guide PC Simulator
- Guide ESP32
- Guide ESP32 C3 OLED
- Guide Raspberry Pi Pico
- Guide STM32
- Guide Arduino
- Guide Zephyr
- Guide Peripheral Settings
- Guide DMA
Hardware
- Hardware API
- Peripheral Configuration
- DMA
- Device Drivers
- Wi Fi and Networking
- U8g2 Displays
- Filesystem
- Threads
- Scheduler
Ports
- Porting and Build Systems
- Port ESP32
- Port RP2040 RP2350
- Port STM32
- Port STM32 Firmware
- Port Zephyr
- Port Arduino
C# and the runtime
Embedding and tuning
Internals
Project