diff --git a/README.md b/README.md new file mode 100644 index 0000000..b33320d --- /dev/null +++ b/README.md @@ -0,0 +1,413 @@ +# Studuino on Linux with Neovim + +A development template for using the Artec Studuino (ATmega168/328, 3.3 V, 8 MHz) on Arch Linux with `arduino-cli`, `clangd`, and Neovim. + +This repository contains the Studuino hardware definition, the legacy Studuino libraries, an Arduino CLI setup script, and optional Neovim integration resources. + +## Features + +- Manual installation of the legacy Studuino hardware platform. +- Arduino CLI project defaults for `studuino:avr:studuino`. +- Installation of the required `Servo` library. +- Optional cleanup of unrelated user libraries. +- A generated `compile_commands.json` for `clangd` and Neovim. +- Optional `picocom` serial-monitor support. +- A LazyVim/Arduino-Nvim configuration template. + +## Requirements + +- Arch Linux or another Linux distribution with `bash`. +- An Artec Studuino board connected over USB. +- A user account with permission to use serial devices. +- Internet access for the initial Arduino CLI core/library installation. + +The setup script installs these Arch packages by default: + +- `arduino-cli` +- `clangd` +- `make` +- `neovim` +- `python` +- `unzip` + +It can optionally install `picocom` with `--with-monitor`. + +## Quick start + +From the repository root: + +```bash +cd /path/to/this/repository +./setup.sh +``` + +The script performs the following tasks: + +1. Installs required Arch packages. +2. Adds the current user to `uucp` and `dialout`. +3. Initializes the Arduino CLI configuration if necessary. +4. Installs the AVR core when it is missing. +5. Installs the Studuino hardware definition. +6. Installs the libraries from `Resources/studuino_v1_lib.zip`. +7. Installs `Servo`, which is required by `Studuino.h`. +8. Ensures the sketch and default FQBN are configured. +9. Generates `compile_commands.json` for `clangd`. +10. Verifies board discovery and compilation. + +Log out and back in after the first run if the script adds you to a new serial-port group. Group changes do not affect the current login session immediately. + +## Setup options + +Show all options: + +```bash +./setup.sh --help +``` + +Useful options: + +```bash +# Do not install Arch packages or modify group membership +./setup.sh --skip-packages --skip-permissions + +# Install picocom for a serial monitor +./setup.sh --with-monitor + +# Remove user libraries not supplied by the Studuino bundle +./setup.sh --clean + +# Replace existing Studuino library and hardware files +./setup.sh --force + +# Skip regeneration of compile_commands.json +./setup.sh --skip-compile-db +``` + +`--clean` is destructive. It preserves the libraries supplied in the Studuino ZIP and `Servo`, but removes other directories under the configured Arduino `libraries` directory. Back up any libraries you want to keep. + +`--force` overwrites existing Studuino library and hardware files. Use it only when intentionally refreshing the Studuino installation. + +## Project layout + +```text +. +├── Arduino.ino # Main sketch +├── sketch.yaml # arduino-cli project defaults +├── .arduino_config.lua # Arduino-Nvim board/port configuration +├── setup.sh # Idempotent setup script +├── compile_commands.json # clangd compilation database +├── hardware/ +│ └── studuino/ +│ └── avr/ +│ ├── boards.txt +│ ├── platform.txt +│ └── platform.local.txt +├── libraries/ # Arduino user libraries +└── Resources/ + ├── arduino_IDE_hardware_settings_ver17x.zip + ├── studuino_v1_lib.zip + └── nvim/ +``` + +The Studuino platform is manually installed because it predates the modern Boards Manager package format. + +## Board and port configuration + +The default target is: + +```text +studuino:avr:studuino +``` + +The project defaults are in `sketch.yaml`: + +```yaml +default_fqbn: studuino:avr:studuino +default_port: /dev/ttyUSB0 +``` + +Update `default_port` if the board appears at another device, such as: + +```text +/dev/ttyACM0 +/dev/ttyUSB1 +``` + +Check connected boards with: + +```bash +arduino-cli board list +``` + +List all boards known to Arduino CLI, including the manually installed Studuino platform: + +```bash +arduino-cli board listall | grep -i studuino +``` + +Expected output includes: + +```text +Studuino (3.3V, 8 MHz) w/ ATmega168 studuino:avr:studuino +``` + +If the port changes, update both `sketch.yaml` and `.arduino_config.lua`, or pass the port explicitly when uploading. + +## Compile and upload + +Compile the current sketch using the project defaults: + +```bash +arduino-cli compile +``` + +Compile with an explicit target: + +```bash +arduino-cli compile --fqbn studuino:avr:studuino . +``` + +Upload using the port from `sketch.yaml`: + +```bash +arduino-cli upload +``` + +Upload with an explicit port: + +```bash +arduino-cli upload \ + --fqbn studuino:avr:studuino \ + --port /dev/ttyUSB0 +``` + +The setup script verifies the project with a clean build. The Studuino target is an ATmega168 target, so the available program storage is smaller than a default Arduino Uno/ATmega328 build. + +### Compile the bundled Blink example + +The Studuino library includes a test example: + +```bash +arduino-cli compile \ + --fqbn studuino:avr:studuino \ + libraries/Studuino/example/Blink +``` + +## Neovim and clangd + +The setup script installs `clangd` and generates `compile_commands.json` in the project root. `clangd` automatically searches the project directory for this file. + +The initial database is generated from the minimal sketch, so it may not contain the Studuino library include paths. If clangd reports `Studuino.h file not found` after adding `#include `, refresh the database with the setup script after changing the sketch. + +If you add a new library or change the FQBN, refresh the database with: + +```bash +./setup.sh --skip-packages --skip-permissions +``` + +Or generate it directly: + +```bash +build_dir="${XDG_CACHE_HOME:-$HOME/.cache}/arduino/studuino-compile-db" +mkdir -p "$build_dir" + +arduino-cli compile \ + --clean \ + --fqbn studuino:avr:studuino \ + --only-compilation-database \ + --build-path "$build_dir" \ + . + +cp "$build_dir/compile_commands.json" ./compile_commands.json +``` + +### Minimal clangd configuration + +For Neovim 0.11 or newer, add this to a Lua configuration or `lazy.nvim` plugin specification: + +```lua +vim.lsp.config("clangd", { + cmd = { + "clangd", + "--compile-commands-dir=" .. vim.fn.getcwd(), + }, +}) + +vim.lsp.enable("clangd") +``` + +For older `nvim-lspconfig` versions, use: + +```lua +require("lspconfig").clangd.setup({ + cmd = { + "clangd", + "--compile-commands-dir=" .. vim.fn.getcwd(), + }, +}) +``` + +Restart `clangd` after changing the board or library configuration if needed. + +## Arduino-Nvim integration + +An optional LazyVim plugin specification is included at: + +```text +Resources/nvim/lua/plugins/arduino.lua +``` + +Copy it into your Neovim configuration if needed: + +```bash +mkdir -p ~/.config/nvim/lua/plugins +cp Resources/nvim/lua/plugins/arduino.lua \ + ~/.config/nvim/lua/plugins/arduino.lua +``` + +The plugin file expects the board and port configuration in the project root: + +```text +.arduino_config.lua +``` + +The repository already contains this file: + +```lua +return { + board = "studuino:avr:studuino", + port = "/dev/ttyUSB0", + baudrate = "115200", +} +``` + +The included keymaps are: + +| Mapping | Action | +| --- | --- | +| `mc` | Compile and verify | +| `mu` | Upload | +| `ms` | Show board, port, and FQBN status | +| `mg` | Open the board/port GUI | +| `mm` | Open the serial monitor | +| `ml` | Open the library manager | +| `mb` | Select a board | +| `mp` | Select a port | +| `mr` | Upload with manual reset | + +See `Resources/nvim/README.md` for the full Neovim resource notes. + +## Serial monitor + +Install `picocom` through the setup script: + +```bash +./setup.sh --with-monitor +``` + +Open the monitor at the configured baud rate: + +```bash +picocom /dev/ttyUSB0 --baud 115200 +``` + +Exit `picocom` with: + +```text +Ctrl-A, Ctrl-X +``` + +The monitor and upload commands cannot use the serial port simultaneously. Close the monitor before uploading. + +## Troubleshooting + +### `invalid version directory` + +If Arduino CLI reports a path like: + +```text +~/Arduino/hardware/studuino/studuino/avr +``` + +the hardware directory has an extra `studuino` component. Run: + +```bash +./setup.sh --skip-packages --skip-permissions +``` + +The supported path is: + +```text +~/Arduino/hardware/studuino/avr/boards.txt +~/Arduino/hardware/studuino/avr/platform.txt +``` + +### `Servo.h` not found + +`Studuino.h` includes the Servo library, but Servo is distributed separately from the Studuino ZIP. Install it with: + +```bash +arduino-cli lib install Servo +``` + +Or rerun: + +```bash +./setup.sh --skip-packages --skip-permissions +``` + +### Permission denied for `/dev/ttyUSB0` + +Check the current groups: + +```bash +id -nG +``` + +The user should belong to `uucp` and/or `dialout`. If the setup script added the groups, log out and back in. + +You can also check the device permissions with: + +```bash +ls -l /dev/ttyUSB* /dev/ttyACM* +``` + +### No board is detected + +Try: + +```bash +arduino-cli board list +``` + +If no board appears: + +1. Check the USB cable and board power. +2. Check `/dev/ttyUSB*` or `/dev/ttyACM*`. +3. Verify serial-port group membership. +4. Try another USB port or cable. +5. Run `arduino-cli board listall | grep -i studuino` to confirm that the platform is installed. + +### The wrong board is used + +Confirm that `sketch.yaml` contains: + +```yaml +default_fqbn: studuino:avr:studuino +``` + +The key must be `default_fqbn`; a top-level `fqbn` key is not used as the default by the installed Arduino CLI version. + +## Re-running setup + +The setup script is designed to be rerun. It skips existing packages, libraries, and core installations unless `--force` is supplied. It always verifies the final board, libraries, and project compilation. + +To inspect all available options: + +```bash +./setup.sh --help +``` + +## License and source materials + +The Studuino hardware archive and Studuino library archive are kept in `Resources/` because they use a legacy/manual Arduino hardware layout. Keep the archives with the project so the setup can be recreated on another Linux machine. diff --git a/Resources/Arduino_environment_setup.pdf b/Resources/Arduino_environment_setup.pdf new file mode 100644 index 0000000..53080b7 Binary files /dev/null and b/Resources/Arduino_environment_setup.pdf differ diff --git a/Resources/Studuino_library_reference.pdf b/Resources/Studuino_library_reference.pdf new file mode 100644 index 0000000..765e10b Binary files /dev/null and b/Resources/Studuino_library_reference.pdf differ diff --git a/compile_commands.json b/compile_commands.json index acbdeb0..865ce12 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -53,6 +53,33 @@ ], "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/CDC.cpp" }, + { + "directory": "/home/alvin/Arduino", + "arguments": [ + "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++", + "-c", + "-g", + "-Os", + "-w", + "-std=gnu++11", + "-fno-exceptions", + "-ffunction-sections", + "-fdata-sections", + "-fno-threadsafe-statics", + "-MMD", + "-mmcu=atmega168", + "-DF_CPU=8000000L", + "-DARDUINO=10607", + "-DARDUINO_AVR_PRO", + "-DARDUINO_ARCH_AVR", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial1.cpp", + "-o", + "/home/alvin/.cache/arduino/studuino-compile-db/core/HardwareSerial1.cpp.o" + ], + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial1.cpp" + }, { "directory": "/home/alvin/Arduino", "arguments": [ @@ -107,60 +134,6 @@ ], "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial3.cpp" }, - { - "directory": "/home/alvin/Arduino", - "arguments": [ - "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++", - "-c", - "-g", - "-Os", - "-w", - "-std=gnu++11", - "-fno-exceptions", - "-ffunction-sections", - "-fdata-sections", - "-fno-threadsafe-statics", - "-MMD", - "-mmcu=atmega168", - "-DF_CPU=8000000L", - "-DARDUINO=10607", - "-DARDUINO_AVR_PRO", - "-DARDUINO_ARCH_AVR", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial2.cpp", - "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/HardwareSerial2.cpp.o" - ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial2.cpp" - }, - { - "directory": "/home/alvin/Arduino", - "arguments": [ - "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++", - "-c", - "-g", - "-Os", - "-w", - "-std=gnu++11", - "-fno-exceptions", - "-ffunction-sections", - "-fdata-sections", - "-fno-threadsafe-statics", - "-MMD", - "-mmcu=atmega168", - "-DF_CPU=8000000L", - "-DARDUINO=10607", - "-DARDUINO_AVR_PRO", - "-DARDUINO_ARCH_AVR", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/PluggableUSB.cpp", - "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/PluggableUSB.cpp.o" - ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/PluggableUSB.cpp" - }, { "directory": "/home/alvin/Arduino", "arguments": [ @@ -209,11 +182,11 @@ "-DARDUINO_ARCH_AVR", "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial1.cpp", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial2.cpp", "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/HardwareSerial1.cpp.o" + "/home/alvin/.cache/arduino/studuino-compile-db/core/HardwareSerial2.cpp.o" ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial1.cpp" + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial2.cpp" }, { "directory": "/home/alvin/Arduino", @@ -263,11 +236,11 @@ "-DARDUINO_ARCH_AVR", "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial0.cpp", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/Tone.cpp", "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/HardwareSerial0.cpp.o" + "/home/alvin/.cache/arduino/studuino-compile-db/core/Tone.cpp.o" ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial0.cpp" + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/Tone.cpp" }, { "directory": "/home/alvin/Arduino", @@ -290,11 +263,36 @@ "-DARDUINO_ARCH_AVR", "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/Stream.cpp", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial0.cpp", "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/Stream.cpp.o" + "/home/alvin/.cache/arduino/studuino-compile-db/core/HardwareSerial0.cpp.o" ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/Stream.cpp" + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/HardwareSerial0.cpp" + }, + { + "directory": "/home/alvin/Arduino", + "arguments": [ + "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-gcc", + "-c", + "-g", + "-Os", + "-w", + "-std=gnu11", + "-ffunction-sections", + "-fdata-sections", + "-MMD", + "-mmcu=atmega168", + "-DF_CPU=8000000L", + "-DARDUINO=10607", + "-DARDUINO_AVR_PRO", + "-DARDUINO_ARCH_AVR", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/WInterrupts.c", + "-o", + "/home/alvin/.cache/arduino/studuino-compile-db/core/WInterrupts.c.o" + ], + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/WInterrupts.c" }, { "directory": "/home/alvin/Arduino", @@ -323,6 +321,60 @@ ], "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/WMath.cpp" }, + { + "directory": "/home/alvin/Arduino", + "arguments": [ + "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++", + "-c", + "-g", + "-Os", + "-w", + "-std=gnu++11", + "-fno-exceptions", + "-ffunction-sections", + "-fdata-sections", + "-fno-threadsafe-statics", + "-MMD", + "-mmcu=atmega168", + "-DF_CPU=8000000L", + "-DARDUINO=10607", + "-DARDUINO_AVR_PRO", + "-DARDUINO_ARCH_AVR", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/Stream.cpp", + "-o", + "/home/alvin/.cache/arduino/studuino-compile-db/core/Stream.cpp.o" + ], + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/Stream.cpp" + }, + { + "directory": "/home/alvin/Arduino", + "arguments": [ + "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++", + "-c", + "-g", + "-Os", + "-w", + "-std=gnu++11", + "-fno-exceptions", + "-ffunction-sections", + "-fdata-sections", + "-fno-threadsafe-statics", + "-MMD", + "-mmcu=atmega168", + "-DF_CPU=8000000L", + "-DARDUINO=10607", + "-DARDUINO_AVR_PRO", + "-DARDUINO_ARCH_AVR", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/PluggableUSB.cpp", + "-o", + "/home/alvin/.cache/arduino/studuino-compile-db/core/PluggableUSB.cpp.o" + ], + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/PluggableUSB.cpp" + }, { "directory": "/home/alvin/Arduino", "arguments": [ @@ -350,189 +402,6 @@ ], "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/USBCore.cpp" }, - { - "directory": "/home/alvin/Arduino", - "arguments": [ - "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++", - "-c", - "-g", - "-Os", - "-w", - "-std=gnu++11", - "-fno-exceptions", - "-ffunction-sections", - "-fdata-sections", - "-fno-threadsafe-statics", - "-MMD", - "-mmcu=atmega168", - "-DF_CPU=8000000L", - "-DARDUINO=10607", - "-DARDUINO_AVR_PRO", - "-DARDUINO_ARCH_AVR", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/Tone.cpp", - "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/Tone.cpp.o" - ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/Tone.cpp" - }, - { - "directory": "/home/alvin/Arduino", - "arguments": [ - "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++", - "-c", - "-g", - "-Os", - "-w", - "-std=gnu++11", - "-fno-exceptions", - "-ffunction-sections", - "-fdata-sections", - "-fno-threadsafe-statics", - "-MMD", - "-mmcu=atmega168", - "-DF_CPU=8000000L", - "-DARDUINO=10607", - "-DARDUINO_AVR_PRO", - "-DARDUINO_ARCH_AVR", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/WString.cpp", - "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/WString.cpp.o" - ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/WString.cpp" - }, - { - "directory": "/home/alvin/Arduino", - "arguments": [ - "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-gcc", - "-c", - "-g", - "-Os", - "-w", - "-std=gnu11", - "-ffunction-sections", - "-fdata-sections", - "-MMD", - "-mmcu=atmega168", - "-DF_CPU=8000000L", - "-DARDUINO=10607", - "-DARDUINO_AVR_PRO", - "-DARDUINO_ARCH_AVR", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/WInterrupts.c", - "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/WInterrupts.c.o" - ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/WInterrupts.c" - }, - { - "directory": "/home/alvin/Arduino", - "arguments": [ - "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++", - "-c", - "-g", - "-Os", - "-w", - "-std=gnu++11", - "-fno-exceptions", - "-ffunction-sections", - "-fdata-sections", - "-fno-threadsafe-statics", - "-MMD", - "-mmcu=atmega168", - "-DF_CPU=8000000L", - "-DARDUINO=10607", - "-DARDUINO_AVR_PRO", - "-DARDUINO_ARCH_AVR", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/main.cpp", - "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/main.cpp.o" - ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/main.cpp" - }, - { - "directory": "/home/alvin/Arduino", - "arguments": [ - "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++", - "-c", - "-g", - "-Os", - "-w", - "-std=gnu++11", - "-fno-exceptions", - "-ffunction-sections", - "-fdata-sections", - "-fno-threadsafe-statics", - "-MMD", - "-mmcu=atmega168", - "-DF_CPU=8000000L", - "-DARDUINO=10607", - "-DARDUINO_AVR_PRO", - "-DARDUINO_ARCH_AVR", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/abi.cpp", - "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/abi.cpp.o" - ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/abi.cpp" - }, - { - "directory": "/home/alvin/Arduino", - "arguments": [ - "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-gcc", - "-c", - "-g", - "-Os", - "-w", - "-std=gnu11", - "-ffunction-sections", - "-fdata-sections", - "-MMD", - "-mmcu=atmega168", - "-DF_CPU=8000000L", - "-DARDUINO=10607", - "-DARDUINO_AVR_PRO", - "-DARDUINO_ARCH_AVR", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_analog.c", - "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/wiring_analog.c.o" - ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_analog.c" - }, - { - "directory": "/home/alvin/Arduino", - "arguments": [ - "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-gcc", - "-c", - "-g", - "-Os", - "-w", - "-std=gnu11", - "-ffunction-sections", - "-fdata-sections", - "-MMD", - "-mmcu=atmega168", - "-DF_CPU=8000000L", - "-DARDUINO=10607", - "-DARDUINO_AVR_PRO", - "-DARDUINO_ARCH_AVR", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_digital.c", - "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/wiring_digital.c.o" - ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_digital.c" - }, { "directory": "/home/alvin/Arduino", "arguments": [ @@ -588,35 +457,16 @@ { "directory": "/home/alvin/Arduino", "arguments": [ - "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-gcc", - "-c", - "-g", - "-x", - "assembler-with-cpp", - "-mmcu=atmega168", - "-DF_CPU=8000000L", - "-DARDUINO=10607", - "-DARDUINO_AVR_PRO", - "-DARDUINO_ARCH_AVR", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", - "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_pulse.S", - "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/wiring_pulse.S.o" - ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_pulse.S" - }, - { - "directory": "/home/alvin/Arduino", - "arguments": [ - "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-gcc", + "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++", "-c", "-g", "-Os", "-w", - "-std=gnu11", + "-std=gnu++11", + "-fno-exceptions", "-ffunction-sections", "-fdata-sections", + "-fno-threadsafe-statics", "-MMD", "-mmcu=atmega168", "-DF_CPU=8000000L", @@ -625,11 +475,38 @@ "-DARDUINO_ARCH_AVR", "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", - "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/hooks.c", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/abi.cpp", "-o", - "/home/alvin/.cache/arduino/studuino-compile-db/core/hooks.c.o" + "/home/alvin/.cache/arduino/studuino-compile-db/core/abi.cpp.o" ], - "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/hooks.c" + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/abi.cpp" + }, + { + "directory": "/home/alvin/Arduino", + "arguments": [ + "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++", + "-c", + "-g", + "-Os", + "-w", + "-std=gnu++11", + "-fno-exceptions", + "-ffunction-sections", + "-fdata-sections", + "-fno-threadsafe-statics", + "-MMD", + "-mmcu=atmega168", + "-DF_CPU=8000000L", + "-DARDUINO=10607", + "-DARDUINO_AVR_PRO", + "-DARDUINO_ARCH_AVR", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/WString.cpp", + "-o", + "/home/alvin/.cache/arduino/studuino-compile-db/core/WString.cpp.o" + ], + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/WString.cpp" }, { "directory": "/home/alvin/Arduino", @@ -656,6 +533,77 @@ ], "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_pulse.c" }, + { + "directory": "/home/alvin/Arduino", + "arguments": [ + "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-gcc", + "-c", + "-g", + "-Os", + "-w", + "-std=gnu11", + "-ffunction-sections", + "-fdata-sections", + "-MMD", + "-mmcu=atmega168", + "-DF_CPU=8000000L", + "-DARDUINO=10607", + "-DARDUINO_AVR_PRO", + "-DARDUINO_ARCH_AVR", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/hooks.c", + "-o", + "/home/alvin/.cache/arduino/studuino-compile-db/core/hooks.c.o" + ], + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/hooks.c" + }, + { + "directory": "/home/alvin/Arduino", + "arguments": [ + "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-gcc", + "-c", + "-g", + "-x", + "assembler-with-cpp", + "-mmcu=atmega168", + "-DF_CPU=8000000L", + "-DARDUINO=10607", + "-DARDUINO_AVR_PRO", + "-DARDUINO_ARCH_AVR", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_pulse.S", + "-o", + "/home/alvin/.cache/arduino/studuino-compile-db/core/wiring_pulse.S.o" + ], + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_pulse.S" + }, + { + "directory": "/home/alvin/Arduino", + "arguments": [ + "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-gcc", + "-c", + "-g", + "-Os", + "-w", + "-std=gnu11", + "-ffunction-sections", + "-fdata-sections", + "-MMD", + "-mmcu=atmega168", + "-DF_CPU=8000000L", + "-DARDUINO=10607", + "-DARDUINO_AVR_PRO", + "-DARDUINO_ARCH_AVR", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_digital.c", + "-o", + "/home/alvin/.cache/arduino/studuino-compile-db/core/wiring_digital.c.o" + ], + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_digital.c" + }, { "directory": "/home/alvin/Arduino", "arguments": [ @@ -680,5 +628,57 @@ "/home/alvin/.cache/arduino/studuino-compile-db/core/wiring_shift.c.o" ], "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_shift.c" + }, + { + "directory": "/home/alvin/Arduino", + "arguments": [ + "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-gcc", + "-c", + "-g", + "-Os", + "-w", + "-std=gnu11", + "-ffunction-sections", + "-fdata-sections", + "-MMD", + "-mmcu=atmega168", + "-DF_CPU=8000000L", + "-DARDUINO=10607", + "-DARDUINO_AVR_PRO", + "-DARDUINO_ARCH_AVR", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_analog.c", + "-o", + "/home/alvin/.cache/arduino/studuino-compile-db/core/wiring_analog.c.o" + ], + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/wiring_analog.c" + }, + { + "directory": "/home/alvin/Arduino", + "arguments": [ + "/home/alvin/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++", + "-c", + "-g", + "-Os", + "-w", + "-std=gnu++11", + "-fno-exceptions", + "-ffunction-sections", + "-fdata-sections", + "-fno-threadsafe-statics", + "-MMD", + "-mmcu=atmega168", + "-DF_CPU=8000000L", + "-DARDUINO=10607", + "-DARDUINO_AVR_PRO", + "-DARDUINO_ARCH_AVR", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino", + "-I/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/variants/standard", + "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/main.cpp", + "-o", + "/home/alvin/.cache/arduino/studuino-compile-db/core/main.cpp.o" + ], + "file": "/home/alvin/.arduino15/packages/arduino/hardware/avr/1.8.8/cores/arduino/main.cpp" } ] \ No newline at end of file diff --git a/hardware/studuino/avr/platform.local.txt b/hardware/studuino/avr/platform.local.txt new file mode 100644 index 0000000..1775d96 --- /dev/null +++ b/hardware/studuino/avr/platform.local.txt @@ -0,0 +1,7 @@ +# Generated by setup.sh for modern arduino-cli AVR tool paths. +compiler.path={runtime.tools.avr-gcc.path}/bin/ +archive_file_path={build.path}/{archive_file} +recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}" +tools.avrdude.path={runtime.tools.avrdude.path} +tools.avrdude.cmd.path={path}/bin/avrdude +tools.avrdude.config.path={path}/etc/avrdude.conf diff --git a/setup.sh b/setup.sh old mode 100644 new mode 100755 index 3601211..e7ae3cf --- a/setup.sh +++ b/setup.sh @@ -1,11 +1,459 @@ -#/bin/bash! -set -eu +#!/usr/bin/env bash +set -Eeuo pipefail -# Add user to serial permissions -sudo usermod -a -G uucp,dialout $USER +# Set up the Artec Studuino sketchbook and its command-line workflow. -# Install Arduino cli -sudo pacman -S arduino-cli +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +ARDUINO_USER_OVERRIDE="${ARDUINO_USER:-}" +ARDUINO_USER="${ARDUINO_USER:-$HOME/Arduino}" +RESOURCE_DIR="$SCRIPT_DIR/Resources" +LIBRARY_ARCHIVE="$RESOURCE_DIR/studuino_v1_lib.zip" +HARDWARE_ARCHIVE="$RESOURCE_DIR/arduino_IDE_hardware_settings_ver17x.zip" +FQBN="studuino:avr:studuino" +BUILD_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/arduino/studuino-compile-db" +VERIFY_BUILD_DIR="$BUILD_DIR/verify" -# Install Studuino hardware definition -arduino-cli config init +FORCE=0 +CLEAN_LIBRARIES=0 +SKIP_PACKAGES=0 +SKIP_PERMISSIONS=0 +SKIP_COMPILE_DB=0 +WITH_MONITOR=0 +CONFIG_FILE="${ARDUINO_CONFIG_FILE:-}" +TEMP_DIRS=() + +cleanup() { + local temporary + for temporary in "${TEMP_DIRS[@]}"; do + [[ -d "$temporary" ]] && rm -rf -- "$temporary" + done +} +trap cleanup EXIT + +usage() { + cat <<'EOF' +Usage: ./setup.sh [options] + +Options: + --force Replace existing Studuino library/hardware files. + --clean Remove user libraries not supplied by the Studuino bundle. + --skip-packages Do not install Arch packages. + --skip-permissions Do not modify serial-port group membership. + --skip-compile-db Do not generate compile_commands.json. + --with-monitor Also install picocom for a serial monitor. + -h, --help Show this help. + +Environment: + ARDUINO_USER Arduino sketchbook directory (default: ~/Arduino). + ARDUINO_CONFIG_FILE Optional arduino-cli configuration file. +EOF +} + +while (($# > 0)); do + case "$1" in + --force) + FORCE=1 + ;; + --clean) + CLEAN_LIBRARIES=1 + ;; + --skip-packages) + SKIP_PACKAGES=1 + ;; + --skip-permissions) + SKIP_PERMISSIONS=1 + ;; + --skip-compile-db) + SKIP_COMPILE_DB=1 + ;; + --with-monitor) + WITH_MONITOR=1 + ;; + -h|--help) + usage + exit 0 + ;; + *) + printf 'Unknown option: %s\n' "$1" >&2 + usage >&2 + exit 2 + ;; + esac + shift +done + +if ((EUID == 0)); then + ROOT_CMD=() + TARGET_USER="${SUDO_USER:-$(id -un)}" +else + ROOT_CMD=(sudo) + TARGET_USER="${USER:-$(id -un)}" +fi + +CLI=(arduino-cli) +if [[ -n "$CONFIG_FILE" ]]; then + CLI+=(--config-file "$CONFIG_FILE") +fi + +info() { + printf '[studuino] %s\n' "$*" +} + +warn() { + printf '[studuino] WARNING: %s\n' "$*" >&2 +} + +die() { + printf '[studuino] ERROR: %s\n' "$*" >&2 + exit 1 +} + +as_root() { + if ((${#ROOT_CMD[@]} > 0)); then + "${ROOT_CMD[@]}" "$@" + else + "$@" + fi +} + +run_cli() { + "${CLI[@]}" "$@" +} + +require_command() { + command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1" +} + +install_packages() { + if ((SKIP_PACKAGES)); then + info "Skipping Arch package installation." + else + require_command pacman + local -a packages=( + arduino-cli + clangd + make + neovim + python + unzip + ) + if ((WITH_MONITOR)); then + packages+=(picocom) + fi + info "Installing required Arch packages." + as_root pacman -S --needed "${packages[@]}" + fi + + require_command arduino-cli + require_command python3 + require_command unzip +} + +ensure_serial_permissions() { + if ((SKIP_PERMISSIONS)); then + info "Skipping serial-port group membership changes." + return + fi + + require_command id + require_command getent + + local current_groups + current_groups="$(id -nG "$TARGET_USER" 2>/dev/null || true)" + local changed=0 + local group + + for group in uucp dialout; do + if ! getent group "$group" >/dev/null 2>&1; then + warn "Serial-port group '$group' does not exist; skipping it." + continue + fi + if ! grep -qw -- "$group" <<<"$current_groups"; then + info "Adding $TARGET_USER to serial-port group '$group'." + as_root usermod -a -G "$group" "$TARGET_USER" + changed=1 + fi + done + + if ((changed)); then + warn "Log out and back in before uploading to a serial device." + fi +} + +init_cli_config() { + if [[ -n "$CONFIG_FILE" ]]; then + mkdir -p -- "$(dirname -- "$CONFIG_FILE")" + if [[ ! -f "$CONFIG_FILE" ]]; then + info "Creating arduino-cli config: $CONFIG_FILE" + arduino-cli config init --dest-file "$CONFIG_FILE" >/dev/null + fi + elif [[ ! -f "$HOME/.arduino15/arduino-cli.yaml" ]]; then + info "Creating the default arduino-cli config." + arduino-cli config init >/dev/null + fi +} + +resolve_arduino_user() { + if [[ -n "$ARDUINO_USER_OVERRIDE" ]]; then + run_cli config set directories.user "$ARDUINO_USER" >/dev/null + info "Using configured Arduino sketchbook: $ARDUINO_USER" + return + fi + + local configured_user + configured_user="$(run_cli config get directories.user 2>/dev/null | sed -n '1p' || true)" + if [[ -n "$configured_user" && "$configured_user" != "null" ]]; then + ARDUINO_USER="$configured_user" + fi +} + +ensure_hardware_layout() { + local vendor_dir="$ARDUINO_USER/hardware/studuino" + local hardware_dir="$vendor_dir/avr" + local nested_dir="$vendor_dir/studuino/avr" + + # Older versions of the setup instructions could create this extra level. + if [[ -d "$nested_dir" ]]; then + if [[ ! -e "$hardware_dir" ]]; then + info "Moving the legacy nested Studuino hardware path." + mv -- "$nested_dir" "$hardware_dir" + rmdir -- "$vendor_dir/studuino" 2>/dev/null || true + elif ((FORCE)); then + info "Removing the redundant nested Studuino hardware path." + rm -rf -- "$nested_dir" + rmdir -- "$vendor_dir/studuino" 2>/dev/null || true + else + die "Both $hardware_dir and $nested_dir exist; re-run with --force to remove the nested copy." + fi + fi + + if [[ -f "$hardware_dir/boards.txt" && -f "$hardware_dir/platform.txt" ]] && (( FORCE == 0 )); then + info "Studuino hardware files are already installed; keeping them." + else + [[ -f "$HARDWARE_ARCHIVE" ]] || die "Hardware archive not found: $HARDWARE_ARCHIVE" + mkdir -p -- "$hardware_dir" + + # The official archive was created on Windows and uses backslashes in ZIP + # entry names. Extract only the Linux/other Studuino platform files. + python3 - "$HARDWARE_ARCHIVE" "$hardware_dir" "$FORCE" <<'PY' +from pathlib import Path +import sys +from zipfile import ZipFile + +archive = Path(sys.argv[1]) +destination = Path(sys.argv[2]) +force = sys.argv[3] == "1" + +wanted = { + "arduino_ide_settings_ver17x/other/hardware/studuino/avr/boards.txt": "boards.txt", + "arduino_ide_settings_ver17x/other/hardware/studuino/avr/platform.txt": "platform.txt", +} +found = set() + +with ZipFile(archive) as zipped: + for info in zipped.infolist(): + normalized = info.filename.replace("\\", "/") + target_name = wanted.get(normalized) + if target_name is None: + continue + target = destination / target_name + if target.exists() and not force: + print(f"[studuino] Keeping existing {target}") + else: + target.write_bytes(zipped.read(info)) + print(f"[studuino] Installed {target}") + found.add(target_name) + +missing = set(wanted.values()) - found +if missing: + raise SystemExit("Missing hardware files in archive: " + ", ".join(sorted(missing))) +PY + fi + + # The archive targets the legacy IDE's tool paths. These overrides make it + # work with the AVR tools installed by arduino-cli. + local local_properties="$hardware_dir/platform.local.txt" + cat >"$local_properties" <<'EOF' +# Generated by setup.sh for modern arduino-cli AVR tool paths. +compiler.path={runtime.tools.avr-gcc.path}/bin/ +archive_file_path={build.path}/{archive_file} +recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}" +tools.avrdude.path={runtime.tools.avrdude.path} +tools.avrdude.cmd.path={path}/bin/avrdude +tools.avrdude.config.path={path}/etc/avrdude.conf +EOF + info "Wrote modern CLI compatibility overrides: $local_properties" + + grep -q '^studuino.name=' "$hardware_dir/boards.txt" \ + || die "The installed Studuino boards.txt is missing its board definition." + grep -q '^name=' "$hardware_dir/platform.txt" \ + || die "The installed Studuino platform.txt is missing its platform name." +} + +ensure_avr_core() { + if run_cli core list | awk '$1 == "arduino:avr" { found = 1 } END { exit !found }'; then + info "AVR core is already installed." + return + fi + + info "Installing the Arduino AVR core." + run_cli core update-index + run_cli core install arduino:avr +} + +install_studuino_libraries() { + [[ -f "$LIBRARY_ARCHIVE" ]] || die "Library archive not found: $LIBRARY_ARCHIVE" + + local temporary + temporary="$(mktemp -d)" + TEMP_DIRS+=("$temporary") + + unzip -q "$LIBRARY_ARCHIVE" -d "$temporary" + local bundle_dir="$temporary/studuino_v1_lib" + [[ -d "$bundle_dir/Studuino" ]] || die "Unexpected Studuino library archive layout." + + mkdir -p -- "$ARDUINO_USER/libraries" + local source_dir destination name + for source_dir in "$bundle_dir"/*; do + [[ -d "$source_dir" ]] || continue + name="${source_dir##*/}" + destination="$ARDUINO_USER/libraries/$name" + + if [[ -L "$destination" ]]; then + die "Refusing to replace symlinked library: $destination" + fi + if [[ -e "$destination" ]] && ((FORCE == 0)); then + info "Keeping existing Studuino library: $destination" + else + if [[ -e "$destination" ]]; then + rm -rf -- "$destination" + fi + cp -a -- "$source_dir" "$destination" + info "Installed Studuino library: $destination" + fi + done + + if ((CLEAN_LIBRARIES)); then + local -A keep=() + for source_dir in "$bundle_dir"/*; do + [[ -d "$source_dir" ]] || continue + keep["${source_dir##*/}"]=1 + done + # Servo is required by Studuino even though it is not in the ZIP. + keep[Servo]=1 + + local installed_dir installed_name + for installed_dir in "$ARDUINO_USER/libraries"/*; do + [[ -d "$installed_dir" && ! -L "$installed_dir" ]] || continue + installed_name="${installed_dir##*/}" + if [[ -z "${keep[$installed_name]+present}" ]]; then + rm -rf -- "$installed_dir" + info "Removed non-Studuino library: $installed_dir" + fi + done + fi +} + +ensure_servo() { + if [[ -n "$(find "$ARDUINO_USER/libraries" -type f -name Servo.h -print -quit 2>/dev/null)" ]]; then + info "Servo library is already installed." + return + fi + + if run_cli lib list | awk '$1 == "Servo" { found = 1 } END { exit !found }'; then + info "Servo library is already registered." + return + fi + + info "Installing Servo, required by Studuino.h." + run_cli lib install Servo +} + +ensure_project_files() { + local metadata="$SCRIPT_DIR/sketch.yaml" + if [[ ! -f "$metadata" ]]; then + cat >"$metadata" <>"$metadata" + fi + + local sketch="$SCRIPT_DIR/Arduino.ino" + if [[ ! -f "$sketch" ]]; then + if [[ -f "$SCRIPT_DIR/sketch.ino" ]]; then + info "Renaming sketch.ino to Arduino.ino." + mv -- "$SCRIPT_DIR/sketch.ino" "$sketch" + else + cat >"$sketch" <<'EOF' +#include + +void setup() { + // Add Studuino initialization here. +} + +void loop() { + // Add Studuino code here. +} +EOF + info "Created a minimal sketch at $sketch" + fi + elif [[ -f "$SCRIPT_DIR/sketch.ino" ]]; then + warn "Both Arduino.ino and sketch.ino exist; leaving sketch.ino untouched." + fi +} + +generate_compile_database() { + if ((SKIP_COMPILE_DB)); then + info "Skipping compile_commands.json generation." + return + fi + + mkdir -p -- "$BUILD_DIR" + info "Generating the Neovim/clangd compilation database." + if run_cli compile --clean --fqbn "$FQBN" --only-compilation-database \ + --build-path "$BUILD_DIR" "$SCRIPT_DIR"; then + cp -- "$BUILD_DIR/compile_commands.json" "$SCRIPT_DIR/compile_commands.json" + info "Wrote $SCRIPT_DIR/compile_commands.json" + else + warn "Could not generate compile_commands.json; the Studuino setup is otherwise complete." + fi +} + +verify_setup() { + info "Verifying board discovery..." + run_cli board listall | grep -F "$FQBN" >/dev/null \ + || die "arduino-cli cannot discover $FQBN." + + info "Verifying the Studuino and Servo libraries..." + run_cli lib list | grep -E '^Studuino[[:space:]]' >/dev/null \ + || die "The Studuino library is not visible to arduino-cli." + run_cli lib list | grep -E '^Servo[[:space:]]' >/dev/null \ + || die "The Servo library is not visible to arduino-cli." + + info "Compiling the project for $FQBN..." + run_cli compile --clean --fqbn "$FQBN" \ + --build-path "$VERIFY_BUILD_DIR" "$SCRIPT_DIR" +} + +install_packages +ensure_serial_permissions +init_cli_config +resolve_arduino_user +ensure_hardware_layout +ensure_avr_core +install_studuino_libraries +ensure_servo +ensure_project_files +generate_compile_database +verify_setup + +info "Studuino setup complete." +info "Compile with: arduino-cli compile" +info "Check the port with: arduino-cli board list" +info "Upload after connecting the board with: arduino-cli upload"