Skip to content

13. Networking and communications

Hero Shot

Assignment :

individual assignment:

  • design, build, and connect wired or wireless node(s) with network or bus addresses and local input &/or output device(s)

group assignment:

  • send a message between two projects

Group assignment

You can find our group page here and this week group assignement here

Individual assignment

During previous week, I already used I2C communications to communicate with an OLED screen. For this week, I d like to experiment wireless communications to transmit data to a screen with a remote device and eventually try SPI communication needed for e-paper display.

Xiao ESP32-C3

To start with wireless communication, I wanted to replicate what I succeed to do during Input Device week with the ESP32-C3 Xiao devboard.

Using Xiao datasheet, I created a little devboard to connect the rotary encoder and get access to other pins.

Using Seeedstudio wiki for ESP32C3, I added Additional Boards Manager URL for ESP32 and get the library installed.

My previous program worked well with I2C communication as expected.

ESP32-2432S028 Cheap-yellow-display

After that, I wanted to communicate wireless with another screen. Following Luc recommandations, I used ESP32-2432S028 board screen as it embedded ESP-WROOM-32 Micro-controller.

First I checked the datasheet and extract important informations (updated as I get through tutorials).

Parameter value remarks
Operating Voltage 5V
Power consumption 115mA
Driver Chip ILI9341 needed for TFT setup
Resolution 320*240 px needed for TFT setup
Effective display Area 43.2*57.6 mm

Starting with the “Getting started page”

After getting the right library as documented in this page, I tried the program as suggested a basic serial communication (don’t forget to change the baud rate of the serial monitor to match the program).

