Skip to content

Output Devices

Scheduling

This week was mostly free because of the Paro Tshechu holidays! So I didn’t really schedule anything since I had nothing but free time šŸ˜­


Individual Assignment

For my individual assignment, I had to add and program an output device to a microcontroller board that I fabricated. For this, Iā€™ve decided to stick to the board I made last week, but Iā€™m gonna make some changes and follow up on the feedback that I got from my regional instructor šŸ™, so if you feel lost anywhere along the process, refer to my documentation for Electronics Week.

KiCad

Like last week, Iā€™m gonna use KiCad to design my board. The entire process is essentially the same as last week, so Iā€™m gonna be making comparisons and documenting what I changed and how I improved my design. Iā€™m gonna use the SEEED STUDIO XIAO RP2040 PINOUT diagram as a reference, but Iā€™ll be switching between that and the SEEED STUDIO WROOM something (they both have the same PINOUT layout so it works).

Hereā€™s a a pretty useful icon introduction if youā€™re new to KiCad.

Name Shortcut Symbol Location Function Symbol
Select item S Schematic Editor Selects an item in the schematic editor alt text
Add a symbol A Schematic Editor Adds a new component to the schematic alt text
Add a power symbol P Schematic Editor Adds a power symbol to the schematic alt text
Add a wire W Schematic Editor Adds a wire for electrical connection alt text
Add a no-connection flag Q Schematic Editor Adds a no-connection flag to indicate no electrical connection alt text
Add a junction J Schematic Editor Adds a junction where two wires meet alt text
Add a global label CTRL+L Schematic Editor Adds a global label to identify a net or signal alt text
Plot N/A Schematic Editor Plots the schematic into a file format alt text
Schematic setup N/A Schematic Editor Sets up the schematic properties alt text
Electrical Rules Checker N/A Schematic Editor Checks the schematic for any electrical rule violations alt text

The Schematic Editor

I copy and pasted my design from last week, but I added some bypass capacitors, fixed my pin orientations and added a new pushbutton.

alt text

Looks like weā€™re good

alt text

PCB Editing

This week, my signal lines were 0.4mm, my power lines were 0.8mm, and my edge cut was 0.6mm.

alt text


Mods CE

Mods CE stuff

alt text

Milling

I went over to the milling machine that we have at our Fab Lab (theĀ monoFab SRM-20), and after the bed was set, I set up the milling machine, and started cutting out my files.

alt text


Soldering

Like usual, I created a ā€œstickyā€ component list for the board, and then I started to solder.

alt text

Hereā€™s how it came out !!!

alt text

Programming

Iā€™m using Arduino IDE to program the board (again), and all my communication is through a USB-C cable and you can find out more about this software here.

Before I get into programming, here’s how all the output devices are interfaced to my board (at different times of course):

asasdas


SSD1306 OLED Display

The SSD1306 OLED Display is a compact, high-resolution screen commonly used in electronics projects for displaying text, graphics, and animations with low power consumption and excellent contrast.


Specifications

The datasheet:

Property Value
Chipset SSD1306
Colour White
Visible angle > 160Ā°
Resolution 128 x 64 pixels
Operating voltage 3.3V to 5V
Operating temperature -30 Ā° C to +80 Ā° C
Low power consumption 0.04W in normal operation
Control I2C / Twi
Pinching VCC, GND, SCL, SDA
Dimensions approx. 27mm x 27mm x 4.1mm

To program an SSD1306 OLED Display, download the following libraries on your Arduino IDE. alt text alt text

Once youā€™re done with that, connect your OLED display


Snowflakes

I decided to start my OLED programming with a snowflake screen, so I asked ChatGPT for some codes and here’s how it came out.

PROMPT

Write an Arduino sketch to simulate falling snowflakes on an OLED display using Adafruit's SSD1306 library. The sketch should initialize the OLED display, randomly generate the positions of multiple snowflakes, animate their falling motion, and reset them when they reach the bottom of the display. Use appropriate delay values to control the speed of the falling snowflakes.

#include <SPI.h>
#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
#define OLED_RESET    -1 // Reset pin # (or -1 if sharing Arduino reset pin)

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

// Snowflake parameters
const int numSnowflakes = 10;
int snowflakeX[numSnowflakes];
int snowflakeY[numSnowflakes];
const int snowflakeSize = 2;
const int snowflakeSpeed = 1;

void setup() {
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  delay(2000);
  display.clearDisplay();

  // Initialize snowflake positions
  for (int i = 0; i < numSnowflakes; i++) {
    snowflakeX[i] = random(0, SCREEN_WIDTH);
    snowflakeY[i] = random(-SCREEN_HEIGHT, 0);
  }
}

