9. Output devices

On this week we worked on connecting an output device to a microcontroller board. On this case I'll be working with the last week board, which have the necessary pin outs for this.

The group assignment

Here is the group assignment task where we learn together.

Starting defining output devices

An output device is any hardware component that receives digital data from a computer or microcontroller and translates it into a tangible form that users can perceive or interact with. These devices play a critical role in turning digital designs or instructions into physical actions or feedback.

On Fab Academy we are allowed to use an speaker, a motor or an OLED display. Knowing that, I'm going to go with the OLED Display.

The OLED Display

The OLED Display (Organic Light-Emitting Diode) display is an alternative for LCD display. The OLED is super-lighnt, almost paper-thin, flexible, and produce a brighter and crisper picture. [*]


OLED Display.
PINOUT.

This display is used frequently in displaying texts, bitmap images, shapes, and different types of clocks. They offer good view angles and pixel density in a cost-effective manner. At first, we will take a look at the 0.96-inch OLED display, its pinout, connections with the Arduino board, and then use Arduino IDE to program our module to display texts, different shapes, and bitmap images on the OLED display.


This is how I'm going to connect my PCB with the OLED Display:

Pin out

This OLED display's communication method is I2C communication protocol. I2C combines the best features of SPI and UARTs. With I2C, you can connect multiple slaves to a single master (like SPI) and you can have multiple masters controlling single, or multiple slaves. This is really useful when you want to have more than one microcontroller logging data to a single memory card or displaying text to a single LCD.

SDA (Serial Data) The line for the master and slave to send and receive data.
SCL (Serial Clock) The line that carries the clock signal.
I2C is a serial communication protocol, so data is transferred bit by bit along a single wire (the SDA line).

I2C communication

Starting with the display: the Hello World! On this first try of using the display, I tried the Hello world code which displays Hello world on the display.

Hello world code. Download here.

And the result was:

Hello world on display
Other example.

Now let's talk about bitmaps. A bitmap is a type of digital image format built from a grid of tiny squares called pixels. Each pixel stores color information, and by combining these colored pixels, the image is formed. Here's a breakdown of how bitmaps work: Grid of pixels: Imagine a checkerboard where each square is a pixel. The more pixels an image has, the higher its resolution and the sharper it will look. Color information: Each pixel holds a value that determines its color. In simpler bitmaps, there might be just black and white pixels. Complex ones can have millions of color possibilities. Think of a mosaic – tiny colored tiles coming together to create a bigger picture. In a bitmap, the tiles are the pixels and their colors combine to form the image.

I wanted to create an example of a bitmap, so for this I'm going to use the Fab Lab logo and show how to display it on the OLED display.


1. Transform the image to monochromatic. For this process I used this page that allow me to transform the image.

Convert to monochromatic.

2. After downloading the image, the next step is to resize it. On macOS we can resize images on the menu when we open it, so in this case I'm going for 50 x 50 pixels

resizing image.

3. Having the image resized, the next step is to create the bitmap. For this I used this page that provides the bitmap on cpp code.

Uploading image to create bitmap.
Getting the bitmap.

4. Almost done we paste that into the code:

Code on arduino. Download here.

5. Finally we got the Fab Lab logo on the screen!!!

Fab lab logo on screen.

Correcting the code I changed the position to show the logo in the middle of the display:

Fab lab logo on screen centered.

After doing this, I feel that something fun could be done, so after a lot of research and looking througt a lot of websites, I created a Flappy Bird Game. This was a little bit difficult but because in internet we have a lot of resources, I got this:

Playing flappy bird.

Here is the arduino Code of the flappy bird. Every line is explained. You can find all the codes on the Files section:

#include  // Include the SPI library for SPI communication
#include  // Include the Wire library for I2C communication
#include  // Include the Adafruit GFX library for graphics functions
#include  // Include the Adafruit SSD1306 library for OLED display control

#define SCREEN_WIDTH 128 // Define the width of the OLED display
#define SCREEN_HEIGHT 64 // Define the height of the OLED display

#define OLED_RESET    -1 // Define the reset pin for the OLED display
#define SCREEN_ADDRESS 0x3C  // Define the I2C address of the OLED display
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Create an instance of the Adafruit_SSD1306 class for OLED display control

#define BUTTON_PIN_PLAY D0 // Define the pin for the play button
#define BUTTON_PIN_JUMP D1 // Define the pin for the jump button

#define BIRD_SIZE 4 // Define the size of the bird sprite
#define BIRD_START_X 10 // Define the initial X position of the bird
#define BIRD_START_Y 20 // Define the initial Y position of the bird
#define GRAVITY 0.5 // Define the gravity force affecting the bird
#define JUMP_STRENGTH -3 // Define the jump strength of the bird

#define PIPE_WIDTH 10 // Define the width of the pipes
#define PIPE_GAP 20  // Define the gap between pipes
#define PIPE_SPEED 1 // Define the speed of the pipes

int birdX, birdY; // Variables to store the position of the bird
float birdYSpeed; // Variable to store the vertical speed of the bird
bool gameover; // Flag to indicate if the game is over

int pipeX; // Variable to store the X position of the pipes
int pipeGapPosition; // Variable to store the Y position of the gap between pipes

