🎂 ESP32 + OLED +无源蜂鸣器的生日快乐歌音乐显示系统(iCEasy商城周年特别版)
分享作者:XIUYUAN
作者昵称:XIUYUAN
评测品牌:iCEasy
评测型号:0.96寸黄蓝双色OLED模块/4P
发布时间:2025-10-14 15:55:57
2
视频链接
【ESP32 + OLED + 无源蜂鸣器唱生日快乐歌(iCEasy商城6周年版)-哔哩哔哩】 https://b23.tv/sRrqrHB
前言
本项目使用 ESP32 微控制器配合 OLED 显示屏和无源蜂鸣器,打造了一个具有*8种视觉特效*和*生日快乐歌伴奏*的酷炫显示系统。开机时会播放完整的生日快乐歌,同时配合视觉动画,非常适合用作生日礼物或创意展示项目。
开源口碑分享内容

📋 项目简介

       本项目使用 ESP32 微控制器配合 OLED 显示屏和无源蜂鸣器,打造了一个具有*8种视觉特效*和*生日快乐歌伴奏*的酷炫显示系统。开机时会播放完整的生日快乐歌,同时配合视觉动画,非常适合用作生日礼物或创意展示项目。


🔧 硬件清单

1. ESP32 开发板

       功能: 主控芯片,负责运算和控制  

参数:

       双核 240MHz 处理器

       内置 Wi-Fi 和蓝牙

       GPIO 丰富,支持 I2C/SPI 等多种通信协议

       工作电压:3.3V

  • ESP32开发板实物图片

2. OLED 显示屏(SSD1306)

       功能: 图形显示输出  

参数:

       分辨率:128×64 像素

       通信方式:I2C

       驱动芯片:SSD1306

       工作电压:3.3V-5V

       屏幕尺寸:0.96英寸

  • OLED显示屏实物图片

3. 无源蜂鸣器模块(高电平触发)

       功能: 播放音乐旋律  

参数:

       类型:无源蜂鸣器(Passive Buzzer)

        触发方式:高电平触发

       工作电压:3.3V-5V

       频率范围:100Hz - 10kHz

       可播放不同音调(用于演奏旋律)

  • 无源蜂鸣器实物图片

       ⚠️ 重要提醒:必须使用*无源蜂鸣器*!有源蜂鸣器只能发出单一"嘀嘀"声,无法播放生日快乐歌。

*识别方法*:

       ✅ 无源蜂鸣器:背面是绿色 PCB 电路板

       ❌ 有源蜂鸣器:背面是黑色圆盘贴片

4. 其他配件

        杜邦线若干(母对母/公对母)

        USB 数据线(Micro-USB 或 Type-C,根据 ESP32 型号)

        面包板(可选,方便接线)


🔌 硬件连接

接线图

        ESP32          →    OLED 显示屏
        GPIO6 (SDA)    →    SDA
        GPIO7 (SCL)    →    SCL
        3.3V           →    VCC
        GND            →    GND
        ESP32          →    无源蜂鸣器模块
        GPIO5          →    I/O (信号输入)
        3.3V/5V        →    VCC
        GND            →    GND

接线示意图

    ┌──────────────┐

    │   ESP32      │

    │              │

    │  GPIO6 ──────┼──→ OLED SDA

    │  GPIO7 ──────┼──→ OLED SCL

    │  GPIO5 ──────┼──→ 蜂鸣器 I/O

    │              │

    │  3.3V  ──────┼──→ OLED VCC + 蜂鸣器 VCC

    │  GND   ──────┼──→ OLED GND + 蜂鸣器 GND

    └──────────────┘


💻 软件环境配置

1. Arduino IDE 设置

       1. 安装 Arduino IDE(版本 1.8.x 或 2.x)

        ARDUINO IDE下载地址  https://www.arduino.cc/en/software/

2. 添加 ESP32 支持包:

  - 文件 → 首选项 → 附加开发板管理器网址

  - 添加:https://dl.espressif.com/dl/package_esp32_index.json

(备用地址:ESP32 Arduino 开发板管理器地址(国内镜像): https://jihulab.com/esp-mirror/espressif/arduino-esp32/-/raw/gh-pages/package_esp32_index_cn.json)

  - 工具 → 开发板管理器 → 搜索"ESP32" → 安装

3. 安装依赖库:

  - 工具 → 管理库 → 搜索并安装:

    - `Adafruit GFX Library`

    - `Adafruit SSD1306`

![Arduino IDE库管理器]


🎨 位图取模说明

       本项目使用了自定义位图显示 "iEasy商城6周年快乐!",这是通过*字模提取软件*生成的点阵数据。

       这个项目中我用的是第一个的图形模式尽心绘画的!!!!

       AI设计并自己修改了一些

完整取模数据(C语言数组格式)

将以下数据添加到代码中,用于显示自定义文字:

// "iEasy商城6周年快乐!" 字模数据 (16x16点阵)//这个在实际中应用可能出现些许问题,但下面的那个没有任何问题!!!!!!!!
const unsigned char customText[][32] PROGMEM = {
  // i (0)
  {0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x70, 
   0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00},
  
  // C (1)
  {0x00, 0x00, 0x00, 0x3E, 0x42, 0x42, 0x80, 0x80, 
   0x80, 0x80, 0x80, 0x42, 0x44, 0x38, 0x00, 0x00},
  
  // E (2)
  {0x00, 0x00, 0x00, 0xFC, 0x42, 0x48, 0x48, 0x78, 
   0x48, 0x48, 0x40, 0x42, 0x42, 0xFC, 0x00, 0x00},
  
  // a (3)
  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 
   0x42, 0x1E, 0x22, 0x42, 0x42, 0x3F, 0x00, 0x00},
  
  // s (4)
  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 
   0x42, 0x40, 0x3C, 0x02, 0x42, 0x7C, 0x00, 0x00},
  
  // y (5)
  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 
   0x42, 0x24, 0x24, 0x28, 0x18, 0x10, 0x10, 0xE0},
  
  // 商 (6)
  {0x00, 0x00, 0x00, 0x01, 0x00, 0x7F, 0xFE, 0x08, 
   0x10, 0x04, 0x20, 0x1F, 0xF8, 0x60, 0x06, 0x44,
   0x22, 0x58, 0x1A, 0x47, 0xF2, 0x48, 0x12, 0x48, 
   0x12, 0x48, 0x12, 0x4F, 0xF2, 0x40, 0x1A, 0x00},
  
  // 城 (7)
  {0x00, 0x00, 0x00, 0x20, 0x20, 0x24, 0x23, 0xFC, 
   0x24, 0x22, 0x24, 0x20, 0x7C, 0x24, 0x27, 0xE4,
   0x24, 0x54, 0x24, 0x58, 0x24, 0x58, 0x2C, 0x58, 
   0x34, 0x9A, 0x65, 0xAA, 0x04, 0x4A, 0x08, 0x86},
  
  // 6 (8)
  {0x00, 0x00, 0x00, 0x1C, 0x24, 0x40, 0x40, 0x58, 
   0x64, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00},
  
  // 周 (9)
  {0x00, 0x00, 0x00, 0x00, 0x3F, 0xFC, 0x41, 0x04, 
   0x4F, 0xFC, 0x41, 0x04, 0x41, 0x04, 0x5F, 0xFC,
   0x40, 0x04, 0x4F, 0xF4, 0x48, 0x14, 0x48, 0x14, 
   0x48, 0x14, 0x4F, 0xF4, 0x40, 0x04, 0x80, 0x3C},
  
  // 年 (10)
  {0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0F, 0xFE, 
   0x10, 0x80, 0x20, 0x80, 0x40, 0x80, 0x0F, 0xFE,
   0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x7F, 0xFE, 
   0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80},
  
  // 快 (11)
  {0x00, 0x00, 0x10, 0x40, 0x10, 0x40, 0x10, 0x40, 
   0x5B, 0xFC, 0x58, 0x44, 0x54, 0x44, 0x94, 0x44,
   0x90, 0x44, 0x97, 0xFE, 0x10, 0x60, 0x10, 0x90, 
   0x10, 0x90, 0x11, 0x08, 0x12, 0x04, 0x14, 0x02},
  
  // 乐 (12)
  {0x00, 0x00, 0x00, 0x00, 0x03, 0xFC, 0x1C, 0x00, 
   0x20, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x80,
   0x20, 0x80, 0x1F, 0xFC, 0x08, 0x90, 0x10, 0x88, 
   0x10, 0x88, 0x20, 0x84, 0x40, 0x84, 0x47, 0x02},
  
  // ! (13)
  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 
   0x0C, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00,
   0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 
   0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00}
};
  • 上述OLED图片显示的字模!!!
