#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// -------------------------- 硬件引脚定义 --------------------------
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define IN1 11
#define IN2 10
#define IN3 6
#define IN4 5
#define BUZZER_PIN 3
#define OBSTACLE_FRONT A4
#define OBSTACLE_LEFT A3
#define OBSTACLE_RIGHT A2
#define VOLTAGE_PIN A0
#define CURRENT_PIN A1
#define LED_PIN 13
// -------------------------- 全局变量定义 --------------------------
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
int voltageAdc = 0;
int currentAdc = 0;
const float ADC_REF = 5.0;
const int ADC_MAX = 1023;
const float VOLT_DIV = 3.0;
const float ALERT_VOLT = 7.0;
int toneFreq[] = {196, 220, 247, 262, 294, 330, 349, 392, 440, 494, 523};
int birthdayNotes[] = {3,3,4,3,6,5, 3,3,4,3,7,6, 3,3,10,8,6,5,4, 9,9,8,6,7,6};
int birthdayDurations[] = {8,8,4,4,4,4, 8,8,4,4,4,4, 8,8,4,4,4,4,4, 8,8,4,4,4,4};
int counter = 0;
// 非阻塞计时变量
unsigned long songTimer = 0;
int songIndex = 0;
unsigned long noteEndTime = 0;
unsigned long alertTimer = 0;
bool isAlertOn = false;
// -------------------------- 函数声明 --------------------------
void playBirthdaySongNonBlock();
void readAndCalculatePower(float *volt, float *curr, float *power);
void displayInfo(float volt, float curr, float power);
void voltageAlertNonBlock();
void setup() {
// 初始化OLED(I2C速率回落至200kHz,平衡速度与稳定性)
Wire.setClock(200000);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
for(;;);
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setTextWrap(false);
// 初始化硬件引脚
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
digitalWrite(LED_PIN, LOW);
}
void loop() {
unsigned long currentMillis = millis();
// 1. 读取电压、电流(每150ms一次,降低数据采集频率)
static unsigned long powerReadTimer = 0;
float voltage = 0, current = 0, power = 0;
if (currentMillis - powerReadTimer >= 50) {
readAndCalculatePower(&voltage, ¤t, &power);
powerReadTimer = currentMillis;
// 2. 同步更新显示(与数据采集绑定,每150ms刷新1次,避免过快闪烁)
displayInfo(voltage, current, power);
counter++;
}
// 3. 电压报警逻辑(非阻塞,报警间隔微调至1.8秒,避免过频)
if (voltage < ALERT_VOLT) {
voltageAlertNonBlock();
} else {
// 4. 非阻塞播放生日歌(音符间隔增加至80ms,歌曲节奏更舒缓)
playBirthdaySongNonBlock();
}
}
// -------------------------- 函数实现 --------------------------
// 非阻塞播放生日歌(放缓节奏)
void playBirthdaySongNonBlock() {
unsigned long currentMillis = millis();
int noteCount = sizeof(birthdayNotes)/sizeof(birthdayNotes[0]);
// 整首歌播放完后,暂停1.5秒再重置(延长间隔)
if (currentMillis - songTimer >= 1500 && songIndex == noteCount) {
songIndex = 0;
songTimer = currentMillis;
noTone(BUZZER_PIN);
return;
}
if (currentMillis >= noteEndTime && songIndex < noteCount) {
int noteIdx = birthdayNotes[songIndex];
int duration = 1000 / (birthdayDurations[songIndex]/2);
if (noteIdx != 0) {
tone(BUZZER_PIN, toneFreq[noteIdx]);
} else {
noTone(BUZZER_PIN);
}
noteEndTime = currentMillis + duration;
songIndex++;
} else if (currentMillis >= noteEndTime && songIndex < noteCount) {
noTone(BUZZER_PIN);
delay(80); // 增加音符间隔,放缓歌曲节奏
}
}
// 读取电压电流并计算功率
void readAndCalculatePower(float *volt, float *curr, float *power) {
voltageAdc = analogRead(VOLTAGE_PIN);
float adcVolt = (voltageAdc * ADC_REF) / ADC_MAX;
*volt = adcVolt * VOLT_DIV;
currentAdc = analogRead(CURRENT_PIN);
float adcCurrVolt = (currentAdc * ADC_REF) / ADC_MAX;
*curr = ((adcCurrVolt - 2.5) / 0.5) * 1000;
*power = *volt * (*curr / 1000.0);
}
// OLED显示逻辑(保持简洁,避免额外开销)
void displayInfo(float volt, float curr, float power) {
display.clearDisplay();
// 显示标题和计数器
display.setCursor(10, 0);
display.print("20");
// 显示核心数据
display.setTextSize(2);
display.setCursor(0, 12);
display.print("U:");
display.print(volt, 1);
display.print("V");
display.setCursor(0, 30);
display.print("I:");
display.print(curr, 0);
display.print("mA");
display.setCursor(0, 50);
display.print("P:");
display.print(power, 2);
display.print("W");
display.display();
}
// 非阻塞电压报警(放缓报警频率)
void voltageAlertNonBlock() {
unsigned long currentMillis = millis();
// 报警间隔从1.5秒延长至1.8秒,避免频繁切换
if (currentMillis - alertTimer >= 1800) {
alertTimer = currentMillis;
isAlertOn = !isAlertOn;
}
if (isAlertOn) {
tone(BUZZER_PIN, 1000);
digitalWrite(LED_PIN, HIGH);
} else {
noTone(BUZZER_PIN);
digitalWrite(LED_PIN, LOW);
}
}
#include<Arduino.h>
#include <SoftwareSerial.h>
// 一、蓝牙模块配置(Nano推荐引脚,烧录时需断开RX/TX接线)
#define BT_RX_PIN 0 // Arduino接收 → 蓝牙模块TX
#define BT_TX_PIN 1 // Arduino发送 → 蓝牙模块RX
SoftwareSerial bluetooth(BT_RX_PIN, BT_TX_PIN);
char btCmd; // 存储蓝牙接收的指令
bool motorStatus = false; // 电机状态:false=关闭,true=开启
// 二、电机引脚配置(左D10/D11,右D5/D6,按你的需求定义)
#define LEFT_IN1 10 // 左电机控制脚1
#define LEFT_IN2 11 // 左电机控制脚2
#define RIGHT_IN3 5 // 右电机控制脚1
#define RIGHT_IN4 6 // 右电机控制脚2
void setup() {
// 1. 初始化串口与蓝牙(HC-05默认9600波特率,其他模块需修改)
Serial.begin(9600);
while (!Serial); // 等待电脑串口就绪
bluetooth.begin(115200);
Serial.println("蓝牙已启动,指令说明:");
Serial.println("F=前进,B=后退,L=左转,R=右转,U=电机开,O=电机关,S=停止");
// 2. 配置电机引脚为输出模式,初始状态电机关闭
pinMode(LEFT_IN1, OUTPUT);
pinMode(LEFT_IN2, OUTPUT);
pinMode(RIGHT_IN3, OUTPUT);
pinMode(RIGHT_IN4, OUTPUT);
stopCar(); // 初始化小车停止
}
void loop() {
// 读取蓝牙指令并执行对应操作
if (bluetooth.available() > 0) {
btCmd = bluetooth.read();
Serial.print("收到指令:");
Serial.println(btCmd);
controlCar(btCmd); // 执行指令对应的小车动作
}
}
// 三、小车核心控制逻辑(指令→动作映射)
void controlCar(char cmd) {
// 只有电机开启(motorStatus=true)时,才能执行运动指令;关闭时仅响应U/O/S
switch(cmd) {
case 'U': // 电机开启(默认进入前进状态)
motorStatus = true;
Serial.println("电机已开启,当前状态:前进");
digitalWrite(LEFT_IN1, HIGH);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, LOW);
digitalWrite(RIGHT_IN4, HIGH);
break;
case 'O': // 电机关闭(所有电机断电)
motorStatus = false;
stopCar();
Serial.println("电机已关闭");
break;
case 'S': // 停止(电机保持开启状态,但当前动作停止)
stopCar();
Serial.println("小车停止");
break;
case 'F': // 前进(需电机已开启)
if (motorStatus) {
Serial.println("小车前进");
digitalWrite(LEFT_IN1, HIGH);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, LOW);
digitalWrite(RIGHT_IN4, HIGH);
} else {
Serial.println("电机未开启,请先发送指令U");
}
break;
case 'B': // 后退(需电机已开启)
if (motorStatus) {
Serial.println("小车后退");
digitalWrite(LEFT_IN1, LOW);
digitalWrite(LEFT_IN2, HIGH);
digitalWrite(RIGHT_IN3, HIGH);
digitalWrite(RIGHT_IN4, LOW);
} else {
Serial.println("电机未开启,请先发送指令U");
}
break;
case 'L': // 左转(需电机已开启:右电机转,左电机停)
if (motorStatus) {
Serial.println("小车左转");
digitalWrite(LEFT_IN1, LOW);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, LOW);
digitalWrite(RIGHT_IN4, HIGH);
} else {
Serial.println("电机未开启,请先发送指令U");
}
break;
case 'R': // 右转(需电机已开启:左电机转,右电机停)
if (motorStatus) {
Serial.println("小车右转");
digitalWrite(LEFT_IN1, HIGH);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, LOW);
digitalWrite(RIGHT_IN4, LOW);
} else {
Serial.println("电机未开启,请先发送指令U");
}
break;
default: // 未知指令
Serial.println("指令错误!仅支持F/B/L/R/U/O/S");
break;
}
}
// 四、电机停止函数(所有控制脚置低,电机断电)
void stopCar() {
digitalWrite(LEFT_IN1, LOW);
digitalWrite(LEFT_IN2, LOW);
digitalWrite(RIGHT_IN3, LOW);
digitalWrite(RIGHT_IN4, LOW);
}
根据这两份代码,生成一份代码,要求可以在小车运动的同时可以播放音乐,电源低于7v是可以预警,小车上的显示屏可以显示电压电流功率。且手机上的蓝牙遥控界面中text会显示显示屏上的电压电流功率