音随律动-基于arduino-nano、ws2812灯带、KEYES麦克风检测模块的声光互动
利用arduino-nano,ws2812灯带以及KEYES麦克风检测模块做了个小项目,灯光可以随着音浪大小而起伏波动,貌似很带感(可以在宿舍蹦迪了(狗头))
模块
如标题所述,主要就用到了三个模块,nano控制板,ws2812灯带以及麦克风模块。均可在淘宝购置。
方案
麦克风模块直接读取音量的大小,灯带是一个16X16的阵列。阵列的高度与音量是正相关的,音量越大,每一列亮的灯的数量也就越多,并在此基础上利用随机数模拟音浪起起伏伏的感觉。话不多说,上代码。
代码
首先是音量数据的读取。
int soundReadAndProcess()
{
int sum = 0;
bool silence = false;
for (int i=0;i<100;i++)
{
sum += analogRead(soundReadPin);
}
VolumeValue = int(sum/100); //对音量数据求100次和取平均,平滑数据
if (VolumeValue>250) {VolumeValue = 250;} //主要是根据模拟量的大小,做了个限幅,这个可以根据你实际情况修改
if(VolumeValue<=10) {silence = true;} //认为音量小于10则静音
else
{
silence = false;
if(VolumeValue/50.0 <=1) {columnLedNumber = 4;}
else if(VolumeValue/50.0 <=2) {columnLedNumber = 8;}
else if(VolumeValue/50.0 <=3) {columnLedNumber = 11;}
else if(VolumeValue/50.0 <=4) {columnLedNumber = 14;}
else {columnLedNumber = 16;} //根据音量的大小分级,每级的初始化列亮的灯数不同
}
if (silence) //如果静音,则不亮灯
{
for (int i=0; i<16; i++){randonArray[i]=0;}
}
else
{
for (int i=0; i<16; i++)
{
randonArray[i] = columnLedNumber + random(7)-3; //在根据等级每列灯亮的数量加上随机数模拟音量的效果
if(randonArray[i] > 16) {randonArray[i] = 16;} //每列最多有16个灯
//波浪灯光模拟,3表示波浪幅度大小,可调。
}
}
}
由于音量是模拟量,数据比较波动,所以简单的做了个均值处理,然后就是根据音量大小做了分级,每一级对应的各列初始化亮的灯数是不一样的。静音状态下不亮灯,超过两百则是全量。当然这个值也得根据自己测量而定,先用串口输出读取的模拟量值,然后测试一下最大,最低值即可。
randonArray这个数组就是记录16列每列实际亮的灯数,这个数就是在分级后的初始化灯数columnLedNumber加上或减去一个随机值,从而达到模拟音浪效果。
接下来就是对上面计算好的每列灯阵实际应该亮的进行控制。
void soundWaveAnalog() // 16*16
{
int beginSeq = 0;
for (int i=0; i<16; i++)
{
int red = random(256); // 随机颜色
int green = random(256);
int blue = random(256);
if(i%2==0) {beginSeq = 16*i; numMinOrPlusFlag=true;} //根据点阵灯的结构,索引有加有减
else{beginSeq = 16*(i+1) -1; numMinOrPlusFlag=false;}
for (int j=0; j<randonArray[i]; j++)// 给需要亮的灯初始化颜色
{
leds[beginSeq] = CRGB(red, green, blue);
if(numMinOrPlusFlag){beginSeq++;}
else{beginSeq--;}
}
for (int k=randonArray[i]; k<16; k++) //剩下不需要亮的灯置黑
{
leds[beginSeq] = CRGB::Black;
if(numMinOrPlusFlag){beginSeq++;}
else{beginSeq--;}
}
}
}
基本逻辑就是对16列进行遍历,每一列随机灯的颜色,由于这个灯带的顺序是“S”型的,所以有numMinOrPlusFlag标志来判断每列的灯带是递增或递减的。然后每列剩下的灯就置黑不亮了。
最后在loop函数中调用即可
void loop() {
//Serial.println(VolumeValue);
soundReadAndProcess();
if (millis() - waveTimer > waveInterval)
{
soundWaveAnalog();
waveTimer = millis();
}
flawLed();
FastLED.show(); // 显示效果
}
首先读取麦克风音量值,然后每隔一段时间间隔执行音量模拟程序,这里之所以要间隔一段时间(大约是50ms),主要是不让灯变化的那么快,时间自己而定。最后FastLED.show();即可。
火焰金睛的你是后还发现了一个函数flawed(),哈哈哈哈这个是因为我之前还有一条灯带,所以本着不浪费的原则就充分利用了,主要就是一个辅助流水灯的效果。
void flawLed()
{
switch (flawState)
{
case 1:
if (millis() - flawTimer > flawInterval1)
{
leds[second] = colorArray[color_order];
second++;
if (second==256+60){second=256; color_order++;}
if(color_order==8){color_order=0;flawState=2;}
flawTimer = millis();
}
break;
case 2:
if (millis() - flawTimer > flawInterval2)
{
int color_random = random(8);
for (int i=256;i<316;i++)
{
leds[i] = colorArray[color_random];
int dark_num = random(256, 316);
leds[dark_num] = CRGB::Black;
// if(dark_num<3){leds[i-1] = CRGB::Black;}
}
state_2_counter ++;
if(state_2_counter==100){state_2_counter=0;flawState=1;}
flawTimer = millis();
}
break;
}
}
由于灯带是接在灯带阵列后面的,所以序号数是在原来256的基础上加了60。这里逻辑比较简单,就不赘诉了。状态一实现顺序亮并且变色的效果,状态二是随机不用颜色亮灭效果,两个效果自动切换。
最后附上所有代码
#include <FastLED.h>
#define LED_PIN 7 //灯的引脚
#define NUM_LEDS 256+60 //灯的数量
CRGB leds[NUM_LEDS];
#define soundReadPin A0 //读取音量大小引脚
int VolumeValue = 0;
int columnLedNumber = 0;
int randonArray[16]; // 用于存放每列点阵中具体要量的灯的个数
bool numMinOrPlusFlag = false; // 由于点阵是s型的,所有灯的索引有加或者减
int second = 256;
unsigned long waveTimer = 0;
unsigned long waveInterval = 50; //用于控制每轮灯闪烁的时间,可调。
unsigned long flawTimer = 0;
unsigned long flawInterval1 = 20; //用于控制每轮灯闪烁的时间,可调。
unsigned long flawInterval2 = 200; //用于控制每轮灯闪烁的时间,可调。
#define NUM_COLORS 8
CRGB colorArray[NUM_COLORS] = {
CRGB::Red, // 红色
CRGB::Green, // 绿色
CRGB::Blue, // 蓝色
CRGB::Yellow, // 黄色
CRGB::Purple, // 紫色
CRGB::Cyan, // 青色
CRGB::Orange, // 橙色
CRGB::Pink // 粉色
};
int flawState = 1;
int color_order = 0;
int state_2_counter = 0;
void setup() {
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS); //灯初始化
//Serial.begin(115200);
}
void loop() {
//Serial.println(VolumeValue);
soundReadAndProcess();
if (millis() - waveTimer > waveInterval)
{
soundWaveAnalog();
waveTimer = millis();
}
flawLed();
FastLED.show(); // 显示效果
}
int soundReadAndProcess()
{
int sum = 0;
bool silence = false;
for (int i=0;i<100;i++)
{
sum += analogRead(soundReadPin);
}
VolumeValue = int(sum/100); //对音量数据求100次和取平均,平滑数据
if (VolumeValue>250) {VolumeValue = 250;} //主要是根据模拟量的大小,做了个限幅,这个可以根据你实际情况修改
if(VolumeValue<=10) {silence = true;} //认为音量小于10则静音
else
{
silence = false;
if(VolumeValue/50.0 <=1) {columnLedNumber = 4;}
else if(VolumeValue/50.0 <=2) {columnLedNumber = 8;}
else if(VolumeValue/50.0 <=3) {columnLedNumber = 11;}
else if(VolumeValue/50.0 <=4) {columnLedNumber = 14;}
else {columnLedNumber = 16;} //根据音量的大小分级,每级的初始化列亮的灯数不同
}
if (silence) //如果静音,则不亮灯
{
for (int i=0; i<16; i++){randonArray[i]=0;}
}
else
{
for (int i=0; i<16; i++)
{
randonArray[i] = columnLedNumber + random(7)-3; //在根据等级每列灯亮的数量加上随机数模拟音量的效果
if(randonArray[i] > 16) {randonArray[i] = 16;} //每列最多有16个灯
//波浪灯光模拟,3表示波浪幅度大小,可调。
}
}
}
void soundWaveAnalog() // 16*16
{
int beginSeq = 0;
for (int i=0; i<16; i++)
{
int red = random(256); // 随机颜色
int green = random(256);
int blue = random(256);
if(i%2==0) {beginSeq = 16*i; numMinOrPlusFlag=true;} //根据点阵灯的结构,索引有加有减
else{beginSeq = 16*(i+1) -1; numMinOrPlusFlag=false;}
for (int j=0; j<randonArray[i]; j++)// 给需要亮的灯初始化颜色
{
leds[beginSeq] = CRGB(red, green, blue);
if(numMinOrPlusFlag){beginSeq++;}
else{beginSeq--;}
}
for (int k=randonArray[i]; k<16; k++) //剩下不需要亮的灯置黑
{
leds[beginSeq] = CRGB::Black;
if(numMinOrPlusFlag){beginSeq++;}
else{beginSeq--;}
}
}
// FastLED.show(); // 显示效果
}
void flawLed()
{
switch (flawState)
{
case 1:
if (millis() - flawTimer > flawInterval1)
{
leds[second] = colorArray[color_order];
second++;
if (second==256+60){second=256; color_order++;}
if(color_order==8){color_order=0;flawState=2;}
flawTimer = millis();
}
break;
case 2:
if (millis() - flawTimer > flawInterval2)
{
int color_random = random(8);
for (int i=256;i<316;i++)
{
leds[i] = colorArray[color_random];
int dark_num = random(256, 316);
leds[dark_num] = CRGB::Black;
// if(dark_num<3){leds[i-1] = CRGB::Black;}
}
state_2_counter ++;
if(state_2_counter==100){state_2_counter=0;flawState=1;}
flawTimer = millis();
}
break;
}
}
演示视频
链接: [https://www.bilibili.com/video/BV1vw411P7vE/?vd_source=60baa4220f53b74733d257650536919b]
总结
自己第一次分享,属于边学边记录,也想以此激励自己。所言有误之处恳请批评指正。