ESP32 and Arduino

Arduino UNO R4 WiFi MQTT Tutorial: Publish Sensor Data with ArduinoMqttClient

Connect the Arduino UNO R4 WiFi to an MQTT broker with WiFiS3 and ArduinoMqttClient, publish analog readings, control the LED matrix from your laptop and move to TLS on port 8883.

Arduino UNO R4 WiFi board with its LED matrix lit, illustrating an Arduino UNO R4 WiFi MQTT connection to the cloud
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

The Arduino UNO R4 WiFi brings Wi-Fi to the familiar UNO form factor, which makes it a great first board for an Arduino UNO R4 WiFi MQTT project. In this tutorial you will use the WiFiS3 library and the official ArduinoMqttClient to publish analog sensor readings, receive commands that draw on the built-in LED matrix, and then switch to an encrypted TLS connection with your own credentials.

How the UNO R4 WiFi Talks to the Network

The UNO R4 WiFi has two chips. The main microcontroller is a Renesas RA4M1 that runs your sketch. Networking is handled by an ESP32-S3 module that acts as a Wi-Fi coprocessor. Your sketch talks to it through the WiFiS3 library, which comes with the "Arduino UNO R4 Boards" package.

This split has two practical consequences:

  1. The TLS handshake runs on the ESP32-S3 module, which ships with a bundle of common root certificates. Encrypted connections to brokers with widely trusted certificates, such as Let's Encrypt, generally work with WiFiSSLClient without pasting any certificate.
  2. The coprocessor has its own firmware. If connections behave strangely, updating that firmware is one of the first fixes to try.

For the MQTT protocol itself you will use ArduinoMqttClient, the official library maintained by Arduino. It works with any Client object, including WiFiClient and WiFiSSLClient from WiFiS3.

What You Need

Item Notes
Arduino UNO R4 WiFi Not the UNO R4 Minima, which has no radio
Arduino IDE 2.x With "Arduino UNO R4 Boards" installed via Boards Manager
ArduinoMqttClient Install from Library Manager
A potentiometer or any analog sensor Connected to A0 (optional, the code works with a floating pin)
Mosquitto CLI tools For testing from your computer
A 2.4 GHz Wi-Fi network The module does not join 5 GHz networks

Before you start, open File → Examples → WiFiS3 → CheckFirmwareVersion and upload it. If the serial monitor reports an outdated firmware, update it with the firmware updater in the Arduino IDE (Tools → Firmware Updater) or the official instructions on docs.arduino.cc.

Step 1: Publish to the Public Broker

The first version connects without encryption to broker.connectmq.com on port 1883, the public MQTT test broker provided by ConnectMQ. Anonymous connections are accepted as long as the client ID is not empty. Use a unique topic prefix and do not send private data, because anyone can subscribe to the public area.

Create a second tab in the sketch called arduino_secrets.h:

#define SECRET_SSID "YOUR_WIFI"
#define SECRET_PASS "YOUR_WIFI_PASSWORD"

Then the main sketch:

#include <WiFiS3.h>
#include <ArduinoMqttClient.h>
#include "Arduino_LED_Matrix.h"
#include "arduino_secrets.h"

const char broker[] = "broker.connectmq.com";
const int  port     = 1883;

const char deviceId[]       = "unor4-8d21f0";   // make this unique
const char topicTelemetry[] = "connectmq/demo/unor4-8d21f0/telemetry";
const char topicCommand[]   = "connectmq/demo/unor4-8d21f0/matrix/set";
const char topicStatus[]    = "connectmq/demo/unor4-8d21f0/status";

WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);
ArduinoLEDMatrix matrix;

unsigned long lastPublish = 0;

// Two simple frames for the 12x8 matrix
const uint32_t FRAME_ON[]  = { 0xffffffff, 0xffffffff, 0xffffffff };
const uint32_t FRAME_OFF[] = { 0x00000000, 0x00000000, 0x00000000 };

void connectWifi() {
  Serial.print("Connecting to ");
  Serial.println(SECRET_SSID);
  while (WiFi.begin(SECRET_SSID, SECRET_PASS) != WL_CONNECTED) {
    Serial.print(".");
    delay(3000);
  }
  // WiFiS3 can report 0.0.0.0 briefly, wait for a real address
  while (WiFi.localIP() == IPAddress(0, 0, 0, 0)) delay(200);
  Serial.print("\nIP address: ");
  Serial.println(WiFi.localIP());
}

