Week 11 – Networking and Communications
This week is focused on Networking and Communications: understanding how two or more devices exchange information, how to assign addresses correctly, and how to integrate communication with a local input and/or output device.
On this page I document:
- The group assignment: sending a message between two projects.
- My individual assignment: designing, building and connecting wired or wireless node(s).
- The communication protocol I selected and why I chose it.
- How I implemented network or bus addresses.
- How the code works.
- The problems I found and how I solved them.
- The design files, source code and a hero shot of the final setup.
Assignment and Learning Outcomes
The weekly assignment is:
- Group assignment:
- Send a message between two projects.
- Individual assignment:
- Design, build, and connect wired or wireless node(s).
- Use network or bus addresses.
- Add a local input and/or output device.
The learning outcomes are:
- Understand the differences between wired and wireless communication.
- Implement a protocol such as I2C, UART, BLE, WiFi or similar.
- Assign and verify addresses correctly.
- Integrate communication with a real embedded system.
- Relate the weekly work to the final project.
Checklist
In this page I answer the required questions:
- Linked to the group assignment page.
- Documented my project and what I learned from implementing networking and/or communication protocols.
- Explained the programming process I used.
- Ensured and documented that the addressing for the boards works.
- Outlined the problems and how I fixed them.
- Included design files and original source code.
- Included a hero shot of my network and/or communications setup.
Types of Communication
In embedded systems and networking, there are different ways devices can exchange data. These communication models define how information flows between nodes and how the system is structured.
1. Point-to-Point Communication
This is the simplest type of communication, where one device communicates directly with another device.
- Only two nodes are involved.
- Direct communication without intermediaries.
- Easy to implement.
Examples: UART, Serial communication, Bluetooth.
In this type of system, one board sends data directly to another board.
2. Master–Slave Communication
In this model, one device (master) controls the communication and the other devices (slaves) respond when requested.
- The master initiates all communication.
- Slaves only respond when addressed.
- Uses addresses to identify devices.
Examples: I2C, SPI.
This type of communication is very common in embedded systems where one microcontroller controls multiple devices.
3. Client–Server Communication
This model is widely used in networking and web systems. One device acts as a client and requests data, while another acts as a server and provides it.
- The client sends requests.
- The server responds.
- Based on request/response logic.
Examples: HTTP, Web servers, REST APIs.
For example, an ESP32 can host a web server and another device can connect to it to retrieve data.
4. Publish–Subscribe Communication
This model is commonly used in IoT systems. Devices do not communicate directly but through an intermediary called a broker.
- Devices can publish messages.
- Devices can subscribe to topics.
- Communication is asynchronous.
Example: MQTT protocol.
In this system, one node publishes data to a topic, and any subscribed devices receive that data automatically.
5. Broadcast and Multicast
In these models, a device sends data to multiple devices at the same time.
- Broadcast: message is sent to all devices.
- Multicast: message is sent to a specific group.
Examples: UDP broadcast, local networks.
Comparison
| Type | Complexity | Scalability | Typical Use |
|---|---|---|---|
| Point-to-Point | Low | Low | UART, Bluetooth |
| Master–Slave | Medium | Medium | I2C, SPI |
| Client–Server | Medium | High | Web, APIs |
| Publish–Subscribe | High | Very High | MQTT, IoT |
| Broadcast | Low | High | Local networks |
UART Communication between XIAO RP2040 and ATOM MATRIX
In this assignment I established a UART communication between a Seeed XIAO RP2040 and an M5Stack ATOM MATRIX. The XIAO RP2040 acts as the transmitter, while the ATOM MATRIX works as the receiver and visualizes the received data on its LED matrix.
Boards Used
- XIAO RP2040 – UART transmitter
- ATOM MATRIX – UART receiver with LED matrix output
Communication Setup
Both boards were connected using UART with the same pins defined in the previous assignment. The TX and RX lines were crossed, and a common GND was shared between both boards.
- XIAO TX → ATOM RX
- XIAO RX ← ATOM TX
- Shared GND
The ATOM MATRIX UART configuration:
- TX = 32
- RX = 26
- Baud rate = 115200
System Behavior
The system transmits simple ASCII values:
- '1' → ON state (green icon)
- '0' → OFF state (red icon)
The ATOM MATRIX reads the incoming byte and updates the LED matrix accordingly. If no data is received, a blue "no data" icon is displayed.
Device Addressing over UART Communication
To enable communication between multiple devices connected to the same UART line, a simple addressing mechanism has been implemented. Each transmitting device is assigned a unique identifier (DEVICE_ID), which is included in every message sent over the serial interface.
The message format follows a structured pattern:
DEVICE_ID:VALUE
For example:
A1:1
A1:0
- DEVICE_ID identifies the sender device (e.g., A1, A2, etc.)
- VALUE represents the transmitted data (in this case, button state: 1 = pressed, 0 = released)
On the receiver side, the system continuously reads incoming UART data and processes complete messages line by line. Each message is parsed by separating the identifier from the value using the ":" delimiter.
The receiver is configured with its own expected DEVICE_ID and only processes messages that match this identifier. All other messages are ignored. This allows multiple devices to share the same communication channel without interfering at the application level.
It is important to note that this approach provides logical separation of devices but does not prevent electrical or timing conflicts if multiple transmitters send data simultaneously. For larger or more robust systems, a bus protocol such as RS485 with a master-slave architecture is recommended.
This addressing method offers a simple and effective way to scale UART-based communication systems while maintaining compatibility with lightweight embedded platforms.
XIAO RP2040 Program
Arduino XIAO Code Show code
#define BUTTON_PIN D7
#define DEVICE_ID "A1" // This is the addres
bool lastStableState = HIGH;
bool lastReading = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 30;
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial1.begin(115200);
Serial.begin(115200);
delay(500);
Serial.println("XIAO RP2040 ready");
}
void loop() {
bool reading = digitalRead(BUTTON_PIN);
if (reading != lastReading) {
lastDebounceTime = millis();
lastReading = reading;
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != lastStableState) {
lastStableState = reading;
if (lastStableState == LOW) {
Serial1.print(DEVICE_ID);
Serial1.println(":1"); // format ID:value
Serial.println("Sent: 1");
} else {
Serial1.print(DEVICE_ID);
Serial1.println(":0");
Serial.println("Sent: 0");
}
}
}
delay(5);
}
ATOM MATRIX Program
Code Show code
import M5
from M5 import *
from hardware import RGB
from hardware import UART
import time
rgb = None
uart1 = None
buffer = ""
DEVICE_ID = "A1" # ID for response
ICONO_ON = [
0, 0, 0, 0, 0,
0, 0x34c545, 0, 0x34c545, 0,
0, 0, 0, 0, 0,
0x34c545, 0, 0, 0, 0x34c545,
0, 0x34c545, 0x34c545, 0x34c545, 0
]
ICONO_OFF = [
0, 0, 0, 0, 0,
0, 0xc5346e, 0, 0xc5346e, 0,
0, 0, 0, 0, 0,
0xc5346e, 0, 0, 0, 0xc5346e,
0, 0xc5346e, 0xc5346e, 0xc5346e, 0
]
ICONO_NODATA = [
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0x2844cc, 0x2844cc, 0x2844cc, 0
]
def setup():
global rgb, uart1, buffer
M5.begin()
Widgets.fillScreen(0x000000)
rgb = RGB()
rgb.set_brightness(20)
rgb.set_screen(ICONO_NODATA)
uart1 = UART(1, baudrate=115200, bits=8, parity=None, stop=1, tx=32, rx=26)
buffer = ""
def loop():
global rgb, uart1, buffer
M5.update()
data = uart1.read()
if data:
try:
buffer += data.decode()
# Procesar líneas completas
while "\n" in buffer:
line, buffer = buffer.split("\n", 1)
line = line.strip()
# Esperamos formato: ID:valor
if ":" in line:
dev_id, value = line.split(":", 1)
if dev_id == DEVICE_ID:
if value == "1":
rgb.set_screen(ICONO_ON)
elif value == "0":
rgb.set_screen(ICONO_OFF)
except Exception as e:
print("Error:", e)
time.sleep_ms(20)
if __name__ == '__main__':
try:
setup()
while True:
loop()
except (Exception, KeyboardInterrupt) as e:
print(e)
Conclusion
This test demonstrates a reliable UART communication between two embedded systems. It also shows how simple serial data can be used to control visual outputs in real time.
Reflection
This comparison helped me understand that choosing the right communication model depends on the application. Simple systems can use point-to-point or master–slave communication, while more complex and scalable systems benefit from publish–subscribe architectures such as MQTT.
You can see the group documentation here:
Spiral 1 – Communication Options and Selection
At the beginning of the week I reviewed the different communication methods available in the lab and compared them according to speed, distance, complexity and power consumption.
Protocols considered
- UART – simple point-to-point wired communication.
- I2C – short-distance wired bus using addresses.
- SPI – fast synchronous communication.
- WiFi – high speed and easy integration with web services.
- BLE – low-power wireless communication.
- LoRa – very long range with low data rate.
Why I selected this solution
After comparing the available options, I decided to continue with a big project for connect my home I2C-WIFI-MQTT-LoRaWAN because it fits well with my project goals, I have this kind of sensors and boards, and the type of data I wanted to exchange.
Comparing protocols such as UART, I2C, SPI, WiFi, BLE and LoRa, and evaluating them in terms of speed, distance and consumption, so I used that comparison as a starting point for my decision. We need fast data fron energy an consumption and i will do it with WIFI and MQTT but the temperature is no necesary and I can use LoRaWAN
Group Assignment – Sending a Message Between Two Projects
For the group assignment we sent a message between two different projects. The objective was to verify that communication worked correctly in practice, not only electrically but also at the software and protocol level.
This week the group task specifically requires sending a message between two projects, which can include boards, computers and/or mobile devices.
Devices Used
- Project 1: WIFI with solar project in small house
- Project 2: Robot
- Protocol: WIFI - MQTT
- Development environment: Node-red / MicroPython
Message Exchange Procedure
- We configured the communication interface on both devices.
- We assigned the required bus or network parameters.
- We programmed one device to send a message.
- We programmed the other device to receive and display the message.
- We repeated the process to verify stable communication.
Example of the Message
- Sent message:topic "fabacademy/group/fotovoltaica" data: 500
- Received by: node B
- Result: successful communication
Node red- M5 Code Show code
{
"id": "7ef3f7a80f4ef648",
"type": "inject",
"z": "796e9e995197ae7d",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "60",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 140,
"y": 560,
"wires": [
[
"2040ba7117c0075d",
"30ea43a1692defea",
"d5f0d79a20a25493"
]
]
},
{
"id": "2040ba7117c0075d",
"type": "function",
"z": "796e9e995197ae7d",
"name": "pv",
"func": "msg.payload=flow.get(\"pv\");\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 590,
"y": 380,
"wires": [
[
"ecee308a56f9b332",
"19e0019aa7b92dc5"
]
]
},
{
"id": "19e0019aa7b92dc5",
"type": "mqtt out",
"z": "796e9e995197ae7d",
"name": "",
"topic": "fabacademy/grupo/fotovoltaica",
"qos": "0",
"retain": "",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "71496b93f3f7918a",
"x": 970,
"y": 320,
"wires": []
},
{
"id": "87ec363573d8d991",
"type": "mqtt out",
"z": "796e9e995197ae7d",
"name": "",
"topic": "fabacademy/grupo/consumo",
"qos": "0",
"retain": "",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "71496b93f3f7918a",
"x": 1080,
"y": 420,
"wires": []
},
{
"id": "66f361c58b8f510a",
"type": "mqtt out",
"z": "796e9e995197ae7d",
"name": "",
"topic": "fabacademy/grupo/temperatura",
"qos": "0",
"retain": "",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "71496b93f3f7918a",
"x": 1110,
"y": 540,
"wires": []
},
{
"id": "30ea43a1692defea",
"type": "function",
"z": "796e9e995197ae7d",
"name": "carga",
"func": "msg.payload=flow.get(\"carga\");\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 590,
"y": 460,
"wires": [
[
"cddb944cba8ca97c",
"87ec363573d8d991"
]
]
},
{
"id": "d5f0d79a20a25493",
"type": "function",
"z": "796e9e995197ae7d",
"name": "temp",
"func": "msg.payload=flow.get(\"temp\");\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 590,
"y": 500,
"wires": [
[
"e7d9521dc8574d51",
"66f361c58b8f510a"
]
]
},
{
"id": "71496b93f3f7918a",
"type": "mqtt-broker",
"name": "mqttabierto",
"broker": "xxxxxx",
"port": "xxx",
"clientid": "",
"autoConnect": true,
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"autoUnsubscribe": true,
"birthTopic": "",
"birthQos": "0",
"birthRetain": "false",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closeRetain": "false",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willRetain": "false",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
}
]
------------------------- M5---------------------
import os, sys, io
import M5
from M5 import *
from umqtt import MQTTClient
from hardware import Button
from hardware import Pin
from hardware import I2C
from unit import PIRUnit
label0 = None
label1 = None
label2 = None
image0 = None
label5 = None
label4 = None
label7 = None
label6 = None
label3 = None
mqtt_client = None
Btn1 = None
i2c0 = None
pir_0 = None
energia = None
humedad = None
temp = None
seguro = None
uno = None
def mqtt_inovacion_e_event(data):
global label0, label1, label2, image0, label5, label4, label7, label6, label3, mqtt_client, Btn1, i2c0, pir_0, energia, humedad, temp, seguro, uno
energia = float(data[1])
def mqtt_inovacion_h_event(data):
global label0, label1, label2, image0, label5, label4, label7, label6, label3, mqtt_client, Btn1, i2c0, pir_0, energia, humedad, temp, seguro, uno
humedad = float(data[1])
def mqtt_inovacion_t_event(data):
global label0, label1, label2, image0, label5, label4, label7, label6, label3, mqtt_client, Btn1, i2c0, pir_0, energia, humedad, temp, seguro, uno
temp = float(data[1])
def btnA_wasPressed_event(state):
global label0, label1, label2, image0, label5, label4, label7, label6, label3, mqtt_client, Btn1, i2c0, pir_0, energia, humedad, temp, seguro, uno
Speaker.tone(1000, 50)
if seguro == 1:
Speaker.tone(5000, 100)
def btnA_wasPressed_event(state):
global label0, label1, label2, image0, label5, label4, label7, label6, label3, mqtt_client, Btn1, i2c0, pir_0, energia, humedad, temp, seguro, uno
Speaker.tone(1000, 50)
if seguro == 1:
Speaker.tone(5000, 100)
def btnA_wasPressed_event(state):
global label0, label1, label2, image0, label5, label4, label7, label6, label3, mqtt_client, Btn1, i2c0, pir_0, energia, humedad, temp, seguro, uno
Speaker.tone(1000, 50)
if seguro == 0:
seguro = 1
label5.setColor(0xff0000, 0x000000)
label5.setText(str('activo'))
else:
seguro = 0
label5.setColor(0x33cc00, 0x000000)
label5.setText(str('seguro'))
def setup():
global label0, label1, label2, image0, label5, label4, label7, label6, label3, mqtt_client, Btn1, i2c0, pir_0, energia, humedad, temp, seguro, uno
M5.begin()
Widgets.setRotation(1)
Widgets.fillScreen(0x101010)
label0 = Widgets.Label("label0", 2, 75, 1.0, 0xe99231, 0x101010, Widgets.FONTS.DejaVu56)
label1 = Widgets.Label("label1", 229, 92, 1.0, 0x4cadeb, 0x191a1a, Widgets.FONTS.DejaVu24)
label2 = Widgets.Label("label2", 40, 215, 1.0, 0xffffff, 0x101010, Widgets.FONTS.DejaVu18)
image0 = Widgets.Image("/flash/res/img/innovacion7.png", 0, 4, scale_x=1, scale_y=1)
label5 = Widgets.Label("label5", 128, 213, 1.0, 0xffffff, 0x222222, Widgets.FONTS.DejaVu18)
label4 = Widgets.Label("label4", 112, 191, 1.0, 0x55d639, 0x222222, Widgets.FONTS.DejaVu9)
label7 = Widgets.Label("label7", 250, 148, 1.0, 0xffffff, 0x222222, Widgets.FONTS.DejaVu18)
label6 = Widgets.Label("label6", 2, 140, 1.0, 0xeaf12e, 0x222222, Widgets.FONTS.DejaVu40)
label3 = Widgets.Label("label3", 231, 213, 1.0, 0xffffff, 0x222222, Widgets.FONTS.DejaVu18)
BtnA.setCallback(type=BtnA.CB_TYPE.WAS_PRESSED, cb=btnA_wasPressed_event)
Btn1 = Button(1, active_low=True, pullup_active=True)
i2c0 = I2C(0, scl=Pin(33), sda=Pin(32), freq=100000)
pir_0 = PIRUnit((36, 26))
pir_0 = PIRUnit((36, 26))
mqtt_client = MQTTClient('3', 'xxxx', port=xxxx, user='xxxx', password='xxxxxx', keepalive=0)
mqtt_client.connect(clean_session=True)
mqtt_client.subscribe('inovacion/e', mqtt_inovacion_e_event, qos=0)
mqtt_client.subscribe('inovacion/h', mqtt_inovacion_h_event, qos=0)
mqtt_client.subscribe('inovacion/t', mqtt_inovacion_t_event, qos=0)
label1.setText(str('0'))
label2.setText(str('Boton1'))
uno = 0
seguro = 0
label4.setText(str('0'))
label3.setText(str('Boton2'))
image0.setVisible(True)
label5.setText(str(''))
pir_0.enable_irq()
def loop():
global label0, label1, label2, image0, label5, label4, label7, label6, label3, mqtt_client, Btn1, i2c0, pir_0, energia, humedad, temp, seguro, uno
M5.update()
label4.setText(str((str('conectado: ') + str((mqtt_client.isconnected())))))
label1.setText(str((str(humedad) + str(' %'))))
label7.setText(str(pir_0.get_status()))
label0.setText(str((str(temp) + str(' C'))))
label6.setText(str((str(energia) + str(' kWh'))))
mqtt_client.check_msg()
if __name__ == '__main__':
try:
setup()
while True:
loop()
except (Exception, KeyboardInterrupt) as e:
try:
from utility import print_error_msg
print_error_msg(e)
except ImportError:
print("please update to latest firmware")
What I Learned from the Group Assignment
- Communication is not only about connecting wires or pairing devices; protocol configuration is essential.
- Addressing, timing and data format are critical for reliable communication.
- Debugging communication often requires checking both hardware and software at the same time.
- Even a simple text message is enough to validate an embedded communication system.
Individual Assignment – Wired or Wireless Nodes with Addressing and Local I/O
For the individual assignment I designed and connected several sensors node and actuators with app using WIFI- MQTT_ LoRaWAN communication.
The goal was to build a real communication setup including:
- A WIFI node Inside the solar system
- A LoRaWAN node on long range network.
- A local input and output device.
- Functional code to send and/or receive data.
According to the weekly guidance, it is not necessary to fabricate a new board this week, but at least one of the boards used in the individual assignment should be one of my own designs.
My system
- Main board: M5Stack Dial (ESP32)
- Secondary board/module: Victron Cerbo GX running Venus OS Large with Node-RED
- Communication method: MQTT over WiFi and LoRaWAN through The Things Industries
- Input device: Environmental sensors, water level sensor, door sensor, and photovoltaic energy data
- Output device: LoRaWAN actuators such as the MClimate 16A Switch and Milesight UC511 irrigation valves
In my case, the purpose of the system was to build a distributed monitoring and control platform for a photovoltaic and environmental management system. The system collects data from different sensors, centralizes all measurements in Node-RED through MQTT, and sends commands to remote actuators depending on the operating conditions.
System Design and Architecture
The architecture of the system is based on communication between different nodes connected through WiFi, MQTT, and LoRaWAN. The central node works as the server and message manager, while the remote devices act as sensors, receivers, and actuators.
- Central node: Victron Cerbo GX running Venus OS Large with Node-RED, used as the main system manager.
- Network server: The Things Industries, used as the LoRaWAN network server and integrated with MQTT.
- User interface node: M5Stack Dial (ESP32) and App Inventor applications, used to display information and interact with the system.
- Sensor nodes: LoRaWAN and WiFi devices that measure environmental and energy variables.
- Actuator nodes: LoRaWAN devices that receive commands from Node-RED through MQTT integration.
The system manager is a Node-RED platform running on a Victron energy system. From this central point, all sensor measurements are collected and all commands to the actuators are distributed. MQTT is used as the common communication layer to exchange data between the different parts of the system.
Communication Layers
- WiFi communication: used to access Victron system data and local services.
- MQTT communication: used to centralize measurements and commands in Node-RED.
- LoRaWAN communication: used for long-range, low-power sensors and actuators connected through The Things Industries.
Devices and Data Sources
The system combines data from energy devices, environmental sensors, and control actuators.
- WiFi / Victron Cerbo:
- Photovoltaic power
- Battery status
- Instantaneous consumption
- LoRaWAN sensors:
- Indoor temperature – Talkpool OY1210
- Outdoor temperature
- Water level – DDS75-LB/LS LoRaWAN Distance Detection Sensor
- Cellar humidity – LHT65N-E31F LoRaWAN Temperature & Humidity Sensor
- Door sensor
- LoRaWAN actuators:
- MClimate 16A Switch
- Milesight UC511 irrigation valves
Addressing Strategy
One of the key requirements this week is to make sure that the board addressing works correctly.
In my implementation I used MQTT topics and LoRaWAN device identities to organize the communication between nodes.
- Bus or network address: MQTT topics and LoRaWAN end-device identifiers
- Sender node: Sensor nodes, Victron Cerbo GX, and Node-RED flows
- Receiver node: M5Stack Dial, App Inventor interface, and LoRaWAN actuators
I verified the addressing by checking that each device published and received data on the correct MQTT topics, and by confirming that commands were delivered to the expected actuator without affecting the others.
Programming Process
The programming process was based on integrating different communication technologies into a single distributed system. I used Node-RED as the main orchestration tool, MQTT as the messaging protocol, and LoRaWAN through The Things Industries to connect remote sensors and actuators.
Main Steps
- Configure Node-RED on Venus OS Large running on the Victron system.
- Connect Node-RED to MQTT topics for data acquisition and command transmission.
- Integrate The Things Industries as the LoRaWAN network server.
- Subscribe to sensor topics in Node-RED to receive measurements.
- Process the data and define control logic inside Node-RED flows.
- Send commands back to the actuators through MQTT integration.
- Display information and user controls on the M5Stack Dial and App Inventor interface.
How the code works
The sensor nodes continuously measure environmental and energy variables and send their values through LoRaWAN or WiFi. These values are received by The Things Industries or directly by the Victron system and then forwarded to Node-RED using MQTT.
Inside Node-RED, the flows subscribe to the corresponding MQTT topics, process the incoming data, and make decisions according to the system logic. When a condition is met, Node-RED publishes a command to the actuator topic. The actuator receives the command and performs the required action, such as switching a load or opening an irrigation valve.
M5 Dial ESP32 / MQTT Logic Show code
import os, sys, io
import M5
from M5 import *
from umqtt import MQTTClient
from hardware import Rotary
import network
from hardware import Button
label1 = None
rect0 = None
rect4 = None
label5 = None
label9 = None
label0 = None
label2 = None
label3 = None
rect1 = None
label6 = None
circle0 = None
image0 = None
rect2 = None
label7 = None
circle1 = None
rect3 = None
label8 = None
label10 = None
label4 = None
mqtt_client = None
rotary = None
wlan_sta = None
Btn1 = None
temporal = None
temperatura = None
litros = None
valvula1 = None
valvula2 = None
envio = None
et = None
def mqtt_M5Stack_d_event(data):
global label1, rect0, rect4, label5, label9, label0, label2, label3, rect1, label6, circle0, image0, rect2, label7, circle1, rect3, label8, label10, label4, mqtt_client, rotary, wlan_sta, Btn1, temporal, temperatura, litros, valvula1, valvula2, envio, et
temporal = data[1]
label2.setText(str(temporal))
def mqtt_M5Stack_t_event(data):
global label1, rect0, rect4, label5, label9, label0, label2, label3, rect1, label6, circle0, image0, rect2, label7, circle1, rect3, label8, label10, label4, mqtt_client, rotary, wlan_sta, Btn1, temporal, temperatura, litros, valvula1, valvula2, envio, et
temperatura = float(data[1])
label5.setText(str(temperatura))
def mqtt_M5Stack_l_event(data):
global label1, rect0, rect4, label5, label9, label0, label2, label3, rect1, label6, circle0, image0, rect2, label7, circle1, rect3, label8, label10, label4, mqtt_client, rotary, wlan_sta, Btn1, temporal, temperatura, litros, valvula1, valvula2, envio, et
litros = int(data[1])
label4.setText(str(litros))
def mqtt_M5Stack_h_event(data):
global label1, rect0, rect4, label5, label9, label0, label2, label3, rect1, label6, circle0, image0, rect2, label7, circle1, rect3, label8, label10, label4, mqtt_client, rotary, wlan_sta, Btn1, temporal, temperatura, litros, valvula1, valvula2, envio, et
temporal = float(data[1])
label6.setText(str(temporal))
def mqtt_M5Stack_i_event(data):
global label1, rect0, rect4, label5, label9, label0, label2, label3, rect1, label6, circle0, image0, rect2, label7, circle1, rect3, label8, label10, label4, mqtt_client, rotary, wlan_sta, Btn1, temporal, temperatura, litros, valvula1, valvula2, envio, et
valvula1 = int(data[1])
def mqtt_M5Stack_c_event(data):
global label1, rect0, rect4, label5, label9, label0, label2, label3, rect1, label6, circle0, image0, rect2, label7, circle1, rect3, label8, label10, label4, mqtt_client, rotary, wlan_sta, Btn1, temporal, temperatura, litros, valvula1, valvula2, envio, et
temporal = float(data[1])
label0.setText(str(temporal))
def btnA_wasClicked_event(state):
global label1, rect0, rect4, label5, label9, label0, label2, label3, rect1, label6, circle0, image0, rect2, label7, circle1, rect3, label8, label10, label4, mqtt_client, rotary, wlan_sta, Btn1, temporal, temperatura, litros, valvula1, valvula2, envio, et
mqtt_client.publish('M5Stack', envio, qos=0)
label2.setText(str(envio))
Speaker.tone(4000, 400)
def mqtt_M5Stack_i2_event(data):
global label1, rect0, rect4, label5, label9, label0, label2, label3, rect1, label6, circle0, image0, rect2, label7, circle1, rect3, label8, label10, label4, mqtt_client, rotary, wlan_sta, Btn1, temporal, temperatura, litros, valvula1, valvula2, envio, et
valvula2 = int(data[1])
label0.setText(str(temporal))
def setup():
global label1, rect0, rect4, label5, label9, label0, label2, label3, rect1, label6, circle0, image0, rect2, label7, circle1, rect3, label8, label10, label4, mqtt_client, rotary, wlan_sta, Btn1, temporal, temperatura, litros, valvula1, valvula2, envio, et
M5.begin()
Widgets.fillScreen(0x222222)
label1 = Widgets.Label("label1", 58, 34, 1.0, 0x00ff0d, 0x222222, Widgets.FONTS.DejaVu24)
rect0 = Widgets.Rectangle(35, 42, 11, 25, 0x5641e4, 0x4fde42)
rect4 = Widgets.Rectangle(27, 42, 4, 115, 0x5322dc, 0x20c5c8)
label5 = Widgets.Label("label5", 71, 180, 1.0, 0xf4f4f5, 0x222222, Widgets.FONTS.DejaVu18)
label9 = Widgets.Label("label9", 121, 150, 1.0, 0xff8a1d, 0x222222, Widgets.FONTS.DejaVu18)
label0 = Widgets.Label("0", 71, 68, 1.0, 0xffa000, 0x222222, Widgets.FONTS.DejaVu72)
label2 = Widgets.Label("label2", 153, 37, 1.0, 0xf7f9f8, 0x0d0d0d, Widgets.FONTS.DejaVu12)
label3 = Widgets.Label("label3", 88, 5, 1.0, 0x5f5bc4, 0x222222, Widgets.FONTS.DejaVu18)
rect1 = Widgets.Rectangle(35, 105, 10, 20, 0x182ce4, 0xe09a28)
label6 = Widgets.Label("label6", 70, 206, 1.0, 0x6f76e7, 0x222222, Widgets.FONTS.DejaVu18)
circle0 = Widgets.Circle(200, 159, 13, 0x5c6fe0, 0x14eb15)
image0 = Widgets.Image("/flash/res/img/logo favicon Y.png", -56, 33, scale_x=1, scale_y=1)
rect2 = Widgets.Rectangle(34, 131, 10, 19, 0x2b1ae2, 0xef2b1d)
label7 = Widgets.Label("label7", 134, 180, 1.0, 0xffffff, 0x222222, Widgets.FONTS.DejaVu18)
circle1 = Widgets.Circle(87, 157, 13, 0x5e87ed, 0x2deb37)
rect3 = Widgets.Rectangle(35, 71, 11, 30, 0x2527e7, 0xf7ea0d)
label8 = Widgets.Label("label8", 132, 205, 1.0, 0x897be9, 0x222222, Widgets.FONTS.DejaVu18)
label10 = Widgets.Label("label10", 32, 184, 1.0, 0xffffff, 0x222222, Widgets.FONTS.DejaVu9)
label4 = Widgets.Label("label4", 23, 160, 1.0, 0xfff21a, 0x222222, Widgets.FONTS.DejaVu12)
BtnA.setCallback(type=BtnA.CB_TYPE.WAS_CLICKED, cb=btnA_wasClicked_event)
rotary = Rotary()
wlan_sta = network.WLAN(network.STA_IF)
wlan_sta.connect('TP-Link_EA20', '15437716')
label0.setText(str(' '))
label1.setText(str('0'))
label2.setText(str(' '))
label3.setText(str(' '))
label5.setText(str(' '))
label6.setText(str(' '))
label7.setText(str('C'))
label8.setText(str('%'))
label9.setText(str('0'))
label10.setText(str(' '))
valvula1 = 0
mqtt_client = MQTTClient('1', 'xxx', port=xxx, user='xxx', password='xxxx', keepalive=0)
mqtt_client.connect(clean_session=True)
mqtt_client.subscribe('M5Stack/d', mqtt_M5Stack_d_event, qos=0)
mqtt_client.subscribe('M5Stack/t', mqtt_M5Stack_t_event, qos=0)
mqtt_client.subscribe('M5Stack/l', mqtt_M5Stack_l_event, qos=0)
mqtt_client.subscribe('M5Stack/h', mqtt_M5Stack_h_event, qos=0)
mqtt_client.subscribe('M5Stack/i', mqtt_M5Stack_i_event, qos=0)
mqtt_client.subscribe('M5Stack/c', mqtt_M5Stack_c_event, qos=0)
mqtt_client.subscribe('M5Stack/i2', mqtt_M5Stack_i2_event, qos=0)
Btn1 = Button(1, active_low=True, pullup_active=True)
mqtt_client.publish('M5Stack', 'conectado', qos=0)
rect0.setVisible(False)
rect1.setVisible(False)
rect2.setVisible(False)
rect3.setVisible(False)
circle0.setVisible(False)
litros = 0
label4.setText(str(litros))
def loop():
global label1, rect0, rect4, label5, label9, label0, label2, label3, rect1, label6, circle0, image0, rect2, label7, circle1, rect3, label8, label10, label4, mqtt_client, rotary, wlan_sta, Btn1, temporal, temperatura, litros, valvula1, valvula2, envio, et
M5.update()
if litros >= 700:
rect0.setVisible(True)
rect1.setVisible(True)
rect2.setVisible(True)
rect3.setVisible(True)
if litros < 700:
if litros >= 600:
rect0.setVisible(False)
rect1.setVisible(True)
rect2.setVisible(True)
rect3.setVisible(True)
if litros < 600:
if litros >= 500:
rect0.setVisible(False)
rect3.setVisible(False)
rect1.setVisible(True)
rect2.setVisible(True)
if litros < 500:
if litros >= 200:
rect0.setVisible(False)
rect1.setVisible(False)
rect3.setVisible(False)
rect2.setVisible(True)
if litros < 200:
rect0.setVisible(False)
rect1.setVisible(False)
rect2.setVisible(False)
rect3.setVisible(False)
if valvula1 == 1:
circle1.setColor(color=0x6600cc, fill_c=0xff0000)
else:
circle1.setColor(color=0x6600cc, fill_c=0x33ff33)
if valvula2 == 1:
circle0.setColor(color=0x6600cc, fill_c=0xff0000)
else:
circle0.setColor(color=0x6600cc, fill_c=0x33ff33)
if wlan_sta.isconnected():
label3.setText(str('con'))
else:
label1.setText(str('error'))
mqtt_client.check_msg()
if rotary.get_rotary_status():
Speaker.tone(2000, 50)
label2.setText(str(' '))
label9.setText(str(rotary.get_rotary_value()))
et = rotary.get_rotary_value()
if et == 0:
label1.setText(str(' '))
envio = ' '
if et == 1:
label1.setText(str('riego1'))
envio = 'marcha'
if et == 2:
label1.setText(str('riego2'))
envio = 'marcha2'
if et == 3:
label1.setText(str('on 1'))
envio = 'on1'
if et == 4:
label1.setText(str('paro 1'))
envio = 'off1'
if et == 5:
label1.setText(str('on 2'))
envio = 'on2'
if et == 6:
label1.setText(str('paro 2'))
envio = 'off2'
if __name__ == '__main__':
try:
setup()
while True:
loop()
except (Exception, KeyboardInterrupt) as e:
try:
from utility import print_error_msg
print_error_msg(e)
except ImportError:
print("please update to latest firmware")
System Behaviour Show code
{
"id": "3668b635052adb91",
"type": "mqtt in",
"z": "038433144c224c22",
"name": "",
"topic": "cabana/fuego/valvula1",
"qos": "0",
"datatype": "auto-detect",
"broker": "XXXXXX",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 160,
"y": 360,
"wires": [
[
"1373f8ec17e39bc7",
"19d61a87a37a72e9",
"4ab1d6199d65b170"
]
]
},
{
"id": "6939dd1dcfc96f37",
"type": "mqtt in",
"z": "038433144c224c22",
"name": "",
"topic": "cabana/fuego/bomba1",
"qos": "0",
"datatype": "auto-detect",
"broker": "XXXXXXX",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 180,
"y": 440,
"wires": [
[
"84bf8e4251b1e69f",
"54c1885508cffee6"
]
]
},
{
"id": "1373f8ec17e39bc7",
"type": "switch",
"z": "038433144c224c22",
"name": "ON",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "ON",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 370,
"y": 320,
"wires": [
[
"5c0f3b2750669514"
]
]
},
{
"id": "37b028c476cad5ae",
"type": "debug",
"z": "038433144c224c22",
"name": "debug 28",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 860,
"y": 300,
"wires": []
},
{
"id": "a71a624baf2f49d8",
"type": "debug",
"z": "038433144c224c22",
"name": "debug 29",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 600,
"y": 440,
"wires": []
},
{
"id": "84bf8e4251b1e69f",
"type": "switch",
"z": "038433144c224c22",
"name": "ON",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "ON",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 370,
"y": 440,
"wires": [
[
"a71a624baf2f49d8"
]
]
},
{
"id": "19d61a87a37a72e9",
"type": "switch",
"z": "038433144c224c22",
"name": "OFF",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "OFF",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 370,
"y": 360,
"wires": [
[
"5d34222c1706403e"
]
]
},
{
"id": "e3068f6bbcd73dc0",
"type": "debug",
"z": "038433144c224c22",
"name": "debug 30",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 780,
"y": 360,
"wires": []
},
{
"id": "54c1885508cffee6",
"type": "switch",
"z": "038433144c224c22",
"name": "OFF",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "OFF",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 370,
"y": 500,
"wires": [
[
"316adf2a29890dd7"
]
]
},
{
"id": "316adf2a29890dd7",
"type": "debug",
"z": "038433144c224c22",
"name": "debug 31",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 600,
"y": 500,
"wires": []
},
{
"id": "4ab1d6199d65b170",
"type": "debug",
"z": "038433144c224c22",
"name": "debug 32",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 240,
"y": 280,
"wires": []
},
{
"id": "5c0f3b2750669514",
"type": "function",
"z": "038433144c224c22",
"name": "abrir valvula 2",
"func": "\nvar puerto=85;\nvar confirmado=true;\nvar campos=0;\nvar bite=[0x01];\nvar co=msg.payload;\n\n\nvar uno = \"/x0hAA==\";\nmsg.payload= {\"downlinks\":[{\"f_port\": puerto,\"frm_payload\":uno,\"priority\": \"NORMAL\",\"confirmed\":confirmado}]};\nreturn msg;\n\n\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 580,
"y": 320,
"wires": [
[
"37b028c476cad5ae"
]
]
},
{
"id": "5d34222c1706403e",
"type": "function",
"z": "038433144c224c22",
"name": "cerrar valvula 2",
"func": "\nvar puerto=85;\nvar confirmado=true;\nvar campos=0;\nvar bite=[0x01];\nvar co=msg.payload;\n\n\nvar uno = \"/x0BAA==\";\nmsg.payload= {\"downlinks\":[{\"f_port\": puerto,\"frm_payload\":uno,\"priority\": \"NORMAL\",\"confirmed\":confirmado}]};\nreturn msg;\n\n\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 560,
"y": 360,
"wires": [
[
"e3068f6bbcd73dc0"
]
]
},
{
"id": "a15e9485.4c07c8",
"type": "mqtt-broker",
"name": "XXXXX",
"broker": "XXXXXXX",
"port": "xXXXX",
"clientid": "",
"autoConnect": true,
"usetls": false,
"compatmode": false,
"protocolVersion": 4,
"keepalive": "60",
"cleansession": true,
"autoUnsubscribe": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
}
]
Validation of Communication and Addressing
To validate the project, I checked both the physical communication links and the logical data flow between the different nodes.
- The Victron Cerbo GX and Node-RED platform powered up correctly.
- The MQTT communication layer initialized correctly.
- The LoRaWAN devices were correctly registered and recognized by The Things Industries.
- The MQTT topics were correctly assigned and subscribed.
- The M5Stack Dial and App Inventor interface received and displayed the correct data.
- The LoRaWAN actuators responded correctly when commands were sent from Node-RED.
The APPinventor code:
⬇ Download CodeResults
The final system successfully demonstrates communication between multiple embedded and IoT nodes using a combination of WiFi, MQTT, and LoRaWAN.
- Sensor data is transmitted correctly from the remote devices.
- Node-RED centralizes all information in a single control platform.
- The user can monitor the system through App Inventor and the M5Stack Dial.
- Commands are sent reliably to the LoRaWAN actuators.
- The addressing strategy using MQTT topics and LoRaWAN identifiers works correctly.
This project helped me understand how different communication technologies can be integrated into a unified architecture for monitoring and control. It also showed me the importance of using a central broker and a clear topic structure when building an IoT system.
Week 11 – Useful Resources
The following links provide access to the main platforms and tools used in this project:
- MIT App Inventor – Mobile Application Development Platform
- The Things Industries – LoRaWAN Network Server
- Eclipse Mosquitto – MQTT Broker
These tools are essential for building, managing, and integrating the communication architecture of the system.
Problems and Fixes
Problem 1 – Devices were not communicating correctly through MQTT
- Problem: Some nodes were not publishing or receiving data on the expected topics.
- Fix: I reviewed the MQTT topic structure, broker configuration, and subscription settings in Node-RED.
Problem 2 – LoRaWAN integration issues
- Problem: Some sensor data was not arriving correctly from The Things Industries to Node-RED.
- Fix: I checked the MQTT integration, payload structure, and device registration parameters.
Problem 3 – Wrong addressing or topic organization
- Problem: Commands could be confused if the topic naming was not clear enough.
- Fix: I reorganized the MQTT topics to make each device and function clearly identifiable.
Problem 4 – Integration of multiple communication technologies
- Problem: Combining WiFi, MQTT, LoRaWAN, and user interfaces made the system more complex to debug.
- Fix: I validated each subsystem separately and then integrated them step by step.
Summary and Reflection
This week helped me understand how communication protocols connect different parts of an embedded and IoT system into a coordinated network.
One of the most important lessons was that communication is not only a matter of hardware. It also depends on protocol selection, addressing, topic structure, message routing, and debugging strategy.
The individual assignment allowed me to integrate real devices using WiFi, MQTT, and LoRaWAN in the same system. I was able to collect data from energy and environmental sensors, centralize everything in Node-RED, and send commands back to remote actuators.
This work is especially relevant for my final project because it demonstrates how a distributed IoT architecture can be used to monitor and control real processes in an efficient and scalable way.