Cannot Connect to TagoIO via MQTT – Authentication Fails

Dear TagoIO Support Team,

I am encountering an issue while trying to connect to your MQTT broker at `mqtt.tago.io` on port `1883`.

Despite following the official documentation precisely and ensuring my device token is correct, the connection fails with the error: “Connection refused: Bad username or password”.

Below is the exact Node.js code I’m using:


const mqtt = require('mqtt');

  

const client = mqtt.connect('mqtt://mqtt.tago.io:1883', {

  username: 'Token', // Per documentation, literal string

  password: '92fc0f15-XXXXXXXXXXXXXXXXXX', // My device token

});

  

client.on('connect', () => {

  console.log('✅ Connected');

});

  

client.on('error', (err) => {

  console.error('❌ Error:', err.message);

});  

I’ve double-checked:

  • Username is "Token" (case-sensitive, string)

  • The device token is current and active (copied from the Device > Access tab)

  • I’m using port 1883 (non-SSL)

  • No firewalls or connection issues on my end

Hi vohongtho.infotech, thanks for reaching out!

Currently the connection with our MQTT broker is available for Starter and Scale accounts. But we understand how important MQTT connectivity is and we’re here to help.
I’ve escalated your case to our team, and they’ve already contacted you via email to assist you further. Please check your inbox.
Also, we’ve edited your post to hide part of your device token by replacing it with “xxx”. This token is a sensitive piece of information — anyone with access to it could potentially send data to your device.
Let me know if you need anything else!

Dear Carolina Hidalgo,

I also experienced this problem. I recently created a free account to test the Tago IO platform. Several videos I watched online talked about the platform’s features, which led me to try it.

I ran into this problem and only discovered it after watching a more recent video on the Tago IO channel.

Now I find myself unable to test and learn about the platform, which makes me very sad.

I have a client who needs to implement an asset monitoring platform in the oil and gas sector, but this limitation prevents me from knowing if Tago IO will be the best tool for my needs.

I would appreciate your help,

Fernando.

Dear Fernando,

I completely understand your frustration. Let me help you with that.

There is an alternative solution available. The Relay:

The TagoIO MQTT Relay is specifically designed to solve this exact problem. This open-source Rust application acts as a bridge between any MQTT broker and TagoIO, and it works with Free accounts. Here’s how you can set it up:

  • Download the MQTT Relay from the GitHub releases page

  • Use any external MQTT broker (like HiveMQ public broker, Eclipse Mosquitto, or EMQX)

  • Configure the relay to forward data from your MQTT broker to TagoIO devices

  • Test your complete IoT workflow without upgrading your account

The relay is completely free and allows you to evaluate TagoIO’s data processing, visualization, and automation capabilities with real MQTT data.

If you want, you can schedule a demo and talk to our team so we can discuss how we can help you to offer a solution for your client by clicking the link below and filling out the form: Request Demo.

If you need anything else, we’re here to help you!

Thank you!

Hi Carolina!

I implemented the Relay, but I can’t insert the data into the widget. The data arrives in TagoIO with the payload variable and the value {“variable”:“tension”, “value”:“110”}.

I expected it to arrive with the voltage variable and the value 110, but my data is the payload variable.

Is there a video that shows the entire relay implementation process until the data is displayed on the dashboard?

I’ve read several topics and watched your videos, but I can’t make any progress. I noticed that a lot has changed in the platform, and the videos haven’t been updated yet.

Buckets no longer exist; now, Entities exist.

Below is an image of an example that I implemented.

Hi ferriautomacao,

Heres a video that demonstrates the entire process.

https://help.tago.io/portal/en/kb/articles/tagoio-mqtt-relay

All data sent via the MQTT Relay is sent as a single payload variable. I would suggest you utilize the network payload parser to parse which device should receive the data. After that you can use that device’s connector payload parser to parse the payload variable into the exact format you need.

I’ve already completed the entire process in the video above, and it’s working perfectly. My question now is how to decode the data with the payload parser for both the network and the connector.

