#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
// ================== 引脚定义(完全匹配Wokwi接线)==================
#define IR_1 14 // 红外接收器1
#define RED_1 2 // 车位1红灯
#define GREEN_1 4 // 车位1绿灯
#define IR_2 27 // 红外接收器2
#define RED_2 5 // 车位2红灯
#define GREEN_2 18 // 车位2绿灯
#define BTN 33 // 入场按钮(Wokwi上拉输入,接GND触发)
#define STRIP_PIN 15 // WS2812灯带数据脚
// ================== OLED配置(Wokwi默认I2C地址0x3C)==================
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// ================== WS2812灯带配置(Wokwi适配)==================
#define PIXELS 16 // 灯珠数量
#define BRIGHTNESS 100 // 亮度(0-255)
Adafruit_NeoPixel strip(PIXELS, STRIP_PIN, NEO_GRB + NEO_KHZ800);
// ================== 系统状态变量(Wokwi优化)==================
int btnPressCount = 0; // 按钮按下次数:0=初始,1=车位1占,2=车位2占,3=复位
bool slot1Occupied = false; // 车位1状态
bool slot2Occupied = false; // 车位2状态
unsigned long lastBtnTime = 0; // 按钮消抖时间戳
const unsigned long DEBOUNCE_DELAY = 80; // Wokwi专用消抖延时(避免仿真误触发)
// ================== 工具函数:更新车位LED ==================
void updateParkingLights() {
// 车位1:占用亮红灯,空闲亮绿灯
digitalWrite(RED_1, slot1Occupied ? HIGH : LOW);
digitalWrite(GREEN_1, slot1Occupied ? LOW : HIGH);
// 车位2:占用亮红灯,空闲亮绿灯
digitalWrite(RED_2, slot2Occupied ? HIGH : LOW);
digitalWrite(GREEN_2, slot2Occupied ? LOW : HIGH);
}
// ================== 工具函数:更新OLED显示(Wokwi适配)==================
void updateOLED() {
display.clearDisplay();
// 标题
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("Parking");
// 车位状态
display.setTextSize(1);
display.setCursor(0, 20);
display.print("Slot 1: ");
display.println(slot1Occupied ? "OCCUPIED" : "EMPTY");
display.setCursor(0, 35);
display.print("Slot 2: ");
display.println(slot2Occupied ? "OCCUPIED" : "EMPTY");
// 红外信号提示
display.setCursor(0, 50);
if(btnPressCount == 0) {
display.println("Status: All Empty");
} else if(btnPressCount == 1) {
display.println("IR1 Received | Slot1 Occupied");
} else if(btnPressCount == 2) {
display.println("IR2 Received | Slot2 Occupied");
} else if(btnPressCount == 3) {
display.println("Status: All Empty (Reset)");
}
display.display();
}
// ================== 工具函数:灯带渐变亮起(Wokwi流畅版)==================
void stripGradualGreen() {
strip.clear();
// 从左到右逐个点亮,Wokwi仿真优化延时
for(int i = 0; i < PIXELS; i++) {
strip.setPixelColor(i, strip.Color(0, 255, 0));
strip.show();
delay(20); // Wokwi专用渐变速度,流畅不卡顿
}
}
// ================== 工具函数:灯带全灭 ==================
void stripClear() {
strip.clear();
strip.show();
}
// ================== 初始化(Wokwi仿真适配)==================
void setup() {
Serial.begin(115200);
Serial.println("Wokwi Parking System Init...");
// 1. 初始化引脚(Wokwi上拉输入适配)
pinMode(IR_1, INPUT);
pinMode(IR_2, INPUT);
pinMode(RED_1, OUTPUT);
pinMode(GREEN_1, OUTPUT);
pinMode(RED_2, OUTPUT);
pinMode(GREEN_2, OUTPUT);
pinMode(BTN, INPUT_PULLUP); // 按钮上拉,Wokwi必须用这个模式
// 2. 初始化灯带(Wokwi驱动适配)
strip.begin();
strip.setBrightness(BRIGHTNESS);
stripClear();
Serial.println("WS2812 Strip Init OK");
// 3. 初始化OLED(Wokwi默认地址0x3C)
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("OLED Init Failed! Check Wiring"));
for(;;); // Wokwi里卡死提示接线错误
}
display.display();
delay(1000);
display.clearDisplay();
Serial.println("OLED Init OK");
// 4. 初始化状态(Wokwi初始状态)
slot1Occupied = false;
slot2Occupied = false;
btnPressCount = 0;
updateParkingLights();
updateOLED();
Serial.println("Wokwi System Init Complete! Ready to Press Button");
}
// ================== 主循环(Wokwi仿真优化)==================
void loop() {
// 1. 按钮消抖检测(Wokwi专用逻辑,解决无响应)
if(digitalRead(BTN) == LOW && (millis() - lastBtnTime > DEBOUNCE_DELAY)) {
lastBtnTime = millis();
// Wokwi二次消抖,避免仿真误触发
delay(20);
if(digitalRead(BTN) == LOW) {
Serial.print("Wokwi Button Pressed! Count: ");
// 按钮次数循环:0→1→2→3→0
btnPressCount = (btnPressCount + 1) % 4;
Serial.println(btnPressCount);
// 2. 根据次数更新状态(完全匹配你的需求)
switch(btnPressCount) {
case 0: // 初始/复位:全空
slot1Occupied = false;
slot2Occupied = false;
stripClear();
break;
case 1: // 第1次按:车位1占用,灯带渐变
slot1Occupied = true;
slot2Occupied = false;
stripGradualGreen(); // 触发渐变效果
break;
case 2: // 第2次按:车位2占用,灯带保持
slot1Occupied = true;
slot2Occupied = true;
// 保持全亮,Wokwi里不闪烁
strip.fill(strip.Color(0, 255, 0));
strip.show();
break;
case 3: // 第3次按:复位全空
slot1Occupied = false;
slot2Occupied = false;
stripClear();
btnPressCount = 0; // 重置计数,回到初始状态
break;
}
// 3. 刷新所有显示(Wokwi同步更新)
updateParkingLights();
updateOLED();
// 4. 等待按钮松开(Wokwi避免连按)
while(digitalRead(BTN) == LOW) {
delay(10);
}
Serial.println("Button Released");
}
}
// 5. 保持灯带状态(Wokwi避免闪烁)
if(btnPressCount == 1 || btnPressCount == 2) {
strip.fill(strip.Color(0, 255, 0));
strip.show();
}
delay(10); // Wokwi稳定循环
}