M5Stack Cardputer ADV · Volume 7

M5Stack Cardputer ADV Volume 7 — Programming Environments

Arduino + PlatformIO + MicroPython + UiFlow 2 + ESP-IDF — the five paths to writing custom code

Contents

SectionTopic
1About this volume
2Arduino IDE 2.x
3PlatformIO (the recommended default)
4MicroPython + mpremote
5UiFlow 2 (block coding)
6ESP-IDF (native Espressif SDK)
7Sketch / project skeletons
8M5Cardputer + M5Unified library reference
9Build-flag inventory for Cardputer ADV
10When to use which environment
11Resources

1. About this volume

Vol 7 covers the programming environments for writing custom code on Cardputer ADV. Five paths:

EnvironmentFrictionPerformanceBest for
Arduino IDE 2.xLowCompiled (good)One-off sketches, learning
PlatformIOLow-mediumCompiled (good)Serious dev — the recommended default for non-trivial projects
MicroPython + mpremoteLowestInterpreted (~50% of compiled speed for typical code)Scripting, education, rapid prototyping
UiFlow 2LowestCompiles to MicroPythonNon-coders, block-style learning
ESP-IDFHighCompiled (excellent — full FreeRTOS access)Low-level work, custom partitions, monitor-mode Wi-Fi

The decision matrix in § 10 picks among them per use case.


2. Arduino IDE 2.x

Setup:

  1. Install Arduino IDE 2.x from https://www.arduino.cc/en/software.
  2. Add ESP32 board manager URL: Preferences → Additional Board Manager URLs → add https://espressif.github.io/arduino-esp32/package_esp32_index.json.
  3. Install ESP32 board package: Tools → Board → Boards Manager → search “esp32” → install “esp32 by Espressif Systems” (v3.0.0 or newer).
  4. Install M5Cardputer library: Sketch → Include Library → Manage Libraries → search “M5Cardputer” → install. Pulls M5Unified + M5GFX as dependencies.

Board settings (Tools menu):

SettingValue
Board”M5Stack-Cardputer” (covers ADV) OR generic “ESP32S3 Dev Module”
Upload Speed1500000
USB CDC On BootEnabled
USB ModeHardware CDC and JTAG
Flash Size8 MB
Partition Scheme”8M with spiffs (3MB APP/1.5MB SPIFFS)“
PSRAMDisabled (Cardputer ADV has no PSRAM)
Core Debug LevelNone (or Info for development)
PortAuto-detect from Tools → Port (usually /dev/ttyACM0 or COM-N)

Hello-world sketch:

#include <M5Cardputer.h>

void setup() {
    auto cfg = M5.config();
    M5Cardputer.begin(cfg, true);

    M5Cardputer.Display.setTextSize(2);
    M5Cardputer.Display.setCursor(10, 10);
    M5Cardputer.Display.println("Hello, Cardputer!");
}

void loop() {
    M5Cardputer.update();

    if (M5Cardputer.Keyboard.isChange()) {
        if (M5Cardputer.Keyboard.isPressed()) {
            Keyboard_Class::KeysState keys = M5Cardputer.Keyboard.keysState();
            for (auto c : keys.word) {
                M5Cardputer.Display.print(c);
            }
        }
    }
    delay(10);
}

Upload, see “Hello, Cardputer!” on screen, type on the keyboard to see characters appear.


Setup:

  • CLI: pip install -U platformio
  • IDE: VS Code + PlatformIO extension (Extensions panel, search “PlatformIO IDE”, install, restart VS Code)

Project skeleton for Cardputer ADV:

platformio.ini:

[env:cardputer-adv]
platform = [email protected]
board = esp32-s3-devkitc-1
framework = arduino
upload_speed = 1500000
monitor_speed = 115200
build_flags =
    -DBOARD_HAS_PSRAM=0
    -DARDUINO_USB_CDC_ON_BOOT=1
    -DARDUINO_USB_MODE=1
    -DESP32S3
    -DCORE_DEBUG_LEVEL=3
lib_deps =
    https://github.com/m5stack/M5Cardputer.git
    https://github.com/m5stack/M5Unified.git
    jgromes/RadioLib@^6.6.0     ; only if doing LoRa work

src/main.cpp:

#include <Arduino.h>
#include <M5Cardputer.h>

