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, how they find the site and where they get stuck, so we can improve it (Google Analytics, Microsoft Clarity).

Show us which of our ad campaigns bring visitors to the site and remember the campaign or partner link you arrived from (Google Ads, LinkedIn, Meta, 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
22.6k 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

TCP Integration

TCP Integration lets devices send data to ThingsBoard over raw TCP connections. The integration runs a TCP server and splits the incoming byte stream into messages. An uplink data converter transforms each message into device or asset data, including telemetry and attributes. ThingsBoard can also send a downlink message back over the device’s open connection.

Use TCP Integration when:

  • Devices use a custom text or binary protocol over raw TCP.
  • Device firmware already connects to a configurable host and port.
  • Devices keep a long-lived connection and must receive commands over it.
  1. Receive data. A device opens a TCP connection to the remote integration and sends data. The handler (Text, JSON, or Binary) splits the byte stream into separate messages.
  2. Decode the payload. The uplink converter identifies the device or asset in each message and extracts attributes and telemetry.
  3. Process the message. ThingsBoard processes the converted data through the Rule Engine. If Allow create devices or assets is enabled, ThingsBoard can create an entity that does not exist yet.
  4. Send downlink data (optional). The downlink converter encodes the Rule Engine message, and the integration writes it to the device’s open connection, followed by \n. If the device is not connected, the downlink waits for its next uplink.
  • Access to ThingsBoard Cloud with integrations enabled for your tenant.
  • Permission to create integrations and data converters.
  • A host for the remote integration. Devices must reach it on the TCP port (default 10560), and it needs outbound access to ThingsBoard’s gRPC port 9090.
  • The payload format and framing your devices use.
  • nc (netcat) to send test data.

Create the uplink converter, assign it to a new TCP integration, start the remote integration, and send a test message.

Sample Payloads

This guide uses device SN-002, which sends temperature and humidity in one of three formats. Pick one format and use the matching decoder, handler, and test command throughout.

Comma-separated records, one per line:

SN-002,default,temperature,25.7\nSN-002,default,humidity,69

The uplink converter reads one framed message and maps it to ThingsBoard entities and data. TCP Integration uses the Generic converter: you write the decoder in TBEL or JavaScript. For converter types and the full decoder reference, see Uplink data converter.

  1. Go to Integrations ⇾ Data converters and click + Add data converter ⇾ Create new converter.

  2. Converter type — keep Uplink.

  3. Integration type — select TCP.

  4. Name — enter a name, for example TCP Uplink Converter.

  5. Main decoding configuration — the editor is pre-filled with the default decoder template for JSON payloads. Replace it with the decoder for your payload format below and, if needed, adapt the field names to your device. The editor opens in TBEL; use the TBEL / JS toggle to switch to JavaScript.

    /** Decoder **/
    // decode payload to string
    var strArray = decodeToString(payload);
    var payloadArray = strArray.replaceAll("\"", "").replaceAll("\\n", "").split(',');
    var telemetryPayload = {};
    for (var i = 2; i < payloadArray.length; i = i + 2) {
    var telemetryKey = payloadArray[i];
    var telemetryValue = parseFloat(payloadArray[i + 1]);
    telemetryPayload[telemetryKey] = telemetryValue;
    }
    // Result object with device attributes/telemetry data
    var result = {
    deviceName: payloadArray[0],
    deviceType: payloadArray[1],
    telemetry: telemetryPayload,
    attributes: {}
    };

    Each call receives one record. Position 0 is the device name, position 1 the device type, then telemetry key–value pairs.

    To adapt:

    • Another delimiter — replace split(','), for example with split(';').
    • Name or type at another position — change the payloadArray[0] and payloadArray[1] indexes.
    • Fixed telemetry keys — replace the loop with explicit assignments: telemetryPayload['temperature'] = parseFloat(payloadArray[2]);.
    • Attributes instead of telemetry — move the fields to attributes.
  6. Click Test decoder function and check the result (see Test the Decoder).

  7. Click Add.

Decoder Inputs

The decoder signature is function decoder(payload, metadata): object | object[].

Variable Type Content
payload byte array One message as framed by the handler: without the delimiter if Strip delimiter is on (Text), or without the stripped header bytes (Binary).
metadata {[key: string]: string} integrationName and the key–value pairs from the integration’s Metadata field.

Built-in helpers:

Function Result
decodeToString(payload) Converts the bytes to a UTF-8 string.
decodeToJson(payload) Parses the bytes as JSON and returns an object.

For more parsing patterns, see Common scripting patterns.

Test the Decoder

Click Test decoder function below the editor. The dialog opens with pre-filled Metadata rows, Payload content type set to JSON, and a sample JSON payload. Replace the payload with a message in your format. For the Text decoder, set Payload content type to Text and enter:

SN-002,default,temperature,25.7

Click Test. The Output panel shows:

{
"deviceName": "SN-002",
"deviceType": "default",
"telemetry": {
"temperature": 25.7
},
"attributes": {}
}

Check that deviceName is set and telemetry holds the expected keys. If the output is empty or shows an error, fix the decoder and test again.

  1. Go to Integrations ⇾ Integrations and click + Add integration.
  2. Basic settings:
    • Integration type — select TCP.
    • Name — enter a name, for example TCP integration.
    • Enable integration and Allow create devices or assets — keep both on, so the device is created on its first uplink.
    • Click Next.
  3. Uplink data converter:
    • Click Select existing and choose TCP Uplink Converter.
    • Alternatively, click Create new to write a decoder here.
    • Click Next.
  4. Downlink data converter:
  5. Connection:
  6. Click Add.
Parameter Description
Port TCP port the remote integration listens on. Default: 10560. Must be free on the remote host. If the port is busy, the integration retries binding every 5 seconds.
Max number of pending connects on the socket Queue length for connections not yet accepted. Default: 128. Raise it for many simultaneous connections.
Size of the buffer for inbound socket (in KB) Socket receive buffer. Default: 64.
Size of the buffer for outbound socket (in KB) Socket send buffer. Default: 64.
Enable sending of keep-alive messages on connection-oriented sockets Sends TCP keep-alive probes to detect dead connections. Off by default. Turn it on for long-lived connections that can drop silently.
Forces a socket to send the data without buffering (disable Nagle’s buffering algorithm) Sends each write immediately instead of batching small packets. On by default.
Cache Size / Cache time to live in minutes Limits of the cache that maps devices to sender addresses for downlinks. Defaults: 1000 entries and 1440 minutes (24 hours).
Execute remotely Always on for TCP. Shows the Integration key and Integration secret for the remote process. See Remote Integration.
Metadata In Advanced settings. Key–value pairs added to the metadata of every message from this integration.

TCP has no message boundaries, so the handler defines how the byte stream is split into messages. Select it in Handler Type (default: Binary).

Reads a length field from each message header and cuts the frame to that length.

Parameter Description
Max Frame Length (in bytes) Largest allowed frame. Default: 128.
Length Field Offset Position of the length field from the start of the frame, in bytes. Default: 0.
Length Field Length Size of the length field: 1, 2, 3, 4, or 8 bytes. Default: 2.
Length Adjustment (the compensation value to add to the value of the length field) Added to the length field value to get the number of bytes that follow it. Range: 0–8. Default: 0.
Number of first bytes to strip out from the decoded frame Bytes removed from the start of the frame before the converter runs, typically the header. Default: 0.
Byte Order of the length field Little Endian (default) or Big Endian.
Thrown exception soon as the decoder notices the length of the frame will exceed max size Raises the error as soon as the length field exceeds Max Frame Length, instead of after the whole frame arrives. Off by default.

Install and start the TCP integration service with the Integration key and Integration secret from the connection step. Follow the remote integration guide. Once the process connects, it opens the TCP port.

Send a message to the integration port, then confirm that ThingsBoard decoded it and created the device.

Run the command for your payload format on the remote integration host (localhost). From another machine, replace localhost with that host’s address. nc -q0 closes the connection right after sending; -q1 -w1 waits 1 second.

One record:

Terminal window
echo -e 'SN-002,default,temperature,25.7' | nc -q0 localhost 10560
Terminal — single text message sent to the TCP integration; no output indicates successful delivery

Two newline-separated records:

Terminal window
echo -e 'SN-002,default,temperature,25.7\nSN-002,default,humidity,69' | nc -q1 -w1 localhost 10560
Terminal — two newline-delimited text messages sent; connection closes after 1 s

No output means the message was delivered.

  • Integration events — go to Integrations ⇾ Integrations, open the integration, and click the Events tab. The uplink appears with status OK.
  • Converter events — open TCP Uplink Converter in Integrations ⇾ Data converters and click the Events tab. In shows the framed message, Out shows the decoded result, and Error shows any error.
  • Device — go to Devices & assets ⇾ Devices. The integration has created SN-002. Open it and confirm temperature = 25.7 on the Latest telemetry tab.

ThingsBoard sends a downlink over the open connection on which the device sent its last uplink, and appends \n to every downlink payload. If that connection is closed, the downlink is held and sent right after the device’s next uplink. The device must therefore keep the connection open to receive commands immediately.

The downlink converter turns a Rule Engine message into the bytes sent to the device. For the full encoder reference, see Downlink data converter.

  1. Open the integration in Integrations ⇾ Integrations and click Toggle edit mode (pencil icon).
  2. In the Downlink data converter field, click Create new. The Downlink converter type and TCP integration type are preselected.
  3. Enter a name, for example TCP Downlink Converter.
  4. Main encoding configuration opens with a default encoder. Use the encoder shown below or your own.
  5. To record downlink events, click the debug icon next to the name, select All messages, and click Apply.
  6. Click Add, then Apply changes.

Encoder used in this guide:

/** Encoder **/
// Encode downlink data from the incoming Rule Engine message.
// msg: JSON message payload.
// msgType: message type, e.g. 'ATTRIBUTES_UPDATED', 'POST_TELEMETRY_REQUEST', etc.
// metadata: key-value pairs containing additional message data.
// integrationMetadata: key-value pairs configured in the integration.
var result = {
// Specify the response body format: JSON, TEXT, or BINARY.
contentType: "JSON",
// Serialize the incoming message and use it as the TCP payload.
data: JSON.stringify(msg),
// Add optional metadata when required by the integration.
metadata: {}
};
return result;

To send downlinks through the integration, modify the Root Rule Chain:

  1. Go to Data processing ⇾ Rule chains and open the Root Rule Chain.
  2. Find the integration downlink node in the node library panel on the left. Drag it onto the canvas.
  3. In the node configuration dialog, enter a name (e.g. Downlink to TCP integration) and select your TCP integration. Click Add.
  4. Connect the message type switch node to the newly created integration downlink node using the Post attributes and Attributes Updated relation types to trigger downlinks whenever shared attributes are created or updated.
  5. Apply the changes.

Open a connection, send an uplink, then update a shared attribute to queue a downlink. The steps below use the Text handler; for another handler, send the matching payload from Sample Payloads.

  1. Open a connection and keep it open:

    Terminal window
    nc localhost 10560
  2. Type an uplink and press Enter. Keep the terminal open.

    Terminal window
    SN-002,default,temperature,25.7
  3. Go to Devices & assets ⇾ Devices, select your device, and open the Attributes tab.

  4. Select Shared attributes and click +.

  5. Set Key to temperatureUploadFrequency, Type to Integer, Value to 60, and click Add. ThingsBoard sends the downlink.

To test from a single command, keep the connection open for 60 seconds with -w60 and add the attribute during that time:

Terminal window
echo -e 'SN-002,default,temperature,25.7\nSN-002,default,humidity,69' | nc -w60 localhost 10560
Terminal — two text messages sent; connection kept open with -w60 to receive a downlink response
  • Terminal — the open connection prints the downlink:

    Terminal window
    john@doe:~$ nc localhost 10560
    SN-002,default,temperature,25.7
    {"temperatureUploadFrequency":60}
  • Converter events — open the downlink converter’s Events tab. In shows the Rule Engine message, and Out shows the payload sent to the device.

Symptom Cause Fix
Device not created deviceName is empty or undefined. Check Out in the converter events. Make sure the decoder extracts deviceName.
Converter error The decoder threw an exception. Open the converter’s Events tab and check Error. Test the decoder with a sample payload.
Messages split or merged wrongly The handler does not match the device framing. Match the Handler Configuration to the protocol. Capture the raw TCP bytes to check the framing.
Messages dropped with a frame-length error A message is longer than Max Frame Length. Raise Max Frame Length (in bytes).
Remote integration does not connect to ThingsBoard Wrong host, port, key, or secret. Check host, port, routingKey, and secret in tb-remote-integration.yml. Make sure gRPC port 9090 is reachable.
Port already in use Another process holds the port. Free the port or pick another one. The integration retries binding every 5 seconds.
Downlink not received The connection closed after the uplink, or no uplink arrived after the downlink converter was added. Keep the connection open (-w60 or a persistent client) and send an uplink first. Check the Root Rule Chain wiring.
Wrong timestamp The device sends ts in seconds. Convert it: ts: data.ts * 1000.
Telemetry not saved The decoder returns {}, [], or empty telemetry. Make sure telemetry holds at least one key.

To read debug events, open the integration or converter, click Events, and select an event: In is the received data, Out is the converter result, and Error holds the error text.