Week 11 - Networking and Communications

Introduction

This week focused on networking and communications. Instead of treating the OLED only as an output device like I did in Week 10, I wanted to understand the communication behind it more clearly.

For this assignment, I reused my modular Seeed XIAO ESP32-C3 board from Week 09 and the I2C OLED display that I tested in Week 10. The main goal was to explore I2C as a bus communication protocol, scan the bus, identify the OLED address, and then use that address in my code to control the display.

The setup for this week was simple, but it helped me understand an important idea: in I2C, the microcontroller is not just sending random signals to a screen. It communicates with a device at a specific bus address.

For Week 11, I worked with:

  • XIAO ESP32-C3 board from my previous weeks
  • SH110X 128x64 I2C OLED display
  • SDA and SCL communication lines
  • I2C scanner code
  • OLED display code using the detected bus address

Group Assignment:

https://week-11-group-assignment-393041.fabcloud.io


The OLED is the local output device, and it is selected through its I2C bus address. This allowed me to satisfy the networking and communication part of the assignment without making the documentation unnecessarily complicated.


Board Used

I reused the same modular XIAO ESP32-C3 board from my Week 09 and Week 10 assignments.

I did not design a new board for this week because the assignment allows using a board that was designed and fabricated earlier. My board was already suitable because it exposed:

  • 3.3V
  • GND
  • SDA
  • SCL
  • GPIO pins

This made it easy to connect an I2C device and focus on the communication protocol itself.

P


Why I Chose I2C

I chose I2C because it is one of the most common communication protocols used between microcontrollers and small modules.

The useful part of I2C is that it only needs two communication wires:

  • SDA, which carries the data
  • SCL, which carries the clock signal

Multiple devices can share the same SDA and SCL lines as long as each device has its own address. In my setup I only used one I2C device, but the addressing idea is still visible because the OLED responds at address 0x3C.

This was different from Week 10. In Week 10, I mainly cared about making the display show text and shapes. This week, I focused more on how the ESP32 finds and talks to the display.


Hardware Setup

Components

  • Seeed XIAO ESP32-C3 board
  • SH110X 128x64 OLED display
  • Jumper wires
  • USB cable for power and programming

Wiring

For the OLED, I used the same I2C pins as before:

OLED PinXIAO ESP32-C3 Connection
VCC3.3V
GNDGND
SDAGPIO6
SCLGPIO7

I2C Addressing

The most important part of this week was understanding the address.

When an I2C device is connected to the bus, the microcontroller does not control it by pin number only. Instead, the microcontroller sends data to a bus address.

In my case, the OLED display was found at:

0x3C

That means the ESP32 sends commands to address 0x3C, and the OLED responds.

This is the main addressing used in my project:

PartAddress / Identity
OLED display0x3C
SDA pinGPIO6
SCL pinGPIO7

Step 1 - Scanning the I2C Bus

Before writing the OLED display program, I first used an I2C scanner.

The scanner checks all possible I2C addresses and prints which devices respond. This was useful because it confirmed that:

  • the wiring was correct
  • SDA and SCL were assigned correctly
  • the OLED was alive
  • the address was 0x3C

I2C Scanner Code

#include <Wire.h>

#define SDA_PIN 6
#define SCL_PIN 7

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

  Serial.println();
  Serial.println("Week 11 - I2C Scanner");
  Serial.println("Starting I2C bus on custom pins...");

  Wire.begin(SDA_PIN, SCL_PIN);

  Serial.print("SDA pin: ");
  Serial.println(SDA_PIN);
  Serial.print("SCL pin: ");
  Serial.println(SCL_PIN);
}

void loop() {
  byte error;
  byte address;
  int devicesFound = 0;

  Serial.println();
  Serial.println("Scanning I2C bus...");

  for (address = 1; address < 127; address++) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at address 0x");

      if (address < 16) {
        Serial.print("0");
      }

      Serial.println(address, HEX);
      devicesFound++;
    }
    else if (error == 4) {
      Serial.print("Unknown error at address 0x");

      if (address < 16) {
        Serial.print("0");
      }

      Serial.println(address, HEX);
    }
  }

  if (devicesFound == 0) {
    Serial.println("No I2C devices found.");
  } else {
    Serial.print("Scan complete. Devices found: ");
    Serial.println(devicesFound);
  }

  delay(3000);
}

(Got this scanner code from ChatGPT)

Scanner Result

The OLED was detected at:

I2C device found at address 0x3C

P

This was the point where the communication setup started to make more sense to me. The screen was not just connected to the board physically. It was a device on a bus, and the ESP32 needed to communicate with the correct address.


Step 2 - Displaying the Address on the OLED

After confirming the I2C address, I wrote a second program that initialized the OLED at 0x3C and displayed a small status screen.

This made the output more meaningful than just printing random text. The OLED displayed:

  • the week number
  • the protocol
  • the bus address
  • the SDA and SCL pins
  • the node name

