Skip to content

Cookie preferences

We use cookies for our own analytics, to see how our campaigns perform and, if you allow it, to load content from other services such as the Google site search. We never sell your data. Necessary cookies keep the site working and cannot be switched off. See our Cookie Policy for details and our Privacy Policy for how we handle personal data.

Security, load balancing and remembering your cookie choice.

Show us which pages people read and how they find the site, so we can improve it (Google Analytics).

Show us which of our ad campaigns bring visitors to the site and remember the campaign or partner link you arrived from (Google Ads, partner program). We do not use these cookies to build advertising profiles.

Load the site search from Google when you use it. Google sets its own cookies and shows ads in the search results.

© 2026 The ThingsBoard Authors
Try for free

ThingsBoard Cloud

Choose your data region

Your data stays in the region you choose, for residency and compliance. No credit card required.

Rather run it yourself? Install on your own servers

Serial connector example

This tutorial walks through building a complete custom connector for the IoT Gateway — the SerialConnector — which reads data from a serial port and forwards it to ThingsBoard. The same connector ships in the gateway’s built-in extensions folder, so you can use it as a reference or starting point for your own implementation.

What we’re building:

Serial device → bytes → SerialUplinkConverter → ConvertedData → ThingsBoard
ThingsBoard → RPC → SerialDownlinkConverter → bytes → Serial device

Sample device payload:

48\r2430947595\n
  • 48 — humidity value, terminated by \r
  • 2430947595 — device serial number, from byte offset 4 to end of message

Step 1. Create the connector configuration

Section titled “Step 1. Create the connector configuration”

Create custom_serial.json in the same folder as your tb_gateway.json:

Terminal window
touch custom_serial.json

Add the following configuration:

{
"name": "Custom serial connector",
"logLevel": "DEBUG",
"uplinkQueueSize": 100000,
"devices": [
{
"name": "SerialDevice1",
"type": "default",
"port": "/dev/ttyUSB0",
"baudrate": 9600,
"converter": "SerialUplinkConverter",
"downlink_converter": "SerialDownlinkConverter",
"telemetry": [
{
"type": "float",
"key": "humidity",
"untilDelimiter": "\r"
}
],
"attributes": [
{
"key": "SerialNumber",
"type": "string",
"fromByte": 4,
"toByte": -1
}
],
"attributeUpdates": [
{
"attributeOnPlatform": "attr1",
"stringToDevice": "value = ${attr1}\n"
}
],
"serverSideRpc": [
{
"method": "setValue",
"type": "int",
"withResponse": true,
"responseType": "string",
"responseUntilDelimiter": "\r",
"responseTimeoutSec": 5
},
{
"method": "getValue",
"type": "string",
"withResponse": false
}
]
}
]
}

Top-level fields:

Field Description
name Connector name — must match the "name" entry in tb_gateway.json.
logLevel Log verbosity: TRACE, DEBUG, INFO, WARNING, ERROR, CRITICAL.
uplinkQueueSize Maximum number of uplink data items to buffer before dropping.
devices Array of device configurations.

Device fields:

Field Description
name Device name on the ThingsBoard platform.
type Device profile name on the platform.
port Serial port path.
baudrate Serial port baud rate.
converter Class name of the uplink converter.
downlink_converter Class name of the downlink converter.
telemetry Array of telemetry datapoint configurations.
attributes Array of attribute datapoint configurations.
attributeUpdates Array of attribute update configurations (platform → device).
serverSideRpc Array of RPC method configurations (platform → device).

Place the connector and converter files inside the extensions folder for your installation type:

Installation Extensions folder path
Docker Compose (default volume) tb-gw-extensions
Daemon /var/lib/thingsboard_gateway/extensions
pip (system-wide) /usr/lib/python3/site-packages/thingsboard_gateway/extensions
pip (user) /usr/local/lib/python3/dist-packages/thingsboard-gateway/extensions

Create a subfolder named serial inside the extensions folder. All connector and converter files go there.


Create extensions/serial/serial_connector.py with the following content. The connector manages SerialDevice worker threads and routes data to ThingsBoard.

