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:
- 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
WiFiSSLClientwithout pasting any certificate. - 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()andendMessage()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
onMessagecallback receives only the size; you callmessageTopic()and thenread()untilavailable()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
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.- Plain 1883 works but 8883 fails: update the Wi-Fi module firmware, and make sure you used
WiFiSSLClientrather thanWiFiClient. - Error 4 (bad username or password) or 5 (not authorised): recheck all three portal values, including the client ID.
- Connection drops after a while: a blocking call in
loop()stoppedpoll()from running, or another device uses the same client ID. - No incoming messages:
onMessage()was not registered, or you subscribed beforeconnect()succeeded. - 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.