// 您的位图数据 //这个是上面的那个0.96OLED(IIC)上显示的图片的字模!!!!!!
//!!!!!!
//!!!!!!!

const unsigned char bitmap[] PROGMEM = {
  0x3C, 0x00, 0x03, 0xF0, 0x00, 0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xE0, 0x00, 0x0E, 0x1E, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0xC0,
  0x80, 0x00, 0xF8, 0x03, 0xC0, 0x08, 0x08, 0x10, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0xC0,
  0x00, 0x03, 0x80, 0x30, 0x70, 0x08, 0x08, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xC0,
  0x00, 0x1E, 0x00, 0x60, 0x11, 0x08, 0x18, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0xF0,
  0x00, 0xF0, 0x00, 0xC0, 0x10, 0x08, 0x60, 0x10, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0,
  0x0F, 0x80, 0x00, 0x80, 0x10, 0x09, 0x80, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0,
  0x38, 0x00, 0xF8, 0x80, 0x10, 0x0E, 0x00, 0x70, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0xC0,
  0x60, 0x01, 0x88, 0x7E, 0x10, 0x08, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0x80,
  0x00, 0x03, 0x08, 0x03, 0x10, 0x00, 0x0F, 0x40, 0x00, 0x00, 0x00, 0x1C, 0x01, 0xC0, 0x10, 0x00,
  0x00, 0x42, 0x08, 0x01, 0x10, 0x00, 0xF8, 0x40, 0x00, 0x00, 0x00, 0x38, 0x00, 0xE0, 0x10, 0x00,
  0x00, 0xC2, 0x18, 0x23, 0x10, 0x03, 0x88, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x10, 0x00,
  0x00, 0x82, 0x71, 0x1E, 0x10, 0x1F, 0x08, 0x40, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x38, 0x30, 0x00,
  0x00, 0x83, 0xC3, 0x00, 0x10, 0x71, 0x09, 0xC0, 0x02, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00,
  0x21, 0x81, 0x86, 0x00, 0x13, 0xC1, 0x0F, 0xC0, 0x07, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00,
  0x31, 0x00, 0xFC, 0x00, 0x1E, 0x41, 0xFE, 0x40, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x78, 0x43, 0xF8, 0x40, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x00, 0x00,
  0x09, 0x81, 0x00, 0x00, 0xF0, 0x47, 0x08, 0x40, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x0E, 0x00,
  0x08, 0x83, 0x00, 0x03, 0x90, 0x7F, 0x08, 0x40, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x11, 0x00,
  0x0C, 0xC6, 0x00, 0x1F, 0x10, 0xF1, 0x08, 0x40, 0x00, 0x00, 0x03, 0x00, 0x03, 0x80, 0x24, 0x80,
  0x06, 0x7C, 0x00, 0x7A, 0x13, 0xC1, 0x08, 0x40, 0x00, 0x00, 0x43, 0x00, 0x01, 0x00, 0x2C, 0x80,
  0x02, 0x00, 0x1F, 0xC6, 0x1E, 0x41, 0x08, 0x40, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x28, 0x80,
  0x03, 0x00, 0x3C, 0x8A, 0x70, 0x41, 0x09, 0xE0, 0x10, 0x00, 0x23, 0x00, 0x00, 0x00, 0x20, 0x80,
  0x00, 0x01, 0xE1, 0x57, 0xD0, 0x41, 0x0F, 0x80, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x80,
  0x00, 0x0F, 0x22, 0x25, 0x10, 0x41, 0x3C, 0x00, 0x28, 0x00, 0x03, 0x1F, 0xFF, 0x00, 0x11, 0x00,
  0x00, 0xF8, 0x54, 0x3E, 0x10, 0x43, 0xE0, 0x00, 0x00, 0x00, 0x03, 0x3F, 0xFF, 0x83, 0x0A, 0x00,
  0x07, 0x88, 0x88, 0xE4, 0x10, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x03, 0x70, 0x01, 0xC3, 0x04, 0x00,
  0x3C, 0x15, 0x07, 0x04, 0x10, 0x5B, 0x00, 0x80, 0x00, 0x00, 0x03, 0xE0, 0x00, 0xE0, 0x0E, 0x00,
  0xFA, 0x22, 0x1E, 0x04, 0x10, 0xF1, 0x80, 0x80, 0x00, 0x08, 0x03, 0xC0, 0x00, 0x70, 0x04, 0x08,
  0x89, 0x40, 0xF2, 0x04, 0x17, 0xB0, 0xC1, 0x40, 0x00, 0x0C, 0x03, 0x86, 0x0C, 0x38, 0x04, 0x18,
  0x08, 0x8F, 0x83, 0x04, 0x3C, 0x18, 0x60, 0x00, 0x00, 0x08, 0x03, 0x0F, 0x1E, 0x18, 0x04, 0x10,
  0x08, 0x78, 0x83, 0x04, 0xE6, 0x0C, 0x30, 0x00, 0x00, 0x00, 0x03, 0x19, 0xB3, 0x18, 0x06, 0x00,
  0x0F, 0xE0, 0xC2, 0x07, 0x83, 0x06, 0x18, 0x00, 0x00, 0x00, 0x03, 0x18, 0xE3, 0x18, 0x02, 0x00,
  0x0C, 0x21, 0xC3, 0x7C, 0x01, 0x83, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x18, 0x43, 0x18, 0x02, 0x00,
  0x18, 0x21, 0x87, 0xC0, 0x00, 0xC1, 0x86, 0x00, 0x00, 0x00, 0x03, 0x18, 0x0B, 0x18, 0x01, 0x00,
  0x10, 0x60, 0x8F, 0x80, 0x10, 0x60, 0xC3, 0x00, 0x01, 0x00, 0x03, 0x18, 0x1B, 0x18, 0x01, 0x00,
  0x10, 0x60, 0xFB, 0x80, 0x10, 0x30, 0x61, 0x80, 0x01, 0x00, 0x03, 0x18, 0x13, 0x18, 0x01, 0x00,
  0x50, 0x21, 0xC1, 0xC0, 0x28, 0x18, 0x30, 0xC0, 0x02, 0x80, 0x03, 0x18, 0x03, 0x18, 0x01, 0x00,
  0x70, 0x3E, 0x01, 0xC0, 0x00, 0x0C, 0x18, 0x71, 0xC4, 0x40, 0x03, 0x0C, 0x06, 0x18, 0x80, 0x00,
  0x70, 0xF0, 0x00, 0xE0, 0x00, 0x06, 0x0C, 0x3B, 0x98, 0x30, 0x03, 0x06, 0x0C, 0x19, 0x81, 0x00,
  0x77, 0x38, 0x00, 0xF0, 0x00, 0x03, 0x06, 0x1E, 0x04, 0x40, 0x23, 0x03, 0x18, 0x18, 0x80, 0x00,
  0x3C, 0x38, 0x03, 0xC0, 0x00, 0x41, 0x83, 0x78, 0x02, 0x80, 0x23, 0x81, 0xB0, 0x38, 0x01, 0x00,
  0xF0, 0x1C, 0x0E, 0x00, 0x40, 0x40, 0xC1, 0xF0, 0x01, 0x00, 0x21, 0xC0, 0xE0, 0x70, 0x14, 0x60,
  0x80, 0x0C, 0x7C, 0x00, 0x40, 0xA0, 0x67, 0xC0, 0x01, 0x00, 0x60, 0xE0, 0x40, 0xE0, 0x01, 0x00,
  0x00, 0x0F, 0xE0, 0x00, 0xA0, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x70, 0x01, 0xC0, 0x00, 0x00,
  0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0x88, 0x02, 0x80,
  0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x0C, 0x08, 0x40,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x03, 0x80, 0xFE, 0x10, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80,
  0x0C, 0x0F, 0x1F, 0x87, 0xE0, 0xFE, 0x18, 0x31, 0x00, 0xF0, 0x3F, 0x8F, 0xE2, 0x20, 0x03, 0x04,
  0x0C, 0x1D, 0x1F, 0x8C, 0x60, 0xC6, 0x0C, 0x23, 0x81, 0x08, 0x24, 0x91, 0x02, 0x20, 0x1C, 0x04,
  0x00, 0x30, 0x18, 0x08, 0x30, 0xC0, 0x04, 0x65, 0x42, 0x00, 0x2E, 0xA1, 0x02, 0xFC, 0x20, 0x04,
  0x0C, 0x30, 0x18, 0x00, 0x30, 0xC0, 0x06, 0xCD, 0x62, 0x00, 0x24, 0x8F, 0xC3, 0x24, 0x22, 0x04,
  0x0C, 0x60, 0x18, 0x00, 0x30, 0xFC, 0x03, 0x95, 0x52, 0x00, 0x2E, 0x89, 0x02, 0xA8, 0x22, 0x04,
  0x0C, 0x60, 0x1F, 0x03, 0xF0, 0x7E, 0x01, 0x9F, 0xF2, 0xF0, 0x20, 0x89, 0x02, 0x30, 0x22, 0x04,
  0x0C, 0x60, 0x1F, 0x06, 0x60, 0x06, 0x03, 0x08, 0x23, 0x08, 0x24, 0xBF, 0xE3, 0xFF, 0x1F, 0xC4,
  0x0C, 0x70, 0x18, 0x0C, 0x30, 0x06, 0x06, 0x0F, 0x22, 0x08, 0x2A, 0x81, 0x0A, 0x20, 0x02, 0x00,
  0x0C, 0x30, 0x18, 0x0C, 0x30, 0xC6, 0x0C, 0x09, 0x22, 0x08, 0x4A, 0x81, 0x12, 0x20, 0x0A, 0x84,
  0x0C, 0x1D, 0x1F, 0x8E, 0x70, 0xFE, 0x18, 0x0D, 0x22, 0x08, 0x44, 0x81, 0x02, 0x50, 0x12, 0x40,
  0x0C, 0x0F, 0x1F, 0x87, 0xEC, 0xFE, 0x30, 0x09, 0x21, 0x10, 0x80, 0x81, 0x02, 0x48, 0x22, 0x30,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x1F, 0xF0, 0xE1, 0x01, 0x81, 0x02, 0x87, 0x06, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
  • 完整Arduin代码!!!
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
 
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
#define SCREEN_ADDRESS 0x3C
#define BUZZER_PIN 5  // �� 改成你的蜂鸣器引脚
 
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
 
// ============ 音符频率定义 ============
#define NOTE_C4  262
#define NOTE_D4  294
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_G4  392
#define NOTE_A4  440
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_D5  587
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_G5  784
 
// 生日快乐歌旋律
int birthdayMelody[] = {
  NOTE_G4, NOTE_G4, NOTE_A4, NOTE_G4, NOTE_C5, NOTE_B4,
  NOTE_G4, NOTE_G4, NOTE_A4, NOTE_G4, NOTE_D5, NOTE_C5,
  NOTE_G4, NOTE_G4, NOTE_G5, NOTE_E5, NOTE_C5, NOTE_B4, NOTE_A4,
  NOTE_F5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_D5, NOTE_C5
};
 
int birthdayDurations[] = {
  4, 4, 4, 4, 4, 2,
  4, 4, 4, 4, 4, 2,
  4, 4, 4, 4, 4, 4, 2,
  4, 4, 4, 4, 4, 2
};
 
// 位图数据
const unsigned char bitmap[] PROGMEM = {
  0x3C, 0x00, 0x03, 0xF0, 0x00, 0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xE0, 0x00, 0x0E, 0x1E, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0xC0,
  0x80, 0x00, 0xF8, 0x03, 0xC0, 0x08, 0x08, 0x10, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0xC0,
  0x00, 0x03, 0x80, 0x30, 0x70, 0x08, 0x08, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xC0,
  0x00, 0x1E, 0x00, 0x60, 0x11, 0x08, 0x18, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0xF0,
  0x00, 0xF0, 0x00, 0xC0, 0x10, 0x08, 0x60, 0x10, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0,
  0x0F, 0x80, 0x00, 0x80, 0x10, 0x09, 0x80, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0,
  0x38, 0x00, 0xF8, 0x80, 0x10, 0x0E, 0x00, 0x70, 0x00, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0xC0,
  0x60, 0x01, 0x88, 0x7E, 0x10, 0x08, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0x80,
  0x00, 0x03, 0x08, 0x03, 0x10, 0x00, 0x0F, 0x40, 0x00, 0x00, 0x00, 0x1C, 0x01, 0xC0, 0x10, 0x00,
  0x00, 0x42, 0x08, 0x01, 0x10, 0x00, 0xF8, 0x40, 0x00, 0x00, 0x00, 0x38, 0x00, 0xE0, 0x10, 0x00,
  0x00, 0xC2, 0x18, 0x23, 0x10, 0x03, 0x88, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x10, 0x00,
  0x00, 0x82, 0x71, 0x1E, 0x10, 0x1F, 0x08, 0x40, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x38, 0x30, 0x00,
  0x00, 0x83, 0xC3, 0x00, 0x10, 0x71, 0x09, 0xC0, 0x02, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00,
  0x21, 0x81, 0x86, 0x00, 0x13, 0xC1, 0x0F, 0xC0, 0x07, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00,
  0x31, 0x00, 0xFC, 0x00, 0x1E, 0x41, 0xFE, 0x40, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x78, 0x43, 0xF8, 0x40, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x00, 0x00,
  0x09, 0x81, 0x00, 0x00, 0xF0, 0x47, 0x08, 0x40, 0x00, 0x00, 0x03, 0x0C, 0x00, 0x00, 0x0E, 0x00,
  0x08, 0x83, 0x00, 0x03, 0x90, 0x7F, 0x08, 0x40, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x11, 0x00,
  0x0C, 0xC6, 0x00, 0x1F, 0x10, 0xF1, 0x08, 0x40, 0x00, 0x00, 0x03, 0x00, 0x03, 0x80, 0x24, 0x80,
  0x06, 0x7C, 0x00, 0x7A, 0x13, 0xC1, 0x08, 0x40, 0x00, 0x00, 0x43, 0x00, 0x01, 0x00, 0x2C, 0x80,
  0x02, 0x00, 0x1F, 0xC6, 0x1E, 0x41, 0x08, 0x40, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x28, 0x80,
  0x03, 0x00, 0x3C, 0x8A, 0x70, 0x41, 0x09, 0xE0, 0x10, 0x00, 0x23, 0x00, 0x00, 0x00, 0x20, 0x80,
  0x00, 0x01, 0xE1, 0x57, 0xD0, 0x41, 0x0F, 0x80, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x80,
  0x00, 0x0F, 0x22, 0x25, 0x10, 0x41, 0x3C, 0x00, 0x28, 0x00, 0x03, 0x1F, 0xFF, 0x00, 0x11, 0x00,
  0x00, 0xF8, 0x54, 0x3E, 0x10, 0x43, 0xE0, 0x00, 0x00, 0x00, 0x03, 0x3F, 0xFF, 0x83, 0x0A, 0x00,
  0x07, 0x88, 0x88, 0xE4, 0x10, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x03, 0x70, 0x01, 0xC3, 0x04, 0x00,
  0x3C, 0x15, 0x07, 0x04, 0x10, 0x5B, 0x00, 0x80, 0x00, 0x00, 0x03, 0xE0, 0x00, 0xE0, 0x0E, 0x00,
  0xFA, 0x22, 0x1E, 0x04, 0x10, 0xF1, 0x80, 0x80, 0x00, 0x08, 0x03, 0xC0, 0x00, 0x70, 0x04, 0x08,
  0x89, 0x40, 0xF2, 0x04, 0x17, 0xB0, 0xC1, 0x40, 0x00, 0x0C, 0x03, 0x86, 0x0C, 0x38, 0x04, 0x18,
  0x08, 0x8F, 0x83, 0x04, 0x3C, 0x18, 0x60, 0x00, 0x00, 0x08, 0x03, 0x0F, 0x1E, 0x18, 0x04, 0x10,
  0x08, 0x78, 0x83, 0x04, 0xE6, 0x0C, 0x30, 0x00, 0x00, 0x00, 0x03, 0x19, 0xB3, 0x18, 0x06, 0x00,
  0x0F, 0xE0, 0xC2, 0x07, 0x83, 0x06, 0x18, 0x00, 0x00, 0x00, 0x03, 0x18, 0xE3, 0x18, 0x02, 0x00,
  0x0C, 0x21, 0xC3, 0x7C, 0x01, 0x83, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x18, 0x43, 0x18, 0x02, 0x00,
  0x18, 0x21, 0x87, 0xC0, 0x00, 0xC1, 0x86, 0x00, 0x00, 0x00, 0x03, 0x18, 0x0B, 0x18, 0x01, 0x00,
  0x10, 0x60, 0x8F, 0x80, 0x10, 0x60, 0xC3, 0x00, 0x01, 0x00, 0x03, 0x18, 0x1B, 0x18, 0x01, 0x00,
  0x10, 0x60, 0xFB, 0x80, 0x10, 0x30, 0x61, 0x80, 0x01, 0x00, 0x03, 0x18, 0x13, 0x18, 0x01, 0x00,
  0x50, 0x21, 0xC1, 0xC0, 0x28, 0x18, 0x30, 0xC0, 0x02, 0x80, 0x03, 0x18, 0x03, 0x18, 0x01, 0x00,
  0x70, 0x3E, 0x01, 0xC0, 0x00, 0x0C, 0x18, 0x71, 0xC4, 0x40, 0x03, 0x0C, 0x06, 0x18, 0x80, 0x00,
  0x70, 0xF0, 0x00, 0xE0, 0x00, 0x06, 0x0C, 0x3B, 0x98, 0x30, 0x03, 0x06, 0x0C, 0x19, 0x81, 0x00,
  0x77, 0x38, 0x00, 0xF0, 0x00, 0x03, 0x06, 0x1E, 0x04, 0x40, 0x23, 0x03, 0x18, 0x18, 0x80, 0x00,
  0x3C, 0x38, 0x03, 0xC0, 0x00, 0x41, 0x83, 0x78, 0x02, 0x80, 0x23, 0x81, 0xB0, 0x38, 0x01, 0x00,
  0xF0, 0x1C, 0x0E, 0x00, 0x40, 0x40, 0xC1, 0xF0, 0x01, 0x00, 0x21, 0xC0, 0xE0, 0x70, 0x14, 0x60,
  0x80, 0x0C, 0x7C, 0x00, 0x40, 0xA0, 0x67, 0xC0, 0x01, 0x00, 0x60, 0xE0, 0x40, 0xE0, 0x01, 0x00,
  0x00, 0x0F, 0xE0, 0x00, 0xA0, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x70, 0x01, 0xC0, 0x00, 0x00,
  0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0x88, 0x02, 0x80,
  0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x0C, 0x08, 0x40,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x03, 0x80, 0xFE, 0x10, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80,
  0x0C, 0x0F, 0x1F, 0x87, 0xE0, 0xFE, 0x18, 0x31, 0x00, 0xF0, 0x3F, 0x8F, 0xE2, 0x20, 0x03, 0x04,
  0x0C, 0x1D, 0x1F, 0x8C, 0x60, 0xC6, 0x0C, 0x23, 0x81, 0x08, 0x24, 0x91, 0x02, 0x20, 0x1C, 0x04,
  0x00, 0x30, 0x18, 0x08, 0x30, 0xC0, 0x04, 0x65, 0x42, 0x00, 0x2E, 0xA1, 0x02, 0xFC, 0x20, 0x04,
  0x0C, 0x30, 0x18, 0x00, 0x30, 0xC0, 0x06, 0xCD, 0x62, 0x00, 0x24, 0x8F, 0xC3, 0x24, 0x22, 0x04,
  0x0C, 0x60, 0x18, 0x00, 0x30, 0xFC, 0x03, 0x95, 0x52, 0x00, 0x2E, 0x89, 0x02, 0xA8, 0x22, 0x04,
  0x0C, 0x60, 0x1F, 0x03, 0xF0, 0x7E, 0x01, 0x9F, 0xF2, 0xF0, 0x20, 0x89, 0x02, 0x30, 0x22, 0x04,
  0x0C, 0x60, 0x1F, 0x06, 0x60, 0x06, 0x03, 0x08, 0x23, 0x08, 0x24, 0xBF, 0xE3, 0xFF, 0x1F, 0xC4,
  0x0C, 0x70, 0x18, 0x0C, 0x30, 0x06, 0x06, 0x0F, 0x22, 0x08, 0x2A, 0x81, 0x0A, 0x20, 0x02, 0x00,
  0x0C, 0x30, 0x18, 0x0C, 0x30, 0xC6, 0x0C, 0x09, 0x22, 0x08, 0x4A, 0x81, 0x12, 0x20, 0x0A, 0x84,
  0x0C, 0x1D, 0x1F, 0x8E, 0x70, 0xFE, 0x18, 0x0D, 0x22, 0x08, 0x44, 0x81, 0x02, 0x50, 0x12, 0x40,
  0x0C, 0x0F, 0x1F, 0x87, 0xEC, 0xFE, 0x30, 0x09, 0x21, 0x10, 0x80, 0x81, 0x02, 0x48, 0x22, 0x30,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x1F, 0xF0, 0xE1, 0x01, 0x81, 0x02, 0x87, 0x06, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
 
// 效果名称
const char* effectNames[] = {
  "TIME TUNNEL", "DNA HELIX", "STAR FIELD", "PORTAL",
  "DECODE", "SHATTER", "WARP SPEED", "QUANTUM"
};
 
// 粒子和星星
struct Star { int x, y, z; };
struct Fragment { int x, y, tx, ty, vx, vy; };
 
#define MAX_STARS 30
#define MAX_FRAGS 20
Star stars[MAX_STARS];
Fragment frags[MAX_FRAGS];
 
unsigned long effectStartTime = 0;
int currentEffect = 0;
 
// ============ 音乐函数 ============
void playHappyBirthday() {
  if (BUZZER_PIN < 0) return;
  
  int numNotes = sizeof(birthdayMelody) / sizeof(birthdayMelody[0]);
  
  for (int i = 0; i < numNotes; i++) {
    int noteDuration = 1200 / birthdayDurations[i];
    tone(BUZZER_PIN, birthdayMelody[i], noteDuration);
    int pauseBetweenNotes = noteDuration * 1.3;
    delay(pauseBetweenNotes);
    noTone(BUZZER_PIN);
  }
}
 
void playTransitionSound() {
  if (BUZZER_PIN < 0) return;
  int notes[] = {NOTE_C4, NOTE_E4, NOTE_G4};
  for (int i = 0; i < 3; i++) {
    tone(BUZZER_PIN, notes[i], 80);
    delay(100);
    noTone(BUZZER_PIN);
  }
}
 
void beep(int duration) {
  if (BUZZER_PIN >= 0) {
    tone(BUZZER_PIN, 1000, duration);
    delay(duration);
    noTone(BUZZER_PIN);
  }
}
 
// ============ 启动动画 ============
void epicBootAnimation() {
  // 脉冲
  for (int i = 0; i < 3; i++) {
    display.clearDisplay();
    display.fillCircle(64, 32, 5 + i * 8, SSD1306_WHITE);
    display.display();
    delay(100);
  }
  
  // 标题
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(15, 10);
  display.println(F("ULTIMATE"));
  display.setCursor(25, 30);
  display.println(F("SYSTEM"));
  display.display();
  delay(800);
  
  // �� 生日祝福
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(10, 15);
  display.println(F("Happy Birthday!"));
  display.setCursor(25, 35);
  display.println(F("Enjoy :)"));
  
  // 绘制蛋糕图标
  display.fillRect(52, 45, 24, 10, SSD1306_WHITE);
  display.drawLine(64, 40, 64, 45, SSD1306_WHITE);
  display.fillCircle(64, 39, 2, SSD1306_WHITE);
  
  display.display();
  
  // �� 播放生日快乐歌!
  playHappyBirthday();
  
  delay(500);
  
  // 扫描线
  for (int y = 0; y < 64; y += 2) {
    display.drawFastHLine(0, y, 128, SSD1306_WHITE);
    display.display();
  }
  delay(500);
}
 
// ============ 过渡动画 ============
void playTransition() {
  for (int i = 0; i < 10; i++) {
    display.clearDisplay();
    int s = i * 13;
    display.drawLine(0, 0, s, s, SSD1306_WHITE);
    display.drawLine(127, 0, 127-s, s, SSD1306_WHITE);
    display.drawLine(0, 63, s, 63-s, SSD1306_WHITE);
    display.drawLine(127, 63, 127-s, 63-s, SSD1306_WHITE);
    display.display();
    delay(30);
  }
}
 
// ============ 效果1: 时空隧道 ============
void effect1_TimeTunnel(unsigned long t) {
  display.clearDisplay();
  int depth = (t / 30) % 40;
  
  for (int i = 0; i < 8; i++) {
    int d = (depth + i * 5) % 40;
    int r = 3 + d;
    if (r < 50) {
      display.drawCircle(64, 32, r, SSD1306_WHITE);
      if (i % 2 == 0) display.drawCircle(64, 32, r-1, SSD1306_WHITE);
    }
  }
  
  float progress = min(1.0, t / 4000.0);
  int maxY = 64 * progress;
  for (int y = 0; y < maxY; y++) {
    for (int x = 0; x < 128; x++) {
      int byteIdx = y * 16 + x / 8;
      int bitIdx = 7 - (x % 8);
      if (pgm_read_byte(&bitmap[byteIdx]) & (1 << bitIdx)) {
        display.drawPixel(x, y, SSD1306_WHITE);
      }
    }
  }
  display.display();
  delay(25);
}
 
// ============ 效果2: DNA双螺旋 ============
void effect2_DNAHelix(unsigned long t) {
  display.clearDisplay();
  float angle = t / 500.0;
  
  for (int x = 0; x < 128; x += 3) {
    float a = angle + x * 0.1;
    int y1 = 32 + sin(a) * 15;
    display.fillCircle(x, y1, 2, SSD1306_WHITE);
    int y2 = 32 - sin(a) * 15;
    display.fillCircle(x, y2, 2, SSD1306_WHITE);
    if (abs(y1 - y2) < 5) display.drawLine(x, y1, x, y2, SSD1306_WHITE);
  }
  
  if (t > 2000) {
    for (int y = 0; y < 64; y += 2) {
      for (int x = 0; x < 128; x += 2) {
        int byteIdx = y * 16 + x / 8;
        int bitIdx = 7 - (x % 8);
        if (pgm_read_byte(&bitmap[byteIdx]) & (1 << bitIdx)) {
          display.drawPixel(x, y, SSD1306_WHITE);
        }
      }
    }
  }
  display.display();
  delay(30);
}
 
// ============ 效果3: 星域冲击 ============
void initStars() {
  for (int i = 0; i < MAX_STARS; i++) {
    stars[i].x = random(-64, 64);
    stars[i].y = random(-32, 32);
    stars[i].z = random(1, 30);
  }
}
 
void effect3_StarField(unsigned long t) {
  display.clearDisplay();
  
  for (int i = 0; i < MAX_STARS; i++) {
    stars[i].z -= 1;
    if (stars[i].z <= 0) {
      stars[i].x = random(-64, 64);
      stars[i].y = random(-32, 32);
      stars[i].z = 30;
    }
    
    int sx = 64 + (stars[i].x * 30) / stars[i].z;
    int sy = 32 + (stars[i].y * 30) / stars[i].z;
    
    if (sx >= 0 && sx < 128 && sy >= 0 && sy < 64) {
      int size = (30 - stars[i].z) / 15 + 1;
      int oldSx = 64 + (stars[i].x * 30) / (stars[i].z + 1);
      int oldSy = 32 + (stars[i].y * 30) / (stars[i].z + 1);
      display.drawLine(sx, sy, oldSx, oldSy, SSD1306_WHITE);
      display.fillCircle(sx, sy, size, SSD1306_WHITE);
    }
  }
  
  if (t > 3000) {
    float scale = min(1.0, (t - 3000) / 2000.0);
    int w = 128 * scale;
    int h = 64 * scale;
    int ox = (128 - w) / 2;
    int oy = (64 - h) / 2;
    
    for (int y = 0; y < h; y += 2) {
      for (int x = 0; x < w; x += 2) {
        int sx = x / scale;
        int sy = y / scale;
        int byteIdx = sy * 16 + sx / 8;
        int bitIdx = 7 - (sx % 8);
        if (byteIdx < 1024 && (pgm_read_byte(&bitmap[byteIdx]) & (1 << bitIdx))) {
          display.drawPixel(ox + x, oy + y, SSD1306_WHITE);
        }
      }
    }
  }
  display.display();
  delay(25);
}
 
// ============ 效果4: 传送门 ============
void effect4_Portal(unsigned long t) {
  display.clearDisplay();
  int maxRadius = min(50, (int)(t / 50));
  
  for (int r = maxRadius; r > 0; r -= 5) {
    if ((r / 5) % 2 == 0) display.drawCircle(64, 32, r, SSD1306_WHITE);
  }
  
  if (t > 1000) {
    float angle = t / 1000.0;
    for (int y = 0; y < 64; y++) {
      for (int x = 0; x < 128; x++) {
        float dx = x - 64;
        float dy = y - 32;
        float dist = sqrt(dx*dx + dy*dy);
        
        if (dist < maxRadius - 10) {
          float a = atan2(dy, dx) + angle * (1.0 - dist / maxRadius);
          int sx = 64 + cos(a) * dist;
          int sy = 32 + sin(a) * dist;
          
          if (sx >= 0 && sx < 128 && sy >= 0 && sy < 64) {
            int byteIdx = sy * 16 + sx / 8;
            int bitIdx = 7 - (sx % 8);
            if (pgm_read_byte(&bitmap[byteIdx]) & (1 << bitIdx)) {
              display.drawPixel(x, y, SSD1306_WHITE);
            }
          }
        }
      }
    }
  }
  display.display();
  delay(30);
}
 
// ============ 效果5: 矩阵解码 ============
void effect5_MatrixDecode(unsigned long t) {
  display.clearDisplay();
  int decodeX = (t / 30) % 140;
  
  for (int y = 0; y < 64; y++) {
    for (int x = 0; x < 128; x++) {
      int byteIdx = y * 16 + x / 8;
      int bitIdx = 7 - (x % 8);
      bool pixel = pgm_read_byte(&bitmap[byteIdx]) & (1 << bitIdx);
      
      if (x < decodeX) {
        if (pixel) display.drawPixel(x, y, SSD1306_WHITE);
      } else if (x == decodeX) {
        display.drawPixel(x, y, SSD1306_WHITE);
      } else {
        if (random(10) < 3 && pixel) display.drawPixel(x, y, SSD1306_WHITE);
      }
    }
  }
  
  for (int i = -3; i <= 3; i++) {
    int x = decodeX + i;
    if (x >= 0 && x < 128) display.drawFastVLine(x, 0, 64, SSD1306_WHITE);
  }
  display.display();
  delay(20);
}
 
// ============ 效果6: 碎片重组 ============
void initShatter() {
  for (int i = 0; i < MAX_FRAGS; i++) {
    frags[i].tx = (i % 5) * 25;
    frags[i].ty = (i / 5) * 16;
    frags[i].x = random(-50, 178);
    frags[i].y = random(-30, 94);
    frags[i].vx = 0;
    frags[i].vy = 0;
  }
}
 
void effect6_Shatter(unsigned long t) {
  display.clearDisplay();
  bool gathering = t > 2000;
  
  for (int i = 0; i < MAX_FRAGS; i++) {
    if (gathering) {
      frags[i].vx = (frags[i].tx - frags[i].x) / 8;
      frags[i].vy = (frags[i].ty - frags[i].y) / 8;
    }
    frags[i].x += frags[i].vx;
    frags[i].y += frags[i].vy;
    
    int fx = frags[i].x;
    int fy = frags[i].y;
    
    for (int dy = 0; dy < 16 && fy + dy < 64; dy++) {
      for (int dx = 0; dx < 25 && fx + dx < 128; dx++) {
        int sy = frags[i].ty + dy;
        int sx = frags[i].tx + dx;
        
        if (sy >= 0 && sy < 64 && sx >= 0 && sx < 128) {
          int byteIdx = sy * 16 + sx / 8;
          int bitIdx = 7 - (sx % 8);
          if (pgm_read_byte(&bitmap[byteIdx]) & (1 << bitIdx)) {
            int py = fy + dy;
            int px = fx + dx;
            if (px >= 0 && px < 128 && py >= 0 && py < 64) {
              display.drawPixel(px, py, SSD1306_WHITE);
            }
          }
        }
      }
    }
    display.drawRect(fx, fy, 25, 16, SSD1306_WHITE);
  }
  display.display();
  delay(25);
}
 
// ============ 效果7: 曲速引擎 ============
void effect7_WarpSpeed(unsigned long t) {
  display.clearDisplay();
  
  for (int i = 0; i < 40; i++) {
    int x = (t * 5 + i * 20) % 160 - 32;
    int y = random(0, 64);
    int len = random(10, 40);
    if (x >= 0 && x < 128) display.drawFastHLine(x, y, min(len, 128-x), SSD1306_WHITE);
  }
  
  for (int y = 0; y < 64; y++) {
    int stretch = sin(t / 300.0 + y * 0.1) * 5;
    for (int x = 0; x < 128; x++) {
      int sx = (x - stretch + 128) % 128;
      int byteIdx = y * 16 + sx / 8;
      int bitIdx = 7 - (sx % 8);
      if (pgm_read_byte(&bitmap[byteIdx]) & (1 << bitIdx)) {
        display.drawPixel(x, y, SSD1306_WHITE);
      }
    }
  }
  display.display();
  delay(20);
}
 
// ============ 效果8: 量子叠加 ============
void effect8_Quantum(unsigned long t) {
  display.clearDisplay();
  int numLayers = 3;
  
  for (int layer = 0; layer < numLayers; layer++) {
    int offsetX = sin(t / 500.0 + layer * 2) * 10;
    int offsetY = cos(t / 600.0 + layer * 2) * 5;
    
    for (int y = 0; y < 64; y += (layer + 1)) {
      for (int x = 0; x < 128; x += (layer + 1)) {
        int sx = x - offsetX;
        int sy = y - offsetY;
        
        if (sx >= 0 && sx < 128 && sy >= 0 && sy < 64) {
          int byteIdx = sy * 16 + sx / 8;
          int bitIdx = 7 - (sx % 8);
          if (pgm_read_byte(&bitmap[byteIdx]) & (1 << bitIdx)) {
            display.drawPixel(x, y, SSD1306_WHITE);
          }
        }
      }
    }
  }
  
  if ((t / 100) % 3 == 0) {
    display.drawRect(0, 0, 128, 64, SSD1306_WHITE);
    display.drawRect(2, 2, 124, 60, SSD1306_WHITE);
  }
  
  int brightness = (sin(t / 300.0) * 127.5) + 127.5;
  display.ssd1306_command(SSD1306_SETCONTRAST);
  display.ssd1306_command(brightness);
  
  display.display();
  delay(35);
}
 
// ============ SETUP ============
void setup() {
  Serial.begin(115200);
  Wire.begin(6, 7);  // �� 改成你的I2C引脚
  
  if (BUZZER_PIN >= 0) pinMode(BUZZER_PIN, OUTPUT);
  
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("初始化失败"));
    for(;;);
  }
  
  epicBootAnimation();  // �� 播放生日快乐歌
  
  effectStartTime = millis();
  initStars();
  
  Serial.println(F("╔═══════════════════════════════╗"));
  Serial.println(F("║  宇宙第一 终极显示系统 V1.0  ║"));
  Serial.println(F("║  ULTIMATE DISPLAY SYSTEM     ║"));
  Serial.println(F("╚═══════════════════════════════╝"));
}
 
