Here comes another week 😮💨
For the group assignment, we measured the power consumption of two output devices, an LED and a servo motor using a multimeter in ammeter mode, connected in series with the circuit.
For the LED, we built a simple circuit with a 220Ω resistor and a 4.7V supply. Using P = V × I, the LED consumed about 18 mW, which was very low.
For the servo, we measured current in two states. At idle it drew 40 mA (188 mW), but under load it jumped to 725 mA (3.41 W) which is nearly 18 times more. This is because the internal motor pulls more current to maintain torque when resisting force.
The key takeaway was that you always need to design your power supply for the worst case, not just the idle current.
You can access our group assignment here for more details.
For the individual assignment, I decided to program three output devices: an OLED display, since I’ll be using it as the main screen for my final project, an LCD screen just to try it out, and a servo motor because I’ve always wanted to experiment with programming one.
I designed a new board for this week using Kicad. I connected pin sockets to all the pins since I wasn’t sure which output devices I would be testing at first.
Miling process:
These were all the components I used to make my board:
The results:
Image source
An OLED (Organic Light Emitting Diode) display is a self emissive display technology where each pixel produces its own light, removing the need for a backlight, resulting in better contrast and lower power consumption. Small OLED modules (0.96" or 1.3") are commonly used in microcontroller projects, communicating via I2C or SPI. The most popular driver IC, the SSD1306, is well supported across Arduino, CircuitPython, and MicroPython, able to display text, graphics, and animations.
I had already programmed an OLED display several times during my final project development while testing the menu structure, but I hadn’t tried creating a simple facial expression. Since I’ll be using a display for my bot’s face, I decided to experiment with making expressive eyes.
A typical small OLED module (0.96") has 4 pins when using I2C communication:
This is the pinout of an OLED display using I²C communication.
Image source
I2C (Inter Integrated Circuit), is a simple two wire communication protocol that allows a microcontroller to communicate with peripheral devices like sensors and displays over just two shared lines, keeping wiring clean and minimal.
SCL and SDA connect directly to the corresponding I2C pins on your microcontroller. Each device on the I2C bus has a unique address — the OLED defaults to 0x3C (sometimes 0x3D), which is used in code so the MCU knows exactly which device it is talking to, much like a phone number. For SPI based OLED modules, there are typically 7 pins including CS, DC, and RES, offering faster communication but using more GPIO pins.
All the connections were straightforward. I connected the OLED’s VCC to the 3V pin socket on my board and GND to GND. Then, I connected the SCL pin of the OLED to the D5 (SCL) pin socket, and the SDA pin to the D4 (SDA) pin socket on my board.
I followed this documentation and found out that to create the popular big robo eyes, I needed to download the FluxGarage RoboEyes Library.
I used Claude AI to generate simple, expressive eyes that cycle through different moods using the FluxGarage RoboEyes Library, with the prompt:
Program my XIAO ESP32 C3 with an OLED display on SDA D4 and SCL D5 to show animated robot eyes using the FluxGarage RoboEyes library and cycles through different moods like happy and tired with auto blinking and eye movement.
This was the code it generated:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <FluxGarage_RoboEyes.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SDA_PIN 6
#define SCL_PIN 7
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
RoboEyes<Adafruit_SSD1306> roboEyes(display);
unsigned long eventTimer;
bool event1wasPlayed = 0;
bool event2wasPlayed = 0;
bool event3wasPlayed = 0;
void setup() {
delay(2000);
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
while (true) { delay(100); }
}
roboEyes.begin(SCREEN_WIDTH, SCREEN_HEIGHT, 100);
roboEyes.setPosition(DEFAULT);
roboEyes.setAutoblinker(ON, 3, 2); // auto random blinking
roboEyes.setIdleMode(ON, 5, 2); // random eye movement when idle
roboEyes.close();
eventTimer = millis();
}
void loop() {
roboEyes.update();
if (millis() >= eventTimer + 2000 && event1wasPlayed == 0) {
event1wasPlayed = 1;
roboEyes.open();
}
if (millis() >= eventTimer + 4000 && event2wasPlayed == 0) {
event2wasPlayed = 1;
roboEyes.setMood(HAPPY);
roboEyes.anim_laugh();
}
if (millis() >= eventTimer + 6000 && event3wasPlayed == 0) {
event3wasPlayed = 1;
roboEyes.setMood(TIRED);
}
if (millis() >= eventTimer + 8000) {
roboEyes.close();
roboEyes.setMood(DEFAULT);
eventTimer = millis();
event1wasPlayed = 0;
event2wasPlayed = 0;
event3wasPlayed = 0;
}
}
Pin definitions:
The first few lines tell the code which pins the OLED is connected to and how big the screen is:
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define SDA_PIN 6
#define SCL_PIN 7
Setting up the display and eyes:
These two lines create the display and the RoboEyes animation system that draws on it:
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
RoboEyes<Adafruit_SSD1306> roboEyes(display);
Animation flags:
Three variables that track which animation steps have already played so they don't repeat:
bool event1wasPlayed = 0;
bool event2wasPlayed = 0;
bool event3wasPlayed = 0;
Setup:
Starts the display, configures the eyes to auto-blink and drift around on their own when idle, then closes them to begin:
roboEyes.setAutoblinker(ON, 3, 2);
roboEyes.setIdleMode(ON, 5, 2);
roboEyes.close();
The animation cycle:
Runs a repeating 8csecond sequence using timestamps instead of delay, so the eyes keep animating smoothly in the background while waiting:
if (millis() >= eventTimer + 2000 && event1wasPlayed == 0) {
roboEyes.open(); // eyes open at 2 seconds
}
if (millis() >= eventTimer + 4000 && event2wasPlayed == 0) {
roboEyes.setMood(HAPPY);
roboEyes.anim_laugh(); // laughing at 4 seconds
}
if (millis() >= eventTimer + 6000 && event3wasPlayed == 0) {
roboEyes.setMood(TIRED); // tired at 6 seconds
}
if (millis() >= eventTimer + 8000) {
roboEyes.close();
roboEyes.setMood(DEFAULT); // reset and restart at 8 seconds
}
I pasted the code in Arduino IDE and then before uploading the code I had to download all the libraries:
And then I uploaded the code and it worked, the moods cycled accordingly:
Here is a video of the oled cycling between different moods.
Next, I tested a LCD screen
Image source
An LCD (Liquid Crystal Display) is a type of display that uses liquid crystals to control the passage of light. Unlike OLED, LCD does not emit its own light — it relies on a backlight behind the screen to illuminate the pixels, with the liquid crystals acting as shutters to block or allow light through.
This is the pinout of the LCD display:
Image source >
When using an LCD with an I2C adapter module, it is simplified down to 4 pins, identical in function to the OLED: GND,VCC,SCL and SDA
I connected all the pins to their corresponding pins on the Xiao ESP32 C3 module, just like the oled.
To make the LCD print a simple message, I asked Claude AI to generate a code and explain it to me.This was the promp I used:
"Program my XIAO ESP32 C3 to display Hello on the first row and Gorgeous! on the second row of a 16x2 I2C LCD connected on SDA D4 and SCL D5."
This was the code it generated:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
Wire.begin(6, 7);
lcd.init();
lcd.backlight();
lcd.clear();
// Print text off-screen to the right so it slides in
for (int i = 0; i < 16; i++) lcd.print(" ");
lcd.setCursor(16, 0);
lcd.print("Hello");
lcd.setCursor(21, 1);
lcd.print("Gorgeous!");
}
void loop() {
lcd.scrollDisplayLeft(); // slides everything left smoothly
delay(300);
}
Code Explanation
Libraries & Display Setup
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
Setup Function
Wire.begin(6, 7);
lcd.init();
lcd.backlight();
lcd.clear();
Scrolling Text Setup
for (int i = 0; i < 16; i++) lcd.print(" ");
lcd.setCursor(16, 0);
lcd.print("Hello");
lcd.setCursor(21, 1);
lcd.print("Gorgeous!");
Loop Function
lcd.scrollDisplayLeft();
delay(300);
Both the OLED and LCD communicate using the I2C protocol, which allows multiple devices to share the same SDA and SCL communication lines. Each device has a unique address that identifies it on the bus. In my setup, the OLED used address 0x3C while the LCD used address 0x27. When the microcontroller sends data, it includes the device address so that only the intended display responds, allowing both displays to operate simultaneously on the same bus.
Then I downloaded the LiquidCrystal I2C Library in arduino ide for the LCD:
And I uploaded the code:
Note: If the text on the LCD appears faint or invisible, you can adjust the contrast by turning the small potentiometer (blue square) on the back of the LCD module using a small screwdriver. I didn't realize this at the time, so my text looked a bit dim. Adjusting the potentiometer would have made it much clearer!
Image source >
The SG90 micro servo is a lightweight micro servo motor that weighs just 9 grams and is small enough to fit in the palm of your hand. It can rotate up to 180 degrees and is controlled by a single signal wire using PWM (Pulse Width Modulation), which tells it exactly what angle to move to. It runs on 5V and has three wires, brown for ground, red for power, and orange for the signal. It is one of the most popular servos for beginner electronics projects because it is cheap, simple to wire, and works straight away with most microcontrollers including the XIAO ESP32 C3.
How a Micro Servo Works
A micro servo is a small motor that rotates to a specific angle (between 0° and 180°) based on a signal from the microcontroller.
The MCU sends a PWM (Pulse Width Modulation) signal — essentially a series of pulses where the width of each pulse tells the servo which angle to move to. The servo's internal circuit reads this signal and drives the motor to the correct position and holds it there.
Here is a pinout I found:
Image source >
The SG90 servo was connected to the XIAO ESP32 C3 using three jumper wires, brown to GND, red to 5V pin, and the orange signal wire to D2 (GPIO4).
The servo was powered from the XIAO's 5V pin through USB power
Then I used Claude ai to program the servo motor to just rotate for this test with the prompt:
"Program my XIAO ESP32-C3 to make an SG90 micro servo motor connected to D2 sweep back and forth from 0 to 180 degrees continuously as a simple test."The code it generated:
#include <ESP32Servo.h>
#define SERVO_PIN 4 // D2 = GPIO4
Servo myServo;
void setup() {
delay(2000);
myServo.attach(SERVO_PIN, 500, 2400);
}
void loop() {
// Sweep from 0 to 180
for (int angle = 0; angle <= 180; angle++) {
myServo.write(angle);
delay(10); // speed — lower = faster
}
// Sweep back from 180 to 0
for (int angle = 180; angle >= 0; angle--) {
myServo.write(angle);
delay(10);
}
}
Library & Pin Setup
#include <ESP32Servo.h>
#define SERVO_PIN 4
Servo myServo;
Setup Function
delay(2000);
myServo.attach(SERVO_PIN, 500, 2400);
Loop Function
for (int angle = 0; angle <= 180; angle++) {
myServo.write(angle);
delay(10);
}
for (int angle = 180; angle >= 0; angle--) {
myServo.write(angle);
delay(10);
}
I then downloaded the ESP32Servo libary to program the micro servo
I uploaded the code and the servo motor started rotating..
A servo motor uses Pulse Width Modulation (PWM) signals to determine its position. The microcontroller sends a pulse approximately every 20 milliseconds. The width of the pulse tells the servo what angle to move to. In my code, the servo was configured with a pulse width range of 500–2400 microseconds. Shorter pulses move the servo toward one end of its range, while longer pulses move it toward the other end. The servo continuously reads these pulse widths and adjusts its position accordingly.
This week was fun too because I got to explore how output devices can interact with a microcontroller.The best part was programming the oled.