:::tip Tutorial Follow our step-by-step tutorial to connect your first device over TCP: TagoTiP TCP — Quick Start :::
Guaranteed delivery and real-time commands. TagoTiP over TCP ensures every data point arrives in order - and the server can push commands to your device the moment they are ready.
tcp.tip.us-e1.tago.io75.2.126.1705693 (plaintext) / 5694 (TLS)tcp.tip.eu-w1.tago.io15.197.224.1535693 (plaintext) / 5694 (TLS)Both ports accept TagoTiP and TagoTiP(s). The server detects the mode once per connection by inspecting the first byte.
CMD frames instantlyDevice TagoIO
| |
|── PUSH|hash|serial|[temp:=25]\n ──────────> |
|<──────── ACK|OK|1\n ─────────────────────── |
| |
|<──────── ACK|CMD|reboot\n ───────────────── | (server push)
The connection stays open. The server can push CMD frames at any time - no polling needed. Each frame must end with \n (byte 0x0A).
#include <WiFi.h>
#include <WiFiClient.h>
const char* SSID = "your-wifi";
const char* PASSWORD = "your-password";
const char* TIP_HOST = "tcp.tip.us-e1.tago.io";
const int TIP_PORT = 5693;
const char* TOKEN_HASH = "4deedd7bab8817ec"; // replace with yours
const char* SERIAL_N = "sensor-01"; // replace with yours
WiFiClient client;
void setup() {
Serial.begin(115200);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("WiFi connected");
if (!client.connect(TIP_HOST, TIP_PORT)) {
Serial.println("Connection failed");
return;
}
Serial.println("Connected to TagoTiP TCP");
}
void loop() {
// reconnect if needed
if (!client.connected()) {
client.connect(TIP_HOST, TIP_PORT);
return;
}
float temperature = analogRead(34) * 0.1; // example reading
char frame[256];
snprintf(frame, sizeof(frame),
"PUSH|%s|%s|[temperature:=%.1f#C]\n",
TOKEN_HASH, SERIAL_N, temperature);
client.print(frame);
// read response
unsigned long timeout = millis() + 5000;
while (!client.available() && millis() < timeout) delay(10);
if (client.available()) {
String response = client.readStringUntil('\n');
Serial.println(response); // ACK|OK|1
// check for server-pushed commands
if (response.startsWith("ACK|CMD|")) {
String cmd = response.substring(8);
Serial.print("Command received: ");
Serial.println(cmd);
}
}
delay(10000); // send every 10 seconds
}
Replace 4deedd7bab8817ec with your token hash and sensor-01 with your serial.
echo 'PUSH|4deedd7bab8817ec|sensor-01|[temperature:=25.5#C]' \
| nc tcp.tip.us-e1.tago.io 5693
ACK|OK|1
Open a persistent connection:
nc tcp.tip.us-e1.tago.io 5693
Type frames and see responses in real time:
PING|4deedd7bab8817ec|sensor-01
ACK|PONG
PUSH|4deedd7bab8817ec|sensor-01|[temperature:=25.5#C;humidity:=60#%]
ACK|OK|2
PULL|4deedd7bab8817ec|sensor-01|[temperature]
ACK|OK|[temperature:=25.5#C@1694567890000]
echo 'PUSH|4deedd7bab8817ec|logger-01|[temp:=25.5@1694567890000;temp:=26.1@1694567900000;temp:=25.8@1694567910000]' \
| nc tcp.tip.us-e1.tago.io 5693
ACK|OK|3
echo 'PUSH|4deedd7bab8817ec|sensor-01|>xDEADBEEF01020304' \
| nc tcp.tip.us-e1.tago.io 5693
Raw bytes are delivered to your device's Payload Parser.
echo 'PULL|4deedd7bab8817ec|sensor-01|[temperature;humidity]' \
| nc tcp.tip.us-e1.tago.io 5693
ACK|OK|[temperature:=25.5#C@1694567890000;humidity:=60#%@1694567890000]
On a persistent connection, the server pushes commands instantly:
ACK|CMD|reboot
ACK|CMD|ota=https://example.com/v2.1.bin
No polling needed - just keep the connection open.
echo 'PUSH|4deedd7bab8817ec|sensor-01|[temperature:=25.5#C]' \
| openssl s_client -connect tcp.tip.us-e1.tago.io:5694 -quiet
Same protocol, same frames - TLS is handled by the load balancer.
:=temperature:=25.5=status=online?=active?=true@=position@=39.74,-104.99Append after the value, in this order:
#temperature:=25.5#C@=speed:=10@=39.74,-104.99@temperature:=25.5@1694567890000^temperature:=25.5^batch_01{}temperature:=25.5{source=dht22}All combined: temperature:=25.5#C@=39.74,-104.99@1694567890000^batch_01{source=dht22,quality=high}
ACK|OK|NN data points storedACK|OK|[...]ACK|PONGACK|CMD|<command>ACK|ERR|invalid_tokenACK|ERR|device_not_foundACK|ERR|invalid_payloadACK|ERR|invalid_seqACK|ERR|rate_limitedACK|ERR|payload_too_largeACK|ERR|server_errorRPM = requests per minute.
PING is exempt from rate limiting on TCP.
Send a PING before the keep-alive idle timeout to keep the connection alive. The connection is closed after the TTL regardless of activity.
For the complete protocol grammar, parsing rules, and ABNF, see the TagoTiP Specification.