void onMqttMessage(int messageSize) {
  String topic = mqttClient.messageTopic();
  String payload;
  while (mqttClient.available()) {
    payload += (char)mqttClient.read();
  }
  Serial.print("Received on ");
  Serial.print(topic);
  Serial.print(": ");
  Serial.println(payload);

  if (payload == "ON")  matrix.loadFrame(FRAME_ON);
  if (payload == "OFF") matrix.loadFrame(FRAME_OFF);
}

bool connectMqtt() {
  mqttClient.setId(deviceId);
  mqttClient.setKeepAliveInterval(30 * 1000L);   // milliseconds

  // Last Will must be set before connect()
  mqttClient.beginWill(topicStatus, true, 1);
  mqttClient.print("offline");
  mqttClient.endWill();

  Serial.print("Connecting to MQTT broker... ");
  if (!mqttClient.connect(broker, port)) {
    Serial.print("failed, error = ");
    Serial.println(mqttClient.connectError());
    return false;
  }
  Serial.println("connected");

  mqttClient.onMessage(onMqttMessage);
  mqttClient.subscribe(topicCommand, 1);

  mqttClient.beginMessage(topicStatus, true, 1);   // retained, QoS 1
  mqttClient.print("online");
  mqttClient.endMessage();
  return true;
}

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 3000) {}
  matrix.begin();
  connectWifi();
  while (!connectMqtt()) delay(5000);
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) connectWifi();
  if (!mqttClient.connected()) {
    delay(5000);
    connectMqtt();
    return;
  }

  mqttClient.poll();   // sends keep alive pings and delivers messages

  if (millis() - lastPublish > 5000) {
    lastPublish = millis();
    int raw = analogRead(A0);
    mqttClient.beginMessage(topicTelemetry);
    mqttClient.print("{\"a0\":");
    mqttClient.print(raw);
    mqttClient.print(",\"rssi\":");
    mqttClient.print(WiFi.RSSI());
    mqttClient.print(",\"uptime\":");
    mqttClient.print(millis() / 1000);
    mqttClient.print("}");
    mqttClient.endMessage();
  }
}

Watch the messages and draw on the matrix from your computer:

mosquitto_sub -h broker.connectmq.com -p 1883 -i pc-8d21f0 -t "connectmq/demo/unor4-8d21f0/#" -v
mosquitto_pub -h broker.connectmq.com -p 1883 -i ctl-8d21f0 -t "connectmq/demo/unor4-8d21f0/matrix/set" -m ON

Turn the potentiometer and the a0 value should follow within five seconds.

How ArduinoMqttClient Differs from PubSubClient

If you have used PubSubClient before, three things will feel different:

  • Messages are streamed. beginMessage(), print() and endMessage() let you build a payload piece by piece without a big string buffer. That fits the UNO R4's RAM nicely.
  • Incoming messages are read like a stream. The onMessage callback receives only the size; you call messageTopic() and then read() until available() returns zero.
  • QoS 1 and 2 publishing are supported, as are retained messages and a Last Will built with beginWill()/endWill().

You must still call poll() regularly, just as you call loop() in PubSubClient. Long delay() calls in loop() delay keep alive pings and incoming messages. Our ArduinoMqttClient library guide covers the whole API, including the meaning of every connectError() value.

Step 2: Arduino UNO R4 WiFi MQTT over TLS

For anything beyond experiments, use a private broker with credentials and TLS. On a fully managed MQTT service like ConnectMQ, you create a broker in the portal and receive a username, password and client ID. Thanks to the certificate bundle on the Wi-Fi module, switching to TLS takes only a few changed lines:

WiFiSSLClient wifiClient;            // was WiFiClient
MqttClient mqttClient(wifiClient);

const char broker[] = "broker.connectmq.com";
const int  port     = 8883;          // MQTT over TLS

bool connectMqtt() {
  mqttClient.setId("YOUR_CLIENT_ID");
  mqttClient.setUsernamePassword("YOUR_USERNAME", "YOUR_PASSWORD");
  mqttClient.setKeepAliveInterval(30 * 1000L);
  if (!mqttClient.connect(broker, port)) {
    Serial.print("TLS MQTT failed, error = ");
    Serial.println(mqttClient.connectError());
    return false;
  }
  // subscribe and publish as before
  return true;
}

Keep credentials in arduino_secrets.h rather than the main sketch so you do not commit them by accident. ConnectMQ binds client IDs to issued credentials, so copy the client ID exactly. On a private broker you can also drop the connectmq/demo/ prefix and design a topic tree for your project. The MQTT security best practices article lists further hardening steps.

