Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Metrum is a framework for decentralized sensor data communication that works over Reticulum networks. It follows a publisher-subscriber pattern and uses standardized formats like SenML (Sensor Measurement Lists) and CBOR (Concise Binary Object Representation) for efficient data transmission.

Communication Philosophy

Metrum's communication philosophy is based on the following principles:

  1. Heterogeneity: Metrum is designed to work in diverse and heterogeneous networks, where devices and sensors may have different capabilities, protocols, and data formats.
  2. Scalability: Metrum is built to scale, handling large amounts of sensor data from multiple devices and sources.
  3. Flexibility: Metrum provides a flexible framework for integrating with different sensors, devices, and systems.
  4. Efficiency: Metrum optimizes data transmission and reception, minimizing overhead and ensuring reliable communication.

Communication Architecture

The system consists of the following components:

flowchart TB
    subgraph "Publisher Node"
        Sensor["Sensor Interface"] --> MetrumPublisher["MetrumSensorPublisher"]
        MetrumPublisher --> CBOR1["CBOR Encoding"]
        CBOR1 --> SenML1["SenML Formatting"]
    end
    
    subgraph "Reticulum Network"
        Network["Decentralized Network"]
    end
    
    subgraph "Subscriber Node"
        MetrumSubscriber["MetrumSensorSubscriber"] --> CBOR2["CBOR Decoding"]
        CBOR2 --> SenML2["SenML Processing"]
        SenML2 --> InfluxDB["InfluxDB Storage"]
    end
    
    MetrumPublisher -->|"Announce/Publish"| Network
    Network -->|"Connect/Subscribe"| MetrumSubscriber
    
    classDef sensor fill:#f9f,stroke:#333,stroke-width:2px
    classDef publisher fill:#bbf,stroke:#333,stroke-width:2px
    classDef network fill:#dfd,stroke:#333,stroke-width:2px
    classDef subscriber fill:#fbb,stroke:#333,stroke-width:2px
    classDef storage fill:#ffd,stroke:#333,stroke-width:2px
    
    class Sensor sensor
    class MetrumPublisher,CBOR1,SenML1 publisher
    class Network network
    class MetrumSubscriber,CBOR2,SenML2 subscriber
    class InfluxDB storage
  • Publishers: Devices or nodes that collect and transmit sensor data.
  • Subscribers: Devices or nodes that receive and process sensor data.
  • Reticulum Network: A decentralized network that enables communication between devices and nodes.
  • InfluxDB: Time series database (TSDV) used for storing IoT Datapoints.

Specifications

Metrum uses the following specifications for communication:

  1. CBOR Encoding: Metrum uses CBOR (Concise Binary Object Representation) encoding for transmitting sensor data.
  2. Reticulum Protocol: Metrum communicates over Reticulum networks using the Reticulum protocol.
  3. Sensor Data Format: Metrum uses a standardized sensor data format, which includes:
  • Metric: A string representing the sensor measurement (e.g., temperature, humidity).
  • Value: The sensor reading value.
  • Unit: The unit of measurement for the sensor reading (e.g., Celsius, Fahrenheit).
  • Timestamp: The timestamp for the sensor reading.

Defining Sensors

To define a sensor in Metrum, you need to create a class that inherits from the SensorInterface abstract base class. The class should implement the following methods:

  1. read_sensors: This method collects sensor data and returns it as a dictionary.

Here is an example of a sensor definition:

from metrum import SensorInterface

class ExampleSensor(SensorInterface):
    """Mock sensor for testing serialization"""

    def read_sensors(self):
        """Return mock sensor readings with proper SenML formatting"""
        readings = {
            "temperature": {
                "value": 25.5,
                "unit": SenmlUnits.SENML_UNIT_DEGREES_CELSIUS,
                "name": SenmlNames.KPN_SENML_TEMPERATURE,
            },
            "pressure": {
                "value": 101325,
                "unit": SenmlUnits.SENML_UNIT_PASCAL,
                "name": SenmlNames.KPN_SENML_PRESSURE,
            },
            "humidity": {
                "value": 65.0,
                "unit": SenmlUnits.SENML_UNIT_RELATIVE_HUMIDITY,
                "name": SenmlNames.KPN_SENML_HUMIDITY,
            },
        }
        return readings

The SensorInterface takes serializes the sensor readings as SenML using CBOR encoding.

Sensor Data Format

The sensor data format used in Metrum is based on the SenML (Sensor Measurement Lists) specification. SenML is a lightweight, binary format for representing sensor data.

A SenML record consists of the following fields:

  • Name: A string representing the sensor measurement (e.g., temperature, humidity).
  • Value: The sensor reading value.
  • Unit: The unit of measurement for the sensor reading (e.g., Celsius, Fahrenheit).
  • Timestamp: The timestamp for the sensor reading.

Communication Flow

The communication flow in Metrum is as follows:

  1. Publisher Announcement: The publisher announces its presence and available metrics to the Reticulum network.
  2. Subscriber Connection: The subscriber establishes a connection to the publisher and requests subscription to specific metrics.
  3. Telemetry Data Transmission: The publisher transmits telemetry data to the subscriber using CBOR encoding.
  4. Data Processing: The subscriber processes the received telemetry data and stores it in a database or performs other actions.
flowchart TB
    subgraph "Data Collection"
        S1["Temperature Sensor"] --> |"Reading"| SI["Sensor Interface"]
        S2["Pressure Sensor"] --> |"Reading"| SI
        S3["Humidity Sensor"] --> |"Reading"| SI
    end
    
    subgraph "Publisher Processing"
        SI --> |"read_sensors()"| SP["SenML Pack"]
        SP --> |"CBOR encoding"| CD["CBOR Data"]
    end
    
    subgraph "Transmission"
        CD --> |"Reticulum Transport"| RN["Reticulum Network"]
        RN --> |"Packet Delivery"| SD["Subscriber Decoder"]
    end
    
    subgraph "Subscriber Processing"
        SD --> |"CBOR decoding"| SP2["SenML Pack"]
        SP2 --> |"Data Processing"| DP["Data Points"]
        DP --> |"Write API"| IF["InfluxDB"]
        DP --> |"If InfluxDB unavailable"| RQ["Retry Queue"]
        RQ -.-> |"Retry later"| IF
    end
    
    classDef collection fill:#e1f5fe,stroke:#333,stroke-width:1px
    classDef publisher fill:#e8f5e9,stroke:#333,stroke-width:1px
    classDef network fill:#fff9c4,stroke:#333,stroke-width:1px
    classDef subscriber fill:#f9e1fd,stroke:#333,stroke-width:1px
    
    class S1,S2,S3,SI collection
    class SP,CD publisher
    class RN network
    class SD,SP2,DP,IF,RQ subscriber

Example Use Case

Here is an example of how to define a sensor and use it with Metrum:

# Define a sensor class like above
publisher = MetrumPublisher(sensor=ExampleSensor(), config_path="config.toml")

# Announce the publisher and publish telemetry data
publisher.announce()
publisher.publish_telemetry()

This example demonstrates how to define a sensor class, create a Metrum publisher instance, and publish telemetry data using the Reticulum protocol.

TODO Later

  • pluggable transport layer
  • use minimal amount of bandwith
  • try to use standards when possible