int score; // Variable to store the player's score

void setup() {
    pinMode(BUTTON_PIN_PLAY, INPUT_PULLUP);  // Set the play button pin as input with pull-up resistor
    pinMode(BUTTON_PIN_JUMP, INPUT_PULLUP);  // Set the jump button pin as input with pull-up resistor

    Serial.begin(9600); // Initialize serial communication

    if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {  // Initialize the OLED display
    Serial.println(F("SSD1306 allocation failed")); // Print an error message if display initialization fails
    for(;;); // Loop indefinitely
    }

    display.display(); // Initialize the display
    delay(2000); // Delay for 2 seconds
    display.clearDisplay(); // Clear the display

    // Display the custom message
    display.setTextSize(1);  // Set text size to 1
    display.setTextColor(SSD1306_WHITE);  // Set text color to white
    display.setCursor(10, 20); // Set cursor position for the first line of text
    display.println("Fab Lab Puebla."); // Print the first line of text
    display.setCursor(10, 30); // Set cursor position for the second line of text
    display.println("By: Victor"); // Print the second line of text
    display.display();  // Display the text on the OLED display

    delay(4000); // Delay for 4 seconds

    startGame(); // Start the game
}

//=============================
void startGame() {
    // Initialize game parameters
    birdX = BIRD_START_X; // Set the initial X position of the bird
    birdY = BIRD_START_Y; // Set the initial Y position of the bird
    birdYSpeed = 0; // Reset the vertical speed of the bird
    gameover = false; // Reset the gameover flag

    pipeX = SCREEN_WIDTH; // Set the initial X position of the pipes
    pipeGapPosition = random(10, SCREEN_HEIGHT - PIPE_GAP - 10); // Set the initial Y position of the gap between pipes

    score = 0; // Reset the score

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

void loop() {
    // Start a new game if play button is pressed and game is over
    if (digitalRead(BUTTON_PIN_PLAY) == HIGH && gameover) {
    startGame();
    }

    if (!gameover) {
    // Game loop
    handleInput(); // Handle player input
    updateBird();  // Update bird position
    updatePipes(); // Update pipes position
    checkCollisions(); // Check for collisions
    draw(); // Draw game elements
    delay(50); // Delay for smoother gameplay
    }
}

void handleInput() {
    // Make the bird jump if jump button is pressed and bird is above the ground
    if (digitalRead(BUTTON_PIN_JUMP) == HIGH && birdY > 0) {
    birdYSpeed = JUMP_STRENGTH; // Set the vertical speed of the bird for jump
    }
}

void updateBird() {
    birdYSpeed += GRAVITY; // Apply gravity to the bird
    birdY += birdYSpeed;                     // Update bird's vertical position

    // Keep the bird within the screen bounds
    if (birdY > SCREEN_HEIGHT - BIRD_SIZE) {
    birdY = SCREEN_HEIGHT - BIRD_SIZE;
    birdYSpeed = 0; // Stop the bird from falling through the ground
    }

    if (birdY < 0) {
    birdY = 0;
    birdYSpeed = 0; // Stop the bird from going above the screen
    }
}

void updatePipes() {
    pipeX -= PIPE_SPEED;  // Move the pipes to the left

    // Reset pipe position if it goes off-screen
    if (pipeX < -PIPE_WIDTH) {
    pipeX = SCREEN_WIDTH;
    pipeGapPosition = random(10, SCREEN_HEIGHT - PIPE_GAP - 10); // Randomize the position of the gap between pipes
    score++;  // Increase the score when a new pipe appears
    }
}

void checkCollisions() {
    // Check for collisions between bird and pipes
    if ((birdX + BIRD_SIZE > pipeX && birdX < pipeX + PIPE_WIDTH) &&
        (birdY < pipeGapPosition || birdY + BIRD_SIZE > pipeGapPosition + PIPE_GAP)) {
    gameOver(); // If collision detected, end the game
    }
}

void gameOver() {
    gameover = true; // Set gameover flag to true
    display.setTextSize(1); // Set text size to 1
    display.setTextColor(SSD1306_WHITE); // Set text color to white
    display.setCursor(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4); // Set cursor position for "GAME OVER" message
    display.println("GAME OVER"); // Display "GAME OVER" message
    display.setCursor(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4 + 20); // Set cursor position for
    display.print("Score: ");
    display.println(score); // Display the final score
    display.display(); // Display the content on the OLED screen
    delay(2000); // Delay for 2 seconds
}

void draw() {
    display.clearDisplay(); // Clear the display buffer

    display.fillRect(birdX, birdY, BIRD_SIZE, BIRD_SIZE, SSD1306_WHITE);  // Draw the bird

    // Draw the upper part of the pipe
    display.fillRect(pipeX, 0, PIPE_WIDTH, pipeGapPosition, SSD1306_WHITE);
    // Draw the lower part of the pipe
    display.fillRect(pipeX, pipeGapPosition + PIPE_GAP, PIPE_WIDTH, SCREEN_HEIGHT - pipeGapPosition - PIPE_GAP, SSD1306_WHITE);

    display.display(); // Display the content on the OLED screen
}
                    
                     

Files