void loop() {
  drawSnowflakes();
  moveSnowflakes();
  delay(50); // Adjust snowflake fall speed
}

void drawSnowflakes() {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);

  for (int i = 0; i < numSnowflakes; i++) {
    display.drawPixel(snowflakeX[i], snowflakeY[i], SSD1306_WHITE);
  }

  display.display();
}

void moveSnowflakes() {
  for (int i = 0; i < numSnowflakes; i++) {
    snowflakeY[i] += snowflakeSpeed;

    // If snowflake reaches bottom of screen, reset its position to the top
    if (snowflakeY[i] > SCREEN_HEIGHT) {
      snowflakeX[i] = random(0, SCREEN_WIDTH);
      snowflakeY[i] = random(-SCREEN_HEIGHT, 0);
    }
  }
}

Ping Pong Animation

PROMPT

Write an Arduino sketch for a simple Pong game using an OLED display and two push buttons for each player's controls. The game should display scores for both players, have paddles that move vertically, and a ball that bounces off the top and bottom walls and the paddles. When the ball goes out of bounds, the opposing player should score a point, and the ball should reset to the center of the screen. Ensure that the paddles are constrained within the screen bounds and adjust the delay value for controlling the game speed.

#include <SPI.h>
#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
#define OLED_RESET -1    // Reset pin # (or -1 if sharing Arduino reset pin)

#define PADDLE_WIDTH 2
#define PADDLE_HEIGHT 10
#define PADDLE_SPEED 2

#define BALL_SIZE 2
#define BALL_SPEED 2

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

int player1Score = 0;
int player2Score = 0;

int paddle1Y = SCREEN_HEIGHT / 2 - PADDLE_HEIGHT / 2;
int paddle2Y = SCREEN_HEIGHT / 2 - PADDLE_HEIGHT / 2;

int ballX = SCREEN_WIDTH / 2 - BALL_SIZE / 2;
int ballY = SCREEN_HEIGHT / 2 - BALL_SIZE / 2;
int ballDirX = BALL_SPEED;
int ballDirY = BALL_SPEED;

void setup() {
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;
  }
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.display();
}

void loop() {
  display.clearDisplay();

  handleInput();
  moveBall();
  movePaddles();
  checkCollisions();
  drawScore();
  drawPaddles();
  drawBall();

  display.display();
  delay(10); // Adjust game speed
}

void handleInput() {
  // Player 1 controls
  if (digitalRead(PADDLE1_UP_BUTTON_PIN) == HIGH) {
    paddle1Y -= PADDLE_SPEED;
  }
  if (digitalRead(PADDLE1_DOWN_BUTTON_PIN) == HIGH) {
    paddle1Y += PADDLE_SPEED;
  }

  // Player 2 controls
  if (digitalRead(PADDLE2_UP_BUTTON_PIN) == HIGH) {
    paddle2Y -= PADDLE_SPEED;
  }
  if (digitalRead(PADDLE2_DOWN_BUTTON_PIN) == HIGH) {
    paddle2Y += PADDLE_SPEED;
  }

  // Keep paddles within screen bounds
  paddle1Y = constrain(paddle1Y, 0, SCREEN_HEIGHT - PADDLE_HEIGHT);
  paddle2Y = constrain(paddle2Y, 0, SCREEN_HEIGHT - PADDLE_HEIGHT);
}

void moveBall() {
  ballX += ballDirX;
  ballY += ballDirY;

  // Bounce off top and bottom walls
  if (ballY <= 0 || ballY >= SCREEN_HEIGHT - BALL_SIZE) {
    ballDirY = -ballDirY;
  }

  // Score points and reset ball if it goes out of bounds
  if (ballX <= 0) {
    player2Score++;
    resetBall();
  }
  if (ballX >= SCREEN_WIDTH - BALL_SIZE) {
    player1Score++;
    resetBall();
  }
}

void movePaddles() {
  // Paddles move automatically in this simple implementation
  // You can adjust this to make the paddles respond to player input if desired
}

void checkCollisions() {
  // Check collision with paddles
  if (ballX <= PADDLE_WIDTH && ballY + BALL_SIZE >= paddle1Y && ballY <= paddle1Y + PADDLE_HEIGHT) {
    ballDirX = BALL_SPEED;
  }
  if (ballX + BALL_SIZE >= SCREEN_WIDTH - PADDLE_WIDTH && ballY + BALL_SIZE >= paddle2Y &&
      ballY <= paddle2Y + PADDLE_HEIGHT) {
    ballDirX = -BALL_SPEED;
  }
}

