9. Output devices

Overview

This week was working with output devices for our boards that we made in week 8. The idea is to test them and get them working. We were given a choice of different options to use, from LCD displays, OLED displays, motors, speakers, etc. The component or actuator I chose is a servo motor, specifically a micro servo.
Through hands-on experimentation with three devices - a metal gear motor, a DC motor and a servo motor - I learned not only how to calculate power by directly measuring current and voltage, but also the complexities associated with inductive loads such as motors, where Ohm's Law does not apply directly. This exercise reinforced the importance of using appropriate tools, such as power supplies and multimeters, to obtain accurate measurements and understand the dynamic behavior of devices under different load conditions.
You can check out the group assignment at the following link.



Micro servo SG90


The SG90 micro servo is a small, lightweight actuator popular in robotics due to its low cost and ease of use. Capable of rotating up to 180 degrees, it allows precise control over motion, making it ideal for applications such as robotic arms, steerable vehicles, and model airplane control systems. This servo typically operates between 4.8 and 6.0 volts, and its compact size, coupled with its wide range of motion, offers a versatile and efficient solution for mechanically controlling a wide range of components in electronic projects.

Its operation is based on the ability to rotate its shaft to specific positions, which are determined by angles. Upon receiving an electrical signal, the servo's internal circuitry interprets this information and adjusts the motor position to that corresponding to the desired angle. This position control is accomplished by pulse width modulation (PWM) pulses, where the duration of the pulse sent to the servo determines the angle at which it should rotate. For example, a pulse of a certain duration might cause the servo to turn 90 degrees, while another pulse of a different duration might take it to 0 or 180 degrees.

Features

Torque

1.6kg - cm (4.8V)

Operating temperature

-30 to +60 degrees Celsius

Dead zone settings

5 microseconds

Operating voltage

3.5V ~ 6V

Size

23x12.2x12.2x29mm

Weight

13g

Function

For Boat / Car / Aircraft / Helicopter / Robot

Servo

Analog servo

The micro servo also includes some arms to test its operation as you can see in the picture.


Testing servo on Arduino UNO

The micro servo I used has 3 pinouts. One is for GND, one for VCC and the last one for the PWM signal. These pinouts are already connected to the servo.


Before testing the servo motor on my week 8 board, I tested it on an arduino I have at home. I did this in order to prevent my board from breaking down. This way I test the servo motor operation without affecting my board.

The arduino I used is an Arduino UNO, I have this one for testing.


To know the connections the Arduino has well defined labels on the board to know which pins to use. The following image is the pinout of the Arduino UNO.


This was the connection I made, it was actually quite easy. To test it, I made a code that basically what it does is to move the servo from 0 to 180 degrees and from 180 to 0.


Here my code:


    // Include the Servo library to control the servo motor.
    #include <Servo.h>

    // Create a servo object named myServo for controlling a servo.
    Servo myServo;  

    // Attach the servo on pin 9 to the servo object for PWM control.
    void setup() {
        myServo.attach(9);  
    }

    // Move the servo from 0 to 180 degrees
    void loop() {
        // Goes from 0 degrees to 180 degrees
        for(int angle = 0; angle <= 180; angle += 1) { 
        myServo.write(angle);              // Tell the servo to go to the position in angle.
        delay(15);                         // Wait 15ms between each position to slow down the movement.
        }
        
        // Move the servo from 180 to 0 degrees
        for(int angle = 180; angle >= 0; angle -= 1) { // Goes from 180 degrees back to 0 degrees
        myServo.write(angle);              // Tell the servo to go to the position in angle.
        delay(15);                         // Wait 15ms between each position to slow down the movement.
        }
    }

Result


SG90 and Xiao ESP32 Servo Pinout

The microcontroller I used in week 8 is a Xiao Esp32C3. For the PWM signal I can use any pinout from D0 to D10. And for the voltage I used 5V.


To find out the connections on the board, check the design I made in KiCad. I used pin 2 for GND, pin 3 for VCC and pin 7 as my PWM signal pin. You can see these pinouts below the Xiao Esp32. That was the configuration I did in week 8. But it is important to know it to make the correct connections. You can check the week 8 page at the following link.


Test Run: Code Implementation and Testing

To test the servo operation, I made a code in Arduino IDE. To test it I had to install a library in the library manager. The library is called ESP32 Servo. This library is made for the microcontroller I used, since the Servo library does not work for the Xiao Esp32.


