You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

337 lines
12 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# System Architecture — EZO RTD Temperature Monitor
> **Purpose of this document:** Provide a complete architectural snapshot of the project so that any contributor (or AI assistant) can understand the full system — hardware, firmware, data pipeline, and frontend — without reading every source file individually.
---
## 1. System Overview
The system is a **single-node embedded IoT monitoring platform** composed of four layers:
```
┌─────────────────────────────────────────────────────────────┐
│ Layer 4 — Presentation index.html / index-css.html │
│ Browser polls JSON via HTTP every 1 s │
├─────────────────────────────────────────────────────────────┤
│ Layer 3 — Web Server Nginx (static file server) │
│ Serves HTML, SVG assets, and JSON data files │
├─────────────────────────────────────────────────────────────┤
│ Layer 2 — Backend Daemon ezortd_daemon (C binary) │
│ Reads I²C → writes JSON + appends CSV │
├─────────────────────────────────────────────────────────────┤
│ Layer 1 — Hardware / HAL EZO-RTD via I²C on RPi │
│ Atlas Scientific ISCCB-2 @ 0x66, I²C bus 1 │
└─────────────────────────────────────────────────────────────┘
```
**Host board:** Raspberry Pi (any model with 40-pin GPIO; developed on Raspberry Pi Zero or similar)
**OS:** Raspberry Pi OS (Debian-based, Linux kernel)
**Language:** C (backend), HTML/CSS/JavaScript (frontend)
**IPC mechanism:** File system — JSON files in `data/` act as the shared state between daemon and web server
---
## 2. Hardware Layer
### 2.1 Sensor — Atlas Scientific EZO-RTD™ (ISCCB-2)
The EZO-RTD is a signal-conditioning circuit for Pt-100 or Pt-1000 RTD probes. It digitizes the analog RTD signal internally and exposes a clean digital interface via I²C or UART.
| Property | Value |
|---|---|
| Module | Atlas Scientific ISCCB-2 |
| Protocol in use | I²C |
| I²C Address | `0x66` (fixed, factory default) |
| I²C Bus | Bus 1 (`/dev/i2c-1`) |
| Supply voltage | 3.3 V |
| Read command | ASCII string `"R"` (1 byte) |
| Response time | ~1000 ms after issuing `"R"` |
| Response format | Byte 0: status code (`0x01` = success); Bytes 1N: null-terminated ASCII float |
| Resolution | 0.001 °C |
### 2.2 Physical Wiring
```
EZO-RTD (ISCCB-2) Raspberry Pi 40-pin Header
───────────────── ──────────────────────────
VCC ────────────────────── Pin 1 (3.3 V)
GND ────────────────────── Pin 6 (GND)
SDA ────────────────────── Pin 3 (GPIO2, I²C1 SDA)
SCL ────────────────────── Pin 5 (GPIO3, I²C1 SCL)
OFF ──── not connected
```
> The `OFF` pin (active-low sleep input) is left floating/unconnected; the circuit is permanently powered. Future implementations may drive this pin from a GPIO to enable power management.
### 2.3 I²C Bus Configuration
- **Bus:** `/dev/i2c-1`
- **Speed:** 100 kHz (standard mode; the EZO-RTD supports up to 400 kHz Fast Mode)
- **Pull-ups:** Provided internally by the Raspberry Pi (1.8 kΩ on SDA/SCL lines)
- **Enable:** `sudo raspi-config` → Interface Options → I2C → Enable
---
## 3. Firmware / HAL Layer (C)
### 3.1 Source Files
| File | Role |
|---|---|
| `sensors/EZORTD/ezortd.h` | Public API: I²C address constant, `getTemperature()` declaration |
| `sensors/EZORTD/ezortd.c` | `getTemperature()` implementation |
| `sensors/EZORTD/main.c` | One-shot binary: single read → JSON to stdout |
| `sensors/EZORTD/ezortd_daemon.c` | Daemon: infinite loop, writes JSON + CSV every second |
| `sensors/EZORTD/Makefile` | Builds `EZORTD` binary (one-shot target) |
### 3.2 `getTemperature()` — Protocol Detail
```c
// Signature
int getTemperature(int fd, double *temperature);
// Returns 0 on success, -1 on error
```
**Communication sequence:**
```
1. ioctl(fd, I2C_SLAVE, 0x66) — select slave address
2. write(fd, "R", 1) — issue read command (ASCII 'R')
3. usleep(1000000) — wait 1000 ms for conversion
4. read(fd, response, 32) — read up to 32 bytes
5. Check response[0] == 0x01 — status byte: 0x01 = success
6. atof(&response[1]) — parse ASCII float from bytes 1..N
```
**Error codes in `response[0]`:**
| Code | Meaning |
|---|---|
| `0x01` | Success |
| `0x02` | Syntax error in command |
| `0xFF` | No data (not ready) |
| `0xFE` | Pending (not ready yet) |
### 3.3 One-Shot Executable (`EZORTD`)
Compiled from `main.c + ezortd.c`. Opens `/dev/i2c-1`, calls `getTemperature()` once, prints result as JSON to stdout, exits.
```sh
./EZORTD
{ "temperature": 24.414 }
```
Used for testing and for cron-based updates.
### 3.4 Daemon (`ezortd_daemon`)
Compiled from `ezortd_daemon.c + ezortd.c`. Runs an infinite loop with 1-second sleep between iterations. On each iteration:
1. Reads temperature via `getTemperature()`
2. Overwrites `data/EZORTD.json` with current value
3. Appends a Unix-timestamp + temperature row to `logs/temp.csv`
**Output paths** (hardcoded in source — update before compiling):
```c
// JSON
"/home/cristian/airquality/basic-ui-dashboard/data/EZORTD.json"
// CSV log
"/home/cristian/airquality/basic-ui-dashboard/logs/temp.csv"
```
**JSON output format:**
```json
{ "temperature": 24.414 }
```
**CSV output format:**
```
timestamp,temperature ← header row (from EZORTD.csv template)
1717027200,24.414 ← Unix epoch (seconds), float to 3 decimal places
```
---
## 4. Data Pipeline
```
[EZO-RTD sensor]
│ I²C (0x66, /dev/i2c-1)
[ezortd_daemon — C process]
├──► data/EZORTD.json (overwrite, every 1 s)
│ { "temperature": 24.414 }
└──► logs/temp.csv (append, every 1 s)
1717027200,24.414
[Future: historical chart]
[Nginx — static file server]
│ HTTP GET ./data/EZORTD.json
[Browser — index.html]
│ setInterval(fetchData, 1000)
[DOM update — temp-value element]
```
---
## 5. Web Server Layer
**Server:** Nginx (static file serving only — no application logic)
**Document root:** Set to the repository root directory in `/etc/nginx/sites-available/default`:
```nginx
root /home/pi/<repo-path>;
```
**Served assets:**
| Path | Description |
|---|---|
| `/` or `/index.html` | Minimal temperature dashboard |
| `/index-css.html` | Extended multi-sensor dashboard |
| `/data/EZORTD.json` | Live temperature JSON (written by daemon) |
| `/images/*.svg` | Ionicons SVG assets (thermometer, water, cloud, sun) |
No server-side scripting is used. The daemon writes files to disk; Nginx serves them as-is.
---
## 6. Frontend Layer
### 6.1 `index.html` — Minimal Dashboard
Single-sensor display. Pure HTML + inline CSS + vanilla JS.
**Data source:** `GET ./data/EZORTD.json`
**Polling interval:** 1000 ms (`setInterval`)
**DOM targets:**
| Element ID | Content |
|---|---|
| `temp-value` | `data.temperature.toFixed(3) + " ℃"` |
| `last-update` | `"Updated: " + new Date().toLocaleTimeString()` |
### 6.2 `index-css.html` — Extended Dashboard
Multi-sensor card layout using Roboto (Google Fonts), flexbox, and CSS animations.
**Data sources (partially commented out):**
```javascript
const res = await Promise.all([
// fetch("./data/BH1750.json"), // light — pending
// fetch("./data/BMP180.json"), // pressure — pending
fetch("./data/HTU21D.json"), // temp + humidity (legacy sensor, active)
// fetch("./data/MH_Z19.json") // CO₂ — pending
]);
```
**Polling interval:** 6000 ms
**DOM targets:** `temp-value`, `humidity-value`, `co2-value`, `pressure-value`
---
## 7. Key Constants and Configuration
| Constant | Value | Location |
|---|---|---|
| EZO-RTD I²C address | `0x66` | `sensors/EZORTD/ezortd.h` |
| HTU21D I²C address | `0x40` | `sensors/HTU21D/htu21d.h` |
| I²C bus device | `/dev/i2c-1` | `sensors/EZORTD/main.c`, `ezortd_daemon.c` |
| Sensor read delay | 1,000,000 µs (1 s) | `sensors/EZORTD/ezortd.c` |
| Daemon loop interval | `sleep(1)` — 1 s | `sensors/EZORTD/ezortd_daemon.c` |
| JSON output path | `/home/cristian/…/data/EZORTD.json` | `ezortd_daemon.c` (**update per deployment**) |
| CSV log path | `/home/cristian/…/logs/temp.csv` | `ezortd_daemon.c` (**update per deployment**) |
| Frontend poll interval (primary) | 1000 ms | `index.html` |
| Frontend poll interval (extended) | 6000 ms | `index-css.html` |
| Nginx document root | `/home/pi/<repo-path>` | `/etc/nginx/sites-available/default` |
---
## 8. Build System
### EZO RTD
```makefile
CC=gcc
CFLAGS=-I.
OBJ=main.o ezortd.o
%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
EZORTD: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS)
```
No external libraries required. Uses only standard Linux I²C headers: `<linux/i2c-dev.h>`, `<sys/ioctl.h>`.
### HTU21D (legacy)
```makefile
EXTRA_LIBS=-li2c
```
Requires `libi2c` (`sudo apt install libi2c-dev`). Uses SMBus wrappers from `<i2c/smbus.h>`.
---
## 9. Legacy Sensor — HTU21D
Retained in `sensors/HTU21D/` for reference and backward compatibility with `index-css.html`.
| Property | Value |
|---|---|
| I²C Address | `0x40` |
| Measurements | Temperature + Relative Humidity |
| Temperature formula | `T = -46.85 + 175.72 × raw / 65536.0` |
| Humidity formula | `RH = -6 + 125 × raw / 65536.0` |
| Library dependency | `libi2c` |
| Command | SMBus block read (`0xE3` temp, `0xE5` humidity) |
---
## 10. Planned Sensors (Not Yet Integrated)
| Sensor | Measurement | Interface | Data file |
|---|---|---|---|
| BH1750 | Ambient light (lux) | I²C | `data/BH1750.json` |
| BMP581 | Atmospheric pressure (hPa) | I²C | `data/BMP581.json` |
| ZMOD4410 | TVOC / IAQ index | I²C | `data/ZMOD4410.json` |
---
## 11. File I/O Summary
| File | Writer | Reader | Format | Update Rate |
|---|---|---|---|---|
| `data/EZORTD.json` | `ezortd_daemon` | Nginx → browser | JSON object | 1 s |
| `logs/temp.csv` | `ezortd_daemon` | (manual / future viz) | CSV (epoch, float) | 1 s (append) |
| `data/HTU21D.json` | `HTU21D` binary (manual / cron) | Nginx → browser | JSON object | On demand |
| `data/EZORTD.csv` | (template only — unused) | — | CSV | — |
---
## 12. Security and Deployment Notes
- The Nginx instance serves on port 80 with no authentication. Suitable for a local LAN network only.
- The daemon runs as root (required for I²C access unless the user is added to the `i2c` group). Use `sudo usermod -aG i2c $USER` to avoid running as root.
- File paths in `ezortd_daemon.c` are hardcoded and must be updated per deployment before compilation.
- No input validation is performed on the JSON data in the browser; malformed JSON causes a silent catch/error in the console.