Before continuing, I decided to check the communication and try ESP_NOW_Broadcast_Master and ESP_NOW_Broadcast_Slave examples (I’m not completely sure but I think they came from the updated library for ESP32 coming from this updated for dev json url) and it worked.

    /*  ESP-NOW Broadcast Master
        Lucas Saavedra Vaz - 2024

        This sketch demonstrates how to broadcast messages to all devices within the ESP-NOW network.
        This example is intended to be used with the ESP-NOW Broadcast Slave example.

        The master device will broadcast a message every 5 seconds to all devices within the network.
        This will be done using by registering a peer object with the broadcast address.

        The slave devices will receive the broadcasted messages and print them to the Serial Monitor.
    */

    #include "ESP32_NOW.h"
    #include "WiFi.h"

    #include <esp_mac.h> // For the MAC2STR and MACSTR macros

    /* Definitions */

    #define ESPNOW_WIFI_CHANNEL 6

    /* Classes */

    // Creating a new class that inherits from the ESP_NOW_Peer class is required.

    class ESP_NOW_Broadcast_Peer : public ESP_NOW_Peer {
    public:
        // Constructor of the class using the broadcast address
        ESP_NOW_Broadcast_Peer(uint8_t channel, wifi_interface_t iface, const uint8_t *lmk)
        : ESP_NOW_Peer(ESP_NOW.BROADCAST_ADDR, channel, iface, lmk) {}

        // Destructor of the class
        ~ESP_NOW_Broadcast_Peer() {
            remove();
        }

        // Function to properly initialize the ESP-NOW and register the broadcast peer
        bool begin() {
            if (!ESP_NOW.begin() || !add()) {
                log_e("Failed to initialize ESP-NOW or register the broadcast peer");
                return false;
            }
            return true;
        }

        // Function to send a message to all devices within the network
        bool send_message(const uint8_t *data, size_t len) {
            if (!send(data, len)) {
                log_e("Failed to broadcast message");
                return false;
            }
            return true;
        }
    };

    /* Global Variables */

    uint32_t msg_count = 0;

    // Create a boradcast peer object
    ESP_NOW_Broadcast_Peer broadcast_peer(ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL);

    /* Main */

    void setup() {
        Serial.begin(115200);
        while (!Serial) delay(10);

        // Initialize the Wi-Fi module
        WiFi.mode(WIFI_STA);
        WiFi.setChannel(ESPNOW_WIFI_CHANNEL);
        while(!WiFi.STA.started()) delay(100);

        Serial.println("ESP-NOW Example - Broadcast Master");
        Serial.println("Wi-Fi parameters:");
        Serial.println("  Mode: STA");
        Serial.println("  MAC Address: " + WiFi.macAddress());
        Serial.printf("  Channel: %d\n", ESPNOW_WIFI_CHANNEL);

        // Register the broadcast peer
        if (!broadcast_peer.begin()) {
            Serial.println("Failed to initialize broadcast peer");
            Serial.println("Reebooting in 5 seconds...");
            delay(5000);
            ESP.restart();
        }

        Serial.println("Setup complete. Broadcasting messages every 5 seconds.");
    }

    void loop() {
        // Broadcast a message to all devices within the network
        char data[32];
        snprintf(data, sizeof(data), "Hello, World! #%lu", msg_count++);

        Serial.printf("Broadcasting message: %s\n", data);

        if (!broadcast_peer.send_message((uint8_t *)data, sizeof(data))) {
            Serial.println("Failed to broadcast message");
        }

        delay(5000);
    }
    /*  ESP-NOW Broadcast Slave
        Lucas Saavedra Vaz - 2024

        This sketch demonstrates how to receive broadcast messages from a master device using the ESP-NOW protocol.

        The master device will broadcast a message every 5 seconds to all devices within the network.

        The slave devices will receive the broadcasted messages. If they are not from a known master, they will be registered as a new master
        using a callback function.
    */

    #include "ESP32_NOW.h"
    #include "WiFi.h"

    #include <esp_mac.h> // For the MAC2STR and MACSTR macros

    #include <vector>

    /* Definitions */

    #define ESPNOW_WIFI_CHANNEL 6

    /* Classes */

    // Creating a new class that inherits from the ESP_NOW_Peer class is required.

    class ESP_NOW_Peer_Class : public ESP_NOW_Peer {
    public:
        // Constructor of the class
        ESP_NOW_Peer_Class(const uint8_t *mac_addr,
                        uint8_t channel,
                        wifi_interface_t iface,
                        const uint8_t *lmk) : ESP_NOW_Peer(mac_addr, channel, iface, lmk) {}

        // Destructor of the class
        ~ESP_NOW_Peer_Class() {}

        // Function to register the master peer
        bool add_peer() {
            if (!add()) {
                log_e("Failed to register the broadcast peer");
                return false;
            }
            return true;
        }

        // Function to print the received messages from the master
        void onReceive(const uint8_t *data, size_t len, bool broadcast) {
            Serial.printf("Received a message from master " MACSTR " (%s)\n", MAC2STR(addr()), broadcast ? "broadcast" : "unicast");
            Serial.printf("  Message: %s\n", (char *)data);
        }
    };

    /* Global Variables */

    // List of all the masters. It will be populated when a new master is registered
    std::vector<ESP_NOW_Peer_Class> masters;

    /* Callbacks */

    // Callback called when an unknown peer sends a message
    void register_new_master(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg) {
        if (memcmp(info->des_addr, ESP_NOW.BROADCAST_ADDR, 6) == 0) {
            Serial.printf("Unknown peer " MACSTR " sent a broadcast message\n", MAC2STR(info->src_addr));
            Serial.println("Registering the peer as a master");

            ESP_NOW_Peer_Class new_master(info->src_addr, ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL);

            masters.push_back(new_master);
            if (!masters.back().add_peer()) {
                Serial.println("Failed to register the new master");
                return;
            }
        } else {
            // The slave will only receive broadcast messages
            log_v("Received a unicast message from " MACSTR, MAC2STR(info->src_addr));
            log_v("Igorning the message");
        }
    }

    /* Main */

    void setup() {
        Serial.begin(115200);
        while (!Serial) delay(10);

        // Initialize the Wi-Fi module
        WiFi.mode(WIFI_STA);
        WiFi.setChannel(ESPNOW_WIFI_CHANNEL);
        while(!WiFi.STA.started()) delay(100);

        Serial.println("ESP-NOW Example - Broadcast Slave");
        Serial.println("Wi-Fi parameters:");
        Serial.println("  Mode: STA");
        Serial.println("  MAC Address: " + WiFi.macAddress());
        Serial.printf("  Channel: %d\n", ESPNOW_WIFI_CHANNEL);

        // Initialize the ESP-NOW protocol
        if (!ESP_NOW.begin()) {
            Serial.println("Failed to initialize ESP-NOW");
            Serial.println("Reeboting in 5 seconds...");
            delay(5000);
            ESP.restart();
        }

        // Register the new peer callback
        ESP_NOW.onNewPeer(register_new_master, NULL);

        Serial.println("Setup complete. Waiting for a master to broadcast a message...");
    }

    void loop() {
        delay(1000);
    }

