// 包含音调定义和伺服库
#include "pitches.h" // 包含了音调的频率定义,方便生成声音
#include <Servo.h> // 伺服电机控制库
#include <LiquidCrystal_I2C.h>
#define I2C_ADDR 0x27
#define LCD_COLUMNS 20
#define LCD_LINES 4
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
// 设定扬声器引脚
#define SPEAKER_PIN 8
// 创建伺服对象来控制伺服电机
Servo myservo;
// 变量来读取模拟引脚值
int val;
// 用于存储前一次的模拟引脚值
int last_val;
// 用于存储伺服位置的变量
int pos = 0;
int randomNumber;
// 设置按钮引脚和相应的音调
const uint8_t buttonPins[] = {12, 11, 10, 9, 7, 6, 5, 4};
const int buttonTones[] = {
NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4,
NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5
};
// 计算音调的数量
const int numTones = sizeof(buttonPins) / sizeof(buttonPins[0]);
void setup() {
pinMode(LED_BUILTIN, OUTPUT); // 将内置LED设置为输出
// 将伺服电机附加到引脚3上
myservo.attach(3);
// 为所有按钮设置为输入模式,并启用内部上拉电阻
for (uint8_t i = 0; i < numTones; i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
}
// 将扬声器引脚设置为输出
pinMode(SPEAKER_PIN, OUTPUT);
// Init
lcd.init();
lcd.backlight();
// Print something
lcd.setCursor(3, 0);
lcd.print("Hello!");
lcd.setCursor(2, 1);
lcd.print("welcome to play");
lcd.setCursor(5, 2);
lcd.print("Deaf mute piano");
lcd.setCursor(7, 3);
lcd.print("Enjoy!");
delay(1000);
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Press:");
randomNumber = 1;
lcd.setCursor(8, 0);
lcd.print(randomNumber);
// 用于存储音调的变量
}
void loop() {
int pitch = 0;
// 检查每个按钮,如果按下则播放相应的音调,并移动伺服到特定位置
for (uint8_t i = 0; i < numTones; i++) {
if (digitalRead(buttonPins[i]) == LOW) { // 如果按钮被按下
pitch = buttonTones[i]; // 选择对应的音调
myservo.write(i*25); // 移动伺服到特定位置
if (i == (randomNumber-1)){ //如果按对了,提示yes
lcd.setCursor(2, 1);
lcd.print("yes");
delay(100);
randomNumber = random(1, 9);
lcd.setCursor(8, 0);
lcd.print(randomNumber);
}
else{ //如果按对了,提示no
lcd.setCursor(2, 1);
lcd.print("no!");
}
}
}
// 如果有音调被选择(按钮被按下),则播放音调
if (pitch) {
tone(SPEAKER_PIN, pitch);
digitalWrite(LED_BUILTIN, HIGH); // 点亮内置与外接LED
} else {
noTone(SPEAKER_PIN); // 如果没有按钮被按下,则停止播放音调
digitalWrite(LED_BUILTIN, LOW); // 熄灭内置与外接LED
}
// 以下代码读取并处理电位计数据,用于动态控制伺服电机的位置
last_val = val;
val = analogRead(A0); // 读取A0引脚的值
val = map(val, 0, 1023, 0, 180); // 将读取的值映射到0-180的范围内
// 如果电位计值发生变化,则更新伺服位置
if(last_val != val) {
myservo.write(val); // 根据映射值移动伺服
delay(15); // 等待伺服运动到指定位置
digitalWrite(LED_BUILTIN, HIGH); // 点亮内置与外接LED
}
else{
digitalWrite(LED_BUILTIN, LOW); // 熄灭内置与外接LED
}
}