The code I made positions the servo at 90 degrees as the starting angle. Instead of using a potentiometer I used the two buttons I had designed in week 8. When I press one button the servo turns at 0 degrees and when I press the other button the servo turns in the other direction at 180 degrees.


    // Include the ESP32Servo library
    #include <ESP32Servo.h>

    // Define pin connections
    const int servoPin = D3;   // Pin connected to the servo
    const int buttonPin1 = D0; // Pin connected to the first button
    const int buttonPin2 = D10; // Pin connected to the second button

    // Create a Servo object
    Servo myServo;

    // Initial servo angle and increment for button press
    int angle = 90; // Initial angle of the servo
    int increment = 3; // Amount to increase/decrease the angle when a button is pressed

    // Variables for debouncing
    unsigned long lastPressTime = 0; // Time when a button was last pressed
    const unsigned long debounceDelay = 50; // Debounce delay to prevent multiple detections

    // Setup configuration
    void setup() {
        pinMode(buttonPin1, INPUT_PULLUP); // Initialize the first button pin as an input with internal pull-up resistor
        pinMode(buttonPin2, INPUT_PULLUP); // Initialize the second button pin as an input with internal pull-up resistor
        myServo.attach(servoPin); // Attach the servo on the servoPin to the Servo object
        myServo.write(angle); // Initialize the servo to its initial position
    }

    // Main program loop
    void loop() {
        // Check for button press with debouncing
        if (millis() - lastPressTime > debounceDelay) {
        // If the first button is pressed, move the servo in one direction
        if (digitalRead(buttonPin1) == LOW) {
            if(angle < 180) { // Check if the angle is less than 180 degrees
            angle += increment; // Increase the angle
            myServo.write(angle); // Move the servo to the new angle
            }
            lastPressTime = millis(); // Update the lastPressTime
        }

        // If the second button is pressed, move the servo in the opposite direction
        if (digitalRead(buttonPin2) == LOW) {
            if(angle > 0) { // Check if the angle is greater than 0 degrees
            angle -= increment; // Decrease the angle
            myServo.write(angle); // Move the servo to the new angle
            }
            lastPressTime = millis(); // Update the lastPressTime
        }
        }
    }

Result



Oled display

Display Oled Blue 128×64 1.3″ I2C SH1106 is SH1106 driver based display that allows displaying text and graphics with I2C communication interface , being able to work with voltage from 3V to 5V.

The 128×64 Blue Oled Display is ideal for displaying text, bitmaps, pixels, rectangles, circles and lines; or in projects for monitoring, industrial equipment, portable medical or Smartwatch.

How it works?

An OLED (organic light-emitting diode) display works by using a layer of organic material that emits light when an electric current is applied. Unlike LCD screens, OLEDs do not require a backlight, as each pixel emits its own light. This allows OLED displays to be thinner and lighter, and provide higher contrast and more vibrant colors. When voltage is applied to the electrodes of the display, electrons and holes combine in the organic material, producing photons (light). The intensity and color of the light can be controlled by varying the voltage and the composition of the organic material.


Features:

Type

OLED Display

Operating Voltage

3V – 5.5V DC

Resolution

128×64 pixels – 1.3 "

Monochrome

White Pixels

Driver

SH1106

Interface

I2C (I2C address: 0x3C)

Pins

4 (VDD, GND, SCK, and SDA)

Dimensions

35mm x 33mm x 4mm

Viewing Angle

>160º

Ultra Low Power Consumption

0.04W when all pixels are on

Operating Temperature

-30ºC ~ 70ºC

Weight

5g

In general, these are the main characteristics of the oled display, but you can consult more information about this component in its datasheet in the following link.


Finally, this image shows the oled display pinout information. The connection is simple, because it has few pins.

Hello world example


	#include <Wire.h>
	#include <Adafruit_GFX.h>
	#include <Adafruit_SSD1306.h>

	#define SCREEN_WIDTH 128 // OLED display width, in pixels
	#define SCREEN_HEIGHT 64 // OLED display height, in pixels

	// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
	#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
	#define SCREEN_ADDRESS 0x3C // I2C address for the OLED display

	Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

	void setup() {
		// Initialize the display
		if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
		Serial.println(F("SSD1306 allocation failed"));
		for (;;);
		}

		// Clear the display buffer
		display.clearDisplay();

		// Set text size and color
		display.setTextSize(2); // Increase text size
		display.setTextColor(SSD1306_WHITE);

		// Set cursor position
		display.setCursor(10, 25);

		// Display "Hello, World!"
		display.println("Hello, World!");

		// Show the buffer on the display
		display.display();
	}

	void loop() {
		// Nothing to do here
	}

