9 Commits

Author SHA1 Message Date
MDietermann
32ab9e03b2 docs: add Plan 1 (Foundation) implementation plan 2026-04-13 18:09:31 +02:00
MDietermann
edbbf47e9f docs: add installer architecture redesign spec
Design for Go rewrite of the Bash installer: single static binary with
Bubble Tea + Huh TUI, four-phase module contract, DAG execution engine,
embedded config + optional overlay, and a minimal Bash pre-install shim.
Scoped to installer architecture only; toolset overhaul, CLI browser,
and TTY Nerd Font support are queued as follow-up specs.
2026-04-13 18:00:29 +02:00
MDietermann
897dc424dc Updated Gitignore 2026-04-13 17:40:22 +02:00
MDietermann
9c3cdbc90f Updated Gitignore 2026-04-13 17:39:18 +02:00
MDietermann
f53663298e fix(wokwi): add serial monitor config and USB_SERIAL_JTAG for ESP32-S3
- Add serialMonitor display config to all diagram templates
- Set USB_SERIAL_JTAG attr on ESP32-S3 for built-in USB serial
- Add TX/RX → $serialMonitor wiring for ESP32 (UART-based serial)
- Remove disconnected breadboard parts (wired directly to MCU pins)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 16:40:42 +02:00
MDietermann
33f1112bf8 fix(renode): wokwi-cli releases are plain binaries, not tarballs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 16:27:07 +02:00
MDietermann
9bec4660f6 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 <leader>aw keybinding in nvim to run Wokwi simulation
- Diagram templates for: uno, mega, nano, esp32, esp32s3 (+ default)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 16:05:24 +02:00
MDietermann
9c38a8414e fix(neovim): only remove lspconfig config, keep conform and other starter plugins
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 15:27:06 +02:00
MDietermann
b512d41dc8 feat: add Renode emulator, nvim-dap debugging, and backup collision fix
- Add Renode installation (AUR/deb) with arduino-sim helper script
  supporting STM32F4, RP2040, and nRF52840 (USB HID) platforms
- Add nvim-dap + nvim-dap-ui with GDB remote adapter for Renode debugging
- Add debug keybindings under <leader>d prefix
- Fix backup_config() path collisions when multiple sources share a
  basename (e.g. ~/.config/nvim and ~/.local/share/nvim)
- Strip NvChad starter's nvim-lspconfig block that conflicts with
  native vim.lsp.config, preserving configs/lazy.lua
- Update step counts from 11 to 12 (TOTAL_STEPS 12→13, 13→14 on WSL)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 15:23:57 +02:00
16 changed files with 3225 additions and 21 deletions

3
.gitignore vendored
View File