Even if code uses MOSI/MISO/SS terminology, I will use SDO/SDI/CS terminology to describe it. Here’s a little explanation about it.

I received the message broadcasted by the Controller board on the Peripheral board.

Then, I tried to display test on the screen and had to use TFT_eSPI Library. once installed, I had to modify User_Setup.h file in the Library to fit the screen parameters. The file can be find in \Arduino\libraries\TFT_eSPI folder.

Choose ILI9341_DRIVER by uncommenting the right line (it was as default in my case)

I did the same for width and height still following the “Getting started page”

Then, I defined pins…

… and pins for backlight.

After that, I couldn’t make it work properly. So I tried another tutorial and another related to it.

I struggle quite some time because the display could turned on but nothing except a white screen was displayed with both examples.

Comparing the User_Setup.h file from the previous one, I found out the SPI clock Frequency was set at 55MHz and its uses an alt ILI9341 driver configuration. Setting the clock frequency at 27MHz and bringing back the previous driver configurion permitted to display what’s intended by the code.
I think the difference comes from the cheap yellow board version : Tutorials are made around ESP32-2432S028R and mine was ESP32-2432S028 (no R)

Here the User_Setup.h, I could make work.

I needed XPT2046 Library for the touchscreen functionnalities.

With those tutorials, I could upload a code to detect the position on the touchscreen.

With another, I could display an On/Off Switch.

And then, I displayed a test code of color and font types.

From this, I tried to make my own code to display a message from my XIAO ESP32-C3 Board.

After struggling a lot, to find out where to set a print in the code, I succeed to make a tft.printf via the void onReceive part.

I made some test to display the informations nicely.

I finally got a satisfactory result.

Here is the code for it :

#include <SPI.h>
#include <TFT_eSPI.h>
#include "ESP32_NOW.h"
#include "WiFi.h"
#include <esp_mac.h> // For the MAC2STR and MACSTR macros
#include <vector>

TFT_eSPI tft = TFT_eSPI();

/* Definitions */
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define FONT_SIZE 4

#define ESPNOW_WIFI_CHANNEL 6

/* Classes */

// Creating a new class that inherits from the ESP_NOW_Peer class is required.

class ESP_NOW_Peer_Class : public ESP_NOW_Peer {
public:
    // Constructor of the class
    ESP_NOW_Peer_Class(const uint8_t *mac_addr,
                    uint8_t channel,
                    wifi_interface_t iface,
                    const uint8_t *lmk) : ESP_NOW_Peer(mac_addr, channel, iface, lmk) {}

    // Destructor of the class
    ~ESP_NOW_Peer_Class() {}

    // Function to register the control peer
    bool add_peer() {
        if (!add()) {
            log_e("Failed to register the broadcast peer");
            return false;
        }
        return true;
    }

    // Function to print the received messages from the control
    void onReceive(const uint8_t *data, size_t len, bool broadcast) {
        Serial.printf("Received a message from control " MACSTR " (%s)\n", MAC2STR(addr()), broadcast ? "broadcast" : "unicast");
        Serial.printf("  Message: %s\n", (char *)data);

        tft.init();// Start the tft display
        tft.setRotation(1);// Set the TFT display rotation in landscape mode

        // Clear the screen before writing to it
        tft.fillScreen(TFT_WHITE);
        tft.setTextColor(TFT_BLACK, TFT_WHITE);

        // Set X and Y coordinates for center of display
        int centerX = SCREEN_WIDTH / 2;
        int centerY = SCREEN_HEIGHT / 2;

        tft.drawCentreString("Communication test!", centerX, centerY-100, FONT_SIZE);
        tft.drawCentreString("Received a message from control :", centerX, centerY-40, 2);
        tft.setCursor(70, 120, 2);
        tft.printf(MACSTR " (%s)\n", MAC2STR(addr()), broadcast ? "broadcast" : "unicast");
        tft.setCursor(70, 140, 2);
        tft.printf("Message: %s\n", (char *)data);

        tft.drawCentreString("From Alexis", 280, 220, 2);
    }
};

/* Global Variables */

// List of all the controls. It will be populated when a new master is registered
std::vector<ESP_NOW_Peer_Class> masters;

/* Callbacks */

