【Follow me第二季第1期】创意任务三: 触控&MIDI钢琴
[复制链接]
本帖最后由 MioChan 于 2024-7-31 00:24 编辑
水果钢琴原理就是当手触摸水果时,检测其对应引脚的电容发生的变化,当其大于某个阈值时,执行if内语句,可以说是把水果当成一个大号“按钮”。除了A0其余引脚都支持这个触摸检测的功能,用CircuitPlayground.readCap(Pin)就可以读取引脚的电容值。
手边没有完整的水果,所以就实现一个最简单的,直接用手触摸A1-A7焊盘就能发出do ra mi .. 对应的音,用水果的话应该稍微改一下判断阈值即可,原理大同小异。
下面是演示:
水果钢琴原理演示
代码:
#include <Adafruit_CircuitPlayground.h>
// 触控引脚及其音符频率
int touchPins[] = {A1, A2, A3, A4, A5, A6, A7};
int touchFrequencies[] = {262, 294, 330, 349, 392, 440, 494};
void setup() {
CircuitPlayground.begin();
Serial.begin(115200);
void loop() {
checkTouch();
delay(5);
}
void checkTouch() {
for (int i = 0; i < 7; i++) {
int pin = touchPins[i];
int touchValue = CircuitPlayground.readCap(pin);
if (touchValue > 1000) {
Serial.print("Touched pad A");
Serial.println(pin - A0);
playTouchNoteAndLight(i);
return;
}
}
}
void playTouchNoteAndLight(int pad) {
int frequency = touchFrequencies[pad];
uint32_t randomColor = CircuitPlayground.colorWheel(random(0, 255));
tone(A0, frequency);//播放
for (int i = 0; i < 10; i++) { //亮灯
CircuitPlayground.setPixelColor(i, randomColor);
}
delay(600); // 播放600毫秒
noTone(A0); //停止播放
for (int i = 0; i < 10; i++) { //关灯
CircuitPlayground.setPixelColor(i, 0);
}
}
下面再实现一个进阶版,可以根据MIDI音符信号,使用板载蜂鸣器进行播放。程序会自动识别传入MIDI音符的音调,时值以及力度,会像创意任务一中的一样用彩色LED给出力度的电平指示,可以作为外部MIDI乐器用DAW或MIDI键盘直接控制。此外,还手动实现了一个PWM发生器,可以通过板子上的两个按钮实时的调整蜂鸣器音量大小。
下面是视频演示:
创意任务3
对应代码:
#include <Adafruit_CircuitPlayground.h>
#include <MIDIUSB.h>
// MIDI音符到频率的映射,A0到C8
const int noteFrequencies[] = {
27, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, 65, 69, 73, 78, 82, 87, 93, 98, 104, 110, 117, 123, 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 4186
};
// 定义渐变颜色数组
uint32_t gradientColors[10] = {
CircuitPlayground.colorWheel(90),
CircuitPlayground.colorWheel(80),
CircuitPlayground.colorWheel(70),
CircuitPlayground.colorWheel(60),
CircuitPlayground.colorWheel(50),
CircuitPlayground.colorWheel(40),
CircuitPlayground.colorWheel(30),
CircuitPlayground.colorWheel(20),
CircuitPlayground.colorWheel(10),
CircuitPlayground.colorWheel(0)
};
int volume = 100; // 初始音量
int currentNote = -1;
unsigned long noteStartTime = 0;
unsigned long noteDuration = 0;
int currentFrequency = 0;
void setup() {
CircuitPlayground.begin();
Serial.begin(115200);
}
void loop() {
checkButtons(); // 检查按钮状态
midiEventPacket_t rx;
do {
rx = MidiUSB.read();
if (rx.header != 0) {
Serial.print("Received: ");
Serial.print(rx.header);
Serial.print("-");
Serial.print(rx.byte1);
Serial.print("-");
Serial.print(rx.byte2);
Serial.print("-");
Serial.println(rx.byte3);
// 检查NOTE ON 消息
if ((rx.byte1 & 0xF0) == 0x90 && rx.byte3 != 0) {
int note = rx.byte2; // 获取音符音高
int velocity = rx.byte3; // 获取音符力度
playNoteAndLight(note, velocity);
} else if ((rx.byte1 & 0xF0) == 0x80 || ((rx.byte1 & 0xF0) == 0x90 && rx.byte3 == 0)) {
stopNoteAndLight();
}
}
} while (rx.header != 0);
if (currentNote != -1 && millis() - noteStartTime >= noteDuration) {
stopNoteAndLight();
}
delay(5);
}
void playNoteAndLight(int note, int velocity) {
if (note >= 21 && note <= 108) { // 确保音符在有效范围内
int frequency = noteFrequencies[note - 21];
currentFrequency = frequency;
currentNote = note;
noteStartTime = millis();
noteDuration = 1000;
// 调用PWM函数来播放音符
startPWM(A0, frequency, volume);
// 计算亮灯的数量
int ledCountToLight = map(velocity, 0, 127, 0, 10);
Serial.print("Velocity: ");
Serial.print(velocity);
Serial.print(", LED Count: ");
Serial.println(ledCountToLight);
// 更新 LED 灯的亮度
for (int i = 0; i < 10; i++) {
if (i < ledCountToLight) {
// 点亮 LED
CircuitPlayground.setPixelColor(i, gradientColors[i]);
} else {
// 关闭 LED
CircuitPlayground.setPixelColor(i, 0);
}
}
}
}
void stopNoteAndLight() {
stopPWM(A0); // 停止自PWM
turnOffAllLEDs(); // 关闭所有 LED 灯
currentNote = -1;
}
void turnOffAllLEDs() {
for (int i = 0; i < 10; i++) {
CircuitPlayground.setPixelColor(i, 0); // 关闭 LED
}
}
void checkButtons() {
if (CircuitPlayground.leftButton()) {
volume = max(volume - 5, 0); // 减小音量
Serial.print("Volume Down: ");
Serial.println(volume);
delay(200);
}
if (CircuitPlayground.rightButton()) {
volume = min(volume + 5, 127); // 增大音量
Serial.print("Volume Up: ");
Serial.println(volume);
delay(200);
}
}
void startPWM(int pin, int frequency, int volume) {
int dutyCycle = map(volume, 0, 127, 0, 255);
analogWrite(pin, dutyCycle);
tone(pin, frequency);
}
void stopPWM(int pin) {
noTone(pin);
}
到此所有任务就都做完了,板子的所有功能也都玩了一遍~
|