Skip to content

Week 14: Networking and Communications

Group Assignment:

  • Send a message between two projects

UART Protocol: From ESP32 Devkit to Xiao ESP32-S3

UART (universal asynchronous receiver-transmitter), is one of the most common device-to-device communication protocols. It is a serial communication protocol that allows data transfer between two devices using two wires; between the transmitter (Tx) and Receiver (Rx).

alt text

UART interface does not use a clock signal to synchronize the transmitter and receiver devices; it transmits data asynchronously. Instead, both devices rely on a common baud rate, which is basically the speed at which information is transferred (bits per second). As long as both devices are configured for the same baud rate, they can understand each other’s data.

UART is a great short-distance serial communication protocol for starters due to its simple, low-cost implementation and minimal wiring requirements. However it has lower data transfer rates compared to modern protocols like USB and limited data frame size (typically 8 bits).

Source and more details on UART Protocol

For this week’s assignment we explored UART with ESP32 Devkit as the Transmitter (Tx) and Xiao ESP32-S3 as the Receiver (Rx).

Board Communication Test

alt text First let us test out whether we can get Tx and Rx to communicate with each other through simple random string data. We followed this tutorial.

The pin-outs are as follow. The Tx0 of the ESP32 Dev Kit has to be wired to the Rx01 of XiaoESP32 and so does Rx0 to Tx1. Both boards need to share the same GND wire. alt text

Transmitter Code (Tx)

The transmitter board will be sending random values from 1-100 to the receiver board with the string message “Data : (random number 1-100)”

// Original code from (https://www.youtube.com/watch?v=TGusjcKSNIU)
void setup() {
  // put your setup code here, to run once:
  Serial.begin (9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println("Data :" + String(random(1,100)));

  delay(1000);

  delay (1000);
}

Receiver Code (Rx)

The receiver board will be expecting data from the transmitter board. When it successfuly receives data - it will display the following message in Serial Monitor, “Message Received: Data : (random number 1-100)”

// Original code from (https://www.youtube.com/watch?v=TGusjcKSNIU) adapted for Xiao ESP32-S3
#define RXp2 44 // GPIO assigned to RX in XIAO ESP32-S3
#define TXp2 43 // GPIO assigned to TX in XIAO ESP32-S3

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial2.begin(9600, SERIAL_8N1, RXp2, TXp2);
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println("Message Received: ");
  Serial.println(Serial2.readString());

}

UART Communication: Success

Pull up the serial monitor to see both boards communicating to each other by sending the string data as intended.

Two Projects - Communicating

Now let us make input and output devices communicate with each other through UART communication protocol.

  • The Transmitter Board will host the current sensor INA219. The INA219 is a current, voltage and power monitor with an I2C- or SMBUS-compatible interface.
  • The Receiver board will host the TFT_ILI9341 Display which has an SPI connection (MISO,MOSI,SCK,SS)

Transmitter (Tx) Board - I2C Protocol

We will be measuring an LED Diode with a power supply of 3V. The current is expected to flow from Vin+ –> Vin- –> Load (LED Diode) –> GND. The INA219 uses an I2C (SDA/SCL) communication protocol; ESP32 Dev Kit, acting as the master, controls the communication flow on the I2C bus of INA219. The I2C bus allows for continuous communication between the INA219 and the ESP32 Dev Kit which enables the microcontroller to monitor and record sensor readings (current, voltage, and power consumption) in real-time.

The following is the system diagram for the Transmitter Board. alt text

Original Code from here. Adapted for UART Interface between ESP32 Dev Kit and Xiao ESP32S3 with the help of ChatGPT.

#include <Wire.h>
#include <Adafruit_INA219.h>

Adafruit_INA219 ina219;

void setup(void) {
  Serial.begin(115200); // Initialize UART communication
  while (!Serial) {
    delay(1);
  }

  if (!ina219.begin()) {
    Serial.println("Failed to find INA219 chip");
    while (1) {
      delay(10);
    }
  }

  Serial.println("Measuring voltage and current with INA219 ...");
}

