// #include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
// pin definitions
#define OLED_SCL 6
#define OLED_SDA 7
#define BUTTON_PIN 10
// initialize u8g2 for ssd1306 display
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// pepe logo
const unsigned char epd_bitmap_pepe_logo[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xf9, 0x18, 0x06, 0x0c, 0x00, 0xe4, 0x70, 0xb4, 0xd9,
0xe6, 0x70, 0x02, 0x00, 0xf2, 0xff, 0x12, 0x00, 0xf2, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// bitmap for slot machine frame
const unsigned char epd_bitmap_slot_machine[] PROGMEM = {
0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f,
0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f,
0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
0x0f, 0xc0, 0xff, 0xff, 0xff, 0x03, 0xfc, 0xff, 0xff, 0x3f, 0xc0, 0xff, 0xff, 0xff, 0x03, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x1f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf8,
0x3f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xfc,
0x7f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xfe,
0xff, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xff,
0xff, 0x41, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x82, 0xff,
0xff, 0x43, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0xc2, 0xff,
0xff, 0x41, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x82, 0xff,
0xff, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xff,
0x7f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xfe,
0x3f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xfc,
0x1f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf8,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0x40, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x02, 0xf0,
0x0f, 0xc0, 0xff, 0xff, 0xff, 0x03, 0xfc, 0xff, 0xff, 0x3f, 0xc0, 0xff, 0xff, 0xff, 0x03, 0xf0,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f,
0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f};
// bitmap for slot reel icon
const unsigned char epd_bitmap_icon_slot_reel[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xcf, 0x07, 0xc0, 0x30, 0x00, 0x60, 0x00, 0x00, 0x20, 0x87, 0x03, 0xa0, 0xcd,
0x06, 0x30, 0x87, 0x03, 0x10, 0x00, 0x00, 0x90, 0xff, 0x07, 0x90, 0x00, 0x00, 0x90, 0xff, 0x07,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// animation parameters
#define WAVE_AMPLITUDE 2
#define WAVE_SPEED 0.2
#define CHAR_SPACING 7
const char* TITLE = "GM MACHINE";
// spin animation parameters
#define SPIN_INITIAL_SPEED 40.0
#define SPIN_DECELERATION 0.95
#define MIN_SPIN_SPEED 2.0
#define ICON_SIZE 24
#define REEL_BORDER 1 // border thickness
#define REEL_TOP 25 // y coordinate where visible reel area starts
#define REEL_BOTTOM 52 // y coordinate where visible reel area ends
#define REEL_HEIGHT (REEL_BOTTOM - REEL_TOP)
#define LEFT_REEL_X 16 // x coordinate of left reel
#define MIDDLE_REEL_X 52 // x coordinate of middle reel
#define RIGHT_REEL_X 88 // x coordinate of right reel
#define VISIBLE_TOP (REEL_TOP + REEL_BORDER)
#define VISIBLE_BOTTOM (REEL_BOTTOM - REEL_BORDER)
// animation states
enum SpinState {
IDLE,
SPINNING,
STOPPING
};
float currentSpeed = 0;
float yOffset = 0;
SpinState spinState = IDLE;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
bool buttonState = HIGH;
bool lastButtonState = HIGH;
void setup() {
Wire.begin(OLED_SDA, OLED_SCL);
u8g2.begin();
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_7x14B_tr);
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
void drawWaveText(const char* text, uint8_t baseY, float time) {
uint8_t textWidth = u8g2.getStrWidth(text);
uint8_t startX = (128 - textWidth) / 2 + CHAR_SPACING;
for(uint8_t i = 0; text[i] != '\0'; i++) {
char currentChar[2] = {text[i], '\0'};
float offset = sin(time + i * 0.5) * WAVE_AMPLITUDE;
uint8_t charWidth = u8g2.getStrWidth(currentChar);
u8g2.drawStr(startX, baseY + offset, currentChar);
startX += charWidth;
}
}
void drawAnimatedIcon(float offset) {
// calculate base position
int baseY = REEL_TOP + (int)offset;
// draw icons for all three reels
for(int i = -1; i <= 1; i++) {
int y = baseY + (i * ICON_SIZE);
// wrap around if needed
while(y < REEL_TOP - ICON_SIZE) y += ICON_SIZE * 2;
while(y > REEL_BOTTOM) y -= ICON_SIZE * 2;
// only draw if the icon will be at least partially visible
if(y + ICON_SIZE >= REEL_TOP && y <= REEL_BOTTOM) {
// enable clipping for each reel
u8g2.setClipWindow(LEFT_REEL_X + REEL_BORDER, VISIBLE_TOP, LEFT_REEL_X + ICON_SIZE - REEL_BORDER, VISIBLE_BOTTOM);
u8g2.drawXBMP(LEFT_REEL_X, y, ICON_SIZE, ICON_SIZE, epd_bitmap_icon_slot_reel);
u8g2.setClipWindow(MIDDLE_REEL_X + REEL_BORDER, VISIBLE_TOP, MIDDLE_REEL_X + ICON_SIZE - REEL_BORDER, VISIBLE_BOTTOM);
u8g2.drawXBMP(MIDDLE_REEL_X, y, ICON_SIZE, ICON_SIZE, epd_bitmap_icon_slot_reel);
u8g2.setClipWindow(RIGHT_REEL_X + REEL_BORDER, VISIBLE_TOP, RIGHT_REEL_X + ICON_SIZE - REEL_BORDER, VISIBLE_BOTTOM);
u8g2.drawXBMP(RIGHT_REEL_X, y, ICON_SIZE, ICON_SIZE, epd_bitmap_icon_slot_reel);
}
}
// reset clipping window
u8g2.setMaxClipWindow();
}
void startSpin() {
if(spinState == IDLE) {
spinState = SPINNING;
currentSpeed = SPIN_INITIAL_SPEED;
yOffset = 0;
}
}
void updateSpin() {
if(spinState == SPINNING || spinState == STOPPING) {
// update position
yOffset += currentSpeed;
if(yOffset >= ICON_SIZE) {
yOffset -= ICON_SIZE;
}
// update speed
currentSpeed *= SPIN_DECELERATION;
// check if we should stop
if(spinState == STOPPING && currentSpeed < MIN_SPIN_SPEED) {
yOffset = round(yOffset / ICON_SIZE) * ICON_SIZE;
spinState = IDLE;
currentSpeed = 0;
}
}
}
void handleButton() {
int reading = digitalRead(BUTTON_PIN);
if(reading != lastButtonState) {
lastDebounceTime = millis();
}
if((millis() - lastDebounceTime) > debounceDelay) {
if(reading != buttonState) {
buttonState = reading;
if(buttonState == LOW) {
if(spinState == IDLE) {
startSpin();
} else if(spinState == SPINNING) {
spinState = STOPPING;
}
}
}
}
lastButtonState = reading;
}
void loop() {
static float animationTime = 0;
handleButton();
updateSpin();
u8g2.clearBuffer();
// draw pepe logo
u8g2.drawXBMP(13, 0, 16, 16, epd_bitmap_pepe_logo);
// draw animated title
drawWaveText(TITLE, 14, animationTime);
// draw slot machine frame
u8g2.drawXBMP(0, 16, 128, 48, epd_bitmap_slot_machine);
// draw spinning icons
drawAnimatedIcon(yOffset);
u8g2.sendBuffer();
animationTime += WAVE_SPEED;
if(animationTime >= 2 * PI) {
animationTime = 0;
}
delay(16);
}Loading
esp32-c3-devkitm-1
esp32-c3-devkitm-1
Loading
ssd1306
ssd1306