from queue import Queue
from threading import Event, Thread, Lock
from typing import List, TYPE_CHECKING
import serial.tools
import serial.tools.list_ports
from thingsboard_gateway.tb_utility.tb_utility import TBUtility
from time import monotonic, sleep
try:
import serial
except ImportError:
print("pyserial library not found - installing...")
TBUtility.install_package("pyserial")
import serial
from thingsboard_gateway.connectors.connector import Connector
from thingsboard_gateway.tb_utility.tb_loader import TBModuleLoader
from thingsboard_gateway.tb_utility.tb_logger import init_logger
if TYPE_CHECKING:
from thingsboard_gateway.gateway.tb_gateway_service import TBGatewayService
class SerialDevice(Thread):

Create extensions/serial/uplink_serial_converter.py. The uplink converter parses raw bytes from the device and produces a ConvertedData object.

from typing import Any, Tuple
from simplejson import loads
from thingsboard_gateway.connectors.converter import Converter
from thingsboard_gateway.gateway.constants import REPORT_STRATEGY_PARAMETER, TELEMETRY_PARAMETER, TIMESERIES_PARAMETER
from thingsboard_gateway.gateway.entities.converted_data import ConvertedData
from thingsboard_gateway.gateway.entities.datapoint_key import DatapointKey
from thingsboard_gateway.gateway.entities.report_strategy_config import ReportStrategyConfig
from thingsboard_gateway.gateway.entities.telemetry_entry import TelemetryEntry
from thingsboard_gateway.tb_utility.tb_utility import TBUtility
class SerialUplinkConverter(Converter):
"""
Converts incoming serial bytes to the ConvertedData format expected by ThingsBoard.
One converter instance is created per configured device.
"""
def __init__(self, config, logger):
self._log = logger
self.__config = config
self.__device_report_strategy = None
self.__device_name = self.__config.get('deviceName', self.__config.get('name', 'SerialDevice'))
self.__device_type = self.__config.get('deviceType', self.__config.get('type', 'default'))
try:

After processing 48\r2430947595\n, the converter produces:

Device name: "SerialDevice1"
Device type: "default"
Telemetry: [{"humidity": 48.0}]
Attributes: {"SerialNumber": "2430947595"}

Create extensions/serial/downlink_serial_converter.py. The downlink converter turns ThingsBoard RPC payloads into raw bytes to send to the device.

from math import ceil
from struct import pack, unpack
from thingsboard_gateway.connectors.converter import Converter
class SerialDownlinkConverter(Converter):
"""
Converts RPC or attribute update payloads into bytes for the serial port.
One converter instance is created per configured device.
"""
def __init__(self, config, logger):
self._log = logger
self.__config = config
def convert(self, config, data) -> bytes:
"""Returns bytes to write to the serial port."""
self._log.debug("Data to convert: %s", data)
if data is None:

Step 6. Register the connector in tb_gateway.json

Section titled “Step 6. Register the connector in tb_gateway.json”

Add the following entry to the "connectors" array in tb_gateway.json:

{
"name": "Serial Connector",
"type": "serial",
"configuration": "custom_serial.json",
"class": "SerialConnector"
}
Field Description
name Connector name — must match the "name" in custom_serial.json.
type Extensions subfolder name (serial).
configuration Path to the connector config file, relative to the gateway config folder.
class Connector class name inside the connector file.

Terminal window
sudo systemctl restart thingsboard-gateway

Default log locations:

Installation Log folder
Docker Compose tb-gw-logs volume
Daemon /var/log/thingsboard-gateway/
Python module (pip) ./logs/

Connect the serial device, then open Devices in the ThingsBoard UI. You should see a device named SerialDevice1. Open it and go to the Latest telemetry tab — the humidity key should appear with the value parsed from the serial stream.


The SerialConnector class implements all required methods from the Connector interface. Key methods used in this example:

Method Role in this connector
__init__ Initialises the uplink queue, logger, and device list from config.
open Starts the connector thread.
run Main loop: loads devices, starts threads, drains the uplink queue, handles reconnects.
close Stops all device threads and the logger.
on_attributes_update Formats the attribute value as a UTF-8 string and writes it to the device’s serial port.
server_side_rpc_handler Delegates RPC handling to SerialDevice.handle_rpc_request and sends the reply.

The SerialUplinkConverter and SerialDownlinkConverter classes implement the Converter interface.

Class convert(config, data)
SerialUplinkConverter config=None for telemetry; config dict for RPC responses. data is the raw bytes from the port. Returns ConvertedData.
SerialDownlinkConverter config is the RPC config section. data is the value from ThingsBoard. Returns bytes.