// ============ LOOP ============
void loop() {
  unsigned long elapsed = millis() - effectStartTime;
  
  if (elapsed >= 6000) {
    playTransition();
    currentEffect = (currentEffect + 1) % 8;
    effectStartTime = millis();
    elapsed = 0;
    
    if (currentEffect == 2) initStars();
    if (currentEffect == 5) initShatter();
    
    Serial.println();
    Serial.print(F(">>> 效果 #"));
    Serial.print(currentEffect + 1);
    Serial.print(F(": "));
    Serial.println(effectNames[currentEffect]);
    
    playTransitionSound();  // �� 音效
  }
  
  switch(currentEffect) {
    case 0: effect1_TimeTunnel(elapsed); break;
    case 1: effect2_DNAHelix(elapsed); break;
    case 2: effect3_StarField(elapsed); break;
    case 3: effect4_Portal(elapsed); break;
    case 4: effect5_MatrixDecode(elapsed); break;
    case 5: effect6_Shatter(elapsed); break;
    case 6: effect7_WarpSpeed(elapsed); break;
    case 7: effect8_Quantum(elapsed); break;
  }
}

🎵 音乐系统说明

生日快乐歌旋律编码

       本项目使用标准音符频率表和节奏数组来编码生日快乐歌:

```cpp
// 音符频率(单位:Hz)
NOTE_G4 = 392Hz    // Sol
NOTE_A4 = 440Hz    // La
NOTE_C5 = 523Hz    // Do (高音)
NOTE_D5 = 587Hz    // Re (高音)
// ... 等等
// 旋律数组(音符序列)
Happy Birthday to you → G G A G C B
Happy Birthday to you → G G A G D C
Happy Birthday dear... → G G G E C B A
Happy Birthday to you → F F E C D C
```