void setup() {
    auto cfg = M5.config();
    M5Cardputer.begin(cfg, true);

    M5Cardputer.Display.setTextSize(2);
    M5Cardputer.Display.setCursor(10, 10);
    M5Cardputer.Display.println("Hello PlatformIO");
}

void loop() {
    M5Cardputer.update();
    delay(100);
}

Build + flash:

cd ~/cardputer-project
pio run -e cardputer-adv               # Compile
pio run -e cardputer-adv -t upload     # Compile + flash
pio device monitor                     # Serial monitor

Why PlatformIO over Arduino IDE for non-trivial projects:

  • Dependency pinninglib_deps locks library versions; reproducible builds across machines and time.
  • Multi-environment[env:cardputer-adv] + [env:t-display-s3] etc. in the same platformio.ini for cross-platform builds.
  • Better source treesrc/ + lib/ + test/ conventions match how Bruce / Marauder / NEMO source trees are laid out.
  • Integrated debugger — JTAG / SWD debugging via Espressif’s USB-JTAG bridge (works on Cardputer ADV’s native USB).
  • CI/CD friendlypio run works in GitHub Actions, GitLab CI, etc.

For modifying existing community firmwares (Bruce, Marauder, NEMO): they all use PlatformIO. Clone the repo, open in VS Code, build.


4. MicroPython + mpremote

Setup:

  1. Flash M5Stack-flavored MicroPython build to Cardputer ADV. M5Burner has this in its firmware catalog as “UIFlow Code” (the underlying MicroPython is the same).
  2. Install mpremote: pip install -U mpremote
  3. Access REPL: mpremote connect /dev/ttyACM0 repl

Hello-world:

# At the REPL:
import M5
M5.begin()

# Display text
Lcd.print("Hello MicroPython", 10, 10)

# Read a key
key = Keyboard.read()
print(f"You pressed: {key}")

File operations:

# Upload a script
mpremote connect /dev/ttyACM0 cp myapp.py :/myapp.py

# Run a script
mpremote connect /dev/ttyACM0 exec "exec(open('/myapp.py').read())"

# List files
mpremote connect /dev/ttyACM0 ls /

# Delete a file
mpremote connect /dev/ttyACM0 rm /myapp.py

Why MicroPython over Arduino/PlatformIO:

  • No compile step — write Python, run immediately. Edit-test loop measured in seconds, not minutes.
  • REPL — interactive exploration of the hardware. Probe sensors, test display drawing, experiment.
  • Easier for non-C++ devs — Python syntax is more accessible.

Why NOT MicroPython:

  • ~50% slower than compiled C++ for typical workloads. CPU-bound code (audio FFT, emulators, packet capture at high rate) is materially slower.
  • More RAM-hungry (interpreter overhead).
  • Smaller library ecosystem than Arduino ecosystem.

For Cardputer ADV: MicroPython is excellent for scripting + learning + custom MicroHydra apps. For high-performance / production firmware: Arduino + PlatformIO.


5. UiFlow 2 (block coding)

Setup:

  1. Flash UiFlow firmware to Cardputer ADV (M5Burner catalog → “UIFlow 2 Cardputer-Adv” → Burn).
  2. Open https://flow.m5stack.com in browser. Pair via USB-CDC (Web Serial).
  3. Drag blocks from the toolbox onto the canvas. Compile to MicroPython under the hood. Run.

Block categories:

  • M5Stack blocks: display, keyboard, speaker, microphone, IMU, IR, LED, SD card
  • M5 Units / HATs / Caps: drag-and-drop blocks for every M5Stack peripheral with auto-detected pin assignments
  • Logic blocks: if/else, loops, conditions
  • Math blocks: arithmetic, comparison, etc.
  • Variables blocks: read / write
  • Functions blocks: define + call

Why UiFlow over MicroPython / Arduino:

  • Lowest barrier of any path. Useful for: education, kids, non-coders, very rapid prototyping.
  • Visual debugging — see the program flow as blocks light up during execution.

Power users typically graduate to MicroPython (mpremote) once they hit UiFlow’s expressiveness ceiling — at which point the underlying MicroPython is the natural next step (UiFlow generates MicroPython, so the transition is gradual).


6. ESP-IDF (native Espressif SDK)

Setup:

