Week 11 – Networking and Communications

Hero shot of my networking and communications setup

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:

Assignment and Learning Outcomes

The weekly assignment is:

The learning outcomes are:

Checklist

In this page I answer the required questions:

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

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.

Group assignment communication test Group assignment communication test

This week the group task specifically requires sending a message between two projects, which can include boards, computers and/or mobile devices.

Devices Used

Message Exchange Procedure

  1. We configured the communication interface on both devices.
  2. We assigned the required bus or network parameters.
  3. We programmed one device to send a message.
  4. We programmed the other device to receive and display the message.
  5. We repeated the process to verify stable communication.
Devices exchanging messages

Example of the Message

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

Individual Assignment – Wired or Wireless Nodes with Addressing and Local I/O

My communication

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:

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

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.

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

Devices and Data Sources

The system combines data from energy devices, environmental sensors, and control actuators.

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.

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

  1. Configure Node-RED on Venus OS Large running on the Victron system.
  2. Connect Node-RED to MQTT topics for data acquisition and command transmission.
  3. Integrate The Things Industries as the LoRaWAN network server.
  4. Subscribe to sensor topics in Node-RED to receive measurements.
  5. Process the data and define control logic inside Node-RED flows.
  6. Send commands back to the actuators through MQTT integration.
  7. 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 APPinventor code:

⬇ Download Code

Results

The final system successfully demonstrates communication between multiple embedded and IoT nodes using a combination of WiFi, MQTT, and LoRaWAN.

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:

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 2 – LoRaWAN integration issues

Problem 3 – Wrong addressing or topic organization

Problem 4 – Integration of multiple communication technologies

Summary and Reflection

Reflection on networking and communications week

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.