ReferenceClientAdvancedBuild Device Driver

Build a Device Driver

Creating custom hardware integrations with device drivers.

While the Synnax Driver is great for the devices it supports, you may need to integrate your own hardware with Synnax. This guide walks you through building a reliable, performant driver using the client libraries. The guide uses an Arduino as an example, but the patterns apply to any serial-based device.

Prerequisites

Before starting, ensure you have:

Read-Only Driver

This section sets up the Arduino to continuously read from an analog input and send the value over serial. The driver script captures each incoming value and writes it to a channel in Synnax.

Upload this code to your Arduino.

const int analogPin = A0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  float analogValue = analogRead(analogPin);
  Serial.println(analogValue);
  delay(100); // ~10Hz sampling rate
}

Create a driver file. Find your Arduino’s port in the Arduino IDE (top-right corner when connected via USB).

Read values in a loop.

Create a client and two channels - an index channel for timestamps and a data channel for values.

Open a writer and stream data.

With the script running, set up a line plot in the Console:

Write-Only Driver

This section covers receiving commands from Synnax to control digital outputs on the Arduino.

Upload this code (uses the built-in LED on pin 13).

const int digitalPin = 13;

void setup() {
  Serial.begin(9600);
  pinMode(digitalPin, OUTPUT);
}

void loop() {
  if (Serial.available() > 0) {
    char command = Serial.read();
    if (command == '1') {
      digitalWrite(digitalPin, HIGH);
      Serial.println("ON");
    } else if (command == '0') {
      digitalWrite(digitalPin, LOW);
      Serial.println("OFF");
    }
  }
  delay(10);
}

Create a virtual command channel. Virtual channels do not store historical values. As a consequence, an index channel is not needed.

Open a streamer to receive commands and write them to serial.

Set up a switch on a schematic.

Read-Write Driver

You may have noticed the previous section set both “State” and “Command” to the same channel. This works, but has a problem: if the driver stops, clicking the switch still appears to work (it toggles visually) even though nothing happens on the hardware.

The solution is to use separate channels: a command channel for sending commands, and a state channel that reflects the actual hardware state. The switch only updates when the state channel confirms the change.

This code reads commands, controls the output, and sends back both state and analog values.

const int digitalPin = 13;
const int analogPin = A0;
int state = 0;

void setup() {
  Serial.begin(9600);
  pinMode(digitalPin, OUTPUT);
}

void loop() {
  if (Serial.available() > 0) {
    char command = Serial.read();
    if (command == '1') {
      digitalWrite(digitalPin, HIGH);
      state = 1;
    } else if (command == '0') {
      digitalWrite(digitalPin, LOW);
      state = 0;
    }
  }
  float analogValue = analogRead(analogPin);
  String output = String(state) + "," + String(analogValue);
  Serial.println(output);
  delay(10);
}

Create four channels: command, time, state, and value.

Stream commands while writing state and values.

Now configure the switch with separate channels:

  • Command: arduino_command
  • State: arduino_state

Add a line plot for arduino_value to see the analog input.

Production Drivers

For production-grade hardware integrations, see the C++ Driver documentation. The C++ driver offers:

  • Built-in support for LabJack, National Instruments, OPC UA, and Modbus devices
  • Better performance for high-frequency data acquisition
  • Device pooling and connection management
  • Cross-platform support (Windows, macOS, Linux)
  • Integration with the Synnax task management system