void drawScore() {
  display.setCursor(SCREEN_WIDTH / 4, 0);
  display.print("Player 1: ");
  display.println(player1Score);
  display.setCursor(SCREEN_WIDTH / 4, 10);
  display.print("Player 2: ");
  display.println(player2Score);
}

void drawPaddles() {
  display.fillRect(0, paddle1Y, PADDLE_WIDTH, PADDLE_HEIGHT, SSD1306_WHITE);
  display.fillRect(SCREEN_WIDTH - PADDLE_WIDTH, paddle2Y, PADDLE_WIDTH, PADDLE_HEIGHT, SSD1306_WHITE);
}

void drawBall() {
  display.fillRect(ballX, ballY, BALL_SIZE, BALL_SIZE, SSD1306_WHITE);
}

void resetBall() {
  ballX = SCREEN_WIDTH / 2 - BALL_SIZE / 2;
  ballY = SCREEN_HEIGHT / 2 - BALL_SIZE / 2;
  ballDirX = BALL_SPEED;
  ballDirY = random(-BALL_SPEED, BALL_SPEED); // Randomize initial vertical direction
}

Waves

PROMPT Create an Arduino sketch to generate a sine wave animation on an OLED display using Adafruit's SSD1306 library. The animation should consist of multiple wave segments moving horizontally across the screen, creating a continuous wave pattern. Adjust the parameters such as wave height, width, and speed to achieve the desired visual effect. Ensure that the wave smoothly wraps around the screen without abrupt jumps or glitches.

#include <SPI.h>
#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
#define OLED_RESET -1    // Reset pin # (or -1 if sharing Arduino reset pin)

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

// Parameters for the wave animation
#define WAVE_HEIGHT 15
#define WAVE_WIDTH 5
#define WAVE_SPEED 10

int wavePosition = 0;

void setup() {
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;
  }
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
}

void loop() {
  display.clearDisplay();

  drawWave();

  display.display();
  delay(50); // Adjust animation speed
}

void drawWave() {
  for (int x = 0; x < SCREEN_WIDTH; x += WAVE_WIDTH * 2) {
    int y = SCREEN_HEIGHT / 2 + sin((x + wavePosition) * 0.05) * WAVE_HEIGHT;
    display.fillRect(x, y, WAVE_WIDTH, WAVE_HEIGHT, SSD1306_WHITE);
  }

  wavePosition += WAVE_SPEED;

  // Reset wave position to prevent overflow
  if (wavePosition > 360) {
    wavePosition = 0;
  }
}

Water Drops

PROMPT Develop an Arduino sketch to create a water drop ripple animation on an OLED display utilizing Adafruit's SSD1306 library. The animation should simulate water drops falling onto the surface, generating concentric ripples that expand outward. Customize parameters such as the number of water drops, drop size, maximum ripple count per drop, and ripple expansion speed to achieve the desired visual effect. Ensure that the animation runs smoothly and efficiently, with drops randomly positioned on the screen and ripples deactivating when reaching the screen edges.

#include <SPI.h>
#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
#define OLED_RESET -1    // Reset pin # (or -1 if sharing Arduino reset pin)

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

// Parameters for the water drop ripple animation
#define DROP_COUNT 3    // Number of water drops
#define DROP_SIZE 3     // Size of each water drop
#define MAX_RIPPLES 8   // Maximum number of concentric ripples per drop
#define RIPPLE_SPEED 1  // Speed at which ripples expand

struct Drop {
  int x;
  int y;
};

struct Ripple {
  int x;
  int y;
  int radius;
  bool active;
};

Drop drops[DROP_COUNT];
Ripple ripples[DROP_COUNT][MAX_RIPPLES];

void setup() {
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;
  }
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  initDrops();
  initRipples();
}

void loop() {
  display.clearDisplay();

  updateRipples();
  drawRipples();

  display.display();
  delay(50); // Adjust animation speed
}

void initDrops() {
  // Initialize drop positions randomly
  for (int i = 0; i < DROP_COUNT; i++) {
    drops[i].x = random(SCREEN_WIDTH);
    drops[i].y = random(SCREEN_HEIGHT);
  }
}

void initRipples() {
  // Initialize ripples for each drop
  for (int i = 0; i < DROP_COUNT; i++) {
    for (int j = 0; j < MAX_RIPPLES; j++) {
      ripples[i][j].x = drops[i].x;
      ripples[i][j].y = drops[i].y;
      ripples[i][j].radius = 0;
      ripples[i][j].active = false;
    }
    // Activate the first ripple (the drop)
    ripples[i][0].active = true;
  }
}

