Crimson's Generic MQTT connector publishes Red Lion tags as JSON to any MQTT topic you choose, in Crimson 3.1 and 3.2. With Ubidots, point it at the Ubidots broker, or at a UbiFunction that reshapes each message first. Use the Generic MQTT connector instead of Crimson's built-in Ubidots MQTT connector when you need your own topics, string tags or several devices per connector.
When to use Generic MQTT instead of the Ubidots connector
Crimson's Ubidots MQTT connector needs three settings: a host, a device name and a token. The Generic MQTT connector exposes the MQTT topic, the login and the message format instead, so it fits setups the Ubidots connector can't express. The Crimson 3.2 Software Guide (LP1153, chapter 24) lists both connectors' settings. For numeric tags going to one device, the Ubidots MQTT connector guide is shorter.
Need | Ubidots MQTT connector | Generic MQTT connector |
MQTT topic | Built from the Device Name | Any Publication Topic, plus a Topic Suffix per tag set in Crimson 3.2 |
Login | Access Token | User Name and Password, which UbiFunctions need |
String tags | Not sent | Sent as JSON text; a UbiFunction stores them in the dot's context |
Message format | Fixed | Force Into Root, Data Encoding, Label Set, Tag Structure, Site Name |
Crimson builds | 3.2.0014.0 and 3.1.3119.001 or later | Earlier builds too |
Writes from Ubidots to tags | Tag Writes on the tag set | Not covered here: the subscription expects Crimson's own JSON format |
Two routes to Ubidots
Route A publishes straight to the Ubidots broker at industrial.api.ubidots.com. Route B publishes to a UbiFunction at functions.ubidots.com, which converts each Crimson message and writes it to Ubidots. Route A has fewer moving parts; Route B keeps more of the data.
Route A: Ubidots broker | Route B: UbiFunction | |
Variables | One per numeric tag, plus connected and a timestamp variable holding Crimson's time in Unix seconds | One per tag, plus connected |
String tags | Dropped | Stored as text in the dot's context |
Time of each value | Arrival time in Ubidots | Crimson's timestamp, so buffered data keeps its original time |
Several devices | One connector per device (Crimson 3.2) | One connector, a Topic Suffix per tag set |
Tested on hardware | Yes, Crimson 3.1 on a CR3000 | No, see Troubleshooting |
Requirements
A Red Lion device programmed with Crimson 3.1 or 3.2 and software Group 2 or higher, for example a FlexEdge, a Graphite or CR3000 HMI, or a DA30D. Download Crimson from HMS Networks' Crimson page.
An Ubidots account, your Ubidots token and, for Route B, your Ubidots username.
A network that lets the device reach industrial.api.ubidots.com (Route A) or functions.ubidots.com (Route B) on port 8883 with TLS, or 1883 without it, with a default gateway and DNS servers.
1. Create the tags and enable the Generic MQTT connector
The Generic MQTT connector publishes the data tags you place in its tag sets. Create your tags under Data Tags; this guide's example uses four numeric tags from a CR3000 HMI. Then open Communications, select Generic MQTT under Connectors, and set Enable Agent to Yes on the Service tab.
Under Diagnostics, type a new tag name such as connectionStatus in Status. Crimson offers to create the tag; click Yes. The connector writes its state to this tag, from 0 to 4.
On the Device Data tab, set Transfer Mode to Disabled. The device block holds placeholders, not data.
On the Tag Set 1 tab, keep Transfer Mode on Periodic, set the Update Rate and drag your tags into Contents. Use more tag sets for tags that need a different rate.
2A. Route A: publish to the Ubidots broker
On Route A, the Generic MQTT connector publishes to a Ubidots device topic, and the broker turns each numeric key into a variable. Crimson's default message nests the tags and sends some values as text, which the broker can't read, so Force into Root and Numeric Only must both be on. Set the Service tab as follows.
Field | Value |
Host Name 1 | industrial.api.ubidots.com |
Client ID | Any name unique to this device, for example line-1-cr3000 |
Publication Topic | /v1.6/devices/<device-label>, for example /v1.6/devices/line-1, in lowercase |
Subscription Topic | Leave empty |
Use Dollar Sign | No |
Force into Root | Enabled |
Data Encoding | Numeric Only |
User Name | Your Ubidots token |
Password | Leave empty |
On the Network tab, set Transport Protocol to TLS and Connect via Port to 8883, then download the database to the device. To feed several Ubidots devices in Crimson 3.2, add one Generic MQTT connector per device under Connectors, each with its own Publication Topic.
2B. Route B: publish to a UbiFunction
On Route B, the Generic MQTT connector publishes Crimson's full JSON to a UbiFunction, and the function writes the values to Ubidots. The function keeps string tags as text, uses Crimson's timestamp as each value's time and reads the device label from the topic. Route B has not been tested on Red Lion hardware; read the reconnect note under Troubleshooting first.
Create the decoder function
In Ubidots, go to Dev Center → Functions and click +.
Set the Name to crimson-decoder, the Method to POST and the Runtime to Python 3.14 Lite.
Replace the sample code with the code below, put your Ubidots token in TOKEN and save the function.
Copy the function's label from the end of its HTTPS endpoint URL, /prv/<your-username>/<function-label>. The label can differ from the name, for example with underscores.
import json
import re
from datetime import datetime
import requests
BASE_URL = "https://industrial.api.ubidots.com"
TOKEN = "PUT-YOUR-TOKEN-HERE"
DEFAULT_DEVICE = "crimson"
# Crimson adds these to every message; they are not tags.
SKIP = {"device", "cid"}
def label(name):
# Ubidots labels: lowercase letters, numbers, - and _. "Group1.Tag1" -> "group1-tag1", "Tag2 [0]" -> "tag2-0"
text = re.sub(r"[^a-z0-9_-]+", "-", str(name).lower())
return text.strip("-")[:50]
def to_number(value):
if isinstance(value, bool):
return int(value)
if isinstance(value, (int, float)):
return value
if isinstance(value, str) and value.lower() in ("true", "false"):
return 1 if value.lower() == "true" else 0
return None
def timestamp_ms(value):
# Numeric Only encoding sends Unix seconds; the other encodings send ISO 8601 ("2018-12-05T18:14:52.000Z")
if isinstance(value, (int, float)) and not isinstance(value, bool):
return int(value * 1000)
if isinstance(value, str):
try:
return int(datetime.fromisoformat(value.replace("Z", "+00:00")).timestamp() * 1000)
except ValueError:
return None
return None
def flatten(tags, prefix=""):
# Folders, labelled tag sets and arrays arrive as nested objects; join their names with "-".
for name, value in tags.items():
if name in SKIP:
continue
full = f"{prefix}-{name}" if prefix else str(name)
if isinstance(value, dict) and "Value" in value: # Send Properties: the data is in "Value"
yield full, value["Value"], value.get("Units") or ""
elif isinstance(value, dict):
yield from flatten(value, full)
else:
yield full, value, ""
def main(args):
payload = args.get("payload") or {}
if isinstance(payload, str):
payload = json.loads(payload)
# Topic: /prv/<username>/<function-label>/<device-label>; a tag set's Topic Suffix can supply the last level.
# Empty levels are skipped, so a suffix written with or without its own "/" gives the same device.
parts = [part for part in args["topic"].split("/") if part]
device_label = label(parts[3]) if len(parts) > 3 else DEFAULT_DEVICE
ts = timestamp_ms(payload.get("timestamp"))
dots = {}
def add(name, value, units=""):
key = label(name)
if not key:
return
number = to_number(value)
if number is not None:
dot = {"value": number}
if units:
dot["context"] = {"units": units}
elif isinstance(value, str):
dot = {"value": 1, "context": {"text": value}} # string tag: the text goes in the dot's context
else:
return
if ts:
dot["timestamp"] = ts
dots[key] = dot
tags = payload.get("tags")
if isinstance(tags, dict):
for name, value, units in flatten(tags):
add(name, value, units)
else:
# Force Into Root: tags sit at the top level next to connected and timestamp
for name, value in payload.items():
if name not in SKIP | {"tags", "timestamp", "connected", "historic", "adhoc"} and not isinstance(value, dict):
add(name, value)
if "connected" in payload: # true in every update, false in the last-will message
add("connected", payload["connected"])
if not dots:
return {"status": "ignored"}
response = requests.post(
f"{BASE_URL}/api/v1.6/devices/{device_label}/",
headers={"X-Auth-Token": TOKEN, "Content-Type": "application/json"},
json=dots,
timeout=10,
)
return {"status": response.status_code, "text": response.text}
Point the connector at the function
Field | Value |
Host Name 1 | functions.ubidots.com |
Client ID | Any name unique to this device |
Publication Topic | /prv/<your-username>/<function-label>/<device-label>, for example /prv/acme/crimson-decoder/line-1 |
Subscription Topic | Leave empty |
Force into Root | Disabled |
Data Encoding | Standard |
User Name | Your Ubidots username |
Password | Your Ubidots token |
Note: the function endpoint takes your username as the MQTT user and your token as the password. That's the reverse of the Ubidots broker in Route A, where the token is the user name.
Set Transport Protocol to TLS and Connect via Port to 8883 on the Network tab. To send tag sets to different devices, end the Publication Topic at the function label and give each tag set a Topic Suffix with its device label, such as /line-1 and /line-2. The function reads the device label from the last topic level.
3. Check the messages and the data
Crimson's web server, on devices with software Group 3 or higher, has a Debug Console that shows every MQTT message the connector sends, with its topic and JSON. Enable it under Web Server → Features → System Pages with Debug Console set to Enabled with Commands, and set Debug Output to Enabled on the connector's Network tab. After downloading the database, open the device's web server and choose System → Debug Console.
The capture above is a Route A message from Crimson 3.1: {"changeValue":0,"connected":1,"connectionInteger":4,"officeTemp":0,"testValue":0,"timestamp":1573231789,"upDown":0}. In Ubidots, go to Devices → Devices: the device has one variable per key, in lowercase.
Troubleshooting
Why don't my Crimson tags show up in Ubidots?
Route A, nothing arrives. The broker can't read Crimson's nested message or text values. Check that Force into Root is Enabled and Data Encoding is Numeric Only, and that the Publication Topic is /v1.6/devices/ followed by a lowercase device label.
The connector never connects. On Route A the user name is your token and the password is empty; on Route B the user name is your username and the password is your token. The port must match the transport: 8883 with TLS, 1883 with TCP.
Route B, connected but no device. Open the function's logs. The topic must start with /prv/<your-username>/<function-label>/, using the label from the endpoint URL, and the function's method must be POST. A 401 or 403 in the logs means the token in the code is wrong.
The Status tag stays below 4. The values mean the same as on the Ubidots connector; see the Status table in the FlexEdge setup guide.
Why does the connector keep reconnecting to functions.ubidots.com?
The Crimson 3.2 Software Guide describes the Generic MQTT connector as subscribing to its Subscription Topic, or to crimson/generic/<client-id>/sub when that field is empty ($crimson/… when Use Dollar Sign is Yes). The function endpoint accepts publishes under /prv/<your-username>/ and rejects subscriptions. If the endpoint closes the connection over that subscription, the Status tag keeps dropping back to 0 or 1. Use Route A for numeric tags, or put a bridge in front: a local Mosquitto broker or Node-RED flow that receives Crimson's messages and forwards them to functions.ubidots.com. For a fleet, Ubidots sales can map a dedicated broker to your function.
FAQ
What JSON does the Crimson Generic MQTT connector publish?
The Generic MQTT connector publishes a device object, a tags object with one key per tag, connected and timestamp. With Force into Root, every value moves to the top level. With Numeric Only, true and false become 1 and 0 and the timestamp becomes Unix seconds. Label Set wraps each tag set in its own object, such as set1.
How do string tags appear in Ubidots?
On Route B, the decoder writes each string tag as a variable with value 1 and the text in the dot's context, under text. Ubidots stores strings in context, not as values. A table widget or the variable's dot list shows the text. Route A drops string tags, because Numeric Only can't encode them.
Does Route A keep Crimson's timestamps?
No. On Route A, each value gets the time it reaches Ubidots, and Crimson's time arrives as a separate timestamp variable in Unix seconds. After an outage, buffered data lands at the replay time. Route B uses Crimson's timestamp as each value's time.
Can Ubidots write values back to Crimson tags through Generic MQTT?
This guide doesn't cover it. The Generic MQTT connector expects writes as Crimson JSON, such as {"tags":{"Tag1":100}}, and Ubidots sends plain values on its /lv topics. Use the Ubidots MQTT connector's Tag Writes instead, described in the FlexEdge setup guide.
Which Crimson versions include the Generic MQTT connector?
Crimson 3.1 and Crimson 3.2 both include it. The Generic MQTT connector predates the Ubidots MQTT connector, which arrived in builds 3.1.3119.001 and 3.2.0014.0 in late 2020, so Generic MQTT also works on devices still running an earlier Crimson build.
Other Red Lion guides