音乐播放原理

       无源蜂鸣器通过 `tone()` 函数驱动:

```cpp
tone(引脚号, 频率Hz, 持续时间ms);
```

程序自动计算每个音符的时长:

       四分音符 = 1200ms ÷ 4 = 300ms

       二分音符 = 1200ms ÷ 2 = 600ms

音符之间自动添加 1.3倍时长的停顿,使旋律更清晰。


🎨 8种视觉特效详解

效果 1: 时空隧道 (Time Tunnel)

       原理: 同心圆从中心向外扩散  

       实现: 使用 drawCircle()绘制多层递增半径的圆  

       效果: 模拟穿越时空隧道的视觉感  

       时长: 6秒

效果 2: DNA 双螺旋 (DNA Helix)

       原理: 正弦波相位相反的双螺旋线  

       实现:

```cpp
y1 = 32 + sin(angle) * 15;  // 上螺旋
y2 = 32 - sin(angle) * 15;  // 下螺旋

       效果: 生物科技感十足的 DNA 双螺旋结构  

       时长: 6秒

效果 3: 星域冲击 (Star Field)

       原理: 3D 星空视差效果  

       实现: 30颗星星在 Z 轴上移动,越近越大  

       公式:

屏幕X = 64 + (星X * 30) / 星Z深度
屏幕Y = 32 + (星Y * 30) / 星Z深度

       效果: 像《星球大战》中的超空间跳跃  

       时长: 6秒

效果 4: 传送门 (Portal)

       原理: 同心圆旋转扭曲  

       实现: 图像根据距离中心的距离进行极坐标旋转  

       效果: 类似《传送门》游戏中的空间扭曲  

       时长: 6秒

效果 5: 矩阵解码 (Matrix Decode)

       原理: 扫描线从左到右逐步"解码"图像  

       实现:

           扫描线左侧:显示正常图像

           扫描线位置:高亮竖线

            扫描线右侧:随机噪点

       效果: 《黑客帝国》风格的数据解码  

       时长: 6秒

效果 6: 碎片重组 (Shatter)

       原理: 图像分割成 20 个碎片,先分散后聚合  

       实现:

           每个碎片 25×16 像素

           初始随机飞散

           2秒后逐渐飞回原位

       效果: 如同玻璃破碎后重新组合  

       时长: 6秒

效果 7: 曲速引擎 (Warp Speed)

       原理: 水平高速运动的光条 + 波形扭曲  

       实现:

            40条随机高度的光条高速移动

           图像按正弦波扭曲

       效果: 《星际迷航》曲速引擎的超光速感觉  

       时长: 6秒

效果 8: 量子叠加 (Quantum)

       原理: 3层图像叠加,各自独立晃动  

       实现:

           每层图像有不同的偏移量

           使用正弦函数控制晃动

           动态调整屏幕亮度

       效果: 量子力学的叠加态视觉表现  

       时长: 6秒


📊 系统工作流程

```mermaid
graph TD
    A[上电启动] --> B[初始化硬件]
    B --> C[播放启动动画]
    C --> D[显示生日祝福文字]
    D --> E[播放生日快乐歌]
    E --> F[进入效果循环]
    F --> G[效果1: 时空隧道]
    G --> H[过渡动画+音效]
    H --> I[效果2: DNA双螺旋]
    I --> J[...]
    J --> K[效果8: 量子叠加]
    K --> F