Thanks for your help.

Hi Ferriautomacao,

Send me an example of the payload variable your device is receiving and what format you are looking for, I’ll create a initial parser for you which should help you out.

The image I sent above in the dialog with Carolina shows the example.

What I need to reach the widget is:

{

“variable”: “RESERVA”

“value”: 0

}

Hi Ferriautomacao,

This should do it, save it in the device payload parser or in the connector payload parser.

Its expecting the following input example:

[
  {
    "metadata": {
      "qos": 0,
      "topic": "/tanacas/123456"
    },
    "value": "{\"variable\": \"RESERVA\", \"value\": 0}",
    "variable": "payload"
  }
]
/**
 * Parses MQTT payload containing JSON data with variable and value information
 *
 * @param {string} jsonPayload - The JSON string payload to be parsed
 * @param {string} group - The group identifier for the data
 * @param {string} receivedTime - The time the payload was received
 * @param {Object} metadata - Additional metadata from the original payload
 * @returns {Array} An array of data objects formatted for TagoIO storage
 **/
function parseMQTTJsonPayload(jsonPayload, group, receivedTime, metadata) {
  const data = [];
  const time = receivedTime || new Date().toISOString();

  try {
    // Parse the JSON string from the payload value
    const parsedJson = JSON.parse(jsonPayload);
    // Validate that the parsed JSON has the expected structure
    if (parsedJson && typeof parsedJson === "object" && parsedJson.variable && parsedJson.value !== undefined) {
      // Create the data object for TagoIO
      const dataObject = {
        variable: parsedJson.variable,
        value: parsedJson.value,
        group: group || undefined,
        time: time,
        metadata: {
          // Preserve original MQTT metadata
          ...metadata,
          // Add any additional metadata from the parsed JSON if present
          ...(parsedJson.metadata || {})
        }
      };

      // Add unit if present in the parsed JSON
      if (parsedJson.unit) {
        dataObject.unit = parsedJson.unit;
      }

      // Add location if present in the parsed JSON
      if (parsedJson.location && parsedJson.location.lat && parsedJson.location.lng) {
        dataObject.location = {
          lat: parsedJson.location.lat,
          lng: parsedJson.location.lng
        };
      }

      data.push(dataObject);
    } else {
      // Handle invalid JSON structure
      data.push({
        variable: "parser_error",
        value: "Invalid JSON structure: missing 'variable' or 'value' fields",
        group: group || undefined,
        time: time,
        metadata: metadata || {}
      });
    }
  } catch (error) {
    // Handle JSON parsing errors
    data.push({
      variable: "parser_error",
      value: `JSON parsing failed: ${error.message}`,
      group: group || undefined,
      time: time,
      metadata: metadata || {}
    });
  }

  return data;
}

// Main decoder logic - Extract MQTT payload data
const mqttPayload = payload.find((item) => item.variable === "payload");

if (mqttPayload && mqttPayload.value) {
  // Extract metadata and other properties
  const group = mqttPayload.group;
  const metadata = mqttPayload.metadata || {};
  // Parse the JSON payload and transform to TagoIO format
  const parsedData = parseMQTTJsonPayload(mqttPayload.value, group, mqttPayload.time, metadata);
  // Replace original payload with parsed data
  payload = parsedData;
}

Thanks, I’ll test it here.

OK, it worked, thanks!

I’ll continue studying the dashboard.

Hello, if I need to send data from TagoIO to the Broker using Relay, how should I proceed?

Thank you!

Hello @ferriautomacao,

Following the Relay tutorial should guide you to set the Middleware URL in your TagoIO Network. Once you’ve set it up correctly, you can use Analysis to publish through the utility at https://js.sdk.tago.io/classes/Network.html#publishtorelay

Example:

import { Analysis, Resources, Utils } from "jsr:@tago-io/sdk";

async function run(context) {
  // Publish to Relay: topic and payload for your external broker
  await Resources.integration.networks.publishToRelay({
     device: "DEVICE_ID",
      topic: "devices/relay-test",
      message: "001201",
      options: { qos: 0 }, // optional
  });

  console.log("Published via Relay");
}

