A web-based Arduino simulator with a browser UI, server-side Arduino compilation and an interactive Arduino Preview for sketches.
Administration: For local and server installation, see
docs/INSTALL_LOCAL.mdanddocs/INSTALL_SERVER.md. Security requirements are indocs/SECURITY.md.
For a complete Docker installation on a private Ubuntu workstation without an
authentication gateway, follow docs/INSTALL_DOCKER_LOCAL.md.
- Code Editor: Monaco editor integration for writing Arduino sketches with syntax highlighting
- Compilation: Compile Arduino code through the UnoSim backend
- Serial Monitor: Real-time output display from simulated Arduino execution
- Pause/Resume Simulation: Pause running sketches to inspect state, change pin values, and resume execution
- Arduino Preview: A preview of analog/digital inputs and outputs directly in the Arduino SVG
- Web-based UI: Use the simulator in a browser; local/server installation runs the backend
- Modern UI: Built with React and TailwindCSS for a responsive, professional interface
- I/O Registry: You can see what Pins are used in your Program!
- Frontend: React, TypeScript, Vite, TailwindCSS, Radix UI
- Backend: Node.js (TypeScript), Express, WebSocket support
- Editor: Monaco Editor
- Testing: Vitest with React Testing Library
- Build Tools: Vite, esbuild
- Node.js 24.20.0 LTS (see
.nvmrc) - npm or yarn
- Clone the repository:
git clone https://github.com/MoDevIO/UnoSim.git
cd UnoSim- Install dependencies:
npm ci- Start the dev-server:
npm run dev:fullThis will start both the backend server and the frontend development server.
UnoSim can be run in several modes depending on your use case.
npm run dev:fullStarts the backend (Express + WebSocket) and the Vite dev server with hot-reload.
The backend runs via tsx (TypeScript execution) and the client is served by Vite on a separate port with HMR.
Compilation uses arduino-cli directly on the host — Docker is not required.
npm run dev starts only the backend on the local listener (127.0.0.1).
npm run dev:lan starts the same development configuration but explicitly
listens on 0.0.0.0, so another device can reach the development server over
the LAN. LAN mode is an explicit local-development convenience and is not a
production deployment mode. Both commands use the configured external-example
source, ref, and host allowlist from the development script.
The accepted target architecture keeps that server configuration as the
default, while allowing each browser to select a validated public GitHub
Examples repository and ref in Settings. The browser preference is
request-scoped, never changes the server default, and never fetches GitHub
directly. The default is ttbombadil/unosim-examples at main; the server
resolves the ref to a full commit SHA before loading a complete snapshot. See
ssot_function_definition_ExternalExamples.md.
| Component | Details |
|---|---|
| Backend | tsx server/index.ts on configured PORT (default 3000) |
| Client | Vite HMR dev server (proxied) |
| Compiler | Direct arduino-cli calls on host |
| Worker Pool | Disabled (CompilerWithFallback.usePool = false outside production) |
npm run build
npm run startBuilds the full stack (client + server + worker) into dist/ and runs the production server.
The Vite-built client is served as static files from dist/public/.
| Component | Details |
|---|---|
| Backend | node dist/index.js on configured PORT (default 3000) |
| Client | Static files from dist/public/ |
| Compiler | Configured Worker Pool (Compose reference: 8 parallel workers) |
| Docker | Required for the documented production docker-sandbox simulation mode |
Note: The development/local mode can compile directly with
arduino-cli. A production deployment usingUNOSIM_SIMULATION_MODE=docker-sandboxrequires a working Docker daemon and the sandbox image; it does not silently downgrade to host-native simulation.
docker build -t unosim-sandbox:latest -f Dockerfile.sandbox .
docker build -t unosim-server:latest .For a manual production container, use the same gateway, sandbox and mount
requirements as Compose. At minimum, production requires Gateway mode,
UNOSIM_GATEWAY_SECRET (at least 32 characters), UNOSIM_TRUSTED_PROXY, and
UNOSIM_ALLOWED_WS_ORIGINS; the backend also needs the Docker socket and the
unosim-sandbox:latest image available to the configured Docker daemon:
docker run --rm -p 3000:3000 \
-e NODE_ENV=production \
-e UNOSIM_SERVER_MODE=docker \
-e UNOSIM_SIMULATION_MODE=docker-sandbox \
-e UNOSIM_TRUST_MODE=gateway \
-e UNOSIM_GATEWAY_SECRET='<secret-from-secret-store>' \
-e UNOSIM_TRUSTED_PROXY='<gateway-ip-or-cidr>' \
-e UNOSIM_ALLOWED_WS_ORIGINS='https://classroom.example.edu' \
-e DOCKER_HOST=unix:///var/run/docker.sock \
-e DOCKER_SANDBOX_IMAGE=unosim-sandbox:latest \
-e ARDUINO_CACHE_DIR=${PWD}/server/arduino-cache \
-e UNOSIM_SHARED_TEMP_DIR=${PWD}/temp \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ${PWD}/server/arduino-cache:${PWD}/server/arduino-cache \
-v ${PWD}/temp:${PWD}/temp \
-v ${PWD}/storage:/app/storage \
unosim-server:latestOr with Docker Compose (backend only):
export DOCKER_GID="$(stat -c '%g' /var/run/docker.sock)"
export UNOSIM_GATEWAY_SECRET="<at-least-32-random-characters>"
export UNOSIM_TRUSTED_PROXY="<gateway-ip-or-cidr>"
export UNOSIM_ALLOWED_WS_ORIGINS="https://classroom.example.edu"
docker compose up --buildCompose starts the UnoSim backend only. It intentionally runs in Gateway mode;
the four variables above are mandatory and must be supplied by the deployment's
secret/environment management. A reverse proxy/auth gateway must forward the
authenticated HTTP and WebSocket requests. For a local browser without such a
gateway, use the local-trust Docker-simulation command in
docs/INSTALL_LOCAL.md instead.
Sandbox execution remains dynamic and uses the Docker socket at runtime.
If you need SonarQube, run it separately in its own stack or service; the UnoSim compose file does not include SonarQube or MCP.
The application runs inside a container and is available at http://localhost:3000.
For sandboxed sketch execution, the server container must use a temp directory that is bind-mounted from the host at the same absolute path. The provided Compose file does this via UNOSIM_SHARED_TEMP_DIR=${PWD}/temp and ${PWD}/temp:${PWD}/temp.
On macOS, make sure your project directory is allowed under Docker Desktop file sharing, otherwise the inner sandbox container cannot see generated files such as sketch.cpp.
| Command | Description |
|---|---|
npm run dev:full |
Start backend + client in development mode |
npm run dev |
Start backend only (no client) |
npm run dev:client |
Start Vite client only |
npm run build |
Build client, server, and worker for production |
npm run start |
Run the production build |
npm run check |
TypeScript type-check (tsc --noEmit) |
npm run test:fast |
Run deterministic unit tests |
npm run test:integration |
Run Arduino/toolchain integration tests |
npm run test:docker |
Run Docker sandbox integration tests |
npm run test:e2e |
Run Playwright browser tests |
npm run test:all |
Run unit, integration and Docker gates |
./run-tests.sh |
Full pipeline: lint, unit tests, Docker build, integration tests, E2E |
RUN_HEAVY_TESTS=1 ./run-tests.sh |
Full pipeline inklusive langsamem Parallel-Instanz-Stresstest |
- Sandbox Runner Pool — Manages runner leases for sketch execution. In the documented production path, each simulation runs in a short-lived isolated Docker sandbox with stdout/stderr capture for serial output and pin state reporting.
- Compilation Worker Pool — In production mode, the configured Node.js Worker Thread pool handles compilations in parallel via
CompilerWithFallback. The Compose reference uses 8 workers; capacity limits are documented indocs/SCALABILITY.md. - WebSocket Layer — Real-time communication between client and server for serial output, pin state batches, and simulation control (start/stop/pause/resume).
- SonarQube Integration — Optional SonarQube scans are wired into the pre-push hook and
./run-tests.shwhenSONAR_TOKENand a reachable SonarQube service are available. Release blocking is controlled byREQUIRE_RELEASE_GATE=1.
The repository contains a robust, fast test pipeline:
- Unit tests (Vitest + React Testing Library) cover business logic and UI
components.
npm run test:unitis the deterministic refactoring gate; toolchain, Docker, browser and load tests are separate gates. - E2E smoke flow comprises three Playwright tests in
e2e/smoke-and-flow.spec.tsfor startup, compile/run with serial output, and dialogs. The repository also contains separate responsive, board, visual, and scalability E2E suites; runnpm run test:e2efor the configured suite. - Heavy stress tests are opt-in. Use
RUN_HEAVY_TESTS=1 ./run-tests.shwhen validating Docker sandbox isolation and cleanup. Load-test commands are listed indocs/RELEASE_RUNBOOK.mdand capacity conclusions belong indocs/SCALABILITY.md.
Local quick‑check example:
npm run test:unitFor release gates, follow docs/RELEASE_RUNBOOK.md.
Testing conventions and timing budgets are maintained in
docs/TESTING_STANDARDS.md.
MIT License - See LICENSE for details
This project uses third-party open-source dependencies under their respective licenses.
- Issues & Bugs: Use the GitHub Issues tracker
- Feature Requests: Create an Pull Request
- Questions: Open a discussion or check existing issues
- Mo Tiltmann (MoDevIO) - Couven-Gymnasium, Aachen
- Tom Tiltmann (ttbombadil) - Technische Hochschule, Köln


