// simple project using Arduino UNO, LED buttons, 128x64px SSD1306 IIC OLED display
// created by upir, 2023
// youtube channel: https://www.youtube.com/upir_upir
// YOUTUBE VIDEO: https://youtu.be/F-xhrRoI7m0
// SOURCE files: https://github.com/upiir/arduino_led_buttons
// Links from the video:
// LED push buttons: https://s.click.aliexpress.com/e/_DlQ021v
// 128x64 SSD1306 OLED Display 1.54": https://s.click.aliexpress.com/e/_DCYdWXb
// 128x64 SSD1306 OLED Display 0.96": https://s.click.aliexpress.com/e/_DCKdvnh
// 128x64 SSD1306 OLED Display 2.42": https://s.click.aliexpress.com/e/_DFdMoTh
// Arduino UNO: https://s.click.aliexpress.com/e/_AXDw1h
// Arduino breadboard prototyping shield: https://s.click.aliexpress.com/e/_ApbCwx
// Image2cpp (convert array to image): https://javl.github.io/image2cpp/
// Photopea (online graphics editor like Photoshop): https://www.photopea.com/
// Arduino UNO + breadboard holder 3D printing file: https://github.com/LaskaKit/LaskaKit-Printed-Parts/tree/main/Arduino%20Uno%20Breadboard%20400%20Holder
// Related videos with Arduino UNO and 128x64 OLED screen:
// Arduino + OLED displays playlist: https://www.youtube.com/playlist?list=PLjQRaMdk7pBZ1UV3IL5ol8Qc7R9k-kwXA
#include <U8g2lib.h> // u8g2 library for drawing on the OLED display
#include <Wire.h> // library requires for IIC communication
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // initialization for the used OLED display, F = fullscreen buffer
// arrays below are drawn using Photopea and generated using the image2cpp website (links above)
// ' btn_down_pressed', 27x31px
const unsigned char epd_bitmap__btn_down_pressed [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x0f, 0x00,
0x40, 0x00, 0x10, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00,
0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00,
0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x7c, 0xff, 0xf7, 0x01, 0x02, 0xff, 0x07, 0x02,
0xfd, 0xff, 0xff, 0x05, 0xfd, 0xff, 0xff, 0x05, 0xfd, 0xff, 0xff, 0x05, 0xfa, 0xff, 0xff, 0x02,
0xf4, 0xff, 0x7f, 0x01, 0xe8, 0xff, 0xbf, 0x00, 0xd0, 0xff, 0x5f, 0x00, 0xa0, 0xff, 0x2f, 0x00,
0x40, 0xff, 0x17, 0x00, 0x80, 0xfe, 0x0b, 0x00, 0x00, 0xfd, 0x05, 0x00, 0x00, 0xfa, 0x02, 0x00,
0x00, 0x74, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00
};
// ' btn_down_unpressed', 27x31px
const unsigned char epd_bitmap__btn_down_unpressed [] PROGMEM = {
0x80, 0xff, 0x0f, 0x00, 0x40, 0x00, 0x10, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00,
0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00,
0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x7c, 0xff, 0xf7, 0x01,
0x02, 0xff, 0x07, 0x02, 0xfd, 0xff, 0xff, 0x05, 0xfd, 0xff, 0xff, 0x05, 0xfd, 0xff, 0xff, 0x05,
0xf9, 0xff, 0xff, 0x04, 0xf1, 0xff, 0x7f, 0x04, 0xe1, 0xff, 0x3f, 0x04, 0xc2, 0xff, 0x1f, 0x02,
0x84, 0xff, 0x0f, 0x01, 0x08, 0xff, 0x87, 0x00, 0x10, 0xfe, 0x43, 0x00, 0x20, 0xfc, 0x21, 0x00,
0x40, 0xf8, 0x10, 0x00, 0x80, 0x70, 0x08, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x02, 0x02, 0x00,
0x00, 0x04, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00
};
// ' btn_left_pressed ', 31x26px
const unsigned char epd_bitmap__btn_left_pressed [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00,
0x00, 0x84, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00, 0x80, 0xbe, 0x00, 0x00,
0x40, 0xbf, 0xff, 0x3f, 0xa0, 0x3f, 0x00, 0x40, 0xd0, 0xff, 0xff, 0x5f, 0xe8, 0xff, 0xff, 0x5f,
0xf4, 0xff, 0xff, 0x5f, 0xfa, 0xff, 0xff, 0x5f, 0xfa, 0xff, 0xff, 0x5f, 0xfa, 0xff, 0xff, 0x5f,
0xf4, 0xff, 0xff, 0x5f, 0xe8, 0xff, 0xff, 0x5f, 0xd0, 0xff, 0xff, 0x5f, 0xa0, 0x3f, 0x00, 0x40,
0x40, 0xbf, 0xff, 0x3f, 0x80, 0xbe, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00,
0x00, 0x84, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00
};
// ' btn_left_unpressed', 31x26px
const unsigned char epd_bitmap__btn_left_unpressed [] PROGMEM = {
0x00, 0x78, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00,
0x80, 0xbe, 0x00, 0x00, 0x40, 0xbf, 0xff, 0x3f, 0xa0, 0x3f, 0x00, 0x40, 0xd0, 0xff, 0xff, 0x5f,
0xe8, 0xff, 0xff, 0x5f, 0xf4, 0xff, 0xff, 0x5f, 0xfa, 0xff, 0xff, 0x5f, 0xf9, 0xff, 0xff, 0x5f,
0xf9, 0xff, 0xff, 0x5f, 0xf1, 0xff, 0xff, 0x5f, 0xe1, 0xff, 0xff, 0x5f, 0xc2, 0xff, 0xff, 0x5f,
0x84, 0x3f, 0x00, 0x40, 0x08, 0x3f, 0x00, 0x40, 0x10, 0x3e, 0x00, 0x40, 0x20, 0x3c, 0x00, 0x40,
0x40, 0xb8, 0xff, 0x3f, 0x80, 0x80, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00,
0x00, 0x84, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00
};
// ' btn_right_pressed', 31x26px
const unsigned char epd_bitmap__btn_right_pressed [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
0x00, 0x80, 0x10, 0x00, 0x00, 0x80, 0x2e, 0x00, 0x00, 0x80, 0x5e, 0x00, 0x00, 0x80, 0xbe, 0x00,
0xfe, 0xff, 0x7e, 0x01, 0x01, 0x00, 0xfe, 0x02, 0xfd, 0xff, 0xff, 0x05, 0xfd, 0xff, 0xff, 0x0b,
0xfd, 0xff, 0xff, 0x17, 0xfd, 0xff, 0xff, 0x2f, 0xfd, 0xff, 0xff, 0x2f, 0xfd, 0xff, 0xff, 0x2f,
0xfd, 0xff, 0xff, 0x17, 0xfd, 0xff, 0xff, 0x0b, 0xfd, 0xff, 0xff, 0x05, 0x01, 0x00, 0xfe, 0x02,
0xfe, 0xff, 0x7e, 0x01, 0x00, 0x80, 0xbe, 0x00, 0x00, 0x80, 0x5e, 0x00, 0x00, 0x80, 0x2e, 0x00,
0x00, 0x80, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00
};
// ' btn_right_unpressed', 31x26px
const unsigned char epd_bitmap__btn_right_unpressed [] PROGMEM = {
0x00, 0x00, 0x0f, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00, 0x80, 0x2e, 0x00, 0x00, 0x80, 0x5e, 0x00,
0x00, 0x80, 0xbe, 0x00, 0xfe, 0xff, 0x7e, 0x01, 0x01, 0x00, 0xfe, 0x02, 0xfd, 0xff, 0xff, 0x05,
0xfd, 0xff, 0xff, 0x0b, 0xfd, 0xff, 0xff, 0x17, 0xfd, 0xff, 0xff, 0x2f, 0xfd, 0xff, 0xff, 0x4f,
0xfd, 0xff, 0xff, 0x4f, 0xfd, 0xff, 0xff, 0x47, 0xfd, 0xff, 0xff, 0x43, 0xfd, 0xff, 0xff, 0x21,
0x01, 0x00, 0xfe, 0x10, 0x01, 0x00, 0x7e, 0x08, 0x01, 0x00, 0x3e, 0x04, 0x01, 0x00, 0x1e, 0x02,
0xfe, 0xff, 0x0e, 0x01, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x80, 0x20, 0x00,
0x00, 0x80, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00
};
// ' btn_up_pressed', 27x31px
const unsigned char epd_bitmap__btn_up_pressed [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00,
0x00, 0x88, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0xfa, 0x02, 0x00, 0x00, 0xfd, 0x05, 0x00,
0x80, 0xfe, 0x0b, 0x00, 0x40, 0xff, 0x17, 0x00, 0xa0, 0xff, 0x2f, 0x00, 0xd0, 0xff, 0x5f, 0x00,
0xe8, 0xff, 0xbf, 0x00, 0xf4, 0xff, 0x7f, 0x01, 0xfa, 0xff, 0xff, 0x02, 0xfd, 0xff, 0xff, 0x05,
0xfd, 0xff, 0xff, 0x05, 0xfd, 0xff, 0xff, 0x05, 0x02, 0xff, 0x07, 0x02, 0x7c, 0xff, 0xf7, 0x01,
0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00,
0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00,
0x40, 0xff, 0x17, 0x00, 0x40, 0x00, 0x10, 0x00, 0x80, 0xff, 0x0f, 0x00
};
// ' btn_up_unpressed', 27x31px
const unsigned char epd_bitmap__btn_up_unpressed [] PROGMEM = {
0x00, 0x70, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0xfa, 0x02, 0x00,
0x00, 0xfd, 0x05, 0x00, 0x80, 0xfe, 0x0b, 0x00, 0x40, 0xff, 0x17, 0x00, 0xa0, 0xff, 0x2f, 0x00,
0xd0, 0xff, 0x5f, 0x00, 0xe8, 0xff, 0xbf, 0x00, 0xf4, 0xff, 0x7f, 0x01, 0xfa, 0xff, 0xff, 0x02,
0xfd, 0xff, 0xff, 0x05, 0xfd, 0xff, 0xff, 0x05, 0xfd, 0xff, 0xff, 0x05, 0x01, 0xff, 0x07, 0x04,
0x01, 0xff, 0x07, 0x04, 0x01, 0xff, 0x07, 0x04, 0x02, 0xff, 0x07, 0x02, 0x7c, 0xff, 0xf7, 0x01,
0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00,
0x40, 0xff, 0x17, 0x00, 0x40, 0xff, 0x17, 0x00, 0x40, 0x00, 0x10, 0x00, 0x40, 0x00, 0x10, 0x00,
0x40, 0x00, 0x10, 0x00, 0x40, 0x00, 0x10, 0x00, 0x80, 0xff, 0x0f, 0x00
};
// 'led_off', 11x20px
const unsigned char epd_bitmap_led_off [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x88, 0x00, 0x04, 0x01, 0x64, 0x01, 0x64, 0x01, 0x54, 0x01,
0x54, 0x01, 0x04, 0x01, 0xfc, 0x01, 0x02, 0x02, 0x02, 0x02, 0xac, 0x01, 0xa8, 0x00, 0xa8, 0x00,
0xa8, 0x00, 0x68, 0x00, 0x28, 0x00, 0x10, 0x00
};
// 'led_on', 11x20px
const unsigned char epd_bitmap_led_on [] PROGMEM = {
0x00, 0x00, 0x50, 0x00, 0x04, 0x01, 0x70, 0x00, 0xfa, 0x02, 0x98, 0x00, 0x9a, 0x02, 0xa8, 0x00,
0xaa, 0x02, 0xf8, 0x00, 0x00, 0x00, 0xfc, 0x01, 0xfc, 0x01, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00,
0x50, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00
};
// 'line_btn_down', 41x9px
const unsigned char epd_bitmap_line_btn_down [] PROGMEM = {
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x55, 0x55, 0x55, 0x55, 0x01
};
// 'line_btn_right', 12x6px
const unsigned char epd_bitmap_line_btn_right [] PROGMEM = {
0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0xa0, 0x0a
};
// 'upir_logo', 13x4px
const unsigned char epd_bitmap_upir_logo [] PROGMEM = {
0x75, 0x1d, 0x55, 0x14, 0x35, 0x0d, 0x13, 0x15
};
// 'line_btn_left', 18x6px
const unsigned char epd_bitmap_line_btn_left [] PROGMEM = {
0x55, 0x15, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x02
};
// 'line_btn_up', 38x6px
const unsigned char epd_bitmap_line_btn_up [] PROGMEM = {
0x55, 0x55, 0x55, 0x55, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x20
};
// end of images generated by image2cpp
#define BUTTON_DOWN_PIN 3 // pin for the button DOWN
#define BUTTON_DOWN_LED 2 // pin for the LED for the button DOWN
#define BUTTON_LEFT_PIN 6 // pin for the button LEFT
#define BUTTON_LEFT_LED 5 // pin for the LED for the button LEFT
#define BUTTON_UP_PIN 9 // pin for the button UP
#define BUTTON_UP_LED 8 // pin for the LED for the button UP
#define BUTTON_RIGHT_PIN 12 // pin for the button RIGHT
#define BUTTON_RIGHT_LED 11 // pin for the LED for the button RIGHT
int button_down_state = HIGH; // current state of the button DOWN
int button_down_state_prev = HIGH; // previous state of the button DOWN
int button_down_led_state = LOW; // current state of the LED for button DOWN
int button_left_state = HIGH; // current state of the button LEFT
int button_left_state_prev = HIGH; // previous state of the button LEFT
int button_left_led_state = LOW; // current state of the LED for button LEFT
int button_up_state = HIGH; // current state of the button UP
int button_up_state_prev = HIGH; // previous state of the button UP
int button_up_led_state = LOW; // current state of the LED for button UP
int button_right_state = HIGH; // current state of the button RIGHT
int button_right_state_prev = HIGH; // previous state of the button RIGHT
int button_right_led_state = LOW; // current state of the LED for button RIGHT
void setup() {
pinMode(BUTTON_DOWN_PIN, INPUT_PULLUP); // set pinmode for button pin to input
pinMode(BUTTON_DOWN_LED, OUTPUT); // set pinmode for LED pin to output
pinMode(BUTTON_LEFT_PIN, INPUT_PULLUP); // set pinmode for button pin to input
pinMode(BUTTON_LEFT_LED, OUTPUT); // set pinmode for LED pin to output
pinMode(BUTTON_UP_PIN, INPUT_PULLUP); // set pinmode for button pin to input
pinMode(BUTTON_UP_LED, OUTPUT); // set pinmode for LED pin to output
pinMode(BUTTON_RIGHT_PIN, INPUT_PULLUP); // set pinmode for button pin to input
pinMode(BUTTON_RIGHT_LED, OUTPUT); // set pinmode for LED pin to output
u8g2.begin(); // start the u8g2 library
}
void loop() {
// get pressed states of the individual buttons
button_down_state = digitalRead(BUTTON_DOWN_PIN); // get the value of the button, HIGH = unpressed, LOW = pressed
button_left_state = digitalRead(BUTTON_LEFT_PIN); // get the value of the button, HIGH = unpressed, LOW = pressed
button_up_state = digitalRead(BUTTON_UP_PIN); // get the value of the button, HIGH = unpressed, LOW = pressed
button_right_state = digitalRead(BUTTON_RIGHT_PIN); // get the value of the button, HIGH = unpressed, LOW = pressed
// toggle LEDs upon pressing and releasing buttons
// DOWN button
if (button_down_state_prev == LOW && button_down_state == HIGH) {
button_down_led_state = !button_down_led_state;
digitalWrite(BUTTON_DOWN_LED, button_down_led_state); // set the LED value based on the button state
}
// LEFT button
if (button_left_state_prev == LOW && button_left_state == HIGH) {
button_left_led_state = !button_left_led_state;
digitalWrite(BUTTON_LEFT_LED, button_left_led_state); // set the LED value based on the button state
}
// UP button
if (button_up_state_prev == LOW && button_up_state == HIGH) {
button_up_led_state = !button_up_led_state;
digitalWrite(BUTTON_UP_LED, button_up_led_state); // set the LED value based on the button state
}
// RIGHT button
if (button_right_state_prev == LOW && button_right_state == HIGH) {
button_right_led_state = !button_right_led_state;
digitalWrite(BUTTON_RIGHT_LED, button_right_led_state); // set the LED value based on the button state
}
// save current state as previous state
button_down_state_prev = button_down_state;
button_left_state_prev = button_left_state;
button_up_state_prev = button_up_state;
button_right_state_prev = button_right_state;
// drawing images to display using the u8g2 library
u8g2.clearBuffer(); // clear the internal memory
u8g2.setBitmapMode(1); // draw transparent images
// draw pressed or unpressed state of the arrow based on the button state (pressed vs. unpressed)
if (button_down_state == HIGH) {
u8g2.drawXBMP(51, 33, 27, 31, epd_bitmap__btn_down_unpressed); //unpressed state
} else {
u8g2.drawXBMP(51, 33, 27, 31, epd_bitmap__btn_down_pressed); // pressed state
}
if (button_left_state == HIGH) {
u8g2.drawXBMP(24, 18, 31, 26, epd_bitmap__btn_left_unpressed); //unpressed state
} else {
u8g2.drawXBMP(24, 18, 31, 26, epd_bitmap__btn_left_pressed); // pressed state
}
if (button_up_state == HIGH) {
u8g2.drawXBMP(51, 0, 27, 31, epd_bitmap__btn_up_unpressed); //unpressed state
} else {
u8g2.drawXBMP(51, 0, 27, 31, epd_bitmap__btn_up_pressed); // pressed state
}
if (button_right_state == HIGH) {
u8g2.drawXBMP(74, 18, 31, 26, epd_bitmap__btn_right_unpressed); //unpressed state
} else {
u8g2.drawXBMP(74, 18, 31, 26, epd_bitmap__btn_right_pressed); // pressed state
}
// LEDs images based on the _led_state variables, using ternary operator to make the code shorter (instead of using if statements)
u8g2.drawXBMP( 2, 37, 11, 20, button_down_led_state ? epd_bitmap_led_on : epd_bitmap_led_off); // down
u8g2.drawXBMP( 2, 6, 11, 20, button_left_led_state ? epd_bitmap_led_on : epd_bitmap_led_off); // left
u8g2.drawXBMP(115, 6, 11, 20, button_up_led_state ? epd_bitmap_led_on : epd_bitmap_led_off); // up
u8g2.drawXBMP(115, 37, 11, 20, button_right_led_state ? epd_bitmap_led_on : epd_bitmap_led_off); // right
// connecting dotted lines between the LEDs and arrows
u8g2.drawXBMP( 13, 49, 41, 9, epd_bitmap_line_btn_down); // down
u8g2.drawXBMP( 13, 15, 18, 6, epd_bitmap_line_btn_left); // left
u8g2.drawXBMP(75, 5, 38, 6, epd_bitmap_line_btn_up); // up
u8g2.drawXBMP(102, 40, 12, 6, epd_bitmap_line_btn_right); // right
u8g2.sendBuffer(); // transfer internal memory to the display
// delay(50); // delay is no longer needed since drawing on the display takes some time
}