Week 10: Output Devices¶
Week 10 Assignment:
-
Group Assignment
- Measure the power consumption of an output device
-
Individual Assignment
- Add an output device to a microcontroller board you’ve designed, and program it to do something
Notes from the Lecture
GROUP ASSIGNMENT¶
I measure the power consumption of LED from this experiment
I used multimeter to measure the power consumption.
INDIVIDUAL ASSIGNMENT¶
This week, I experimented with different kinds of output devices, including LCD, OLED, Buzzer and LED.
LCD vs OLED
-
LCD (Liquid Crystal Display) -> There are backlight (a big light source in the back) and a layer of liquid crystals in front. The response time is slower, the crystals have to move/rotate to change color.
-
OLED (Organic Light-Emitting Diode) -> Unlike LCD, OLED doesn’t have backlight. Each individual pixel is made of organic material that glows when you give it electricity. The response time is instant: when electricity hits the organic material, it changes light output immediately.
For all the experiments, I used the XIAO ESP32C3 as the microcontroller.

Moreover, I used the Grove Base for XIAO as an expansion board. This board acts as a bridge between the Seeed Studio XIAO and the Grove modules.

LCD RGB Backlight, LCD (White on Blue)¶
-
Prepare the materials needed:
- XIAO ESP32C3
- Grove Base for XIAO
- Grove - LCD RGB Backlight
Grove - LCD RGB Backlight:
It enables the user to set any color preference with a simple and concise Grove interface.
Features:
- RGB Backlight
- I2C communication
- Built-in English and Japanese fonts
- 16x2 LCD
Specifications:
Item Value Input Voltage 3.3/5V Operating Current <60mA CGROM 10880 bit CGRAM 64x8 bit LCD I2C Address 0X3E RGB I2C Address 0X62 
-
Connect Grove-LCD RGB Backlight to I2C port of Grove Base
-
Plug XIAO ESP32C3 to Grove Base
-
Connect XIAO ESP32C3 to PC via a USB-C cable
-
Download the Grove-LCD RGB Backlight Library from Github
-
Open Arduino IDE. Click on
Sketch -> Include Library -> Add .ZIP Library -
Check if the library install correctly. Click on
File -> Examples -> Grove - LCD RGB Backlight -> choose an example, I chose “HelloWorld”.
There are 13 examples in the library:
Autoscroll,Blink,Cursor,CustomCharacter,CustomCharacterProgmem,Display,fade,HelloWorld,Scroll,SerialDisplay,setColor,setCursor,TextDirection -
I uploaded the code, but somehow it didn’t work. It only showed red RGB backlight on the Grove - LCD RGB Backlight.

LCD not working
- Power and RGB backlight chip are working (I2C part at address 0x62)
- The LCD controller (text driver, usually 0x3E) is not communicating properly
I tried to use another LCD: Grove - 16x2 LCD (White on Blue), and it worked well:
Grove - 16x2 LCD (White on Blue):
It enables the user to set any color preference with a simple and concise Grove interface.
Features:
- Display construction: 16 Characters, 2 Lines
- Display mode: STN
- On board MCU
- I2C-bus interface
- Support English and Japanese fonts
Specifications:
Item Value Operating Voltage 3.3V / 5V Operating temperature 0 to 50℃ Storage temperature -10 to 60℃ Driving method 1/16 duty, 1/5 bias Interface I2C I2C Address 0X3E
Buzzer¶
Since I am going to use OLED/LCD and buzzer for my final project, I want to experiment with these output components together. I followed this experiment: Toothbrushing Instructor
-
Prepare the materials needed:
- XIAO ESP32C3
- Grove Base for XIAO
- Grove - Mech Keycad
- Grove - LCD RGB Backlight
- Grove - Buzzer
Grove - Buzzer:
This module has a piezo buzzer which can be connected to digital outputs and it will emit a tone when the output is HIGH. It can also be connected to an analog pulse-width modulation output to generate various tones and effects.
Feature:
- Easy to use piezoelectric buzzer
- Uses Standard 4-pin Grove Cables to connect to other Grove modules such as - Grove Power Modules and Grove - Base Shield
- Digital port
Specifications:
Item Value Operating Voltage 3.3V/5V Sound Output ≥85dB Resonant Frequency 2300±300Hz 
-
Connect Grove - 16x2 LCD to port I2C, Grove - Mech Keycad to port D0, and Grove - Buzzer to port D2 of Grove Base
-
Plug XIAO ESP32C3 to Grove Base