# Clone ESP-IDF
git clone -b master --recursive https://github.com/espressif/esp-idf.git ~/esp-idf
cd ~/esp-idf

# Install toolchain for ESP32-S3
./install.sh esp32s3

# Source the environment (every shell session)
. ./export.sh

Hello-world project:

cd ~/projects
idf.py create-project cardputer_hello
cd cardputer_hello
idf.py set-target esp32s3
idf.py menuconfig                  # Configure: USB CDC on boot, flash 8 MB, etc.
idf.py build
idf.py -p /dev/ttyACM0 -b 1500000 flash monitor

Why ESP-IDF over Arduino + PlatformIO:

  • Custom partition tables — Arduino’s partition options are predefined; ESP-IDF lets you build any layout.
  • Monitor-mode Wi-Fiesp_wifi_set_promiscuous(true) works in Arduino too, but ESP-IDF exposes more control over the underlying ESP32-S3 Wi-Fi peripheral.
  • Secure boot v2 — ESP-IDF’s secure-boot toolchain is much better than Arduino’s (which mostly hides it).
  • Native FreeRTOS — direct task scheduling, custom IPC, priority management.
  • Flash encryption — fully exposed in ESP-IDF.
  • 802.11 frame injection — works in Arduino, but ESP-IDF has more reliable / well-documented APIs.

Why NOT ESP-IDF:

  • Steeper learning curve.
  • More boilerplate per project.
  • Smaller community than Arduino + PlatformIO for Cardputer ADV specifically.

When to reach for ESP-IDF: building production firmware with secure boot, custom flash encryption, or hardware-feature work Arduino doesn’t cleanly expose. For most Cardputer ADV use cases, Arduino + PlatformIO suffices.

M5Stack publishes the official factory firmware as an ESP-IDF starting point. Useful reference for IDF-on-Cardputer-ADV.


7. Sketch / project skeletons

Three canonical project skeletons (templates live in ../../04-templates/):

Arduino single-file .ino (one-off sketches):

#include <M5Cardputer.h>

void setup() {
    auto cfg = M5.config();
    M5Cardputer.begin(cfg, true);
    M5Cardputer.Display.println("Hello");
}

void loop() {
    M5Cardputer.update();
    // ... your code ...
    delay(10);
}

PlatformIO multi-file project:

my-project/
├── platformio.ini
├── src/
│   ├── main.cpp
│   └── my_module.cpp
├── include/
│   └── my_module.h
└── lib/
    └── (auto-fetched via lib_deps)

MicroHydra app (.py on SD at /apps/<AppName>/__init__.py):

from lib.hydra.app import App
from lib.display import Display

class HelloApp(App):
    name = "HelloWorld"
    icon = "rocket"
    def main(self):
        d = Display()
        d.text("Hello", 10, 10)
        while not self.exit_pressed():
            self.tick()

8. M5Cardputer + M5Unified library reference

Key APIs from m5stack/M5Cardputer (which extends m5stack/M5Unified):

Initialization:

#include <M5Cardputer.h>

void setup() {
    auto cfg = M5.config();
    M5Cardputer.begin(cfg, true);    // true = enable M5GFX display backend
}

Display (M5Cardputer.Display, descended from M5GFX):

M5Cardputer.Display.fillScreen(BLACK);
M5Cardputer.Display.setTextColor(WHITE, BLACK);
M5Cardputer.Display.setTextSize(2);             // Multiplier on 6×8 default font
M5Cardputer.Display.setCursor(10, 20);
M5Cardputer.Display.printf("Hello %d", value);
M5Cardputer.Display.drawLine(0, 0, 100, 100, RED);
M5Cardputer.Display.fillRect(0, 0, 50, 50, BLUE);
M5Cardputer.Display.drawString("Centered", 120, 67);

Keyboard (M5Cardputer.Keyboard):

M5Cardputer.update();   // Must call every loop

if (M5Cardputer.Keyboard.isChange()) {           // True on any key state change
    if (M5Cardputer.Keyboard.isPressed()) {       // True if any key currently pressed
        Keyboard_Class::KeysState keys = M5Cardputer.Keyboard.keysState();
        // keys.word: characters of currently-pressed keys
        // keys.modifiers: ctrl, alt, shift, fn states
        // keys.del: backspace key pressed
        // keys.enter: enter key pressed
    }
}

Speaker / audio:

M5Cardputer.Speaker.tone(440, 100);              // 440 Hz for 100 ms
M5Cardputer.Speaker.playRaw(audioBuffer, length); // Play PCM audio

IMU (BMI270):

auto data = M5Cardputer.Imu.getAccelData();
float ax = data.x, ay = data.y, az = data.z;     // in g
data = M5Cardputer.Imu.getGyroData();
// rotation rates in deg/sec

Battery telemetry:

int level = M5Cardputer.Power.getBatteryLevel();  // 0-100
float voltage = M5Cardputer.Power.getBatteryVoltage();

SD card (use Arduino SD.h):

#include <SD.h>
SD.begin(12);   // CS = GPIO 12 on Cardputer ADV
File f = SD.open("/myfile.txt", FILE_WRITE);
f.println("Hello SD");
f.close();

Full API reference: M5Cardputer / M5Unified GitHub READMEs.


9. Build-flag inventory for Cardputer ADV

Key build_flags in platformio.ini for Cardputer ADV builds:

FlagPurposeRequired
-DBOARD_HAS_PSRAM=0Disable PSRAM (Cardputer ADV has none)Yes
-DARDUINO_USB_CDC_ON_BOOT=1Native USB-CDC at bootYes for serial console
-DARDUINO_USB_MODE=1Device mode (1) or Host OTG (0)1 for typical apps; 0 for USB-host work
-DESP32S3Compile-time ESP32-S3 identifierImplicit but explicit for clarity
-DCORE_DEBUG_LEVEL=3Serial debug verbosity (0-5)0 for production, 3 for development
-DCONFIG_ESP32_PHY_MAX_TX_POWER=20Max Wi-Fi TX power in dBmOptional
-DDISABLE_ALL_LIBRARY_WARNINGSSuppress library warningsOptional

If using TFT_eSPI directly (rare — M5GFX wraps it):

build_flags =
    -DUSER_SETUP_LOADED=1
    -DST7789_DRIVER
    -DTFT_WIDTH=240
    -DTFT_HEIGHT=135
    -DTFT_MISO=39
    -DTFT_MOSI=14
    -DTFT_SCLK=40
    -DTFT_CS=10
    -DTFT_DC=9
    -DTFT_RST=8
    -DTFT_BL=13
    -DTFT_BACKLIGHT_ON=HIGH
    -DLOAD_GLCD
    -DLOAD_FONT2
    -DLOAD_FONT4
    -DLOAD_FONT6
    -DSPI_FREQUENCY=40000000

(Cross-ref Vol 3 § 2 for the GPIO assignments these flags reference.)


10. When to use which environment

Use caseEnvironmentWhy
Learning, educationUiFlow 2 → MicroPythonLowest barrier, visual feedback first
Quick test / one-off sketchArduino IDECompile + flash in one click
Rapid scripting prototypeMicroPython + mpremoteREPL iteration, no compile
Multi-file production projectPlatformIOBetter than Arduino IDE for >1 source file; reproducible builds
Modifying existing firmware (Bruce, Marauder, NEMO, M5Launcher)PlatformIOAll major firmwares use PlatformIO
Custom partition tablePlatformIO or ESP-IDFArduino IDE has predefined options only
Wi-Fi monitor modeArduino + PlatformIOesp_wifi_set_promiscuous() works fine
802.11 frame injectionESP-IDF (preferred) or ArduinoESP-IDF has cleaner APIs
Secure boot v2ESP-IDFArduino doesn’t cleanly expose
Production firmware with flash encryptionESP-IDFSame
MicroHydra appMicroPythonRequired (MicroHydra IS MicroPython)
ESPHome deviceESPHome (own toolchain)Required
BadUSB DuckyScriptEither text file on SD (no compile) or compile a BadCard fork in PlatformIODuckyScript runs interpreted

The recommended default for tjscientist: Arduino IDE for first-time learning + PlatformIO for serious development.


11. Resources

Tools / IDEs

M5Stack libraries

Espressif docs

MicroPython

UiFlow 2

Forward references


This is Volume 7 of a twelve-volume series. Next: Vol 8 covers flashing and updating firmware — M5Burner, web flashers, esptool.py, OTA, factory backup, and crash-loop recovery.