Designing Topics and Payloads That Scale

A single board on the public broker can use any layout, but it pays to design topics as if you will have ten boards next month. A predictable structure lets dashboards subscribe with wildcards and keeps commands clearly separated from telemetry.

Topic Direction QoS Retained Purpose
<prefix>/<deviceId>/telemetry Board publishes 0 No Periodic sensor readings
<prefix>/<deviceId>/status Board publishes, Will 1 Yes online / offline presence
<prefix>/<deviceId>/matrix/set Board subscribes 1 No Commands from apps or scripts
<prefix>/<deviceId>/config Board subscribes 1 Yes Settings applied at every boot

The retained config topic is a handy trick: publish the desired publish interval or threshold once with the retain flag, and every time the board reconnects it receives the latest settings immediately, without any extra request logic in your firmware.

Keep payloads compact. The UNO R4's RA4M1 has 32 KB of SRAM, which is generous compared with the classic UNO but still small, so avoid building large String objects in long-running sketches. Streaming with print(), as the example does, keeps memory use flat. For noisy analog inputs, average several analogRead() samples before publishing, and consider publishing only when the value changes by more than a small threshold. Fewer, more meaningful messages are easier on the network and easier to read on a dashboard.

Troubleshooting Checklist

  1. connectError() returns -2 (connection refused) or the connect call fails immediately: wrong host or port, or no Wi-Fi. Confirm the board has a real IP address, not 0.0.0.0.
  2. Plain 1883 works but 8883 fails: update the Wi-Fi module firmware, and make sure you used WiFiSSLClient rather than WiFiClient.
  3. Error 4 (bad username or password) or 5 (not authorised): recheck all three portal values, including the client ID.
  4. Connection drops after a while: a blocking call in loop() stopped poll() from running, or another device uses the same client ID.
  5. No incoming messages: onMessage() was not registered, or you subscribed before connect() succeeded.
  6. Upload works but the serial monitor stays empty: set the monitor to 115200 baud; the R4 uses native USB, so open the monitor after the upload finishes.

Where to Go Next

Once you have a reliable connection, add a real sensor and build a dashboard. If you need more GPIO, Bluetooth or local processing, an ESP32 might suit you better; the ESP32 IoT tutorial walks through a complete project. If you own a different Arduino, the Arduino Nano 33 IoT MQTT guide follows the same library pattern with WiFiNINA.

Get Your UNO R4 Online with ConnectMQ

ConnectMQ is a cloud MQTT broker supporting MQTT 3.1.1 and 5.0, TLS on 8883 and WebSockets for browser dashboards. The portal includes an in-browser MQTT client and a live topic explorer so you can see your UNO's readings arrive. Create a free Serverless broker at app.connectmq.com with no credit card and switch your sketch to TLS today.

Frequently Asked Questions

Do I need to upload a root certificate to the UNO R4 WiFi?

Usually not. The ESP32-S3 Wi-Fi module carries a bundle of common root certificates, so WiFiSSLClient can verify brokers that use widely trusted authorities like Let's Encrypt. If verification fails, update the module firmware first.

Can I use PubSubClient on the UNO R4 WiFi?

Yes, PubSubClient works with any Arduino Client, including WiFiS3's clients. ArduinoMqttClient is the officially supported option and adds QoS 1 and 2 publishing.

Does the UNO R4 WiFi support MQTT 5?

ArduinoMqttClient implements MQTT 3.1.1, which is fully supported by ConnectMQ alongside MQTT 5. Your MQTT 5 dashboards and backends can subscribe to the same topics.

Why does my UNO R4 WiFi show IP 0.0.0.0?

WiFiS3 can report connected before DHCP finishes. Wait until WiFi.localIP() returns a real address before connecting to the broker, as the example does.

Related articles

ESP-IDF MQTT Example: Build an esp-mqtt Client from Scratch

Build an ESP-IDF project from an empty folder with the esp-mqtt component: Wi-Fi station setup, the MQTT event handler, publish and subscribe, MQTT 5 properties and TLS with the certificate bundle.

8 min read

Using esp-mqtt MQTT 5 Features on ESP32 with ESP-IDF

A hands-on ESP-IDF project that enables MQTT 5.0 in esp-mqtt and uses user properties, message expiry, topic aliases, session expiry and response topics against a real MQTT 5 broker.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.