UNIHH-IOTUNIHH-IOT
Home
Guide
Manual
  • 简体中文
  • English
GitHub
Home
Guide
Manual
  • 简体中文
  • English
GitHub
  • Guide

    • Introduction
    • Quick Start
    • Architecture
    • Driver Development Guide
    • Deployment and Operations

Driver Development Guide

Southbound drivers are built on the unihh-protocol foundation. MQTT connectivity, registration, heartbeats, config sync and command listening are all handled by the framework — you only implement protocol encoding and decoding.

Three Steps to a New Driver

Step 1: Create a project and add the SDK

Create a Maven project and add the unihh-protocol dependency:

<dependency>
    <groupId>com.unihh.iot</groupId>
    <artifactId>unihh-protocol</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

Step 2: Extend AbstractDriverPlugin

package com.unihh.iot.driver.custom;

import com.unihh.iot.common.model.DeviceConfig;
import com.unihh.iot.common.model.ThingModel;
import com.unihh.iot.protocol.annotation.IoTDriver;
import com.unihh.iot.protocol.base.AbstractDriverPlugin;
import com.unihh.iot.protocol.model.DriverMetadata;
import com.unihh.iot.protocol.model.ParsedPayload;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
@IoTDriver(code = "custom-protocol", name = "Custom Protocol Driver", version = "2.0.0")
public class CustomDriverPlugin extends AbstractDriverPlugin {

    @Override
    public String getDriverCode() {
        return "custom-protocol";
    }

    @Override
    public DriverMetadata getMetadata() {
        DriverMetadata meta = new DriverMetadata();
        meta.setDriverCode(getDriverCode());
        meta.setDriverName("Custom Protocol Driver");
        meta.setVersion("2.0.0");
        return meta;
    }

    @Override
    public boolean openConnection(DeviceConfig config) throws Exception {
        // Establish a long connection with the physical device
        return true;
    }

    @Override
    public ParsedPayload readPoints(DeviceConfig config, List<ThingModel.PropertySpec> points) throws Exception {
        ParsedPayload payload = new ParsedPayload();
        // Fill in acquired points:
        // payload.addPoint(pointCode, value, quality, timestamp)
        return payload;
    }

    @Override
    public boolean writePoint(DeviceConfig config, String pointCode, String address, Object value, String dataType) throws Exception {
        // Write-back: encode the write frame and send it to the device
        return true;
    }
}

What each method does:

MethodInvoked whenResponsibility
openConnectionAfter device config is pushedOpen the connection; returning false schedules a retry
readPointsOn each polling cycleBatch-read points and fill in values, quality and timestamps
writePointOn a control commandEncode and send the write frame; return success or failure

Channel mutex

Calls to readPoints and writePoint are already serialized through the framework's getChannelLock. For RS485 half-duplex buses or single-session TCP devices, do not add another lock for the same device inside your plugin.

Step 3: Add a bootstrap class and run

package com.unihh.iot.driver.custom;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = {"com.unihh.iot"})
public class CustomDriverApplication {
    public static void main(String[] args) {
        System.setProperty("server.port", "28019");
        SpringApplication.run(CustomDriverApplication.class, args);
    }
}

Once started, the framework handles registration, heartbeats, config pulls and polling automatically. The new driver will appear on the platform's Driver Management page within moments, showing as online.

What the Framework Provides

CapabilityDetails
Self-registrationBroadcasts metadata and HTTP endpoint to iot/driver/register at startup
Periodic heartbeatReports every 5 seconds with active channel metrics
LWT last-willEMQX publishes the will message if the process dies; the platform reacts in seconds
Hot config syncDevice/point changes are pushed via iot/device/config/update — no driver restart
Polling schedulerDriverPollingEngine invokes readPoints on each device's acquisition cycle
Command listenerSubscribes to iot/driver/{code}/cmd and dispatches to writePoint
REST debug endpointBuilt-in /api/v1/driver/status used by the platform's ping self-check

Data Types and Point Mapping

Thing model properties support: FLOAT, DOUBLE, INT, INT32, INT64, BOOL, STRING, ENUM.

When binding points to a device you configure:

  • Physical register address: protocol-specific (e.g. Modbus 40001, S7 DB1.DBW0);
  • Scale and offset: engineering value = raw value × Scale + Offset, applied platform-side — drivers only report raw values;
  • Sampling mode: PERIODIC (fixed cycle) or COV (report on change).

Reference Drivers

Three production drivers ship with the repository and make good templates:

DriverLocationWorth studying for
Modbusdrivers/unihh-driver-modbusTCP/RTU dual mode, multi-slave polling, register write-back
Siemens S7drivers/unihh-driver-s7S7-200 SMART/300/1200/1500, DB blocks and memory areas
OPC UAdrivers/unihh-driver-opcuaNodeId subscription and method calls

Contributing a Driver

Drivers for BACnet, DL/T 645, Mitsubishi MC, Omron FINS and other protocols are welcome. See the contributing section in the repository README for the workflow.

Edit this page on GitHub
Last Updated: 11/13/26, 3:34 AM
Contributors: sunqi
Prev
Architecture
Next
Deployment and Operations