Talos is a Rust-based hardware daemon for reliable, low-latency I2C communication with Sequent Microsystems HATs on Raspberry Pi. It is the sole owner of the I2C bus — all hardware reads and writes flow through its HTTP API on port 6100.
Part of the NexusEdge industrial control platform by AutomataNexus.
- Polls all enabled Sequent Microsystems boards every 1 second
- Caches all sensor readings in memory (sub-millisecond API response)
- REST API for reading cached values and writing outputs
- Reports raw hardware metrics to local Aegis-DB every 5 seconds
- I2C bus retry and error recovery
- GPIO pin support for direct-relay boards (e.g. Waveshare)
- Static musl binaries — no GLIBC dependency, runs on any ARM Linux
| Board | I2C Base | Channels | Use Case |
|---|---|---|---|
| MegaBAS | 0x48 |
8 AI, 4 AO, 4 triacs, 8 dry contacts, 8× 1K/10K resistance | General HVAC I/O |
| MegaIND | 0x50 |
4 voltage in, 4 current in, 4 voltage out, 4 current out | Industrial I/O |
| UnivIn16 | 0x40 |
16 universal inputs (voltage, 1K, 10K) | Large input count |
| UOut16 | 0x60 |
16 analog outputs (0–10V) | Large output count |
| RelInd16 | 0x58 |
16 relays | Relay banks |
| RelInd8 | 0x38 |
8 relays | Relay banks |
Each board type supports stacking up to 8 units (address = base + stack, 0–7).
┌─────────────────────────────────┐
│ main.rs │
│ Loads config, spawns threads, │
│ starts HTTP server │
└────┬──────┬──────┬──────┬───────┘
│ │ │ │
┌────────────▼┐ ┌──▼────┐ ┌▼─────▼──────┐
│ poller.rs │ │writer │ │ server.rs │
│ (std thread) │ │.rs │ │ (tokio) │
│ Reads I2C │ │Writes │ │ Axum HTTP │
│ every 1s │ │I2C │ │ on :6100 │
│ Updates cache│ │from Q │ │ │
└──────┬───────┘ └───┬───┘ └──────┬──────┘
│ │ │
┌──────▼─────────────▼─────────────▼──────┐
│ cache.rs │
│ SharedCache (parking_lot::RwLock) │
│ HashMap<stack, BoardData> per board type │
└──────────────────────────────────────────┘
│
┌──────▼──────┐
│ aegisdb.rs │
│ (tokio task) │
│ Reports to │
│ local :9090 │
└─────────────┘
Thread model:
- Poller (std thread) — owns one I2C bus fd, reads all boards, updates shared cache
- Writer (std thread) — owns separate I2C bus fd, processes write commands from crossbeam channel
- HTTP server (tokio) — serves API requests, reads cache, sends write commands to writer queue
- Aegis-DB reporter (tokio task) — reads cache every 5s, batch-inserts to local Aegis-DB
Download the latest binary from Releases:
# Raspberry Pi 3/4 (32-bit) — ARMv7
wget https://github.com/AutomataNexus/Talos/releases/latest/download/talos-hwdaemon-armv7
chmod +x talos-hwdaemon-armv7
sudo mv talos-hwdaemon-armv7 /usr/local/bin/talos-hwdaemon
# Raspberry Pi 4/5 (64-bit) — AArch64
wget https://github.com/AutomataNexus/Talos/releases/latest/download/talos-hwdaemon-aarch64
chmod +x talos-hwdaemon-aarch64
sudo mv talos-hwdaemon-aarch64 /usr/local/bin/talos-hwdaemonCopy the example config to /etc/nexusedge/ or the daemon's working directory:
sudo mkdir -p /etc/nexusedge
sudo cp hardware-daemon.toml /etc/nexusedge/hardware-daemon.tomlEdit the config to enable your boards and set stack addresses:
[server]
host = "127.0.0.1"
port = 6100
[polling]
interval_ms = 1000
[i2c]
bus = 1
retry_count = 3
retry_delay_ms = 10
[boards.megabas]
enabled = true
stacks = [0]
[boards.megaind]
enabled = false
stacks = []pm2 start talos-hwdaemon --name talos-daemon
pm2 save --forceAll endpoints serve on http://127.0.0.1:6100 (localhost only).
# Full cache (all boards, all channels)
curl -s http://localhost:6100/cache
# Health + board connectivity
curl -s http://localhost:6100/health
# MegaBAS channels
curl -s http://localhost:6100/megabas/analog_inputs?stack=0
curl -s http://localhost:6100/megabas/resistance_10k?stack=0
curl -s http://localhost:6100/megabas/contacts?stack=0
curl -s http://localhost:6100/megabas/triacs?stack=0# Set triac (channel 1-4)
curl -s -X POST http://localhost:6100/megabas/triac \
-H "Content-Type: application/json" \
-d '{"channel": 1, "state": true}'
# Set analog output (channel 1-4, 0-10V)
curl -s -X POST http://localhost:6100/megabas/analog_output \
-H "Content-Type: application/json" \
-d '{"channel": 2, "value": 5.0}'| Voltage | Position | Heat |
|---|---|---|
| 0.00V | 100% OPEN | Maximum heat |
| 5.00V | 50% OPEN | Half heat |
| 10.00V | 0% (CLOSED) | No heat |
Inverted: Lower voltage = more heat. Formula: voltage = ((100 - heat%) / 100) × 10
| Voltage | Position | Cooling |
|---|---|---|
| 0.00V | 0% (CLOSED) | No cooling |
| 5.00V | 50% OPEN | Half cooling |
| 10.00V | 100% OPEN | Maximum cooling |
Formula: voltage = (cool% / 100) × 10
The daemon returns raw resistance (ohms). Convert to temperature using Steinhart-Hart:
// 10K NTC Type 2 (Beta = 3950)
const T0 = 298.15, R0 = 10000, B = 3950;
const tempK = 1 / (1/T0 + (1/B) * Math.log(ohms / R0));
const tempF = (tempK - 273.15) * 9/5 + 32;| Resistance | Temperature |
|---|---|
| 30K Ω | ~33°F |
| 10K Ω | ~77°F (25°C) |
| 5K Ω | ~110°F |
See the examples/ directory for a complete working integration:
coordinator.js— Manages executor lifecycle, health monitoring, graceful shutdownexecutor.js— Reads sensors from Talos API, runs control logic, writes outputslogic/equipment/temperature_control.js— Pure control logic for AHU heating/cooling with rate-limited valve ramping
If you see GLIBC_2.32 not found, the binary was built with the wrong target. The release binaries use musleabihf/musl (static linking) and have no GLIBC dependency.
i2cdetect -y 1 # Should show devices at 0x48+ rangeVerify board DIP switch settings match stacks = [...] in your config.
The SM megabas CLI reads the same registers. Note: CLI r10krd returns kΩ, daemon returns Ω.
megabas 0 adcrd 1 # Compare with /megabas/analog_inputs
megabas 0 r10krd 1 # kΩ → multiply by 1000 for daemon Ω
Built by AutomataNexus, LLC
© 2026 AutomataNexus. All rights reserved.
