Week 11 — Networking and Communications

This week focused on networking and communications. I built a two-node wireless system using MQTT. The input node was a XIAO ESP32S3 Sense with a camera and a local fruit recognition model, and the output node was a XIAO ESP32-C3 connected to a Grove RGB LED module.

My final goal was to make the S3 recognize an apple, publish an MQTT message, and then make the C3 turn on the RGB LED for five seconds after receiving the message.

Tools used

Key outputs


Overview

This week was about communication between different nodes. In previous weeks, most of my input and output tests happened on one board. This time, I separated the system into two microcontroller nodes and used MQTT to connect them wirelessly.

The XIAO ESP32S3 Sense was used as the input node. It used the camera and a local fruit recognition model to detect whether there was an apple in front of it. When the model detected an apple, the S3 published the message apple through MQTT.

The XIAO ESP32-C3 was used as the output node. It subscribed to the same MQTT topic. When the C3 received the message apple, it turned on the Grove RGB LED for five seconds.


Assignment

Group Assignment

The group assignment was to send a message between two projects. I tested MQTT communication with MQTT Explorer and used it to send messages to the XIAO ESP32-C3 receiver node.

Individual Assignment

The individual assignment was to design, build, and connect wired or wireless node(s) with network or bus addresses and local input and/or output device(s). I built a wireless two-node system: the S3 worked as the input node, and the C3 worked as the output node.


System Design

I used MQTT as the communication method. MQTT uses a publish and subscribe structure. The sender publishes a message to a topic, and the receiver subscribes to that topic. The two boards do not need to be directly connected by wires.

Final System Logic

XIAO ESP32S3 Sense camera sees an apple
        ↓
Local fruit recognition model detects apple
        ↓
S3 publishes MQTT message: apple
        ↓
MQTT broker receives the message
        ↓
XIAO ESP32-C3 subscribes to the same topic
        ↓
C3 receives apple
        ↓
Grove RGB LED turns red for five seconds
Item Value
MQTT broker broker.hivemq.com
Port 1883
Topic litchee/garlen/week11/apple
Message apple

Step 1: Installing MQTT Explorer

At the beginning, I needed a tool to observe and test MQTT messages before connecting the two boards together. I used MQTT Explorer because it can show MQTT topics clearly and can also manually publish test messages.

MQTT Explorer can be downloaded from its official website: https://mqtt-explorer.com/ . At first, I tried to download the app from the website, but macOS blocked the downloaded application. To avoid spending too much time on this installation problem, I downloaded MQTT Explorer directly from the App Store.

Downloading MQTT Explorer from the App Store
I downloaded MQTT Explorer from the App Store and used it to monitor and manually publish MQTT messages.

MQTT Explorer Download and Broker Setting

For the MQTT server, I used the HiveMQ public MQTT broker. The broker address was broker.hivemq.com. HiveMQ provides a public MQTT broker for testing MQTT clients and messages: HiveMQ Public MQTT Broker .

In the MQTT Explorer connection window, I filled in the following information. I used the normal MQTT protocol without TLS encryption, so the port was 1883. I did not need a username or password for this public broker.

Setting Value Explanation
Name Week11 Apple MQTT This is only the local name of my connection in MQTT Explorer.
Protocol mqtt:// I used the normal MQTT protocol.
Host broker.hivemq.com This is the public MQTT broker server used in my test.
Port 1883 Port 1883 is the standard MQTT port without TLS.
Username Empty No username was required for this public test broker.
Password Empty No password was required for this public test broker.
Validate certificate Off I did not use TLS, so certificate validation was not needed.
Encryption / TLS Off I used mqtt:// and port 1883.
MQTT Explorer broker setting for HiveMQ
MQTT Explorer connection setting. I used broker.hivemq.com as the host and 1883 as the MQTT port.

Step 2: Manual MQTT Test with the C3

Before adding the S3 camera node, I first tested whether the C3 could receive MQTT messages and control the RGB LED. In MQTT Explorer, I manually published two messages: apple and no_apple.

In this early test, apple was used to turn on the RGB LED, and no_apple was used to turn it off. This helped me confirm that the C3, the MQTT topic, and the RGB LED wiring were working correctly.

Manual Publish Setting in MQTT Explorer

After connecting to the broker, I used the Publish panel on the right side of MQTT Explorer to send test messages. This allowed me to test the C3 receiver before adding the S3 camera node.

Publish setting Value
Topic litchee/garlen/week11/apple
Payload type raw
Payload for LED on apple
Payload for early LED off test no_apple
QoS 0
Retain Unchecked

In the final version, I only kept the apple message. The C3 turned on the RGB LED for five seconds after receiving apple, and then the LED turned off automatically.

Manual MQTT publish test using apple message
Manual publish test in MQTT Explorer. I used the topic litchee/garlen/week11/apple and published the payload apple.
Testing apple and no_apple messages with the C3 receiver and RGB LED.