void updateRipples() {
  // Update active ripples for each drop
  for (int i = 0; i < DROP_COUNT; i++) {
    for (int j = 0; j < MAX_RIPPLES; j++) {
      if (ripples[i][j].active) {
        ripples[i][j].radius += RIPPLE_SPEED;
        // Deactivate ripple when it reaches the screen edge
        if (ripples[i][j].radius >= SCREEN_WIDTH / 2) {
          ripples[i][j].active = false;
        }
      }
    }
    // Activate next ripple when the previous one has expanded sufficiently
    for (int j = 1; j < MAX_RIPPLES; j++) {
      if (!ripples[i][j].active && ripples[i][j - 1].radius > DROP_SIZE) {
        ripples[i][j].active = true;
      }
    }
  }
}

void drawRipples() {
  // Draw active ripples for each drop
  for (int i = 0; i < DROP_COUNT; i++) {
    for (int j = 0; j < MAX_RIPPLES; j++) {
      if (ripples[i][j].active) {
        display.drawCircle(ripples[i][j].x, ripples[i][j].y, ripples[i][j].radius, SSD1306_WHITE);
      }
    }
  }
}

DC Motor

A DC motor is an electrical machine that converts electrical energy into mechanical energy through the interaction of magnetic fields. It operates on the principle of Lorentz force, where a current-carrying conductor experiences a force when placed in a magnetic field, resulting in rotation.


Specifications

The datasheet:

Property Value
Type Standard 130 DC motor
Operating Voltage 4.5V to 9V
Recommended/Rated Voltage 6V
Current at No load 70mA (max)
No-load Speed 9000 rpm
Loaded current 250mA (approx)
Rated Load 10g*cm
Motor Size 27.5mm x 20mm x 15mm
Weight 17 grams

There wasnā€™t really any coding required but Iā€™m glad that I was able to operate one by connecting the GND and VCC pins to my FTDI pins from the board I made last week.


0.25 Watt Speaker

The 8-ohm 0.25-watt speaker is a compact audio device suitable for low-power applications. With its standard impedance and modest power handling capability, it’s commonly utilized in small electronic gadgets, toys, and communication devices.

Specifications:

The datasheet:

Property Value
Resonant Frequency 440 Ā±20% HZ
Impedance 8 ohm Ā±15% at 1500HZ
Rated Input Power 0.25 W
Maximum Input Power 0.5W - printed wattage on back of speaker may have either value
Frequency Range 440~20KHz

Programming

I decided to use an Arduino UNO to program it.

PROMPT Write an Arduino sketch to play the 'Happy Birthday' song using a buzzer connected to pin 14. Define the frequencies of the song notes and their corresponding durations. Implement a loop to iterate through the notes and durations arrays, playing each note for the specified duration. Ensure a brief pause between repetitions of the song. Verify that the buzzer pin is set as an OUTPUT in the setup function.

// Pin connected to the buzzer
int buzzerPin = 14;

// Frequencies of the "Happy Birthday" song notes
int notes[] = {
  262, 262, 294, 262, 349, 330,
  262, 262, 294, 262, 392, 349,
  262, 262, 523, 440, 349, 330, 294,
  466, 466, 440, 349, 392, 349
};

// Note durations (in milliseconds)
int durations[] = {
  500, 500, 1000, 1000, 1000, 2000,
  500, 500, 1000, 1000, 1000, 2000,
  500, 500, 1000, 1000, 1000, 1000, 2000,
  500, 500, 1000, 1000, 1000, 2000
};

void setup() {
  // Set the buzzer pin as an OUTPUT
  pinMode(buzzerPin, OUTPUT);
}

void loop() {
  // Play the "Happy Birthday" song
  for (int i = 0; i < sizeof(notes) / sizeof(notes[0]); i++) {
    tone(buzzerPin, notes[i]);
    delay(durations[i]);
    noTone(buzzerPin);
  }

  // Pause between repetitions
  delay(2000);
}

Reflection

This week was definitely one of the more fun weeks of this experience. I learned a lot about OLED screens, and I’m confident that I can use one in my final project. The sound speaker was also really fun to tamper with, Yangtshel and I had a really good time playing around with the sounds and trying to get it to sound decent.

- Wozer


The Mentioned Files

Traces Interior
alt text alt text

click on the PNG render to access the SVG version on my remote repository for downloading !!!


This website was created using a template provided byĀ Mr. Anith GhalleyĀ and was used with his permission.*


Last update: July 10, 2024