-
Connect XIAO ESP32C3 to PC via a USB-C cable
-
Copy the following code into Arduino IDE and upload the code:
This code is generated from ChatGPT
#include <Wire.h> #include "rgb_lcd.h" rgb_lcd lcd; const int buzzerPin = D2; const int buttonPin = D0; // LCD color (blue) const int colorR = 0; const int colorG = 0; const int colorB = 200; void setup() { Wire.begin(); lcd.begin(16, 2); lcd.setRGB(colorR, colorG, colorB); lcd.setCursor(0, 0); lcd.print("TIME 2:00"); lcd.setCursor(0, 1); lcd.print("PRESS BUTTON"); pinMode(buzzerPin, OUTPUT); pinMode(buttonPin, INPUT); } void loop() { if (digitalRead(buttonPin) == HIGH) { delay(200); // simple debounce StartToothBrushTimer(); } delay(100); } void StartToothBrushTimer() { int totalSeconds = 120; for (int i = totalSeconds; i >= 0; i--) { int minute = i / 60; int second = i % 60; lcd.clear(); lcd.setCursor(0, 0); // format time if (second < 10) lcd.print("TIME: " + String(minute) + ":0" + String(second)); else lcd.print("TIME: " + String(minute) + ":" + String(second)); // buzz every 30 sec if (second == 0 || second == 30) { doBuzz(); lcd.setCursor(0, 1); if (i > 90) lcd.print("OUTSIDE TOP"); else if (i > 60) lcd.print("INSIDE TOP"); else if (i > 30) lcd.print("OUTSIDE BOT"); else lcd.print("INSIDE BOT"); } delay(1000); } lcd.clear(); lcd.setCursor(0, 0); lcd.print("TIME 2:00"); lcd.setCursor(0, 1); lcd.print("PRESS BUTTON"); } void doBuzz() { digitalWrite(buzzerPin, HIGH); delay(200); digitalWrite(buzzerPin, LOW); } -
Press the button (Grove – Mech Keycap) and the counter will start. A ‘beep’ sound will come from the buzzer.
OLED, LED, Buzzer¶
These are all the output devices (OLED 0.96”, LED, and Buzzer) I will use for my final projects. So I want to experiment with these materials.
-
Prepare the materials needed:
- XIAO ESP32C3
- OLED 0.96” (I tested the I2C connection of the OLED 0.96” with logic analyzer. I documented it on Week 6 (Logic Analyzer))
- Button 5x
- LED + Resistor
- Grove - Buzzer
- Jumper Wires
- Breadboard (I acknowledge that is not recommended to use the breadboard due to its problem in wiring connections, but the CNC milling is not ready to use yet, and I just want to temporary check the circuit. If everything is already ok, maybe I will order the PCB to PCB design house)
-
Then, I connected all the materials with the XIAO ESP32C3:

-
OLED 0.96”
- GND -> GND
- VCC -> 3.3V
- SCC -> GPIO 7
- SDA -> GPIO 6
-
Button 5x:
- Button A -> GPIO 10
- Button B -> GPIO 9
- Button C -> GPIO 8
- Button D -> GPIO 20
- Button 1 -> GPIO 5
- the other side of the buttons going to GND
-
LED + resistor:
- GPIO 4 -> Resistor -> LED (+)
- LED (-) -> GND
-
Buzzer
- GND -> GND
- VCC -> 5V
- SIG -> GPIO 3
-
This code is generated from ChatGPT:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// -------- PIN DEFINITIONS --------
// Buttons
#define BTN_A 10
#define BTN_B 9
#define BTN_C 8
#define BTN_D 20
#define BTN_1 5
// Outputs
#define LED_PIN 4
#define BUZZER_PIN 3
// -------- LED TIMER --------
unsigned long ledTimer = 0;
bool ledOn = false;
const int LED_DURATION = 500; // 0.5 seconds
// -------- SETUP --------
void setup() {
Serial.begin(115200);
// I2C init (SDA = 6, SCL = 7)
Wire.begin(6, 7);
// OLED init
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED not found");
while (true);
}
showCentered("AWL READY?");
// Button inputs
pinMode(BTN_A, INPUT_PULLUP);
pinMode(BTN_B, INPUT_PULLUP);
pinMode(BTN_C, INPUT_PULLUP);
pinMode(BTN_D, INPUT_PULLUP);
pinMode(BTN_1, INPUT_PULLUP);
// Outputs
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
}
// -------- MAIN LOOP --------
void loop() {
if (pressed(BTN_1)) {
showCentered("RAISEHANDS");
beep();
turnOnLED();
delay(200);
}
if (pressed(BTN_A)) {
showCentered("A");
beep();
turnOnLED();
delay(200);
}
if (pressed(BTN_B)) {
showCentered("B");
beep();
turnOnLED();
delay(200);
}
if (pressed(BTN_C)) {
showCentered("C");
beep();
turnOnLED();
delay(200);
}
if (pressed(BTN_D)) {
showCentered("D");
beep();
turnOnLED();
delay(200);
}
// LED auto OFF after 0.5 seconds
if (ledOn && millis() - ledTimer > LED_DURATION) {
digitalWrite(LED_PIN, LOW);
ledOn = false;
}
}
// -------- FUNCTIONS --------
bool pressed(int pin) {
return digitalRead(pin) == LOW;
}
void turnOnLED() {
digitalWrite(LED_PIN, HIGH);
ledTimer = millis();
ledOn = true;
}
// Improved beep using tone
void beep() {
tone(BUZZER_PIN, 1000); // 1000 Hz sound
delay(150);
noTone(BUZZER_PIN);
}
void showCentered(String text) {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
int16_t x, y;
uint16_t w, h;
display.getTextBounds(text, 0, 0, &x, &y, &w, &h);
int centerX = (SCREEN_WIDTH - w) / 2;
int centerY = (SCREEN_HEIGHT - h) / 2;
display.setCursor(centerX, centerY);
display.print(text);
display.display();
}