Step 3: Building the C3 RGB LED Output Node

The output node was built with a XIAO ESP32-C3 and a Grove RGB LED module. The C3 subscribed to the MQTT topic and controlled the RGB LED according to the received message.

RGB LED Wiring

Grove RGB LED XIAO ESP32-C3 Function
Red wire 3V3 Power
Black wire GND Ground
Yellow wire D3 Data
White wire D2 Clock
Grove RGB LED red wire     → XIAO ESP32-C3 3V3
Grove RGB LED black wire   → XIAO ESP32-C3 GND
Grove RGB LED yellow wire  → XIAO ESP32-C3 D3
Grove RGB LED white wire   → XIAO ESP32-C3 D2

C3 Receiver Code

The C3 connected to Wi-Fi, connected to the MQTT broker, and subscribed to the topic litchee/garlen/week11/apple. When the received message contained apple, the RGB LED turned red for five seconds.

#include <WiFi.h>
#include <PubSubClient.h>
#include <ChainableLED.h>

const char* ssid = "LitcheeLab";
const char* password = "********";

const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
const char* topic = "litchee/garlen/week11/apple";

WiFiClient espClient;
PubSubClient client(espClient);

const int DATA_PIN = D3;
const int CLOCK_PIN = D2;

ChainableLED leds(CLOCK_PIN, DATA_PIN, 1);

void rgbOff() {
  leds.setColorRGB(0, 0, 0, 0);
  Serial.println("RGB LED OFF.");
}

void rgbRedForFiveSeconds() {
  Serial.println("Apple detected. RGB LED will stay ON for 5 seconds.");

  for (int i = 0; i < 50; i++) {
    leds.setColorRGB(0, 255, 0, 0);
    delay(100);
  }

  rgbOff();
  Serial.println("5 seconds finished.");
}

At first, the RGB LED only flashed briefly. I found that sending the color command only once was not stable enough for this module. To make the LED stay on, I repeatedly sent the red color command during the five-second output period.


Step 4: Timed MQTT Publishing Test with the S3

After the C3 receiver worked, I tested whether another board could publish messages to the same topic. At this stage, the S3 did not use the camera yet. I programmed the S3 to publish apple every ten seconds. When the C3 received the message, the RGB LED turned on for five seconds.

This was only a communication test. It proved that the S3 could publish MQTT messages and that the C3 could respond to messages from another board.

S3 timed MQTT publishing test. The S3 published apple every ten seconds, and the C3 turned on the RGB LED after receiving the message.

Step 5: Trying SenseCraft AI

After the basic MQTT communication worked, I started to test apple recognition on the XIAO ESP32S3 Sense. My first method was SenseCraft AI. I connected the S3 to the SenseCraft workspace and loaded an object recognition model.

SenseCraft AI workspace for XIAO ESP32S3 Sense
SenseCraft AI workspace for the XIAO ESP32S3 Sense.

The SenseCraft preview could recognize the apple successfully. The preview showed the apple label with a confidence value.

SenseCraft AI detecting an apple
SenseCraft AI successfully recognized the apple in the camera preview.

However, when I tried to use the MQTT output from SenseCraft, the response was slower than I expected. It also used its own topic structure, such as sscma/v0/#, which made the communication less direct for my final test.

SenseCraft MQTT configuration
MQTT configuration in SenseCraft AI. I tested this method but did not use it as the final method because the response was slow.
C3 serial monitor subscribing to SenseCraft topic
During the SenseCraft test, I also tried subscribing to the SenseCraft topic structure sscma/v0/# on the C3.
SenseCraft MQTT discovery message in MQTT Explorer
MQTT Explorer received a SenseCraft discovery message, but the workflow was not as direct as my final MQTT setup.
SenseCraft AI test video. The model could recognize the apple, but the MQTT response was delayed, so I changed to a local model library.

Step 6: Switching to a Local Fruit Recognition Library

To make the response faster and easier to control, I switched to a local fruit recognition Arduino library. This library can run directly on the XIAO ESP32S3 Sense and can classify apples, bananas, and grapes.

Finding the Library

I downloaded the library from the Seeed Studio Edge Impulse tutorial: Seeed Studio Wiki — Edge Impulse for XIAO ESP32S3 Sense . On this page, I found the section called Fruit identification (apples, bananas, grapes) Arduino Library.

Fruit identification Arduino library from Seeed Studio Wiki
The fruit identification Arduino library for XIAO ESP32S3 Sense. This library can classify apples, bananas, and grapes.

Adding the ZIP Library to Arduino IDE

The downloaded model library file was:

xiao-esp32s3-fruits-classify_inferencing.zip

I added the ZIP library to Arduino IDE through:

Sketch
→ Include Library
→ Add .ZIP Library
→ xiao-esp32s3-fruits-classify_inferencing.zip

