Individual assignment
Design, build, and connect a wired or wireless node with network or bus addresses and local input and/or output device(s).
Group assignment
Send a message between two projects.
I²C uses exactly two signal lines:
Both lines are pulled high by resistors and any device on the bus can pull them low. This open-drain architecture means multiple devices can share the same wires without hardware conflicts.
Overview of serial bus families and the general concept of serial communication with a master and multiple slaves sharing the same bus lines (SDA / SCL). I²C sits within the industrial/embedded category alongside SPI.
Source: AN10216-01 I²C Manual — Philips Semiconductors, 2003.
Every I²C communication follows a master–slave model:
Each I²C device has a unique 7-bit address (or 10-bit for larger systems). When the master wants to communicate, it broadcasts the target address on the bus. Every slave listens, but only the one whose address matches responds with an ACK (acknowledge) signal. Devices with the same address would collide — which is why multiplexers exist for systems that require multiple identical devices.
The SSD1306 display used in this project uses the address 0x3C (or 60 in decimal), which is hard-coded in the initialization line: display.begin(SSD1306_SWITCHCAPVCC, 0x3C).
Every I²C transaction follows a fixed structure:
I²C theory of operation: hardware architecture showing open-drain structure with pull-up resistors on SDA and SCL; speed mode specifications (Standard 100 kHz, Fast 400 kHz, High-Speed 3.4 MHz); and key bus terminology — master, slave, transmitter, receiver, SDA, SCL.
Source: AN10216-01 I²C Manual — Philips Semiconductors, 2003.
I²C START and STOP conditions (top: signal diagram showing when SDA transitions relative to SCL) and 7-bit addressing format (bottom: the first byte after START contains the slave address and the R/W bit). The SSD1306 OLED display in this project uses address 0x3C.
Source: AN10216-01 I²C Manual — Philips Semiconductors, 2003.
| Mode | Max Bit Rate | Typical Use |
|---|---|---|
| Standard | 100 kbit/s | Sensors, EEPROM, slow displays |
| Fast | 400 kbit/s | Most modern peripherals including the SSD1306 |
| High-Speed | 3.4 Mbit/s | High-bandwidth applications |
The SSD1306 OLED display supports Fast mode (400 kHz). The Adafruit library initializes I²C at this speed by default on the XIAO RP2040.
For the individual assignment I chose to demonstrate I²C communication by sending a bitmap image to an SSD1306 OLED display connected to my XIAO RP2040 board. The image I chose was Snoopy — a recognizable character with enough contrast and detail to make a compelling test for a 128×64 pixel monochrome display.
The complete workflow involved four stages: preparing the image file, converting it to a binary format the display can understand, writing and testing the Arduino code, and finally running the sketch on the physical board.
I started with a PNG file of the Snoopy character — a black-and-white illustration with a clean outline, which is ideal for a monochrome OLED. The display resolution is 128 × 64 pixels, so the image needed to be resized to exactly those dimensions before conversion.
The original Snoopy PNG image selected for the project. The clean black-and-white illustration with a transparent background is well-suited for conversion to a monochrome bitmap for the 128×64 OLED display.
Using Microsoft Paint, I opened the image and used Image → Resize to set the dimensions to exactly 128 pixels wide and 64 pixels tall. I selected "Pixels" as the unit (not percentage) to set the values precisely.
The Paint "Cambiar de tamaño" (Resize) dialog with the dimensions set to 128 × 64 pixels. The "Píxeles" (Pixels) option is selected and aspect ratio lock is disabled to allow free resizing to the exact OLED resolution.
After resizing, I saved the file in Paint using File → Save As → Monochromatic BMP (*.bmp; *.dib). This format stores each pixel as a single bit — 1 (white) or 0 (black) — which is exactly what the SSD1306 display expects. No color data, no compression, just a grid of bits.
The Paint Save As dialog showing the file saved as "week-11_3" with type "Mapa de bits monocromático (*.bmp;*.dib)" — monochromatic bitmap. This format encodes each pixel as a single bit, matching the SSD1306's 1-bit-per-pixel data format.
The SSD1306 display cannot accept a BMP file directly — it needs the image encoded as an array of bytes in C/C++ format, stored in program memory (PROGMEM). I used the online tool image2cpp (javl.github.io/image2cpp/) to perform this conversion.
Settings used in image2cpp:
The tool generates a preview of the image and then produces the hex byte array that goes directly into the Arduino sketch.
The image2cpp tool at javl.github.io/image2cpp/ showing the preview of the Snoopy BMP (step 3) and the output configuration: "Plain bytes" format, "Horizontal — 1 bit per pixel" draw mode. The tool generates the hex byte array ready to paste into the Arduino sketch.
I copied the generated byte array output from image2cpp and pasted it into the Arduino sketch inside a PROGMEM array. The sketch uses three Adafruit libraries:
Wire.h — for I²C communicationAdafruit_GFX.h — graphics primitivesAdafruit_SSD1306.h — SSD1306 display driverThe display is initialized at I²C address 0x3C. In the loop, display.drawBitmap() draws the image starting at pixel (0,0) across the full 128×64 canvas, and display.display() pushes the buffer to the physical screen over I²C.
The Arduino IDE 2.3.6 showing the week11.ino sketch with the Seeed XIAO RP2040 selected as the target board. The snoopy PROGMEM array is visible starting at line 19 — the hex values are the 1024 bytes encoding the 128×64 monochrome bitmap.
Before uploading to the physical board, I tested the sketch in Wokwi — a browser-based Arduino/RP2040 simulator. Wokwi simulates the I²C bus and the SSD1306 display, making it possible to verify that the image renders correctly without needing the physical hardware.
The Wokwi project included an Arduino Uno (which the simulator uses to run the sketch) connected to an SSD1306 OLED via the simulated I²C pins. The display connection is: VCC → 5V, GND → GND, SCL → A5, SDA → A4.
The Wokwi simulation project before running — the sketch and the simulated Arduino Uno + SSD1306 OLED display connected via I²C. The OLED screen is blank; the simulation has not started yet.
The Wokwi simulation running at 00:05.672 (99% speed). The Snoopy bitmap is correctly rendered on the simulated SSD1306 OLED — confirming that the byte array, draw mode, and I²C initialization are all correct before flashing the physical board.
With the simulation confirmed, I uploaded the sketch to my XIAO RP2040 board using Arduino IDE 2.3.6. The board communicates with the SSD1306 OLED over its hardware I²C pins (SDA on D4, SCL on D5 on the XIAO RP2040). The same I²C address 0x3C and the same byte array work identically on the physical hardware.
The video below shows the board running the sketch and displaying the Snoopy image on the OLED screen in real time:
The XIAO RP2040 displaying the Snoopy bitmap on the SSD1306 OLED display via I²C. The image is transmitted as a 1024-byte array (128×64 pixels × 1 bit/pixel) over the SDA and SCL lines to the display's address 0x3C.
Every time the display.display() function is called, the Adafruit library performs the following over the I²C bus:
0x3C with a Write bitThis entire transfer happens at up to 400 kHz (Fast mode), meaning the full frame can be sent in approximately 20 ms — fast enough that the image appears instantaneously to the human eye.
| XIAO RP2040 Pin | SSD1306 Pin | Function |
|---|---|---|
| 3.3V | VCC | Power |
| GND | GND | Ground |
| D4 (SDA) | SDA | I²C data |
| D5 (SCL) | SCL | I²C clock |
Licensed under CC BY-NC-SA 4.0 — © Marita Chang