@@ -1 +1,2 @@
.idea .idea
WokwiKey.txt

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,228 @@
# Installer Architecture Redesign
**Status:** Design approved, pending implementation plan
**Date:** 2026-04-13
**Scope:** Installer architecture only. Toolset overhaul, CLI browser, and TTY-font story are queued as follow-up specs.
## Context
The current Archduino installer is a Bash entry point (`install.sh`) that sources 13 files under `lib/`. It has grown beyond what Bash handles comfortably:
- **Monolithic / ordering-fragile.** `install.sh` sources lib files in a fixed order at top level; hard to run a single step and hard to re-run safely.
- **Not idempotent / no resume.** Re-running either duplicates work or silently skips things.
- **Mixed concerns per file.** Detection, templating, package install, and theme application are intertwined in every lib.
- **Thin UX.** Only choices are theme + verbose; no dry-run, no module selection, no preview.
- **Platform coupling.** Arch vs Ubuntu-WSL branches are scattered throughout.
The goal is a principled rebuild across all five axes.
## Decision summary
| Area | Choice |
|---|---|
| Language / runtime | Go, single static binary |
| UI | Bubble Tea + Huh (wizard for input, dashboard for execution) |
| Pre-install shim | Minimal Bash script: ensures `sudo`, `git`, `curl`, `ca-certificates`, fetches binary |
| Module contract | Four-phase: `Detect → Plan → Apply → Verify` |
| Configuration | Embedded defaults + optional `~/.config/archduino/overlay.yaml` |
| Privilege model | User-mode binary; `sudo` per privileged command; upfront `sudo -v` + keepalive goroutine |
| State / logging | `Detect()` is authoritative; run log is advisory UX only |
| Failure policy | DAG execution; failed node fails its descendants; siblings proceed; `--halt-on-error` flag available |
| Subcommands | One binary: `install`, `theme`, `new`, `doctor` |
| Platform scope (v1) | Arch-only native + WSL2; `PackageManager` interface ready for Debian/Fedora later |
## Architecture
### Module contract
Every installable unit implements:
```go
type Module interface {
Meta() ModuleMeta // id, human name, description, deps []string
Detect(ctx) (State, error) // read current system
Plan(ctx, State) (Plan, error) // diff vs desired; []Action with descriptions
Apply(ctx, Plan, *Reporter) error // execute; stream progress to Reporter
Verify(ctx) error // confirm desired state reached
}
```
- `State` is module-defined.
- `Plan` is a flat list of `Action{Description, Kind, Exec}` so the engine can render plans without knowing modules.
- `Reporter` decouples modules from the TUI.
- **`Detect()` is authoritative.** No cached state overrides what the system reports.
Initial module set (1:1 with today's `lib/*.sh`):
`packages`, `fonts`, `zsh`, `starship`, `zellij`, `neovim`, `yazi`, `arduino-cli`, `renode`, `git` (lazygit), `scaffold`, `shell-config`, `themes`.
### Execution engine
- **DAG-scheduled.** Modules declare `deps []string`. Engine topologically sorts and runs independent branches concurrently, default parallelism 4.
- **Failure policy.** Failed node marks its descendants `skipped-due-to-dep`; siblings proceed. End-of-run summary: `X succeeded, Y failed, Z skipped`.
- **Dry-run** (`--dry-run`, or wizard "Review plan" step): runs Detect + Plan for every selected module, renders the combined plan, exits.
- **Halt mode** (`--halt-on-error`): opts into fail-fast for scripted use.
- **Resume hint.** If a prior run log exists, TUI offers "last run: `starship` failed — resume? / start over? / pick modules?" Hint only; Detect decides what actually runs.
### Configuration
- **Embedded defaults** via `//go:embed`: package lists, theme files, Zellij layouts, config templates.
- **Optional overlay** at `~/.config/archduino/overlay.yaml`:
```yaml
version: 1
extra_packages: [htop, ncdu]
disabled_modules: [renode]
theme_overrides:
starship: ~/my-starship.toml
```
- Overlay is merged at load time. Schema is versioned; a migration path exists when it grows.
### Privilege model
- Binary runs as the invoking user.
- On `install` start, one `sudo -v` call; a keepalive goroutine refreshes the timestamp every 50 seconds while the installer is live.
- Privileged operations shell out to `sudo pacman …`, `sudo usermod …`. If sudo needs to re-prompt (timestamp expired), the TUI is suspended via `tea.ExecProcess`.
- Config files, git clones, and anything under `$HOME` run as the user — no root-owned files in home.
### Platform abstraction
```go
type PackageManager interface {
Install(pkgs []string) error
IsInstalled(pkg string) (bool, error)
Update() error
Name() string
}
```
- V1 ships `Pacman` only. Debian/Ubuntu/Fedora impls are stubbed; wiring them is a later spec.
- `Platform{Distro, IsWSL, Arch}` detection is centralized. WSL path sets `/etc/wsl.conf` hints and adds the user to `dialout`/`uucp`.
### State and logs
- `~/.local/state/archduino/runs/<timestamp>.json` — per-run structured log: `{module, phase, status, duration, error}`.
- `~/.local/state/archduino/last.json` — most recent run; read by TUI for the resume hint.
- Log rotation: keep the last 10 runs.
- Safe to delete at any time; never read for correctness decisions.
## User-facing surface
### Pre-install shim (`scripts/bootstrap.sh`)
~30 lines of Bash, served from a stable URL. Invocation:
```bash
curl -fsSL https://archduino.dev/install.sh | bash
```
Responsibilities in order:
1. Refuse to run as root.
2. Detect distro; abort with a clear message if not Arch (v1 scope).
3. Ensure `sudo`, `git`, `curl`, `ca-certificates` are installed (`pacman -S --needed`).
4. Detect architecture, fetch the matching release binary from GitHub Releases, verify its SHA256 against the published checksum file, install to `~/.local/bin/archduino`, `chmod +x`.
5. `exec archduino install`.
Everything further (base-devel, multilib, user groups, toolset) is handled by modules inside the binary.
### Subcommands
| Command | Purpose |
|---|---|
| `archduino install` | Default; wizard + dashboard flow |
| `archduino theme [name]` | Switch colorscheme across all tools; no arg = TUI picker |
| `archduino new <name> <board>` | Scaffold a project (current `arduino-new` behavior) |
| `archduino doctor` | Run Detect across all modules; report drift in a read-only dashboard |
| `archduino --version` / `completion <shell>` | Standard |
Global flags: `--dry-run`, `--halt-on-error`, `--verbose`, `--no-tui`, `--only <mods>`, `--skip <mods>`.
Shell aliases `theme` and `arduino-new` are preserved via thin wrappers dropped in `~/.local/bin` by the `shell-config` module.
### TUI flow (`install`)
**Wizard phase (Huh forms):**
1. Welcome — detected platform, WSL status, binary version.
2. Theme picker — 5 options, palette preview.
3. Module selector — checkbox list; pre-ticked from overlay or "all" by default; disabled modules grayed with reason.
4. Advanced (collapsible) — parallelism, halt-on-error toggle, overlay path override.
5. Review plan — rendered `Plan()` grouped by module, expandable. Back-navigable.
6. Confirm — `[y/N]` gate into Apply.
**Apply phase (Bubble Tea dashboard):**
```
┌─ archduino install ──────────────────────────────────┐
│ ✓ packages 12/12 2.4s │
│ ✓ fonts 1/1 0.8s │
│ ⠋ neovim 3/7 running: :MasonInstall… │
│ ⠋ arduino-cli 2/4 running: core install… │
│ · zellij pending (waits: packages) │
│ ✗ starship failed — config template parse │
│ ⊘ shell-config skipped (dep: starship) │
├──────────────────────────────────────────────────────┤
│ [tab] focus module [enter] expand log [q] abort │
└──────────────────────────────────────────────────────┘
```
- Concurrent modules visible live.
- `enter` on a row expands a scrollable log tail.
- On completion: summary view; footer offers `[r]etry failed / [v]iew full log / [q]uit`.
## Project layout
```
archduino/
├── cmd/archduino/ # main.go, cobra wiring
├── internal/
│ ├── engine/ # DAG scheduler, Reporter, dry-run, resume
│ ├── module/ # Module interface, Plan/Action types, registry
│ ├── modules/ # one package per module
│ │ ├── packages/
│ │ ├── zsh/
│ │ └── ...
│ ├── platform/ # detection; PackageManager interface + pacman impl
│ ├── privilege/ # sudo -v + keepalive
│ ├── config/ # embedded defaults + overlay merge
│ ├── state/ # run log, last-run tracking
│ ├── theme/ # theme model + switcher
│ └── tui/
│ ├── wizard/ # Huh forms
│ └── dashboard/ # Bubble Tea Apply view
├── assets/ # embedded configs, themes, zellij layout, templates
│ ├── themes/
│ ├── templates/
│ └── layouts/
├── scripts/bootstrap.sh
├── .github/workflows/
│ ├── ci.yml
│ └── release.yml # goreleaser
└── docs/
```
## Testing
- **Unit** — every module's `Detect` and `Plan` against an injected `System` fake; table-driven cases for clean / already-installed / partial / drifted.
- **Engine** — DAG scheduling, failure propagation, dry-run, resume; pure Go, fully covered.
- **Integration** — Dockerfile on `archlinux:base` runs `archduino install --no-tui --yes` end-to-end in CI; asserts key files/packages/services. One matrix entry for native Arch. WSL smoke test remains manual for v1.
- **TUI** — `teatest` for wizard navigation and dashboard state transitions. No screenshot tests.
## Distribution
- **GitHub Releases** via `goreleaser`: `linux/amd64`, `linux/arm64` static binaries; `SHA256SUMS` file.
- No AUR package for v1; the shim is the distribution channel. AUR can follow later.
- Semver; breaking changes bump major.
## Migration
- Existing `install.sh` + `lib/*.sh` stay on `master` at a tagged `v0-bash-final` so users can still run the old installer if they want.
- `feat/renode-dap` merges first; a new `feat/go-rewrite` branch hosts the rewrite.
- First Go release (`v1.0.0`) lands when feature parity with the current Bash installer is verified on a clean Arch VM.
## Out of scope (queued for follow-up specs)
- Toolset redesign (re-evaluating which CLI tools are curated).
- CLI browser integration.
- Nerd Font rendering on bare TTY (constrained by the Linux console's 512-glyph PSF limit; needs a separate design covering `fbterm`/`kmscon` or the "SSH-in-from-a-real-terminal" fallback).
- Non-Arch platform support (Debian/Ubuntu/Fedora).
- AUR packaging.

View File

@@ -82,6 +82,7 @@ source "$SCRIPT_DIR/lib/zellij.sh"
source "$SCRIPT_DIR/lib/neovim.sh" source "$SCRIPT_DIR/lib/neovim.sh"
source "$SCRIPT_DIR/lib/yazi.sh" source "$SCRIPT_DIR/lib/yazi.sh"
source "$SCRIPT_DIR/lib/arduino.sh" source "$SCRIPT_DIR/lib/arduino.sh"
source "$SCRIPT_DIR/lib/renode.sh"
source "$SCRIPT_DIR/lib/git.sh" source "$SCRIPT_DIR/lib/git.sh"
source "$SCRIPT_DIR/lib/scaffold.sh" source "$SCRIPT_DIR/lib/scaffold.sh"
source "$SCRIPT_DIR/lib/shell-config.sh" source "$SCRIPT_DIR/lib/shell-config.sh"
@@ -104,6 +105,9 @@ echo " arduino-new my-project uno # scaffold a project"
echo " adev # launch Arduino workspace" echo " adev # launch Arduino workspace"
echo " <leader>av # compile in Neovim" echo " <leader>av # compile in Neovim"
echo " <leader>au # upload in Neovim" echo " <leader>au # upload in Neovim"
echo " wokwi-cli . # simulate with Wokwi (visual)"
echo " arduino-sim build/fw.elf # simulate with Renode (GDB)"
echo " <leader>ds # attach debugger to Renode"
echo " theme # switch colorscheme" echo " theme # switch colorscheme"
echo "" echo ""
warn "A reboot is recommended to apply all changes (shell, groups, PATH)." warn "A reboot is recommended to apply all changes (shell, groups, PATH)."

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Step 7: Arduino CLI + board cores # Step 7: Arduino CLI + board cores
step "9/11 — Installing Arduino CLI + board cores" step "9/12 — Installing Arduino CLI + board cores"
if $IS_ARCH; then if $IS_ARCH; then
$PKG_INSTALL arduino-cli $PKG_INSTALL arduino-cli

View File

@@ -13,7 +13,7 @@ NC='\033[0m'
VERBOSE=true VERBOSE=true
LOG_FILE="/tmp/archduino-install.log" LOG_FILE="/tmp/archduino-install.log"
CURRENT_STEP=0 CURRENT_STEP=0
TOTAL_STEPS=12 TOTAL_STEPS=13
info() { echo -e "${GREEN}[✓]${NC} $1"; } info() { echo -e "${GREEN}[✓]${NC} $1"; }
warn() { echo -e "${YELLOW}[!]${NC} $1"; } warn() { echo -e "${YELLOW}[!]${NC} $1"; }
@@ -39,7 +39,7 @@ draw_progress() {
} }
begin_quiet_mode() { begin_quiet_mode() {
$IS_WSL && TOTAL_STEPS=13 $IS_WSL && TOTAL_STEPS=14
: >"$LOG_FILE" : >"$LOG_FILE"
# Save real terminal to fd 3/4, redirect everything else to log # Save real terminal to fd 3/4, redirect everything else to log
@@ -82,9 +82,12 @@ BACKUP_DIR="$HOME/.config/archduino/backups/$(date +%Y%m%d_%H%M%S)"
backup_config() { backup_config() {
local src="$1" local src="$1"
[[ ! -e "$src" ]] && return 0 [[ ! -e "$src" ]] && return 0
mkdir -p "$BACKUP_DIR" # Use parent path to avoid collisions when different sources share a basename
cp -a "$src" "$BACKUP_DIR/" # e.g. ~/.config/nvim and ~/.local/share/nvim both have basename "nvim"
info "Backed up $(basename "$src") → $BACKUP_DIR/" local dest="$BACKUP_DIR${src#"$HOME"}"
mkdir -p "$(dirname "$dest")"
cp -a "$src" "$dest"
info "Backed up ${src/#"$HOME"/\~} → ${dest/#"$HOME"/\~}"
} }
# ── TLS workaround ─────────────────────────────────────────────── # ── TLS workaround ───────────────────────────────────────────────

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Step 2: Nerd Font installation # Step 2: Nerd Font installation
step "2/11 — Installing Nerd Font" step "2/12 — Installing Nerd Font"
FONT_DIR="$HOME/.local/share/fonts/NerdFonts" FONT_DIR="$HOME/.local/share/fonts/NerdFonts"
FONT_NAME="JetBrainsMono" FONT_NAME="JetBrainsMono"

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Step 8: Git + Lazygit # Step 8: Git + Lazygit
step "10/11 — Setting up Git + Lazygit" step "11/12 — Setting up Git + Lazygit"
if $IS_ARCH; then if $IS_ARCH; then
$PKG_INSTALL lazygit $PKG_INSTALL lazygit

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Steps 5+6: Neovim + NvChad # Steps 5+6: Neovim + NvChad
step "6/11 — Installing Neovim (latest)" step "6/12 — Installing Neovim (latest)"
NVIM_NEEDED=true NVIM_NEEDED=true
if command -v nvim &>/dev/null; then if command -v nvim &>/dev/null; then
@@ -21,7 +21,7 @@ if $NVIM_NEEDED; then
info "Neovim installed: $(nvim --version | head -1)" info "Neovim installed: $(nvim --version | head -1)"
fi fi
step "7/11 — Setting up NvChad" step "7/12 — Setting up NvChad"
NVIM_CFG="$HOME/.config/nvim" NVIM_CFG="$HOME/.config/nvim"
NVCHAD_FRESH=false NVCHAD_FRESH=false
@@ -29,7 +29,7 @@ MIGRATED_PLUGINS_DIR="/tmp/archduino-migrated-plugins"
rm -rf "$MIGRATED_PLUGINS_DIR" rm -rf "$MIGRATED_PLUGINS_DIR"
# Files managed by Archduino or LazyVim-specific — skip during migration # Files managed by Archduino or LazyVim-specific — skip during migration
SKIP_PLUGINS="gruvbox.lua|theme.lua|lsp.lua|arduino.lua|lspconfig.lua" SKIP_PLUGINS="gruvbox.lua|theme.lua|lsp.lua|arduino.lua|lspconfig.lua|dap.lua"
# ── Detect existing config ────────────────────────────────────── # ── Detect existing config ──────────────────────────────────────
if [[ -d "$NVIM_CFG/lua" ]]; then if [[ -d "$NVIM_CFG/lua" ]]; then
@@ -94,6 +94,10 @@ if $NVCHAD_FRESH; then
info "NvChad starter cloned" info "NvChad starter cloned"
fi fi
# ── Strip starter's nvim-lspconfig block (we use native vim.lsp) ─
sed -i '/neovim\/nvim-lspconfig/,/},/d' "$NVIM_CFG/lua/plugins/init.lua"
rm -f "$NVIM_CFG/lua/configs/lspconfig.lua"
# ── Restore migrated plugins ───────────────────────────────────── # ── Restore migrated plugins ─────────────────────────────────────
if [[ -d "$MIGRATED_PLUGINS_DIR" ]] && ls "$MIGRATED_PLUGINS_DIR"/*.lua &>/dev/null; then if [[ -d "$MIGRATED_PLUGINS_DIR" ]] && ls "$MIGRATED_PLUGINS_DIR"/*.lua &>/dev/null; then
mkdir -p "$NVIM_CFG/lua/plugins" mkdir -p "$NVIM_CFG/lua/plugins"
@@ -165,8 +169,67 @@ vim.lsp.enable("clangd")
return {} return {}
LUA LUA
# Clean up legacy configs/lspconfig.lua if present # ── DAP (debug adapter) — for Renode GDB remote debugging ──────
rm -f "$NVIM_CFG/lua/configs/lspconfig.lua" cat >"$NVIM_CFG/lua/plugins/dap.lua" <<'LUA'
return {
{
"mfussenegger/nvim-dap",
keys = { "<leader>d" },
config = function()
local dap = require("dap")
-- GDB remote adapter (connects to Renode's GDB server)
dap.adapters.gdb_remote = {
type = "executable",
command = vim.fn.executable("arm-none-eabi-gdb") == 1
and "arm-none-eabi-gdb"
or "gdb-multiarch",
args = { "-i", "dap" },
}
-- Default debug configurations for Arduino/embedded
dap.configurations.c = {
{
name = "Attach to Renode (GDB :3333)",
type = "gdb_remote",
request = "attach",
target = "localhost:3333",
cwd = "${workspaceFolder}",
program = function()
return vim.fn.input("ELF path: ", vim.fn.getcwd() .. "/build/", "file")
end,
},
}
dap.configurations.cpp = dap.configurations.c
dap.configurations.arduino = dap.configurations.c
-- Breakpoint signs
vim.fn.sign_define("DapBreakpoint", { text = "●", texthl = "DiagnosticError" })
vim.fn.sign_define("DapStopped", { text = "▶", texthl = "DiagnosticOk", linehl = "DiffAdd" })
end,
},
{
"rcarriga/nvim-dap-ui",
dependencies = { "mfussenegger/nvim-dap", "nvim-neotest/nvim-nio" },
keys = { "<leader>du" },
config = function()
local dapui = require("dapui")
dapui.setup()
-- Auto open/close UI on debug sessions
local dap = require("dap")
dap.listeners.after.event_initialized["dapui_config"] = function() dapui.open() end
dap.listeners.before.event_terminated["dapui_config"] = function() dapui.close() end
dap.listeners.before.event_exited["dapui_config"] = function() dapui.close() end
end,
},
{
"nvim-neotest/nvim-nio",
lazy = true,
},
}
LUA
# ── Keymaps ───────────────────────────────────────────────────── # ── Keymaps ─────────────────────────────────────────────────────
cat >"$NVIM_CFG/lua/mappings.lua" <<'LUA' cat >"$NVIM_CFG/lua/mappings.lua" <<'LUA'
@@ -180,6 +243,16 @@ map("n", "<leader>av", "<cmd>ArduinoVerify<cr>", { desc = "Arduino Verify (compi
map("n", "<leader>au", "<cmd>ArduinoUpload<cr>", { desc = "Arduino Upload" }) map("n", "<leader>au", "<cmd>ArduinoUpload<cr>", { desc = "Arduino Upload" })
map("n", "<leader>as", "<cmd>ArduinoSerial<cr>", { desc = "Arduino Serial Monitor" }) map("n", "<leader>as", "<cmd>ArduinoSerial<cr>", { desc = "Arduino Serial Monitor" })
map("n", "<leader>ai", "<cmd>ArduinoInfo<cr>", { desc = "Arduino Board Info" }) map("n", "<leader>ai", "<cmd>ArduinoInfo<cr>", { desc = "Arduino Board Info" })
map("n", "<leader>aw", "<cmd>!wokwi-cli .<cr>", { desc = "Arduino Wokwi Simulate" })
-- Debug (DAP)
map("n", "<leader>ds", function() require("dap").continue() end, { desc = "Debug Start/Continue" })
map("n", "<leader>db", function() require("dap").toggle_breakpoint() end, { desc = "Debug Toggle Breakpoint" })
map("n", "<leader>dn", function() require("dap").step_over() end, { desc = "Debug Step Over" })
map("n", "<leader>di", function() require("dap").step_into() end, { desc = "Debug Step Into" })
map("n", "<leader>do", function() require("dap").step_out() end, { desc = "Debug Step Out" })
map("n", "<leader>dx", function() require("dap").terminate() end, { desc = "Debug Terminate" })
map("n", "<leader>du", function() require("dapui").toggle() end, { desc = "Debug UI Toggle" })
LUA LUA
info "NvChad configured ($THEME_DISPLAY_NAME + Arduino + LSP)" info "NvChad configured ($THEME_DISPLAY_NAME + Arduino + LSP + DAP)"

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Step 1: Base packages # Step 1: Base packages
step "1/11 — Installing base packages" step "1/12 — Installing base packages"
if $IS_ARCH; then if $IS_ARCH; then
sudo pacman -Syu --noconfirm sudo pacman -Syu --noconfirm

116
lib/renode.sh Normal file
View File

@@ -0,0 +1,116 @@
#!/bin/bash
# Step 10: Renode emulator for Arduino simulation with USB HID support
step "10/12 — Installing Renode emulator"
# gdb-multiarch is needed for nvim-dap to connect to Renode's GDB server
if $IS_ARCH; then
if ! command -v renode &>/dev/null; then
info "Installing Renode from AUR..."
yay -S --needed --noconfirm renode-bin
else
info "Renode already installed"
fi
$PKG_INSTALL arm-none-eabi-gdb
else
if ! command -v renode &>/dev/null; then
info "Installing Renode..."
RENODE_VERSION=$(curl -ks "https://api.github.com/repos/renode/renode/releases/latest" | jq -r '.tag_name' | sed 's/^v//')
curl -ksSLo /tmp/renode.deb "https://github.com/renode/renode/releases/download/v${RENODE_VERSION}/renode_${RENODE_VERSION}_amd64.deb"
sudo apt install -y /tmp/renode.deb
rm -f /tmp/renode.deb
else
info "Renode already installed"
fi
$PKG_INSTALL gdb-multiarch
fi
info "Renode installed: $(renode --version 2>/dev/null | head -1 || echo 'OK')"
# ── Renode helper script ─────────────────────────────────────────
# Provides a quick way to launch Renode with a GDB server for a firmware ELF
cat >~/.local/bin/arduino-sim <<'SIMSCRIPT'
#!/bin/bash
set -euo pipefail
usage() {
echo "Usage: arduino-sim <firmware.elf> [platform]"
echo ""
echo "Launch Renode with a GDB server for debugging firmware."
echo "Connect nvim-dap or gdb to localhost:3333"
echo ""
echo "Platforms:"
echo " stm32f4 STM32F4 Discovery (default)"
echo " rp2040 Raspberry Pi Pico"
echo " nrf52840 nRF52840 (USB HID capable)"
echo ""
echo "Example:"
echo " arduino-sim build/firmware.elf nrf52840"
exit 1
}
[[ $# -lt 1 ]] && usage
ELF="$1"
PLATFORM="${2:-stm32f4}"
[[ ! -f "$ELF" ]] && echo "Error: $ELF not found" && exit 1
# Map platform to Renode machine definition
case "$PLATFORM" in
stm32f4)
RENODE_PLATFORM="platforms/boards/stm32f4_discovery.repl"
MACHINE="STM32F4_Discovery"
;;
rp2040)
RENODE_PLATFORM="platforms/cpus/rp2040.repl"
MACHINE="RP2040"
;;
nrf52840)
RENODE_PLATFORM="platforms/cpus/nrf52840.repl"
MACHINE="nRF52840"
;;
*)
echo "Unknown platform: $PLATFORM"
usage
;;
esac
# Generate a temporary Renode script
RESC=$(mktemp /tmp/renode-XXXXXX.resc)
cat >"$RESC" <<EOF
using sysbus
mach create "$MACHINE"
machine LoadPlatformDescription @$RENODE_PLATFORM
sysbus LoadELF @$(realpath "$ELF")
machine StartGdbServer 3333
showAnalyzer sysbus.uart0 Antmicro.Renode.Analyzers.LoggingUartAnalyzer
start
EOF
echo "Starting Renode ($MACHINE) with GDB server on :3333"
echo "Connect debugger: nvim → <leader>ds or gdb → target remote :3333"
echo ""
renode --disable-xwt "$RESC"
rm -f "$RESC"
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 "https://github.com/wokwi/wokwi-cli/releases/download/v${WOKWI_VERSION}/wokwi-cli-linuxstatic-x64"
sudo install -m 755 /tmp/wokwi-cli /usr/local/bin/wokwi-cli
rm -f /tmp/wokwi-cli
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)"

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Step 9: Project scaffolding tool + templates # Step 9: Project scaffolding tool + templates
step "11/11 — Creating project scaffolding tool" step "12/12 — Creating project scaffolding tool"
mkdir -p ~/.local/bin ~/.config/archduino/templates mkdir -p ~/.local/bin ~/.config/archduino/templates
@@ -67,9 +67,38 @@ build/
*.bin *.bin
EOF 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 <<EOF
[wokwi]
version = 1
firmware = "$WOKWI_FW"
elf = "build/$PROJECT.ino.elf"
EOF
# Board-specific diagram with button + LED on breadboard
DIAGRAM_TEMPLATE="$TEMPLATE_DIR/diagram-$BOARD.json"
if [[ -f "$DIAGRAM_TEMPLATE" ]]; then
cp "$DIAGRAM_TEMPLATE" diagram.json
else
cp "$TEMPLATE_DIR/diagram-default.json" diagram.json
fi
git init -q git init -q
echo "✓ Created '$PROJECT' for $BOARD ($FQBN)" echo "✓ Created '$PROJECT' for $BOARD ($FQBN)"
echo " cd $PROJECT && nvim $PROJECT.ino" echo " cd $PROJECT && nvim $PROJECT.ino"
echo ""
echo " Simulate: arduino-cli compile --fqbn $FQBN --output-dir build ."
echo " wokwi-cli ."
SCAFFOLD SCAFFOLD
chmod +x ~/.local/bin/arduino-new chmod +x ~/.local/bin/arduino-new
@@ -135,6 +164,141 @@ void loop() {
delay(1000); delay(1000);
} }
INO INO
# ── Wokwi diagram templates ──────────────────────────────────────
cat >~/.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-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", []]
],
"serialMonitor": { "display": "auto" }
}
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-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:TX", "$serialMonitor:RX", "", []],
["esp:RX", "$serialMonitor:TX", "", []],
["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", []]
],
"serialMonitor": { "display": "auto" }
}
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, "attrs": { "serialInterface": "USB_SERIAL_JTAG" } },
{ "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", []]
],
"serialMonitor": { "display": "auto" }
}
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-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", []]
],
"serialMonitor": { "display": "auto" }
}
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-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", []]
],
"serialMonitor": { "display": "auto" }
}
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-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", []]
],
"serialMonitor": { "display": "auto" }
}
JSON
# ── Install theme files + theme switcher ───────────────────────── # ── Install theme files + theme switcher ─────────────────────────
mkdir -p ~/.config/archduino/themes mkdir -p ~/.config/archduino/themes
cp "$SCRIPT_DIR/lib/themes/"*.sh ~/.config/archduino/themes/ cp "$SCRIPT_DIR/lib/themes/"*.sh ~/.config/archduino/themes/

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Step 3: Starship prompt # Step 3: Starship prompt
step "4/11 — Installing Starship" step "4/12 — Installing Starship"
if $IS_ARCH; then if $IS_ARCH; then
$PKG_INSTALL starship $PKG_INSTALL starship

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Step 8: Yazi file manager # Step 8: Yazi file manager
step "8/11 — Installing Yazi" step "8/12 — Installing Yazi"
if $IS_ARCH; then if $IS_ARCH; then
$PKG_INSTALL yazi ffmpegthumbnailer p7zip poppler $PKG_INSTALL yazi ffmpegthumbnailer p7zip poppler

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Step 4: Zellij terminal multiplexer # Step 4: Zellij terminal multiplexer
step "5/11 — Installing Zellij" step "5/12 — Installing Zellij"
if $IS_ARCH; then if $IS_ARCH; then
$PKG_INSTALL zellij $PKG_INSTALL zellij

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Step 2: Zsh + Oh My Zsh + plugins # Step 2: Zsh + Oh My Zsh + plugins
step "3/11 — Setting up Zsh" step "3/12 — Setting up Zsh"
if [[ ! -d "$HOME/.oh-my-zsh" ]]; then if [[ ! -d "$HOME/.oh-my-zsh" ]]; then
RUNZSH=no KEEP_ZSHRC=yes CHSH=no sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended || RUNZSH=no KEEP_ZSHRC=yes CHSH=no sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended ||