From 9bec4660f61fb4075f7c3a2f27f68f8bc1774d01 Mon Sep 17 00:00:00 2001 From: MDietermann Date: Fri, 10 Apr 2026 16:05:24 +0200 Subject: [PATCH] feat: add Wokwi CLI for visual breadboard simulation - Install wokwi-cli binary from GitHub releases - Generate wokwi.toml and board-specific diagram.json in arduino-new with a button + LED breadboard layout for each supported board - Add aw keybinding in nvim to run Wokwi simulation - Diagram templates for: uno, mega, nano, esp32, esp32s3 (+ default) Co-Authored-By: Claude Opus 4.6 --- install.sh | 3 +- lib/neovim.sh | 1 + lib/renode.sh | 13 ++++ lib/scaffold.sh | 162 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 1 deletion(-) diff --git a/install.sh b/install.sh index fa7fcb3..c650786 100644 --- a/install.sh +++ b/install.sh @@ -105,7 +105,8 @@ echo " arduino-new my-project uno # scaffold a project" echo " adev # launch Arduino workspace" echo " av # compile in Neovim" echo " au # upload in Neovim" -echo " arduino-sim build/fw.elf # simulate with Renode" +echo " wokwi-cli . # simulate with Wokwi (visual)" +echo " arduino-sim build/fw.elf # simulate with Renode (GDB)" echo " ds # attach debugger to Renode" echo " theme # switch colorscheme" echo "" diff --git a/lib/neovim.sh b/lib/neovim.sh index bf30bf4..a691e99 100644 --- a/lib/neovim.sh +++ b/lib/neovim.sh @@ -243,6 +243,7 @@ map("n", "av", "ArduinoVerify", { desc = "Arduino Verify (compi map("n", "au", "ArduinoUpload", { desc = "Arduino Upload" }) map("n", "as", "ArduinoSerial", { desc = "Arduino Serial Monitor" }) map("n", "ai", "ArduinoInfo", { desc = "Arduino Board Info" }) +map("n", "aw", "!wokwi-cli .", { desc = "Arduino Wokwi Simulate" }) -- Debug (DAP) map("n", "ds", function() require("dap").continue() end, { desc = "Debug Start/Continue" }) diff --git a/lib/renode.sh b/lib/renode.sh index c3c2ac1..d2676f8 100644 --- a/lib/renode.sh +++ b/lib/renode.sh @@ -102,3 +102,16 @@ SIMSCRIPT chmod +x ~/.local/bin/arduino-sim info "Renode simulation helper created (arduino-sim)" + +# ── Wokwi CLI (visual breadboard simulation) ──────────────────── +if ! command -v wokwi-cli &>/dev/null; then + info "Installing Wokwi CLI..." + WOKWI_VERSION=$(curl -ks "https://api.github.com/repos/wokwi/wokwi-cli/releases/latest" | jq -r '.tag_name' | sed 's/^v//') + curl -ksSLo /tmp/wokwi-cli.tar.gz "https://github.com/wokwi/wokwi-cli/releases/download/v${WOKWI_VERSION}/wokwi-cli-linuxstatic-x64.tar.gz" + tar xzf /tmp/wokwi-cli.tar.gz -C /tmp wokwi-cli + sudo install -m 755 /tmp/wokwi-cli /usr/local/bin/wokwi-cli + rm -f /tmp/wokwi-cli /tmp/wokwi-cli.tar.gz +else + info "Wokwi CLI already installed" +fi +info "Wokwi CLI installed (set WOKWI_CLI_TOKEN to enable — get one at https://wokwi.com/dashboard/ci)" diff --git a/lib/scaffold.sh b/lib/scaffold.sh index 825af5d..d1dca02 100644 --- a/lib/scaffold.sh +++ b/lib/scaffold.sh @@ -67,9 +67,38 @@ build/ *.bin EOF +# ── Wokwi simulation config ───────────────────────────────────── +# Determine firmware path format based on board family +case "$BOARD" in + esp32|esp32s3|esp8266) + WOKWI_FW="build/$PROJECT.ino.merged.bin" + ;; + *) + WOKWI_FW="build/$PROJECT.ino.hex" + ;; +esac + +cat > wokwi.toml <~/.config/archduino/templates/diagram-default.json <<'JSON' +{ + "version": 1, + "author": "Archduino", + "editor": "wokwi", + "parts": [ + { "type": "wokwi-arduino-uno", "id": "mcu", "top": 0, "left": 0 }, + { "type": "wokwi-breadboard-half", "id": "bb1", "top": 200, "left": -80 }, + { "type": "wokwi-led", "id": "led1", "top": 110, "left": 50, "attrs": { "color": "green" } }, + { "type": "wokwi-resistor", "id": "r1", "top": 150, "left": 30, "attrs": { "value": "220" } }, + { "type": "wokwi-pushbutton", "id": "btn1", "top": 110, "left": 150, "attrs": { "color": "blue" } } + ], + "connections": [ + ["mcu:2", "led1:A", "green", []], + ["led1:C", "r1:1", "black", []], + ["r1:2", "mcu:GND.1", "black", []], + ["mcu:4", "btn1:1.l", "blue", []], + ["btn1:2.l", "mcu:GND.2", "black", []] + ] +} +JSON + +cat >~/.config/archduino/templates/diagram-esp32.json <<'JSON' +{ + "version": 1, + "author": "Archduino", + "editor": "wokwi", + "parts": [ + { "type": "board-esp32-devkit-c-v4", "id": "esp", "top": 0, "left": 0 }, + { "type": "wokwi-breadboard-half", "id": "bb1", "top": 200, "left": -80 }, + { "type": "wokwi-led", "id": "led1", "top": 110, "left": 50, "attrs": { "color": "green" } }, + { "type": "wokwi-resistor", "id": "r1", "top": 150, "left": 30, "attrs": { "value": "220" } }, + { "type": "wokwi-pushbutton", "id": "btn1", "top": 110, "left": 150, "attrs": { "color": "blue" } } + ], + "connections": [ + ["esp:2", "led1:A", "green", []], + ["led1:C", "r1:1", "black", []], + ["r1:2", "esp:GND.1", "black", []], + ["esp:4", "btn1:1.l", "blue", []], + ["btn1:2.l", "esp:GND.2", "black", []] + ] +} +JSON + +cat >~/.config/archduino/templates/diagram-esp32s3.json <<'JSON' +{ + "version": 1, + "author": "Archduino", + "editor": "wokwi", + "parts": [ + { "type": "board-esp32-s3-devkitc-1", "id": "esp", "top": 0, "left": 0 }, + { "type": "wokwi-breadboard-half", "id": "bb1", "top": 200, "left": -80 }, + { "type": "wokwi-led", "id": "led1", "top": 110, "left": 50, "attrs": { "color": "green" } }, + { "type": "wokwi-resistor", "id": "r1", "top": 150, "left": 30, "attrs": { "value": "220" } }, + { "type": "wokwi-pushbutton", "id": "btn1", "top": 110, "left": 150, "attrs": { "color": "blue" } } + ], + "connections": [ + ["esp:2", "led1:A", "green", []], + ["led1:C", "r1:1", "black", []], + ["r1:2", "esp:GND.1", "black", []], + ["esp:4", "btn1:1.l", "blue", []], + ["btn1:2.l", "esp:GND.2", "black", []] + ] +} +JSON + +cat >~/.config/archduino/templates/diagram-uno.json <<'JSON' +{ + "version": 1, + "author": "Archduino", + "editor": "wokwi", + "parts": [ + { "type": "wokwi-arduino-uno", "id": "mcu", "top": 0, "left": 0 }, + { "type": "wokwi-breadboard-half", "id": "bb1", "top": 200, "left": -80 }, + { "type": "wokwi-led", "id": "led1", "top": 110, "left": 50, "attrs": { "color": "green" } }, + { "type": "wokwi-resistor", "id": "r1", "top": 150, "left": 30, "attrs": { "value": "220" } }, + { "type": "wokwi-pushbutton", "id": "btn1", "top": 110, "left": 150, "attrs": { "color": "blue" } } + ], + "connections": [ + ["mcu:2", "led1:A", "green", []], + ["led1:C", "r1:1", "black", []], + ["r1:2", "mcu:GND.1", "black", []], + ["mcu:4", "btn1:1.l", "blue", []], + ["btn1:2.l", "mcu:GND.2", "black", []] + ] +} +JSON + +cat >~/.config/archduino/templates/diagram-mega.json <<'JSON' +{ + "version": 1, + "author": "Archduino", + "editor": "wokwi", + "parts": [ + { "type": "wokwi-arduino-mega", "id": "mcu", "top": 0, "left": 0 }, + { "type": "wokwi-breadboard-half", "id": "bb1", "top": 200, "left": -80 }, + { "type": "wokwi-led", "id": "led1", "top": 110, "left": 50, "attrs": { "color": "green" } }, + { "type": "wokwi-resistor", "id": "r1", "top": 150, "left": 30, "attrs": { "value": "220" } }, + { "type": "wokwi-pushbutton", "id": "btn1", "top": 110, "left": 150, "attrs": { "color": "blue" } } + ], + "connections": [ + ["mcu:2", "led1:A", "green", []], + ["led1:C", "r1:1", "black", []], + ["r1:2", "mcu:GND.1", "black", []], + ["mcu:4", "btn1:1.l", "blue", []], + ["btn1:2.l", "mcu:GND.2", "black", []] + ] +} +JSON + +cat >~/.config/archduino/templates/diagram-nano.json <<'JSON' +{ + "version": 1, + "author": "Archduino", + "editor": "wokwi", + "parts": [ + { "type": "wokwi-arduino-nano", "id": "mcu", "top": 0, "left": 0 }, + { "type": "wokwi-breadboard-half", "id": "bb1", "top": 200, "left": -80 }, + { "type": "wokwi-led", "id": "led1", "top": 110, "left": 50, "attrs": { "color": "green" } }, + { "type": "wokwi-resistor", "id": "r1", "top": 150, "left": 30, "attrs": { "value": "220" } }, + { "type": "wokwi-pushbutton", "id": "btn1", "top": 110, "left": 150, "attrs": { "color": "blue" } } + ], + "connections": [ + ["mcu:D2", "led1:A", "green", []], + ["led1:C", "r1:1", "black", []], + ["r1:2", "mcu:GND.1", "black", []], + ["mcu:D4", "btn1:1.l", "blue", []], + ["btn1:2.l", "mcu:GND.2", "black", []] + ] +} +JSON + # ── Install theme files + theme switcher ───────────────────────── mkdir -p ~/.config/archduino/themes cp "$SCRIPT_DIR/lib/themes/"*.sh ~/.config/archduino/themes/