制作频谱灯

本文介绍了如何使用ESP32和相关模块如Max4466和WS2812B,配合ArduinoFFT库,实现将声音信号转换为频谱灯的项目。作者详细描述了电路设计、编程代码和操作过程,旨在户外露营提供照明和声音分析体验。
摘要由CSDN通过智能技术生成

最近研究了下傅里叶变换,用它可以通过采集声音信号由时域转换到频域内,从而得到声音的频谱信息,可以做个频谱灯。

主要使用ESP32来实现了他,实现效果如下:

频谱灯

为了可以带出去露营,我把它做的很大,这样露营的时候效果会更好!

总体架子是木头设计的,高度60cm,如果用3d打印,这么大会挺费钱。

 

 

电路部分,主要使用Esp32板子,加上声音传感器模块Max4466,还有灯条ws2812B,用了128个灯,每列16个,共八列。

在Arduino里写的代码如下: 需要先加载两个库,一个是FFT,如下图第二个

还有个FastLed 

频谱灯源码如下,4口输入声音电压信号,22口输出控制led信号


#include <TimeLib.h>
#include <FastLED.h>
#include "arduinoFFT.h"


/********************FFT相关定义**********************************/
#define CHANNEL 4  //选择音频输入IO口序号为4

arduinoFFT FFT = arduinoFFT(); //创建FFT对象

const uint16_t samples = 64; //采样点数,必须为2的整数次幂
const double samplingFrequency = 4000; //Hz, 声音采样频率

unsigned int sampling_period_us;
unsigned long microseconds;

double vReal[samples]; //FFT采样输入样本数组
double vImag[samples]; //FFT运算输出数组

int16_t vvalue;  //fft后的值

int16_t volume[8]; //保存下降数据

static uint32_t t=0, dt = 70;
static uint8_t flag=0;

//FFT参数,保持默认即可
#define SCL_INDEX 0x00
#define SCL_TIME 0x01
#define SCL_FREQUENCY 0x02
#define SCL_PLOT 0x03
/********************FFT相关定义*********************************/


/*******************灯板参数定义*********************************/
#define LED_PIN     22  //灯板输入IO口选择
#define NUM_LEDS    128  //灯珠数量
#define BRIGHTNESS  10  //默认背光亮度
#define LED_TYPE    WS2812  //灯珠类型
#define COLOR_ORDER GRB  //色彩顺序

CRGB leds[NUM_LEDS];  //定义LED对象
/*******************灯板参数定义*********************************/


void drawBar(int idx, int16_t value, uint8_t *flag)  //绘制函数,按序号和幅度值绘制条形块
{
  //static 
  constrain(value,0,16); //幅度限制在0-8范围内

  if(volume[idx] < value)  //采集到的数据比之前大则更新,实现上冲效果
    volume[idx] = value;

  //if(idx%2){ //余2运算判断序号是否为奇数
   // for(int i = 0;i<16-volume[idx];i++) leds[idx*16+i] = CRGB::Black;
  //}else{
    for(int i = volume[idx];i<16;i++) leds[idx*16+i] = CRGB::Black;
 // }

  //for(int i = volume[idx];i<16;i++) leds[i]=CRGB::Black;

  

  if(*flag){
    volume[idx] -= 1;  //达到时间则减小1,表示下落
    if(idx == 7) *flag = 0;  //第0-7列均更新完毕则清除标记
  }
}

void setup(){
  sampling_period_us = round(1000000*(1.0/samplingFrequency)); //计算采样频率
  pinMode(CHANNEL,INPUT); //初始化麦克风接口为输入模式,表示读取麦克风数据
  
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);//初始化LED灯带
  FastLED.setBrightness( BRIGHTNESS ); //LED亮度设置,取值范围为0-255
  //Serial.begin(9600);
}

void loop() 
{  
  /*采样*/
  microseconds = micros();
  for(int i=0; i<samples; i++)
  {
      vReal[i] = analogRead(CHANNEL);  //读取模拟值,信号采样
      vImag[i] = 0;
      while(micros() - microseconds < sampling_period_us){
        //empty loop
      }
      microseconds += sampling_period_us;
  }

  /*FFT运算*/
  FFT.Windowing(vReal, samples, FFT_WIN_TYP_HAMMING, FFT_FORWARD);  /* Weigh data */
  FFT.Compute(vReal, vImag, samples, FFT_FORWARD); /* Compute FFT */
  FFT.ComplexToMagnitude(vReal, vImag, samples); /* Compute magnitudes */
    
  fill_rainbow((leds), 128/*数量*/, 0/*开始色值*/, 2/*递增值*/); //设置彩虹渐变,先填充满,然后根据取值大小填充黑色,表示熄灭灯
  for(int i = 0; i < 8; i++){  //循环遍历八列LED
    vvalue=(vReal[i*3+2]+vReal[i*3+3]+vReal[i*3+4])/3/100;
    //if(i==6)
    //{
    //Serial.println(vvalue);
    //}
    vvalue=vvalue-2;
    if(vvalue>16) vvalue=16;
    drawBar(i, vvalue, &flag); //选取频谱中取平均后的8个值,传递时间标志到绘制函数
  }
  FastLED.show(); //显示灯条
  
  if((millis()-t) > dt){ //读取时间,判断是否达到掉落时长
      flag = 1;  //达到则标记为1
      t = millis(); //更新时间
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值