Notes for getting the repo running from scratch on macOS / Linux. Windows users:
use WSL — several scripts depend on Unix tools (lsof, pkill, fuser,
ifconfig, /dev/tcp/...) that aren't available in PowerShell or Git Bash.
git clone https://github.com/calstar/STAR.git
cd STARThe repo is a monorepo of mostly-independent projects, each with its own
top-level directory, its own README, and its own setup.sh:
daq-server/— the DAQ server / FSW (C++ + TypeScript backend + Next.js GUI)firmware/— Arduino/PlatformIO firmware for every board (PT, TC, RTD, LC, Encoder, Actuator), subtree ofcalstar/DiabloAvionicsEngineDesign/— engine design & optimization pipeline (Python + FastAPI + React)pid-designer/— P&ID editor (FastAPI + React Flow)star-openrocket/— Onshape centre-of-mass viewer (FastAPI + React/three.js)lib/DAQv2-Comms/— wire-protocol library shared by bothdaq-serverandfirmware/(the latter via a symlink atfirmware/libraries/DAQv2-Comms)
firmware/libraries/DAQv2-Comms is a relative symlink to lib/DAQv2-Comms.
On macOS / Linux this works out of the box; on Windows you need:
git config --global core.symlinks truebefore cloning, OR use WSL (recommended — see above).
./setup.sh # interactive menu
./setup.sh --daq-server # single project (non-interactive)
./setup.sh --all --yes # everything, non-interactive
./setup.sh --list # list projects and exit
./setup.sh --help # all flagsSetup also offers to add the STAR shell aliases to your ~/.bashrc (or
~/.zshrc) — engine-dev, daq-logs, star-status, and the rest; run
star-help afterwards for the list. Pass --no-aliases to skip, and it never
adds a duplicate if you already source them.
The top-level setup.sh is a dispatcher over per-project setup scripts.
Each subproject owns its own steps; you only install what you'll actually use:
| Project | Script | Approx. install time (cold) |
|---|---|---|
pid-designer/ |
pid-designer/setup.sh |
~30s (Python venv + npm) |
star-openrocket/ |
star-openrocket/setup.sh |
~40s (Python venv + npm) |
EngineDesign/ |
EngineDesign/setup.sh |
~2 min (--ci skips rocketcea) |
firmware/ |
firmware/setup.sh |
~30s (PlatformIO CLI only) |
daq-server/ |
daq-server/setup.sh |
~5-10 min (C++ + Rust + Node + Python) |
Any flags the dispatcher doesn't consume are passed through to the sub-scripts,
so e.g. ./setup.sh --engine-design --ci --no-frontend works. Each sub-script
is also runnable directly (e.g. bash daq-server/setup.sh --no-build); run it
with --help to see what it accepts.
The shared bits (black==25.11.0 on the global $PATH for format.sh, a
Windows-symlink sanity check) run once in the dispatcher.
star-openrocket/setup.sh additionally writes an empty, gitignored
star-openrocket/.env for the Onshape API key pair. Fill it in from
https://dev-portal.onshape.com before building a model — no script in this repo
ever holds a credential. Onshape bills per API call against a finite quota, so
see star-openrocket/README.md for what each action
costs; the tests and CI make no calls at all.
New to the repo, or is setup misbehaving? scripts/setup-test/ runs setup.sh
in an isolated scratch copy of the tree and then smoke-checks the result — so
you can confirm a fresh install works end-to-end, or reproduce a breakage
without touching your real checkout:
bash scripts/setup-test/run-macos.sh pid-designer # quick sanity (~2 min)
bash scripts/setup-test/run-macos.sh all # every project (~20-30 min)On a brand-new Mac, install Homebrew first (https://brew.sh) — the harness
installs project deps through brew but not brew itself. See
scripts/setup-test/README.md for the
Docker/Linux path and full details.
The integration test (daq-server/test/test_integration.sh) launches the whole
stack — Elodin DB, DAQ bridge, sequencer / heartbeat / config-broadcast /
calibration / controller services, the Node backend, and a board simulator —
and verifies data flows end-to-end.
./setup.sh --daq-server installs everything the test needs on Linux/WSL and
almost everything on macOS. Two things it deliberately does NOT do:
The script uses ${PIDS[-1]} (bash 4.2+). macOS ships /bin/bash 3.2 (frozen
in 2007 over GPL licensing). Install via Homebrew:
brew install bashThis installs to /opt/homebrew/bin/bash (Apple Silicon) or
/usr/local/bin/bash (Intel). It does not replace /bin/bash — SIP
prevents that. Invoke explicitly when running the integration test (see below).
On Linux you can use the system bash directly.
The board simulator binds each simulated board to a distinct 127.0.0.x
address. macOS doesn't route those without explicit aliases. The test script
will offer to add them with sudo ifconfig lo0 alias 127.0.0.<n> up the first
time you run it — confirm the sudo prompt. (On Linux all 127.0.0.x resolve
to lo automatically; no action needed.)
For reference — you don't need to run any of this manually if you ran
./setup.sh --daq-server:
- C++ build deps:
cmake,openssl@3,libeigen3-dev,build-essential,ninja-build,ccache,pkg-config(list mirrorsdaq-server-ci.yml) elodin-db(Rust binary) — installed via the upstream prebuilt installer, or built from source as a fallback- Node 20 (from NodeSource on Linux, Homebrew on macOS) +
npm installfor bothdiablo_server/backend/anddiablo_server/frontend/ - Python venv at
daq-server/.venvwith everything inrequirements.txt cmake+makefor the target binaries (skip with--no-build)
From the repo root:
# macOS
/opt/homebrew/bin/bash daq-server/test/test_integration.sh
# Linux
bash daq-server/test/test_integration.shAdd -v / --verbose for noisier output. First run takes ~3-5 minutes
(builds C++ binaries, installs Node modules, configures cmake). Subsequent
runs ~1-2 minutes.
════════════════════════════════════════════════════════════
Results: 58 passed, 0 failed
════════════════════════════════════════════════════════════
...
═══════════════════════════════════════════════════════════════
✅ INTEGRATION TEST PASSED
═══════════════════════════════════════════════════════════════
The test exits non-zero on any failure. Logs are kept under
daq-server/.tmp/integration_*_<pid>.log (one per service).
Five layers, end-to-end:
- Sensor data flow — fake PT/TC/RTD/LC/encoder packets → DAQ bridge → Elodin DB → backend → WebSocket → frontend assertions on every channel
- Sensor config + Boards pane — config broadcast, board status updates, SELF_TEST replay on late connect
- State machine — UI sends state change → backend → sequencer → confirmed back through the WebSocket and Elodin DB
- Actuator commands — open/close commands round-trip; UDP packets verified on local listener
- Controller service — connects to Elodin as publisher + subscriber, VTables registered, loop ticking
The build subdirectory existed but was empty (e.g. from a stale checkout). Fix:
rm -rf daq-server/build
# re-run the test; it will cmake + make from scratchStale process from a previous run still has the port. The script tries to clean up, but a hard kill is sometimes needed:
pkill -9 -f daq_bridge
pkill -9 -f sequencer_service
pkill -9 -f heartbeat_service
pkill -9 -f config_broadcast_service
pkill -9 -f calibration_service
pkill -9 -f controller_service
pkill -9 -f elodin-db
pkill -9 -f 'tsx.*server\.ts'
pkill -9 -f board_simulatorBackend died mid-test. Almost always because another process killed it —
usually a concurrent run, or your own cleanup pkill while a test was
running. Wait for the first test to finish, OR kill everything (above) and
start fresh.
Do not run two integration tests at the same time. They share fixed ports (Elodin 2241, backend WebSocket 8181, etc.).
You ran the script with /bin/bash (bash 3.2) instead of the Homebrew bash.
Use the explicit /opt/homebrew/bin/bash path.
The C++ services read state-transition / calibration CSVs from the
firmware/ subtree (after the May 2026 cleanup) — paths like
firmware/test_guis/state_transitions.csv. If your working tree is missing
firmware/ (e.g. a partial clone or someone deleted it), restore it:
git checkout HEAD -- firmware/If you change anything in firmware/, validate locally before pushing:
./setup.sh --firmwarefirmware/setup.sh runs pip3 install --user platformio and sanity-checks
the firmware/libraries/DAQv2-Comms symlink. The pio binary lands at
~/Library/Python/<ver>/bin/pio on macOS, or ~/.local/bin/pio on Linux;
add that dir to your $PATH (the script prints the exact line to append to
your shell rc if pio isn't already on $PATH).
PIO downloads ESP32 toolchains on first build (~500 MB — one-time).
Pass --with-toolchain to firmware/setup.sh to pre-download them now.
cd firmware/Hotfire_Code/Hotfire_Tests
pio test -e nativeExpected: 63 test cases: 63 succeeded. Covers sensor + actuator state
machines, ADS126X self-test, DAQv2-Comms packet round-trip, sensor data
collection.
for proj in PT_Hotfire TC_Hotfire RTD_Hotfire LC_Hotfire Actuator_Hotfire; do
(cd firmware/Hotfire_Code/$proj && pio run)
doneExpected: [SUCCESS] for each. Catches lib-resolution and link issues that
the host tests can't.
STAR/
├── daq-server/ # DAQ server / FSW
│ ├── CMakeLists.txt # top-level: daqv2_comms lib → ../lib/...
│ ├── config/config.toml # board IPs, calibration CSV paths,
│ │ # state-machine durations, etc.
│ ├── diablo_server/ # C++ source for daq_bridge, sequencer,
│ │ ├── daq_bridge/ # heartbeat, config_broadcast,
│ │ ├── services/ # calibration, controller services
│ │ │ ├── sequencer/ #
│ │ │ ├── calibration/ #
│ │ │ ├── heartbeat/ #
│ │ │ ├── config_broadcast/ #
│ │ │ └── controller/ #
│ │ ├── backend/ # TypeScript backend (server.ts), WS + REST
│ │ ├── frontend/ # Next.js + React UI (TypeScript)
│ │ └── lib/ # shared FSW C++ lib
│ ├── test/test_integration.sh # the integration test (this doc's focus)
│ └── build/ # cmake build dir (auto-created)
│
├── firmware/ # subtree of calstar/DiabloAvionics
│ ├── libraries/
│ │ ├── ads126X/ # ADC driver (canonical, post-cleanup)
│ │ ├── DAQv2-Comms → ../../lib/DAQv2-Comms # symlink
│ │ ├── EthernetHandler/
│ │ ├── STAR_ISM330DH/
│ │ ├── STAR_LIS3DH/
│ │ └── STAR_MCP3201/
│ ├── Hotfire_Code/
│ │ ├── PT_Hotfire/ # flight firmware per board
│ │ ├── TC_Hotfire/
│ │ ├── RTD_Hotfire/
│ │ ├── LC_Hotfire/
│ │ ├── Actuator_Hotfire/
│ │ └── Hotfire_Tests/ # Unity unit tests (pio test -e native)
│ ├── test_guis/ # state_transitions.csv, etc.
│ ├── PT_Board/, TC_Board/, ... # per-board calibration CSVs + test sketches
│ └── Archive/ # deprecated projects parked here
│
└── lib/
└── DAQv2-Comms/ # wire-protocol library — single source of
# truth, used by both daq-server and firmware
Before pushing anything that touches daq-server/ or firmware/, run the
relevant tests:
| Changed | Test |
|---|---|
daq-server/ C++, config.toml, backend |
bash daq-server/test/test_integration.sh (must pass 58/58) |
firmware/ unit-testable logic |
pio test -e native in firmware/Hotfire_Code/Hotfire_Tests (63/63) |
firmware/Hotfire_Code/*/ board firmware |
pio run in each affected board project |
EngineDesign/ |
python -m pytest tests/ -q in EngineDesign/ |
star-openrocket/ |
.venv/bin/python -m pytest tests/ -q in star-openrocket/ (offline; no credentials needed) |
CI runs the same checks on every push (see .github/workflows/):
daq-server-ci.yml, firmware-ci.yml, pid-designer-ci.yml,
engine-design-ci.yml, and star-openrocket-ci.yml build and test their
respective subprojects; large-files.yml rejects files over 5 MB on every PR;
and docs.yml publishes the Doxygen docs. Running the tests above locally
before pushing keeps CI green.