I also uploaded the ZIP file as a source file for this week, so that the local model library can be downloaded and reused: xiao-esp32s3-fruits-classify_inferencing.zip .

../files/week_11/xiao-esp32s3-fruits-classify_inferencing.zip

After adding the library, I could include the model in my Arduino code with:

#include <xiao-esp32s3-fruits-classify_inferencing.h>

Step 7: Final Local Model and MQTT Integration

In the final version, the S3 ran the fruit recognition model locally. The board only published apple when the model detected apple or apples with a confidence value higher than 0.70.

This final code did not send apple automatically every ten seconds. The delay in the code only prevents repeated messages when the apple stays in front of the camera.

Important S3 Code

#include <xiao-esp32s3-fruits-classify_inferencing.h>

#include <WiFi.h>
#include <PubSubClient.h>
#include "esp_camera.h"
#include "img_converters.h"
#include "edge-impulse-sdk/dsp/image/image.hpp"

const char* ssid = "LitcheeLab";
const char* password = "********";

const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
const char* topic = "litchee/garlen/week11/apple";

WiFiClient espClient;
PubSubClient client(espClient);

void publishApple() {
  if (!client.connected()) {
    reconnect_mqtt();
  }

  client.loop();
  client.publish(topic, "apple");

  Serial.print("Published to topic: ");
  Serial.println(topic);
  Serial.println("Message: apple");
}

After the classifier ran, I checked all labels. If the label was apple or apples, and the confidence was higher than 0.70, the S3 published the MQTT message.

bool appleDetectedNow = false;

for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
  String label = result.classification[ix].label;
  float confidence = result.classification[ix].value;

  Serial.print(label);
  Serial.print(": ");
  Serial.println(confidence, 4);

  label.toLowerCase();

  if ((label == "apple" || label == "apples") && confidence > 0.70) {
    appleDetectedNow = true;
  }
}

if (appleDetectedNow) {
  Serial.println("Apple detected by local AI model.");

  if (millis() - lastAppleSendTime > resendDelay) {
    publishApple();
    lastAppleSendTime = millis();
  }
}

Final Test

In the final test, I placed an apple in front of the S3 camera. The local fruit model detected the apple, the S3 published the MQTT message apple, and the C3 received the message and turned on the RGB LED for five seconds.

Final test. The local fruit model on the S3 detected the apple, the S3 published the MQTT message, and the C3 turned on the RGB LED.

Result

The final system worked successfully. The S3 recognized the apple locally and sent an MQTT message. The C3 received the message and controlled the RGB LED as the output device.


Problems and Solutions

Problem Reason Solution
MQTT Explorer was blocked by macOS. The manually downloaded app could not be opened. I downloaded MQTT Explorer directly from the App Store.
PubSubClient.h could not be found. The MQTT library was not installed in Arduino IDE. I installed the PubSubClient library from the Arduino Library Manager.
The C3 could not connect to Wi-Fi at first. The first Wi-Fi network was not suitable for the ESP32 board. I changed the Wi-Fi to LitcheeLab, and the C3 connected successfully.
The RGB LED only flashed briefly. Sending the RGB color command only once was not stable enough for this Grove RGB LED module. I repeatedly sent the red color command during the five-second lighting period.
The S3 timed publish test was not the final goal. It only tested communication, not apple recognition. I replaced the timed publish logic with model-based publishing.
SenseCraft AI MQTT output was slow. SenseCraft used its own MQTT topic structure and the response was delayed in my test. I switched to a local fruit recognition Arduino library and published my own MQTT message from the S3.
The S3 upload port was busy. The Serial Monitor or SenseCraft browser page was still connected to the board. I closed the Serial Monitor and SenseCraft page, unplugged and replugged the S3, then uploaded again.
The S3 showed Failed to allocate snapshot buffer. The camera and AI model required more memory than normal internal RAM could provide. I enabled PSRAM in Arduino IDE and allocated the image buffer in PSRAM using ps_malloc().

What I Learned

This week helped me understand how to test a networked system step by step. Instead of trying to build the final system immediately, I first tested the MQTT tool, then the C3 receiver, then S3 publishing, then image recognition, and finally the complete integration.

I learned that MQTT is useful because the sender and receiver do not need to be directly connected. They only need to use the same broker and topic. This makes the system flexible and easier to debug.

I also learned that running camera recognition on the XIAO ESP32S3 Sense requires careful memory management. Enabling PSRAM was important for making the camera and local model work together.


Reflection

This week connected several previous topics together: input devices, output devices, embedded programming, and networking. The S3 acted as a smart input node, while the C3 acted as a physical output node.

The most important part was not only sending an MQTT message, but deciding when the message should be sent. The final system only sent apple after the local model detected an apple, instead of sending messages at a fixed time interval.

For my final project, this workflow is very useful. My emotional companion robot may need to recognize visual information and trigger a physical response, such as light, movement, or sound. This week's test gives me a basic structure for that kind of interaction.