Architecture

Oizom edge software runs on Raspberry Pi (ARM), containerized with Docker, and communicates with a Gateway container over HTTP/WebSocket.

Layer Stack

main.py
  └── Sensor/Sensor.py          (orchestrator)
        ├── OzWrapper/*         (sensor category wrappers)
        │     └── drivers/*     (hardware drivers: I2C, SPI, UART, Modbus)
        ├── Manager/Manager.py  (HTTP client → Gateway)
        └── Network/Network.py  (connectivity monitor)

Entry point: main.pySensor.setup()Sensor.loop()

Key Components

Sensor (Orchestrator)

Sensor/Sensor.py initializes all sensor wrappers based on device config fetched from the Gateway, then runs the sensing loop: read → average → publish. It manages threads and queues for async communication (Modbus, LoRa, Sigfox, HMI, Socket).

OzWrapper (Sensor Abstractions)

Each wrapper in OzWrapper/ covers one sensor category (temperature, dust, CO₂, wind, etc.) and abstracts over multiple hardware drivers for the same measurement type. All wrappers extend SensorBase.GenericSensor.

GenericSensor ABC

Every wrapper must implement these four methods:

Method

Purpose

initialize()

One-time setup, config parsing

initializeSensor()

Hardware init, GPIO/bus open

getSensorReading()

Read raw values from driver

putSensorValue()

Post-process and publish

drivers (Hardware Drivers)

Low-level drivers in drivers/ communicate directly with sensor ICs over I2C, SPI, UART, or Modbus. One directory per sensor IC (e.g. drivers/BME280/, drivers/CubicPM3006/).

Manager (Gateway Client)

Manager/Manager.py is the HTTP client to the Gateway container. It:

  • Fetches device config via GET /query/config

  • Posts averaged sensor data via POST /query/data

  • Posts realtime data for live dashboards

Network (Connectivity Monitor)

Network/Network.py checks internet reachability and communicates with a Network Manager service. If connectivity is lost, data is queued locally until reconnect.

Config Flow

Gateway /query/config
        ↓
Manager.getConfig()
        ↓
Sensor.setup()  ← determines which sensors/wrappers to initialize
        ↓
Sensor.loop()   ← read → average → publish

Device config comes from the Gateway at startup — it determines which sensors are enabled. Static base config lives in Configuration/config.json.

Communication Interfaces

Async interfaces use queue.Queue for decoupled message passing:

Interface

Transport

Queue

OzSocket

WebSocket

OzModbus

RS-485 Modbus

OzLora

LoRa radio

OzSigfox

Sigfox cellular

OzTCP

Raw TCP

Logging

All logging uses utils/oizom_logger.py (OizomLogger). Never use bare print() or logging.getLogger() — always use the context_logger.info_with_context() pattern.