//Task 3: MQTT + JSON – Pico W
#include <WiFi.h>
#include <PubSubClient.h>
#include "SimpleJson.h"
const char* WIFI_SSID = "Wokwi-GUEST";
const char* WIFI_PASS = "";
const char* MQTT_BROKER = "broker.hivemq.com";
const int MQTT_PORT = 1883;
const char* TOPIC_DATA = "iem/task3/pico/data";
const char* TOPIC_CMD = "iem/task3/pico/cmd";
const int LED_PIN = 28, BUTTON_PIN = 2, POT_PIN = 26;
const char* TOKEN = "iem2026";
const int INTERVAL_MIN = 50, INTERVAL_MAX = 2000;
bool blinkEnabled = false, ledState = false, inSafeState = false;
int overrideInterval = -1;
unsigned long lastValidCmd = 0, lastBlink = 0, lastPublish = 0;
int expectedSeqNr = -1;
unsigned long seqOutgoing = 0;
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
SimpleJson jsonOut, jsonIn;
void setupWiFi() {
Serial.println("Connecting WiFi...");
WiFi.begin(WIFI_SSID, WIFI_PASS);
int tries = 0;
while (WiFi.status() != WL_CONNECTED && tries < 40) {
delay(500);
Serial.print(".");
tries++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi....CONNECTED");
} else {
Serial.println("\nWiFi....FAILED");
}
}
bool validateMessage(const SimpleJson& msg) {
if (strcmp(msg.getString("token"), TOKEN) != 0) return false;
if (strcmp(msg.getString("source"), "nano") != 0) return false;
return true;
}
bool checkSequence(const SimpleJson& msg) {
int seq = msg.getInt("seq", -999);
if (seq == -999) return true;
if (seq == 0) { expectedSeqNr = 1; return true; }
if (expectedSeqNr < 0) { expectedSeqNr = seq + 1; return true; }
if (seq < expectedSeqNr) return false;
expectedSeqNr = seq + 1;
return true;
}
void processCommand(const SimpleJson& cmd) {
if (cmd.hasKey("blinkEnabled")) {
blinkEnabled = cmd.getBool("blinkEnabled");
Serial.print("Blink: "); Serial.println(blinkEnabled);
}
if (cmd.hasKey("ledOn")) {
ledState = cmd.getBool("ledOn");
blinkEnabled = false;
digitalWrite(LED_PIN, ledState ? HIGH : LOW);
Serial.print("LED: "); Serial.println(ledState);
}
if (cmd.hasKey("interval")) {
int iv = cmd.getInt("interval");
if (iv >= INTERVAL_MIN && iv <= INTERVAL_MAX) {
overrideInterval = iv;
Serial.print("Interval: "); Serial.println(iv);
}
}
if (cmd.hasKey("useLocalPot")) overrideInterval = -1;
}
void mqttCallback(char* topic, byte* payload, unsigned int length) {
char buf[256];
memcpy(buf, payload, length);
buf[length] = '\0';
Serial.print("RX: "); Serial.println(buf);
if (!jsonIn.parse(buf) || !validateMessage(jsonIn) || !checkSequence(jsonIn)) return;
lastValidCmd = millis();
inSafeState = false;
processCommand(jsonIn);
}
void mqttReconnect() {
if (mqtt.connected()) return;
Serial.print("MQTT connect...");
String id = "pico-" + String(random(0xFFFF), HEX);
if (mqtt.connect(id.c_str())) {
Serial.println("CONNECTED");
mqtt.subscribe(TOPIC_CMD);
} else {
Serial.println("FAILED");
}
}
void publishData(int pot, int interval) {
jsonOut.clear();
jsonOut.setString("token", TOKEN);
jsonOut.setString("source", "pico");
jsonOut.setInt("seq", (int)seqOutgoing++);
jsonOut.setInt("potValue", pot);
jsonOut.setBool("blinkEnabled", blinkEnabled);
jsonOut.setInt("interval", interval);
jsonOut.setBool("ledState", ledState);
jsonOut.setBool("safeState", inSafeState);
jsonOut.setInt("uptime", (int)(millis() / 1000));
char buf[256];
jsonOut.toCharArray(buf, sizeof(buf));
mqtt.publish(TOPIC_DATA, buf);
}
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("=== Pico W Task 3 ===");
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
setupWiFi();
mqtt.setServer(MQTT_BROKER, MQTT_PORT);
mqtt.setCallback(mqttCallback);
}
void loop() {
if (!mqtt.connected()) {
mqttReconnect();
delay(500);
return;
}
mqtt.loop();
// Watchdog: safe state after 10 sec no command
if (lastValidCmd > 0 && (millis() - lastValidCmd) > 10000) {
if (!inSafeState) {
inSafeState = true;
overrideInterval = -1;
Serial.println("Safe state");
}
}
// Button toggle blink
static bool lastBtn = HIGH;
bool btn = digitalRead(BUTTON_PIN);
if (btn == LOW && lastBtn == HIGH) {
blinkEnabled = !blinkEnabled;
Serial.print("Button: "); Serial.println(blinkEnabled);
delay(50);
}
lastBtn = btn;
// Potentiometer controls interval
int pot = analogRead(POT_PIN);
int potInterval = map(pot, 0, 4095, INTERVAL_MIN, INTERVAL_MAX);
int interval = (overrideInterval >= 0) ? overrideInterval : potInterval;
// Blink
if (blinkEnabled && (millis() - lastBlink) >= interval) {
lastBlink = millis();
ledState = !ledState;
digitalWrite(LED_PIN, ledState ? HIGH : LOW);
}
// Publish every 500ms
if ((millis() - lastPublish) >= 500) {
lastPublish = millis();
publishData(pot, interval);
}
}
Loading
pi-pico-w
pi-pico-w