Analysis.use(run);

Hi zsu[@user:10102206948]zsu,

Great job getting your device talking to TagoIO through MQTT Bridge and publishing via publishToRelay. To send commands from the dashboard in a structured way, I recommend using an Input Form widget and setting it to trigger your Analysis.

How to set it up

  1. Add an Input Form widget to your dashboard.
  2. Create the Fields you need for each command parameter (e.g., mode, setpoint, output_pct, etc.).
  3. In the widget’s Action settings, choose “Trigger Analysis” and select your Analysis.
  4. When the user submits the form, the field values will be delivered to your Analysis in the scope parameter. You can then parse those values and build the exact payload your device expects.

Example (adapting your Analysis)

  • Suppose your Input Form has fields:

    • command (e.g., “set_mode”, “set_output”)
    • value (e.g., “AUTO” or 73)
  • Your Analysis can read from scope and publish to your broker:

import { Analysis, Network } from "jsr:@tago-io/sdk";

  

async function run(context, scope) {

const network \= new Network({ token: "YOUR-NETWORK-TOKEN" });

  

// scope will contain an array of variables coming from the Input Form

// Example extraction helper:

const get \= (key) \=> (scope.find((x) \=> x.variable \=== key) || {}).value;

  

const command \= get("command");

const value \= get("value");

  

// Build your device message based on the form inputs

// Replace this logic with your device’s protocol

const topic \= "/tanacas/123456";

const message \= command \=== "set\_mode"

? (value \=== "AUTO" ? "001201" : "001200")

: command \=== "set\_output"

? \`02${String(value).padStart(3, "0")}\`

: "";

  

if (!message) {

context.log("Invalid command payload");

return;

}

  

await network.publishToRelay({

device: "688988e9544bbb0xxxxxxxxx",

topic,

message,

options: { qos: 0 },

});

  

context.log("Command sent via Relay");

}

  

export default new Analysis({ token: "ANALYSIS-TOKEN" }).run(run);

Tips

  • The Field “Key” names you configure in the Input Form become the variable names found in scope (scope.variable and scope.value). Use clear, consistent keys.
  • Validate and normalize inputs (limits, types, mapping to hex/binary) before building the payload.
  • Keep tokens in environment vars or Analysis secrets when possible.
  • If your commands require binary or hex encoding, convert the scope values accordingly before publishToRelay.

Hi Mateus!

Can I only send this with an input form?

I need to send three analog data points and one digital data point from a button.

With the components I used in the dashboard as shown in the image I sent, I can now write data from the buckets to four variables. Couldn’t I send this data by directly retrieving the information from the buckets with my analysis?

widget → buckets → Analysis → relay → broker

I’m trying to understand your suggestion, but I can’t figure out how to connect my input form to the suggested analysis.

Below is an image of my input form.

Could you please adapt the Analysis for this form of mine with my device and my variables?

Hi ferriautomacao,

You can keep the three “Step Button” widgets you have created and read their values in your Analysis using the SDK method getDeviceData:

https://js.sdk.tago.io/classes/_internal_.Devices.html#getdevicedata

In the screenshot you shared, I don’t see an Analysis selected to trigger when the “Enviar” button is pressed. Please confirm whether you assigned an Analysis to that button.

The code I provided is only an example and must be adapted to your use case. If you get stuck at any step, share which part is failing and I’ll help you resolve it.

I resolved it using this analysis in the input form.

import { Analysis, Network } from "jsr:@tago-io/sdk";

const run = async (context, scope) => {
  const network = new Network({ token: "xxxxxxxxxxx" });

  const get = (key) => (scope.find((x) => x.variable === key) || {}).value;

  const dataToSend = {
    tr_auto: get("tr_auto"),
    csr_auto: get("csr_auto"),
    aj_manual: get("aj_manual"),
    auto_manual: get("auto_manual"),
  };