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.