```

🎨 进阶改进建议

1. 添加更多歌曲

       可以添加超级玛丽、小星星等旋律:

```cpp
void playMarioTheme() {
  int melody[] = {NOTE_E5, NOTE_E5, 0, NOTE_E5, ...};
  // 播放代码...
}
```

2. 触摸按钮切换效果

       添加触摸传感器手动切换:

```cpp
if (touchRead(T0) < 40) {
  currentEffect++;
  // 切换效果
}
```

3. 联网显示

       连接 Wi-Fi 显示天气、时间等:

```cpp
#include <WiFi.h>
WiFi.begin(ssid, password);
// 获取网络数据...
```

4. 彩色 OLED 升级

       更换 SSD1351 彩色屏幕,获得更炫酷的视觉效果


💝 项目应用场景

       🎂 生日礼物:个性化生日祝福装置

       🏢 公司庆典:周年庆展示装置

       🎨 创客作:技术展示项目

       📚 教学演示:嵌入式系统教学

       🎁 DIY 礼物:送给科技爱好者


🙏 致谢

       iEasy商城 6周年庆典,祝越办越好!🎉

       如果觉得本项目有帮助,请点个👍或评论留下您想听的歌曲哦!!!

       Happy Making! 🎂🎵✨



全部评论
暂无评论
0/144