This code initializes an OLED display and shows the message "Hello, World!" with an increased text size. First, the necessary libraries for handling the OLED display are included, and the screen dimensions and I2C address are defined. In the setup function, the display is initialized and the buffer is cleared. Then, the text size and color are set, as well as the cursor position. Finally, the message "Hello, World!" is displayed on the screen. The loop function is empty because the message does not need to be updated continuously.

Result


Bouncing ball game

To further test the oled display, I decided to make a game. Thinking which one I could make I chose "bouncing ball game" because I have 2 buttons on my board to move the bar. I liked the result very much.


Here my code:


	#include <Wire.h>
	#include <Adafruit_GFX.h>
	#include <Adafruit_SSD1306.h>

	#define SCREEN_WIDTH 128 // OLED display width, in pixels
	#define SCREEN_HEIGHT 64 // OLED display height, in pixels

	// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
	#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
	#define SCREEN_ADDRESS 0x3C // I2C address for the OLED display

	Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

	const int button1Pin = D0;
	const int button2Pin = D10;

	int barX = SCREEN_WIDTH / 2; // Initial X position of the bar
	int barY = SCREEN_HEIGHT - 10; // Y position of the bar
	int barWidth = 20; // Width of the bar
	int barHeight = 5; // Height of the bar
	int barSpeed = 4; // Speed of the bar

	int dotX, dotY; // Position of the dot
	int dotSpeedX = 2; // Speed of the dot in X direction
	int dotSpeedY = 2; // Speed of the dot in Y direction

	void setup() {
		// Initialize buttons
		pinMode(button1Pin, INPUT_PULLUP);
		pinMode(button2Pin, INPUT_PULLUP);

		// Initialize the display
		if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
		Serial.println(F("SSD1306 allocation failed"));
		for (;;);
		}

		// Clear the display buffer
		display.clearDisplay();
		display.display();

		// Initialize the dot position
		dotX = random(0, SCREEN_WIDTH);
		dotY = 0;
	}

	void loop() {
		// Read the state of the buttons
		int button1State = digitalRead(button1Pin);
		int button2State = digitalRead(button2Pin);

		// Move the bar based on button presses
		if (button1State == LOW) {
		barX -= barSpeed; // Move left
		if (barX < 0) barX = 0;
		}
		if (button2State == LOW) {
		barX += barSpeed; // Move right
		if (barX > SCREEN_WIDTH - barWidth) barX = SCREEN_WIDTH - barWidth;
		}

		// Move the dot
		dotX += dotSpeedX;
		dotY += dotSpeedY;

		// Check if the dot hits the walls
		if (dotX < 0 || dotX > SCREEN_WIDTH) {
		dotSpeedX = -dotSpeedX;
		}

		// Check if the dot hits the top
		if (dotY < 0) {
		dotSpeedY = -dotSpeedY;
		}

		// Check if the dot hits the bottom (lose condition)
		if (dotY > SCREEN_HEIGHT) {
		// Reset the game
		display.clearDisplay();
		display.setTextSize(2);
		display.setTextColor(SSD1306_WHITE);
		display.setCursor(10, 20);
		display.println("You lost!");
		display.display();
		delay(2000); // Show "You lost!" for 2 seconds
		// Reinitialize positions
		dotX = random(0, SCREEN_WIDTH);
		dotY = 0;
		barX = SCREEN_WIDTH / 2;
		return;
		}

		// Check collision with the bar
		if (dotX >= barX && dotX <= (barX + barWidth) && dotY >= barY && dotY <= (barY + barHeight)) {
		dotSpeedY = -dotSpeedY;
		}

		// Clear the display
		display.clearDisplay();

		// Draw the bar
		display.fillRect(barX, barY, barWidth, barHeight, SSD1306_WHITE);

		// Draw the dot
		display.fillCircle(dotX, dotY, 3, SSD1306_WHITE);

		// Update the display
		display.display();

		// Small delay for stability
		delay(50);
	}

The code controls an OLED display and two buttons to move a bar and a dot on the screen. First, it includes the necessary libraries and defines the dimensions and I2C address of the OLED display. Then, it sets up the pins for the buttons and initializes the positions of the bar and the dot. In the main loop, the code reads the button states to move the bar left or right. The dot moves automatically and bounces when it hits the screen edges. If the dot reaches the bottom of the screen without hitting the bar, a "You lost!" message is displayed and the game resets after a short delay.


Files

Servo code in Arduino (.ino) Download
Servo code in XiaoEsp32 (.ino) Download
Bouncing ball game (.ino) Download
Hello world (.ino) Download