Skip to content

13. Networking and communications

Group 1 (Ito-Yamada)

This is the group assignment of the week.

==send a message between two projects ==

We will try communicating between ESP32C3 using UDP. UDP (User Datagram Protocol) communication is a type of protocol used to transmit data over networks like the Internet. Unlike TCP, it allows data packets called datagrams to be sent without establishing a connection first. This makes it suitable for real-time applications. However, it lacks delivery confirmation and retransmission capabilities, making it less reliable than TCP. UDP is used in applications like VoIP and online gaming.

alt text

We referred to the website for the program:

First, here is the program for the master device (server). alt text

#include <WiFi.h>
#include <WiFiUdp.h>

const char ssid[] = "ESP32_Server";       // SSID
const char password[] = "1234567890";     // password
const int localPort = 10000;              // port number
const IPAddress ip(192, 168, 4, 1);       // Server IP address
const IPAddress gateway(192, 168, 4, 1);  // gateway IP address
const IPAddress subnet(255, 255, 255, 0); // subnet mask

WiFiUDP udp;
char packetBuffer[256];

void setup() {
  Serial.begin(115200);

  WiFi.softAP(ssid, password);
  delay(100);
  WiFi.softAPConfig(ip, gateway, subnet);

  Serial.println("Starting UDP");
  udp.begin(localPort);
}

void loop() {
  int i;

  while (1) {
    int packetSize = udp.parsePacket();
    if (packetSize) {
      for (i = 0; i < 256; i++) packetBuffer[i] = 0;
      udp.read(packetBuffer, 256);
      Serial.println(packetBuffer);
    }
  }
}

Next is the program for the subsidiary device (client). It sends the string “LABLAB Kannai” to the master device every 5 seconds. The variable “WiFi_FLG” is used to determine if the connection to the master has been established. If not connected, it does nothing.
alt text

#include <WiFi.h>
#include <WiFiUdp.h>

const char ssid[] = "ESP32_Server";    // SSID
const char password[] = "1234567890";  // password

static WiFiUDP wifiUdp;
static const char *kRemoteIpadr = "192.168.4.1";  // remote IP address
static const int kRmoteUdpPort = 10000;           // remote port number
static const int kLocalPort = 5000;               // local port number
bool WiFi_FLG;

void setup() {
  int i;

  Serial.begin(115200);
  WiFi_FLG = true;
  i = 0;
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    i++;
    if (i > 30) {
      WiFi_FLG = false;
      break;
    }
    delay(500);
  }
  if (WiFi_FLG == true) wifiUdp.begin(kLocalPort);
}

void loop() {
  if (WiFi_FLG == true) {
    wifiUdp.beginPacket(kRemoteIpadr, kRmoteUdpPort);
    wifiUdp.print("LABLAB Kannai");
    wifiUdp.endPacket();
  }
  delay(5000);
}
Confirming on the server side via the serial monitor that “Fablab Kannai” is being sent every 5 seconds. It is a success.

Updates on June 29, 2024

The above documenation did not mention about reading/writing messages betweem the boards developed by the Fab Academy students. Therefore, Ito and Yamada met on June 29, 2024, and checked if we could establish communication between Ito’s Seeed Xiao ESP32C3 and Yamada’s Raspberry Pi PicoW boards.

Before we proceeded to the test, Yamada did the similar communication test with his two PicoW boards. The board he designed for his Final Project was the BLE peripheral device which reads the on-board temeerature sensor and sends the serial temperature data. The other PicoW board was the BLE central device connected to PC with which he could read the serial data and plot them in the console of the Thonny IDE. (See Koji’s Individual Assignment for Week13 page.)

Building on Koji’s preparation, we replaced the BLE central device with Ito’s Xiao board.

alt text

alt text

(i) BLE Peripheral Device (Yamada. PicoW in MicroPython)

The following is the codes we used for the PicoW board.

#BLE Peripheral Reading the On-Board Temp Sensor Values
from micropython import const
import uasyncio as asyncio
import aioble
import bluetooth
import struct

# UUID Definition
_ENV_SENSE_UUID = bluetooth.UUID(0x181A)  # org.bluetooth.service.environmental_sensing
_ENV_SENSE_TEMP_UUID = bluetooth.UUID(0x2A6E)  # org.bluetooth.characteristic.temperature
_ADV_APPEARANCE_GENERIC_THERMOMETER = const(768)  # org.bluetooth.characteristic.gap.appearance.xml

_ADV_INTERVAL_MS = 250_000  # Advertising interval (ms)

