10. Output Devices¶
OLED¶

A 1.3-inch OLED display was used in this project and identified as using the SH1106 controller. It worked correctly with the Adafruit SH110X library, while the SSD1306 library produced incorrect output such as noise and distortion.
Library¶
A library in embedded programming is a collection of pre-written code that simplifies interaction with hardware components. Libraries reduce development time, minimize errors, and make code more readable.
I used the following libraries for the I2C OLED Display:
<Adafruit_GFX.h>: Provides basic drawing functions such as lines, rectangles, circles, triangles, and text rendering.<Adafruit_SH110X.h>: Simplifies controlling SH1106 or SH1107 OLED displays with Arduino.
Circuit diagram

Testing OLED Properties¶
The OLED properties help ensure correct communication, layout, and display control. They prevent errors by defining screen limits, addressing, and how content appears on the screen.
I intended to know the properties of the OLED i will use in my Final Project to display status of water quality in my water purification system.
gpt prompt: cpp Generate Arduino code for XIAO ESP32C3 using SH1106 OLED (Adafruit_SH110X) with I2C on SDA=6 SCL=7, initialize display and draw rectangle, diagonal line, and bottom text. Include Serial debug messages and proper setup initialization with display.begin(0x3C).
// Include required libraries
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
// Display configuration
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
// Create display object
Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(115200);
delay(2000); // Wait for Serial Monitor
Serial.println("=== SH1106 OLED Properties ===");
// Initialize I2C (SDA = 6, SCL = 7)
Wire.begin(6, 7);
// Initialize display
if (!display.begin(0x3C, true)) {
Serial.println("SH1106 OLED not found!");
while (1); // Halt execution
}
Serial.println("OLED detected successfully!");
// Test drawing on OLED
display.clearDisplay();
display.drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SH110X_WHITE); // border
display.drawLine(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, SH110X_WHITE); // diagonal
display.setTextSize(1);
display.setCursor(0, SCREEN_HEIGHT - 10);
display.println("Bottom Text Test");
display.display();
Serial.println("Check OLED for rectangle, diagonal line, and bottom text");
}
void loop() {
// Nothing to run continuously
}
Results Example:
=== SH1106 OLED Properties ===
OLED detected successfully!
Screen Width: 128
Screen Height: 64
I2C Address: 0x3C
Text color constant: 1
Max Cursor X: 127
Max Cursor Y: 63
=== Test drawing functions ===
Check OLED for rectangle, diagonal line, and bottom text
I used this chatgpt prompt to get the code below; Write an Arduino program for an ESP32 (using custom I2C pins SDA = 6 and SCL = 7) to interface with a 128x64 SH1106 OLED display using the Adafruit_SH110X and Adafruit_GFX libraries.
The program should:
Initialize serial communication at 115200 baud Initialize the OLED display at I2C address 0x3C Check if the display is connected and print an error message on Serial if not found If detected, print a confirmation message on Serial Monitor Display the following screen properties on the OLED: Screen width Screen height I2C address Text color Maximum cursor X and Y positions Use proper cursor positioning so each line is neatly spaced Keep the loop() empty since no continuous updates are needed
Make the code clean and well-structured.
Display Properties on OLED¶
// Include required libraries
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(115200);
delay(2000);
Wire.begin(6, 7);
if (!display.begin(0x3C, true)) {
Serial.println("OLED not found!");
while (1);
}
Serial.println("OLED detected successfully!");
display.clearDisplay();
display.setTextColor(SH110X_WHITE);
display.setTextSize(1);
int y = 0;
display.setCursor(0, y); display.println("Screen Properties:"); y += 8;
display.setCursor(0, y); display.print("Width: "); display.println(SCREEN_WIDTH); y += 8;
display.setCursor(0, y); display.print("Height: "); display.println(SCREEN_HEIGHT); y += 8;
display.setCursor(0, y); display.print("I2C Addr: "); display.println("0x3C"); y += 8;
display.setCursor(0, y); display.print("Text Color: "); display.println(SH110X_WHITE); y += 8;
display.setCursor(0, y); display.print("Max Cursor X: "); display.println(SCREEN_WIDTH - 1); y += 8;
display.setCursor(0, y); display.print("Max Cursor Y: "); display.println(SCREEN_HEIGHT - 1); y += 8;
display.setCursor(0, y); display.println("End of Properties");
display.display();
}
void loop() {
// Nothing to run continuously
}

OLED Cheat Sheet¶

Output Turbidity readings Working on my Final Project where turbidity sensor for water quality monitoring shall be used to measure the amount of suspended particles present in water before it is consumed. I i will therefore display this to a screen OLED for users to see when to drink the water or not.
Now i tetsed my turbidity and converted the raw readings to percentages to enalble users underatdn easily;
Circuit Diagram


gpt prompt: cpp Generate Arduino code for XIAO ESP32C3 using SH1106 OLED (Adafruit_SH110X) with I2C on SDA=6 SCL=7, include a turbidity sensor on analog pin D1 (or A0), implement averaging function for stable readings, map raw values to turbidity percentage and water quality status, and display results on OLED with labels (H2O title, Turbidity, Quality, Status GOOD/OK/BAD) plus Serial debug output
I used the code below
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Turbidity sensor pin
#define TURBIDITY_PIN D1
int turbidityPercent = 0;
int waterQuality = 0;
// ===== Function to stabilize readings =====
int readTurbidity() {
long sum = 0;
for (int i = 0; i < 10; i++) {
sum += analogRead(TURBIDITY_PIN);
delay(5);
}
return sum / 10; // average value
}
void setup() {
Serial.begin(115200);
delay(2000);
Wire.begin(6, 7); // SDA, SCL
if (!display.begin(0x3C, true)) {
Serial.println("OLED not found!");
while (1);
}
display.clearDisplay();
display.setTextColor(SH110X_WHITE);
}
void loop() {
// ===== READ SENSOR (STABLE) =====
int rawValue = readTurbidity();
// ===== MAP VALUES (ADJUSTABLE) =====
turbidityPercent = map(rawValue, 1000, 3000, 100, 0);
turbidityPercent = constrain(turbidityPercent, 0, 100);
// Water quality = same as turbidity scale (clean = high)
waterQuality = turbidityPercent;
// ===== SERIAL DEBUG =====
Serial.print("Raw: "); Serial.print(rawValue);
Serial.print(" | Turbidity: "); Serial.print(turbidityPercent);
Serial.print("% | Quality: "); Serial.print(waterQuality);
Serial.println("%");
// ===== OLED DISPLAY =====
display.clearDisplay();
// Title
display.setTextSize(2);
display.setCursor(25, 0);
display.println("H2O");
display.drawLine(0, 20, SCREEN_WIDTH, 20, SH110X_WHITE);
// Data
display.setTextSize(1);
display.setCursor(0, 25);
display.print("Turb:");
display.setCursor(85, 25);
display.print(turbidityPercent);
display.print("%");
display.setCursor(0, 38);
display.print("Qual:");
display.setCursor(85, 38);
display.print(waterQuality);
display.print("%");
display.setCursor(0, 51);
display.print("Status:");
display.setCursor(85, 51);
if (waterQuality > 75) display.print("GOOD");
else if (waterQuality > 40) display.print("OK");
else display.print("BAD");
display.display();
delay(1000);
}