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 | |
Add a symbol | A | Schematic Editor | Adds a new component to the schematic | |
Add a power symbol | P | Schematic Editor | Adds a power symbol to the schematic | |
Add a wire | W | Schematic Editor | Adds a wire for electrical connection | |
Add a no-connection flag | Q | Schematic Editor | Adds a no-connection flag to indicate no electrical connection | |
Add a junction | J | Schematic Editor | Adds a junction where two wires meet | |
Add a global label | CTRL+L | Schematic Editor | Adds a global label to identify a net or signal | |
Plot | N/A | Schematic Editor | Plots the schematic into a file format | |
Schematic setup | N/A | Schematic Editor | Sets up the schematic properties | |
Electrical Rules Checker | N/A | Schematic Editor | Checks the schematic for any electrical rule violations |
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.
Looks like weāre good
PCB Editing¶
This week, my signal lines were 0.4mm, my power lines were 0.8mm, and my edge cut was 0.6mm.
Mods CE¶
Mods CE stuff
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.
Soldering¶
Like usual, I created a āstickyā component list for the board, and then I started to solder.
Hereās how it came out !!!
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):
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.
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 |
---|---|
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.*