// Callback called when an unknown peer sends a message
void register_new_master(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg) {
    if (memcmp(info->des_addr, ESP_NOW.BROADCAST_ADDR, 6) == 0) {
        Serial.printf("Unknown peer " MACSTR " sent a broadcast message\n", MAC2STR(info->src_addr));
        Serial.println("Registering the peer as a master");

        ESP_NOW_Peer_Class new_master(info->src_addr, ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL);

        masters.push_back(new_master);
        if (!masters.back().add_peer()) {
            Serial.println("Failed to register the new master");
            return;
        }
    } else {
        // The slave will only receive broadcast messages
        log_v("Received a unicast message from " MACSTR, MAC2STR(info->src_addr));
        log_v("Ignoring the message");
    }
}


void setup() {
Serial.begin(115200);
    while (!Serial) delay(10);

    // Initialize the Wi-Fi module
    WiFi.mode(WIFI_STA);
    WiFi.setChannel(ESPNOW_WIFI_CHANNEL);
    while(!WiFi.STA.started()) delay(100);

    Serial.println("ESP-NOW Example - Broadcast Slave");
    Serial.println("Wi-Fi parameters:");
    Serial.println("  Mode: STA");
    Serial.println("  MAC Address: " + WiFi.macAddress());
    Serial.printf("  Channel: %d\n", ESPNOW_WIFI_CHANNEL);

    // Initialize the ESP-NOW protocol
    if (!ESP_NOW.begin()) {
        Serial.println("Failed to initialize ESP-NOW");
        Serial.println("Reeboting in 5 seconds...");
        delay(5000);
        ESP.restart();
    }

    // Register the new peer callback
    ESP_NOW.onNewPeer(register_new_master, NULL);

    Serial.println("Setup complete. Waiting for a control to broadcast a message...");

tft.init();// Start the tft display
tft.setRotation(1);// Set the TFT display rotation in landscape mode

// Clear the screen before writing to it
tft.fillScreen(TFT_WHITE);
tft.setTextColor(TFT_BLACK, TFT_WHITE);

// Set X and Y coordinates for center of display
int centerX = SCREEN_WIDTH / 2;
int centerY = SCREEN_HEIGHT / 2;

tft.drawCentreString("Communication test!", centerX, centerY-100, FONT_SIZE);

tft.drawCentreString("Communication will start soon", centerX, centerY, 2);

tft.drawCentreString("From Alexis", 280, 220, 2);
}

void loop() {

    delay(1000);
}

Communicate Inputs

To advance on my final projects, I wanted to display on the screen inputs from remote controller board.

Using the previous rotary encoder code, I modified it with with blocks of code from examples.

I succeeded to have a feedback of the SW button being pushed or released but not from the rotary encoder.

Remaining bugs

After this, I tried to debug the rotary encoder but I struggled with extremely slow compiling (I measured one of 20 min) and systematic upload errors with Arduino IDE. After hours of tries and fails, boots and resets, USB ports and cable changes, I finally succeed to reupload with previous version of Arduino IDE. It seemed my co-student as quite the same difficulties with the XIAO ESP32-C3 too.

I could figured out that I had a bracket error on my code that made the rotary encoder part of the code didn’t start. But I only had the clockwise direction working. For the counterclockwise, at each step, the couter decrements 1 but immediately increment 1. I think it’s probably due to a not sufficient debounce time even if it works well with the wired OLED screen.

Also, I had problems with the code didn’t launching if the serial monitor not opened, not constantly but always with previous version of Arduino IDE. On my first version of the code, I had this lines (imported from examples) that forced to have serial communication.

void setup() {
        Serial.begin(115200);
        while (!Serial) delay(10);
}

After erasing the while condition, I still had the difficulties to have the code starting.

And finally on wednesday morning, the code vanished from the ESP32, and at this point, I can’t upload any program on it, neither with old version of IDE neither with the latest. I share screenshots (from previous working code) in case I’m missing an obvious mistake.

I also tried to reprogram the cheap yellow display to ensure the was not a software or computer problems. I could overwrite the code with both version of IDE with no trouble. So it’s specific to XIAO ESP32-C3.

Class Archive

Impressions of the week

That was a very frustrating week ! Sometimes everything works fine, and it’s a real enjoyment and an hour later nothing works anymore. I spent an entire day to get back a code that work quite fine the previous day, and to lost it again the next day. Beside this difficulties, I learned a lot for the TFT screen and a bit for the touchscreen (I shoud not need it for my project) but not enough, in my opinion, for the networking. I succeeded to send data from inputs to a distant and transform it as display outputs but I would have liked to debug more the inputs or transmitting more than one data at a time.
Also, I didn’t test e-paper and image display. That makes a lot to do for the next weeks.