Aether is a small C99 application that displays sensor readings from configured HTTP data sources in a real-time Raylib dashboard.
- Load any number of sensors from a YAML config, each with an arbitrary
list of named metrics carrying units (
temperature (C),pressure (hPa), ...) - Adaptive card grid: each metric renders as a chip with its value, sparkline, and up/down change indicator
- Card detail view with large trend lines and min/avg/max stats per metric
- Tabbed navigation: a tab holds as many cards as fit the window;
overflow goes to numbered tabs (
1 2 ... N), clickable and bound toAlt/Cmd + 1..9 - Settings modal to toggle trend lines, value animation, and change indicators at runtime
- HTTP data sources polled on background threads (CivetWeb + OpenSSL), so slow or unreachable endpoints never freeze the dashboard
- Liveness model per metric: live, stale, offline, or error with reason
- Fixed-slot task scheduler decoupled from data handling
- Bounded per-sensor reading history (ring buffers)
- Unit tests for configuration, history, scheduler, sensor, source, HTTP mapping, and dashboard layout modules
- C compiler with C99 support (GCC or Clang)
makepkg-config- Raylib
- libyaml
- OpenSSL 3 (OpenSSL 1.1 also works)
CivetWeb (HTTP client) and cJSON (JSON parsing) are vendored under
third_party/ — both MIT licensed, no installation needed.
brew install raylib libyaml openssl pkg-configsudo apt-get install gcc make pkg-config cmake \
libx11-dev libxrandr-dev libxi-dev libxcursor-dev libxinerama-dev \
libgl1-mesa-dev libglu1-mesa-dev libasound2-dev \
libyaml-dev libssl-dev(The X11/OpenGL headers are needed because raylib links them even for a headless compile.)
Build the application:
makeRun it:
./mainClean generated files:
make cleanBuild and run all unit tests:
make testThe test suite covers:
- sensor metrics, source binding, reading application, and liveness;
- HTTP JSON path mapping, backoff, timeouts, and error handling (against loopback mock servers);
- reading history ordering, oldest-entry eviction, and metric statistics;
- task scheduling;
- YAML configuration loading, including malformed input, unknown keys, missing ids, growing registries, and source validation;
- dashboard layout math: tab capacity, pagination limits, and range mapping.
Sensor data is loaded from:
config/sensors.yaml
Example:
sensors:
- id: 1
name: temperature_sensor
metrics:
- name: temperature
unit: C
value: 22.5
- name: humidity
unit: "%"
value: 45Each sensor requires:
| Field | Description |
|---|---|
id |
Unique numeric sensor identifier |
name |
Readable sensor name |
metrics |
List of metrics, each with name, unit and value |
Note: a % unit must be quoted ("%") because YAML reserves the percent sign.
Live values come from HTTP endpoints declared in:
config/sources.yaml
Example:
sources:
- id: weather_api
name: Weather API
url: https://example.com/metrics.json
poll_interval_s: 10
timeout_s: 5
headers:
Authorization: Bearer TOKENMetrics opt into live polling from config/sensors.yaml:
metrics:
- name: temperature
unit: C
value: 22.5 # initial display value until live data arrives
source: weather_api # id from sources.yaml
path: current.temperature_2m # dot-separated JSON pathEach source is polled on a background thread, so slow or unreachable
endpoints never freeze the dashboard. Metrics without a source keep
their configured value statically.
| Input | Action |
|---|---|
| Click the cogwheel (top right) | Open/close the settings modal |
| Click a card | Open that sensor's detail view |
| Click a numbered tab (bottom) | Jump to that tab |
Alt/Cmd + 1..9 |
Jump directly to tab N |
← / → |
Previous / next tab |
Esc |
Close the detail view or settings modal |
.
├── assets/fonts/ Bundled font (JetBrains Mono, SIL OFL)
├── config/ Sensor and source configuration YAML
├── docs/ Screenshots and demo media
├── third_party/ Vendored CivetWeb (HTTP) and cJSON (JSON), both MIT
├── config/ YAML parsing: sensors, sources, settings (libyaml)
├── sensor/ Sensor data model and source-bound readings
├── source/ Data-source types, status model, validation
├── history/ Bounded per-sensor reading history (ring buffer)
├── scheduler/ Periodic task scheduling
├── http/ HTTP polling workers (CivetWeb + cJSON + pthreads)
├── ui/ Dashboard rendering, layout math, theme (Raylib)
├── tests/ Unit tests (one executable per module)
├── main.c Application entry point
└── Makefile Build and test commands
sensors.yaml + sources.yaml
│
▼
configuration parser (binds metrics to sources)
│
▼
sensor registry ◄── HTTP poll workers (one thread per source)
│ │
▼ ▼
Raylib dashboard per-sensor reading history (ring buffers)
This project is licensed under the MIT License.