# Registration of GATT Server
temp_service = aioble.Service(_ENV_SENSE_UUID)  # Define the service
temp_characteristic = aioble.Characteristic(
    temp_service, _ENV_SENSE_TEMP_UUID, read=True, notify=True
)  # Setting of the Temp Read Characteristic (reading possible, notify possible)
aioble.register_services(temp_service)  # Register temp server

sensor_temp = machine.ADC(4)  # Define PIN for taking data from On-board Temp Sensor
conversion_factor = 3.3 / (65535)

# Define temp read from On-board Temp Sensor
def _read_temperature():
    reading = sensor_temp.read_u16() * conversion_factor
    temperature = 27 - (reading - 0.706)/0.001721
    return temperature

def _encode_temperature(temp_deg_c):
    # Encode temp sensor values to the transmittable format
    # To be more specific, it goes throught the following process.
    # - Multiply Temp Sensor Values by 100 so that they could be read as integer value
    # - Convert the bytearray to binary code for packing

    return struct.pack("<h", int(temp_deg_c * 100))

async def sensor_task():
    # Renew the temp sensor values after writing
    # - Read temp sensor values
    # - Write them in the Characteristics
    # - Pause for 1000ms
    while True:
        t = _read_temperature()  # Read temp sensor 
        temp_characteristic.write(_encode_temperature(t))
        await asyncio.sleep_ms(1000)

async def peripheral_task():
    # Keep advertising until connected
    # When connected, write its MAC address on the BLE central device
    # Wait until it's disconnected
    while True:
        async with await aioble.advertise(
            _ADV_INTERVAL_MS,
            name="mpy-temp",
            services=[_ENV_SENSE_UUID],
            appearance=_ADV_APPEARANCE_GENERIC_THERMOMETER,
        ) as connection:
            print("Connection from", connection.device)
            await connection.disconnected()

async def main():
    # Define the function to undertake the two tasks in async
    # t1(task1):Renewal of the Temp Sensor read
    # t2(task2):Tas as BLE Peripheral 
    t1 = asyncio.create_task(sensor_task())
    t2 = asyncio.create_task(peripheral_task())
    await asyncio.gather(t1, t2)

asyncio.run(main())

(ii) BLE Central Device (Xiao ESP32C3 in C++ in Arduino IDE)

The following is the code we tested with the help of our instructor.

```cpp
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

// UUID definition
#define ENV_SENSE_UUID "181A"
#define ENV_SENSE_TEMP_UUID "2A6E"

// Helper to decode the temperature characteristic encoding (sint16, hundredths of a degree).
float decodeTemperature(uint8_t* data) {
    int16_t temp;
    memcpy(&temp, data, sizeof(int16_t));
    return temp / 100.0;
}

BLEAdvertisedDevice* tempSensorDevice = nullptr;
bool deviceFound = false;

// Callback for handling discovered devices
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
        if (advertisedDevice.haveName() && advertisedDevice.getName() == "mpy-temp" && advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(BLEUUID(ENV_SENSE_UUID))) {
            tempSensorDevice = new BLEAdvertisedDevice(advertisedDevice);
            deviceFound = true;
            BLEDevice::getScan()->stop();
        }
    }
};

void setup() {
    Serial.begin(115200);
    BLEDevice::init("");

    // Start BLE scan
    BLEScan* pBLEScan = BLEDevice::getScan();
    pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
    pBLEScan->setActiveScan(true);
    pBLEScan->start(5, false);
}

void loop() {
    if (deviceFound && tempSensorDevice) {
        Serial.println("Connecting to device...");
        BLEClient* pClient = BLEDevice::createClient();

        if (pClient->connect(tempSensorDevice)) {
            Serial.println("Connected to device");
            BLERemoteService* pRemoteService = pClient->getService(BLEUUID(ENV_SENSE_UUID));

            if (pRemoteService) {
                BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(BLEUUID(ENV_SENSE_TEMP_UUID));

                if (pRemoteCharacteristic && pRemoteCharacteristic->canRead()) {
                    while (true) {
                        std::string value = pRemoteCharacteristic->readValue();
                        float temperature = decodeTemperature((uint8_t*)value.data());
                        Serial.printf("Temperature: %.2f\n", temperature);
                        delay(1000);  // Sleep for 1000ms
                    }
                }
            }

            pClient->disconnect();
        } else {
            Serial.println("Failed to connect to device");
        }

        deviceFound = false;
        tempSensorDevice = nullptr;

        // Restart BLE scan
        BLEDevice::getScan()->start(5, false);
    }

    delay(1000);
}
```

(iii) Result

After a series of compilation errors, we could finally proceed to uploading the program to Xiao board. It started receiving the messages from PicoW board and showing the on-board temperature sensor read periodically.


Last update: June 30, 2024