void loop(void) {
  float shuntvoltage = ina219.getShuntVoltage_mV();
  float busvoltage = ina219.getBusVoltage_V();
  float current_mA = ina219.getCurrent_mA();
  float power_mW = ina219.getPower_mW();
  float loadvoltage = busvoltage + (shuntvoltage / 1000);

  // Send data over UART
  Serial.print(loadvoltage);
  Serial.print(",");
  Serial.print(current_mA);
  Serial.print(",");
  Serial.print(power_mW);
  Serial.println("");

   // Debug print to Serial Monitor
  Serial.print("Sent: ");
  Serial.print(loadvoltage);
  Serial.print(",");
  Serial.print(current_mA);
  Serial.print(",");
  Serial.print(power_mW);
  Serial.println("");

  delay(2000); // Wait for 2 seconds before sending the next data
}

Receiver (Rx) Board - SPI Protocol

The receiver board will be our output board. In this case - we are utilising TFT ILI9341 Display to show the reading from the Tx Board. The ILI9341 TFT display utilizes SPI (Serial Peripheral Interface) communication to connect with microcontrollers for displaying visuals and grahics. Unlike I2C, SPI is a synchronous protocol where a clock signal (SCK) synchronizes data exchange between the master (Xiao ESP32-S3) and slave (ILI9341) devices. This ensures both devices are in step during data transmission.

SPI uses three dedicated data lines for communication, in the case for ILI9341:

  • SCK (Serial Clock): The master device generates clock signal to synchronize data transfer. On each SCK pulse, a data bit is transmitted or received.
  • MOSI (Master Out, Slave In): The master transmits data to the slave device on this line. In the case of the ILI93141, the master sends commands and data bytes to control the display.
  • MISO (Master In, Slave Out): MISO allows the slave device (ILI9341) to transmit data back to the master device (microcontroller). This can be useful for functionalities like: sending acknowledgement signals after receiving data, transferring sensor readings from the slave to the master.

The following is the system diagram for the receiver board: alt text

The Rx board will receive the Voltage, Current and Power values from the Tx board and have it displayed on the TFT ILI9341 Screen.

Original Code from here. Adapted for UART Interface between ESP32 Dev Kit and Xiao ESP32S3 with the help of ChatGPT.

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

// Define pins for the ILI9341 display
#define TFT_RST A0
#define TFT_DC  A1
#define TFT_CS  D5  // SS
#define TFT_MOSI D10  // MOSI
#define TFT_MISO D9
#define TFT_CLK D8  // SCK
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);

// Define pins for Serial2
#define RX2_PIN 44 // Change to your RX2 pin
#define TX2_PIN 43 // Change to your TX2 pin

void setup(void) {
  Serial.begin(115200); // Initialize UART0 for debugging
  Serial2.begin(115200, SERIAL_8N1, RX2_PIN, TX2_PIN); // Initialize UART2

  tft.begin();
  tft.setRotation(3); // Adjust based on your display orientation
  tft.fillScreen(ILI9341_BLACK);

  Serial.println("Waiting for data from the sender...");
}

void loop(void) {
  if (Serial2.available() > 0) {
    String data = Serial2.readStringUntil('\n');
    float loadvoltage = 0;
    float current_mA = 0;
    float power_mW = 0;

    sscanf(data.c_str(), "%f,%f,%f", &loadvoltage, &current_mA, &power_mW);

    tft.fillScreen(ILI9341_BLACK);
    displayVoltageCurrent(loadvoltage, current_mA);
    delay(2000);
    tft.fillScreen(ILI9341_BLACK);
    displayPower(power_mW);
    delay(2000);
  }
}

void displayVoltageCurrent(float voltage, float current) {
  tft.setTextSize(2); // Draw 2X-scale text
  tft.setTextColor(ILI9341_WHITE);
  tft.setCursor(0, 0); // Start at top-left corner
  tft.print(voltage);
  tft.print("V");
  tft.setCursor(0, 40); // Adjust cursor position based on text size
  tft.print(current);
  tft.print("mA");
}

void displayPower(float power) {
  tft.setTextSize(2); // Draw 2X-scale text
  tft.setTextColor(ILI9341_WHITE);
  tft.setCursor(0, 60); // Adjust cursor position based on text size
  tft.print(power);
  tft.print("mW");
}

Communication between the Two Projects

System Diagram alt text

Communication: Success ??

We got the current, voltage and power reading from the Tx board to the Rx board… However, for whatever reason we also got 0 value in-between the readings.