Skip to content

Week 10: Output Devices

Week 10 Assignment:
  1. Group Assignment

    1. Measure the power consumption of an output device
  2. Individual Assignment

    1. 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)

  1. Prepare the materials needed:

    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

  2. Connect Grove-LCD RGB Backlight to I2C port of Grove Base

  3. Plug XIAO ESP32C3 to Grove Base

  4. Connect XIAO ESP32C3 to PC via a USB-C cable

  5. Download the Grove-LCD RGB Backlight Library from Github

  6. Open Arduino IDE. Click on Sketch -> Include Library -> Add .ZIP Library

  7. 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

  8. 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

  1. 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

  2. Connect Grove - 16x2 LCD to port I2C, Grove - Mech Keycad to port D0, and Grove - Buzzer to port D2 of Grove Base

  3. Plug XIAO ESP32C3 to Grove Base

  4. Connect XIAO ESP32C3 to PC via a USB-C cable

  5. 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);
    }
    
  6. 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.

  1. 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)
  2. Then, I connected all the materials with the XIAO ESP32C3:

    1. OLED 0.96”

      • GND -> GND
      • VCC -> 3.3V
      • SCC -> GPIO 7
      • SDA -> GPIO 6
    2. 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
    3. LED + resistor:

      • GPIO 4 -> Resistor -> LED (+)
      • LED (-) -> GND
    4. 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();
}