音随律动-基于arduino-nano、ws2812灯带、KEYES麦克风检测模块的声光显示

本文介绍了如何使用ArduinoNano、WS2812灯带和KEYES麦克风模块创建一个声光互动装置,通过音乐的音量变化控制灯带亮度,实现动态灯光效果。作者分享了代码细节和实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

音随律动-基于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]

总结

自己第一次分享,属于边学边记录,也想以此激励自己。所言有误之处恳请批评指正。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值