OLED Address Display Code

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>

#define SDA_PIN 6
#define SCL_PIN 7

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C

Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

const char* nodeID = "oled_node_01";

bool checkI2CDevice(byte address) {
  Wire.beginTransmission(address);
  byte error = Wire.endTransmission();

  if (error == 0) {
    return true;
  } else {
    return false;
  }
}

void drawStatusScreen() {
  display.clearDisplay();

  display.setTextSize(1);
  display.setTextColor(SH110X_WHITE);

  display.setCursor(0, 0);
  display.println("Week 11");

  display.setCursor(0, 12);
  display.println("Networking + Comm");

  display.drawLine(0, 24, 127, 24, SH110X_WHITE);

  display.setCursor(0, 30);
  display.println("Protocol: I2C");

  display.setCursor(0, 40);
  display.println("Address: 0x3C");

  display.setCursor(0, 50);
  display.println("Node: oled_node_01");

  display.display();
}

void drawBusScreen() {
  display.clearDisplay();

  display.setTextSize(1);
  display.setTextColor(SH110X_WHITE);

  display.setCursor(0, 0);
  display.println("I2C Bus Info");

  display.drawLine(0, 12, 127, 12, SH110X_WHITE);

  display.setCursor(0, 20);
  display.println("SDA: GPIO6");

  display.setCursor(0, 32);
  display.println("SCL: GPIO7");

  display.setCursor(0, 44);
  display.println("Device OK");

  display.display();
}

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

  Serial.println();
  Serial.println("Week 11 - OLED I2C Node");

  Wire.begin(SDA_PIN, SCL_PIN);

  Serial.print("Checking OLED at address 0x");
  Serial.println(OLED_ADDR, HEX);

  if (!checkI2CDevice(OLED_ADDR)) {
    Serial.println("OLED not found. Check wiring or address.");

    while (true) {
      delay(1000);
    }
  }

  Serial.println("OLED found.");

  if (!display.begin(OLED_ADDR, true)) {
    Serial.println("OLED library initialization failed.");

    while (true) {
      delay(1000);
    }
  }

  display.clearDisplay();
  display.display();

  drawStatusScreen();
}

void loop() {
  drawStatusScreen();
  delay(2000);

  drawBusScreen();
  delay(2000);

  Serial.print("Node ");
  Serial.print(nodeID);
  Serial.print(" is active at I2C address 0x");
  Serial.println(OLED_ADDR, HEX);
}

How the Code Works

The code starts by including the libraries:

  • Wire.h for I2C communication
  • Adafruit_GFX.h for drawing text and shapes
  • Adafruit_SH110X.h for controlling the SH110X OLED display

The important line for my board is:

Wire.begin(SDA_PIN, SCL_PIN);

This tells the ESP32 to use GPIO6 as SDA and GPIO7 as SCL.

Then the code checks whether a device responds at:

0x3C

If the device is found, the display is initialized and the OLED shows the status screens. If the OLED is not found, the program stops and prints an error in the Serial Monitor. I added this because it makes debugging much easier.


Result

The OLED successfully responded at address 0x3C, and the display showed the I2C information correctly.

This confirmed that:

  • the board can communicate over I2C
  • the OLED has a working bus address
  • the SDA and SCL pins are correct
  • the OLED can be used as a local output device
  • the code can address the device intentionally instead of just assuming it is connected

Problems and Fixes

Problem 1 - OLED library confusion

In Week 10, I first tried to use an SSD1306 library, but my display worked correctly with the SH110X library. This was important again this week because using the wrong OLED library can make the display look broken even when the wiring is correct.

Fix

I used:

#include <Adafruit_SH110X.h>

Problem 2 - SDA and SCL pin setup

At first, it is easy to assume that the board will automatically know which pins to use for I2C. Since I was using my own wiring, I wanted to be explicit.

Fix

I defined the pins clearly:

#define SDA_PIN 6
#define SCL_PIN 7

and started I2C with:

Wire.begin(SDA_PIN, SCL_PIN);

This made the code match my actual hardware setup.


Problem 3 - Address could be different

Many small OLED displays use 0x3C, but some can use 0x3D. If I only guessed the address, I could waste time debugging the wrong thing. (Got this information from ChatGPT, later checked it from other resouces as well.)

Fix

I ran the I2C scanner first. This confirmed that my display responded at 0x3C.


Reflection

This week helped me understand I2C as an actual communication system, not just a way to connect an OLED. This was not the most complex networking system, but it was a good step for me because it connected directly to my previous work.

I used the screen to understand I2C addressing, bus scanning, and communication between a microcontroller and an addressed device.

This also connects to my final project direction. If I use a display, sensors, or multiple I2C modules later, I will need to understand how all of them share the same bus without confusing the microcontroller. This week made that idea much clearer.