102浏览
查看: 102|回复: 2

[项目] 【Arduino 动手做】8-64通道FFT频谱分析仪

[复制链接]
这与 FFT 频谱分析仪有关。它有 8、16、24、32 甚至 64 个频率箱(通道),如果您修改固件,您也许可以将该频率箱数量增加一倍。
PCB 能够驱动像素矩阵(WS2812 或 LED 矩阵),或者您可以连接一个或多个 HUB75E 显示器,但您必须选择使用哪一个并相应地调整设置中的参数。
您可以使用音频输入连接音频信号,也可以使用麦克风输入连接小型电容式麦克风。虽然使用麦克风会因为它的局限性而限制频率响应。
输入灵敏度是自动的,它将调整到输入的电平。您可以调整亮度和峰值保持时间。当它没有收到任何输入信号时,一段时间后,它将进入点火模式,在这种模式下,一些 led/显示器会像火一样亮起。
我为我的原型使用了两块 HUB75E 面板,因为它们安装在木架上。PCB 安装在显示器的背面。我为电子产品设计了一个 PCB。PCB 可以在我的 Tindie 网上商店购买。固件 (Arduino Sketch ) 是开源的,您可以根据需要对其进行修改。
以下是规格:
音频中的线路
音频中的麦克风
8、16、24、32 或 64 个通道
不同的图案和颜色
可调峰值延迟时间
可选输入滤波器
每隔几秒钟自动更改模式(可设置为开/关)
基于ESP32
PCB 提供板载前置放大器和连接器或 HUB75 显示器
可驱动 WS2812 等灯带
可驱动系列HUB75€面板
可调节整体亮度
PCB 提供预组装的 SMD 元件。您只需要添加 ESP32 板以及连接器/运算放大器和插座。
提供原理图和PCB布局以及gerber文件(PCB生产文件)
正在进行的工作:
未来的固件更新将包括:
平滑的 LED 过渡,因为 LED 速度非常快,它会跟随数据流。我想在每个 LED 转动时实现一种余辉。这样移动的酒吧看起来就不那么忙碌了。
Web 界面,我想在 wifi 上输出数据,以便在 Web 浏览器上实时可视化频谱。尽管从技术上讲,这已经完成,但挑战将是同时使用 ADC。那些了解 ESP32 架构的人现在会明白我的意思。
更改频段数 运行时间 与现在不同,它是在编译之前完成的,并且只能通过上传具有更改参数的固件来更改。
详细化和自动化校准模式。

我设计了一个 PCB,您可以在我的 Tindie 商店购买:
https://www.tindie.com/products/markdonners/pcb-8-...
所有 SMD 组件均已预组装,您只需添加一些插座、连接器和 e ESP32 板。
我使用了 DOIT 的 ESP32 DEVKIT V1
您还需要一个像样的电源,仅使用 USB 为此 PCB 和显示器或 ledmatrix 供电会损坏您的 ESP32,因为板载稳压器无法处理那么大的功率。
您还必须决定要使用哪种可视化效果。您可以使用一个或多个 HUB75 显示器,也可以使用基于像素的矩阵。您可以购买一个矩阵,也可以制作一个带有多个 LED 灯条的矩阵。
您需要相应地调整 arduino 草图中的设置。

我使用了 Arduino IDE。它可以在线免费获得,并且可以完成这项工作。然而,我最近偶然发现了一种叫做 Sloeber Beryllium 的东西,这是一个很棒的工具,提供了更好的编译器接口。然而,它有一点学习曲线,但我保证,这是值得的!你为什么不去看看呢?您还可以使用 Visual Studio 或其他一些出色的 IDE。但是,重要的是 正确的库 最好不要安装不需要的内容,因为它可能会在编译时给您带来错误。确保您的 Arduino IDE 设置为使用 ESP32。如果您不知道如何作,请谷歌或查看 youtube 视频。有一些非常明确的说明,设置 IDE 并不难。你可以的!

8-64 通道 FFT 频谱分析仪

音频中的线路
音频中的麦克风
8、16、24、32 或 64 个通道
不同的图案和颜色
可调峰值延迟时间
可选输入滤波器
每隔几秒钟自动更改模式(可设置为开/关)
基于ESP32
PCB 提供板载前置放大器和连接器或 HUB75 显示器
可驱动 WS2812 等灯带
可驱动系列HUB75€面板
可调节整体亮度 PCB 提供预组装的 SMD 元件。您只需要添加 ESP32 板以及连接器/运算放大器和插座。 提供原理图和PCB布局以及gerber文件(PCB生产文件) 正在进行的工作: 未来的固件更新将包括:
平滑的 LED 过渡,因为 LED 速度非常快,它会跟随数据流。我想在每个 LED 转动时实现一种余辉。这样移动的酒吧看起来就不那么忙碌了。
Web 界面,我想在 wifi 上输出数据,以便在 Web 浏览器上实时可视化频谱。尽管从技术上讲,这已经完成,但挑战将是同时使用 ADC。那些了解 ESP32 架构的人现在会明白我的意思。
更改频段数 运行时间 与现在不同,它是在编译之前完成的,并且只能通过上传具有更改参数的固件来更改。
详细化和自动化校准模式。

【Arduino 动手做】8-64通道FFT频谱分析仪图5

【Arduino 动手做】8-64通道FFT频谱分析仪图1

【Arduino 动手做】8-64通道FFT频谱分析仪图2

【Arduino 动手做】8-64通道FFT频谱分析仪图3

【Arduino 动手做】8-64通道FFT频谱分析仪图6

【Arduino 动手做】8-64通道FFT频谱分析仪图4

【Arduino 动手做】8-64通道FFT频谱分析仪图7

驴友花雕  中级技神
 楼主|

发表于 2025-8-2 09:52:54

【Arduino 动手做】8-64通道FFT频谱分析仪

项目代码

  1. /********************************************************************************************************************************************************
  2. *                                                                                                                                                       *
  3. *  Project:         FFT Spectrum Analyzer                                                                                                               *
  4. *  Target Platform: ESP32                                                                                                                               *
  5. *                                                                                                                                                       *
  6. *  Version: 1.0                                                                                                                                         *
  7. *  Hardware setup: See github                                                                                                                           *
  8. *  Spectrum analyses done with analog chips MSGEQ7                                                                                                      *
  9. *                                                                                                                                                       *
  10. *  Mark Donners                                                                                                                                         *
  11. *  The Electronic Engineer                                                                                                                              *
  12. *  Website:   www.theelectronicengineer.nl                                                                                                              *
  13. *  facebook:  https://www.facebook.com/TheelectronicEngineer                                                                                            *
  14. *  youtube:   https://www.youtube.com/channel/UCm5wy-2RoXGjG2F9wpDFF3w                                                                                  *
  15. *  github:    https://github.com/donnersm                                                                                                               *
  16. *                                                                                                                                                       *  
  17. *********************************************************************************************************************************************************
  18. * Version History                                                                                                                                       *
  19. *  1.0 First release, code extraced from 14 band spectrum analyzer 3.00 and modified to by used with FFT on a ESP32. No need for frequency board or     *
  20. *      MCGEQ7 chips.                                                                                                                                    *
  21. *      - HUB75 interface or                                                                                                                             *
  22. *      - WS2812 leds ( matrix/ledstrips)                                                                                                                *
  23. *      - 8/16/32 or 64 channel analyzer                                                                                                                 *
  24. *      - calibration for White noise, pink noise, brown noise sensitivity included and selectable                                                         *
  25. *      - Fire screensaver                                                                                                                               *
  26. *      - Display of logo and interface text when used with HUB75                                                                                        *
  27. *                                                                                                                                                       *
  28. *********************************************************************************************************************************************************
  29. * Version FFT 1.0 release July 2021                                                                                                                     *
  30. *********************************************************************************************************************************************************
  31. *  Status   | Description                                                                                                                               *
  32. *  Open     | Some Hub75 displays use a combination of chipsets of are from a different productions batch which will not work with this libary          *
  33. *  Open     | Sometime the long press for activating/de-activating the autoChange Pattern mode doesn't work                                             *
  34. *  Solved   | When using 64 bands, band 0 is always at max value. This was caused by the array dize [64]-> solved by chnaging it to 65                  *
  35. * Not a bug | Different types of HUB75 displays require different libary settings.It is what it is and it all depends on what the distributer sends you.*
  36. *           | For into on the libary settings, see the library documentation on Github: https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA  *
  37. * Wish      | Web interface. not possible without some heavy workaround cant use WIFI and ADC at same time                                              *
  38. * *******************************************************************************************************************************************************         
  39. * People who inspired me to do this build and figure out how stuff works:
  40. * Dave Plummer         https://www.youtube.com/channel/UCNzszbnvQeFzObW0ghk0Ckw
  41. * Mrfaptastic          https://github.com/mrfaptastic
  42. * Scott Marley         https://www.youtube.com/user/scottmarley85
  43. * Brian Lough          https://www.youtube.com/user/witnessmenow
  44. * atomic14             https://www.youtube.com/channel/UC4Otk-uDioJN0tg6s1QO9lw
  45. *
  46. * Make sure your arduino IDE settings: Compiler warnings is set to default to make sure the code will compile                                           */
  47. #define VERSION     "V1.0"
  48. #include <FastLED_NeoMatrix.h>
  49. #include <arduinoFFT.h>
  50. #include "I2SPLUGIN.h"
  51. #include <math.h>
  52. #include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
  53. #include "FFT.h"
  54. #include "LEDDRIVER.H"
  55. #include "Settings.h"
  56. #include "PatternsHUB75.h"
  57. #include "PatternsLedstrip.h"                           
  58. #include "fire.h"
  59. #include "logos.h"
  60. int skip=true;
  61. int ButtonOnTimer=0;
  62. int ButtonStarttime=0;
  63. int ButtonSequenceCounter=0;
  64. int ButtonOffTimer=0;
  65. int ButtonStoptime=0;
  66. int ButtonPressedCounter=0;
  67. int ButtonReleasedCounter=0;
  68. int ShortPressFlag=0;
  69. int LongPressFlag=0;
  70. int LongerPressFlag=0;
  71. boolean Next_is_new_pressed= true;
  72. boolean Next_is_new_release = true;
  73. int PreviousPressTime=0;
  74. #define up  1
  75. #define down 0
  76. int PeakDirection=0;
  77. long LastDoNothingTime = 0;                       // only needed for screensaver
  78. int DemoModeMem=0;                                   // to remember what mode we are in when going to demo, in order to restore it after wake up
  79. bool AutoModeMem=false;                                // same story
  80. bool DemoFlag=false;                               // we need to know if demo mode was manually selected or auto engadged.
  81. char LCDPrintBuf[30];
  82. void setup() {
  83. Serial.begin(115200);
  84. Serial.println("Setting up Audio Input I2S");
  85. setupI2S();
  86. Serial.println("Audio input setup completed");
  87. delay(1000);
  88. #ifdef Ledstrip
  89.   SetupLEDSTRIP();
  90. #endif  
  91. #ifdef HUB75
  92.   SetupHUB75();
  93.   if (kMatrixHeight>60){
  94.   dma_display->setBrightness8(100);
  95.   #if LogoBoot
  96.    drawLogo();
  97.    delay(2500);
  98.   #endif
  99.   }
  100. #endif
  101. }
  102. void loop() {
  103. size_t bytesRead = 0;
  104. int TempADC=0;
  105. if (skip==false)i2s_adc_disable(I2S_NUM_0);
  106. skip=false; // we only want to skip this the very first loop run.
  107. //Handle Userinterface
  108. {
  109.   // set brightness and test if button is pressed
  110.   TempADC = analogRead(BRIGHTNESSPOT);
  111.   if (TempADC<10){ // ADC value < 10 so button is pressed
  112.     ButtonOffTimer=0;
  113.     ButtonOnTimer=millis()-ButtonStarttime;
  114.     ButtonStoptime=millis();
  115.   }
  116.   else { // no Button pressed so ADC value is not related to button and can be proccessed
  117.   ButtonOnTimer=0;
  118.   ButtonOffTimer=millis()-ButtonStoptime;
  119.   ButtonStarttime = millis();
  120.   
  121.   // read potmeters and process
  122.   Peakdelay=map(analogRead(PEAKDELAYPOT),0,4095,1,100);
  123.   BRIGHTNESSMARK=map(TempADC,100,2100,BRIGHTNESSMIN,BRIGHTNESSMAX);
  124. // dbgprint("potm:%d",BRIGHTNESSMARK);
  125.   #ifdef Ledstrip
  126.     FastLED.setBrightness(BRIGHTNESSMARK);
  127.   #endif
  128.   #ifdef HUB75
  129.     dma_display->setBrightness8(map(TempADC,100,2100,BRIGHTNESSMIN,BRIGHTNESSMAX));
  130.    #endif
  131.   }
  132.   if(ButtonOffTimer>ButtonTimeout){
  133.     ButtonStoptime=millis(); // time that no switch was presset will reset the counter.
  134.     ButtonSequenceCounter=0; // reset the sequencecounter
  135.     if (ShortPressFlag==1){
  136.      // Serial.printf("Short press detected\n");
  137.       buttonPushCounter = (buttonPushCounter + 1) % 13;
  138.       #ifdef HUB75
  139.       dma_display->clearScreen();
  140.       #endif
  141.       Serial.printf("Pattern Mode changed to: %d\n",buttonPushCounter);
  142.       ShortPressFlag=0;
  143.     }
  144.   }
  145.   if ((ButtonOnTimer>LongerPress)&&(ButtonOnTimer<(LongerPress+ShortPress))){LongerPressFlag=1;}
  146.   else if  ((ButtonOnTimer>LongPress)&&(ButtonOnTimer<(LongPress+ShortPress))){LongPressFlag=1;}
  147.   else if ((ButtonOnTimer>ShortPress)&&(ButtonOnTimer<(2*ShortPress))){
  148.     ShortPressFlag=1;
  149.     if((millis()-PreviousPressTime)<ButtonSequenceRepeatTime){
  150.       ButtonSequenceCounter++;
  151.       dbgprint("Multible press counter: %d\n",ButtonSequenceCounter);
  152.       ShortPressFlag=0;
  153.       CalibrationType= (CalibrationType +1)% 4;
  154.       Serial.printf("Calibration table changed to: %s\n",Filtername[CalibrationType]);
  155.       
  156.       sprintf(LCDPrintBuf,"Cal Filter: %s",Filtername[CalibrationType]);
  157.       DisplayPrint(LCDPrintBuf);
  158.       
  159.     }
  160.     PreviousPressTime=millis();
  161.   }
  162.   if (LongerPressFlag==1){
  163.     dbgprint( "Longer press detected\n");
  164.     autoChangePatterns = !autoChangePatterns;
  165.     if(autoChangePatterns == true){
  166.       Serial.print("Patterns wil now change every few seconds\n");
  167.       DisplayPrint((char*) "Autochange ON");
  168.     }
  169.     else {
  170.       Serial.print("Automatically changing of pattern is now disabled\n");
  171.       DisplayPrint((char*) "Autochange OFF");
  172.     }
  173.     LongerPressFlag=0;
  174.     ShortPressFlag=0;
  175.   }
  176.   else if (LongPressFlag==1){
  177.     dbgprint("long press detected\n");
  178.     autoChangePatterns = !autoChangePatterns;
  179.     if(autoChangePatterns == true){
  180.       Serial.print("Patterns wil now change every few seconds\n");
  181.       DisplayPrint((char*) "Autochange ON");
  182.     }
  183.     else {
  184.       Serial.print("Automatically changing of pattern is now disabled\n");
  185.       DisplayPrint((char*)"Autochange OFF");
  186.     }
  187.     LongPressFlag=0;
  188.     ShortPressFlag=0;
  189.   }
  190. } // end user interface
  191.   //############ Step 1: read samples from the I2S Buffer ##################
  192. i2s_adc_enable(I2S_NUM_0);
  193. i2s_read(I2S_PORT,
  194.          (void*)samples,
  195.           sizeof(samples),  
  196.           &bytesRead,   // workaround This is the actual buffer size last half will be empty but why?
  197.           portMAX_DELAY); // no timeout
  198. if (bytesRead != sizeof(samples)){
  199.    Serial.printf("Could only read %u bytes of %u in FillBufferI2S()\n", bytesRead, sizeof(samples));
  200.    // return;
  201. }
  202. //############ Step 2: compensate for Channel number and offset, safe all to vReal Array   ############
  203. for (uint16_t i = 0; i < ARRAYSIZE(samples); i++) {
  204.    vReal[i] = offset-samples[i];
  205.    vImag[i] = 0.0; //Imaginary part must be zeroed in case of looping to avoid wrong calculations and overflows
  206.    #if PrintADCRAW
  207.     Serial.printf("%7d,",samples[i]);
  208.    #endif
  209.    #if VisualizeAudio
  210.     Serial.printf("%d\n",samples[i]);
  211.    #endif
  212.   }
  213.    #if PrintADCRAW
  214.     Serial.printf("\n");
  215.    #endif
  216. //############ Step 3: Do FFT on the VReal array  ############
  217.   // compute FFT
  218.   FFT.DCRemoval();
  219.   FFT.Windowing(vReal, SAMPLEBLOCK, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  220.   FFT.Compute(vReal, vImag, SAMPLEBLOCK, FFT_FORWARD);
  221.   FFT.ComplexToMagnitude(vReal, vImag, SAMPLEBLOCK);
  222.   FFT.MajorPeak(vReal, SAMPLEBLOCK, samplingFrequency);
  223.   for (int i = 0; i < numBands; i++) {
  224.     FreqBins[i] = 0;
  225.   }
  226. //############ Step 4: Fill the frequency bins with the FFT Samples ############
  227. float averageSum = 0.0f;
  228. for (int i = 2; i < SAMPLEBLOCK / 2; i++){
  229.    averageSum+=vReal[i];
  230.    if (vReal[i] > NoiseTresshold){
  231.      int freq = BucketFrequency(i);
  232.      int iBand = 0;
  233.      while (iBand < numBands){
  234.        if (freq < BandCutoffTable[iBand])break;
  235.        iBand++;
  236.      }
  237.      if (iBand > numBands)iBand = numBands;
  238.      FreqBins[iBand]+= vReal[i];
  239.      //  float scaledValue = vReal[i];
  240.      //  if (scaledValue > peak[iBand])
  241.      //    peak[iBand] = scaledValue;
  242.    }
  243. }
  244. // bufmd[0]=FreqBins[12];
  245. #if PrintRAWBins
  246.   for ( int y=0; y<numBands;y++){
  247.     Serial.printf("%7.1f,",FreqBins[y]);
  248.   }
  249.   Serial.printf("\n");
  250. #endif
  251.   
  252. //############ Step 5: Determine the VU value  and mingle in the readout...( cheating the bands ) ############ Step
  253. float t=averageSum / (SAMPLEBLOCK / 2);
  254. gVU = max(t, (oldVU * 3 + t) / 4);
  255. oldVU = gVU;
  256. if(gVU>DemoTreshold)LastDoNothingTime = millis(); // if there is signal in any off the bands[>2] then no demo mode
  257.   // Serial.printf("gVu: %d\n",(int) gVU);
  258. for(int j=0;j<numBands;j++){
  259.    if (CalibrationType==1)FreqBins[j]*= BandCalibration_Pink[j];
  260.    else if (CalibrationType==2)FreqBins[j]*= BandCalibration_White[j];
  261.     else if (CalibrationType==3)FreqBins[j]*= BandCalibration_Brown[j];
  262. }
  263. //*
  264. //############ Step 6: Averaging and making it all fit on screen
  265. //for (int i = 0; i < numBands; i++) {
  266.    //Serial.printf ("Chan[%d]:%d",i,(int)FreqBins[i]);
  267.     //FreqBins[i] = powf(FreqBins[i], gLogScale); // in case we want log scale..i leave it in here as reminder
  268.    //  Serial.printf( " - log: %d \n",(int)FreqBins[i]);
  269. // }
  270. static float lastAllBandsPeak = 0.0f;
  271. float allBandsPeak = 0;
  272. //bufmd[1]=FreqBins[13];
  273. //bufmd[2]=FreqBins[1];
  274. for (int i = 0; i < numBands; i++){
  275.    //allBandsPeak = max (allBandsPeak, FreqBins[i]);
  276.    if (FreqBins[i]> allBandsPeak){
  277.      allBandsPeak = FreqBins[i];
  278.    }
  279. }   
  280. if (allBandsPeak < 1)allBandsPeak = 1;
  281. //  The followinf picks allBandsPeak if it's gone up.  If it's gone down, it "averages" it by faking a running average of GAIN_DAMPEN past peaks
  282. allBandsPeak = max(allBandsPeak, ((lastAllBandsPeak * (GAIN_DAMPEN-1)) + allBandsPeak) / GAIN_DAMPEN);  // Dampen rate of change a little bit on way down
  283. lastAllBandsPeak = allBandsPeak;
  284. if (allBandsPeak < 80000)allBandsPeak = 80000;
  285. for (int i = 0; i < numBands; i++){
  286.    FreqBins[i] /= (allBandsPeak * 1.0f);
  287. }
  288. // Process the FFT data into bar heights
  289. for (int band = 0; band < numBands; band++) {
  290.    int barHeight = FreqBins[band]*kMatrixHeight-1;  //(AMPLITUDE);
  291.    if (barHeight > TOP-2) barHeight = TOP-2;
  292.   
  293.    // Small amount of averaging between frames
  294.    barHeight = ((oldBarHeights[band] * 1) + barHeight) / 2;
  295.    // Move peak up
  296.    if (barHeight > peak[band]) {
  297.      peak[band] = min(TOP, barHeight);
  298.      PeakFlag[band]=1;
  299.    }
  300.     bndcounter[band]+=barHeight; // ten behoeve calibratie
  301.     // if there hasn't been much of a input signal for a longer time ( see settings ) go to demo mode
  302.    if ((millis() - LastDoNothingTime) > DemoAfterSec && DemoFlag==false)
  303.    { dbgprint("In loop 1:  %d", millis() - LastDoNothingTime);
  304.     DemoFlag=true;
  305.     // first store current mode so we can go back to it after wake up
  306.     DemoModeMem=buttonPushCounter;
  307.     AutoModeMem=autoChangePatterns;
  308.     autoChangePatterns=false;
  309.     buttonPushCounter=12;
  310.     #ifdef HUB75
  311.     dma_display->clearScreen();
  312.     #endif
  313.     dbgprint("Automode is turned of because of demo");
  314.    }
  315.    // Wait,signal is back? then wakeup!     
  316.     else if (DemoFlag==true &&   (millis() - LastDoNothingTime) < DemoAfterSec   )   
  317.     { //("In loop 2:  %d", millis() - LastDoNothingTime);
  318.       // while in demo the democounter was reset due to signal on one of the bars.
  319.       // So we need to exit demo mode.
  320.       #ifdef HUB75
  321.       dma_display->clearScreen();
  322.       #endif
  323.       buttonPushCounter=DemoModeMem; // restore settings
  324.       dbgprint ("automode setting restored to: %d",AutoModeMem);
  325.       autoChangePatterns=AutoModeMem;// restore settings
  326.       DemoFlag=false;  
  327.     }
  328.     #if BottomRowAlwaysOn
  329.       if (barHeight==0)barHeight=1; // make sure there is always one bar that lights up
  330.     #endif
  331.   // Now visualize those bar heights
  332.   switch (buttonPushCounter) {
  333.     case 0:
  334.      #ifdef HUB75
  335.       PeakDirection=down;
  336.       BoxedBars(band, barHeight);
  337.       BluePeak(band);
  338.      #endif
  339.      #ifdef Ledstrip
  340.       changingBarsLS(band, barHeight);
  341.      #endif
  342.      break;
  343.      
  344.     case 1:
  345.      #ifdef HUB75
  346.       PeakDirection=down;
  347.       BoxedBars2(band, barHeight);
  348.       BluePeak(band);
  349.      #endif
  350.      #ifdef Ledstrip
  351.      TriBarLS(band, barHeight);
  352.      TriPeakLS(band);
  353.      #endif
  354.      break;
  355.     case 2:
  356.      #ifdef HUB75
  357.       PeakDirection=down;
  358.       BoxedBars3(band, barHeight);
  359.       RedPeak(band);
  360.      #endif
  361.      #ifdef Ledstrip
  362.       rainbowBarsLS(band, barHeight);
  363.       NormalPeakLS(band, PeakColor1);
  364.      #endif
  365.       break;
  366.     case 3:
  367.      #ifdef HUB75
  368.       PeakDirection=down;
  369.       RedBars(band, barHeight);
  370.       BluePeak(band);
  371.      #endif
  372.      #ifdef Ledstrip
  373.       purpleBarsLS(band, barHeight);
  374.       NormalPeakLS(band, PeakColor2);
  375.      #endif
  376.       break;
  377.     case 4:
  378.      #ifdef HUB75
  379.       PeakDirection=down;
  380.       ColorBars(band, barHeight);
  381.      #endif
  382.      #ifdef Ledstrip
  383.       SameBarLS(band, barHeight);
  384.       NormalPeakLS(band, PeakColor3);
  385.      #endif
  386.       break;
  387.     case 5:
  388.      #ifdef HUB75
  389.       PeakDirection=down;
  390.       Twins(band, barHeight);
  391.       WhitePeak(band);
  392.      #endif
  393.      #ifdef Ledstrip
  394.       SameBar2LS(band, barHeight);
  395.       NormalPeakLS(band, PeakColor3);
  396.      #endif
  397.      break;
  398.     case 6:
  399.      #ifdef HUB75
  400.       PeakDirection=down;
  401.       Twins2(band, barHeight);
  402.       WhitePeak(band);
  403.      #endif
  404.      #ifdef Ledstrip
  405.       centerBarsLS(band, barHeight);
  406.      #endif
  407.       break;
  408.     case 7:
  409.      #ifdef HUB75
  410.        PeakDirection=down;
  411.       TriBars(band, barHeight);
  412.       TriPeak(band);
  413.      #endif
  414.      #ifdef Ledstrip
  415.       centerBars2LS(band, barHeight);
  416.      #endif
  417.       break;
  418.     case 8:
  419.      #ifdef HUB75
  420.       PeakDirection=up;
  421.       TriBars(band, barHeight);
  422.       TriPeak(band);
  423.      #endif
  424.      #ifdef Ledstrip
  425.       centerBars3LS(band, barHeight);
  426.      #endif
  427.       break;
  428.     case 9:
  429.      #ifdef HUB75
  430.      PeakDirection=down;
  431.      centerBars(band, barHeight);
  432.      #endif
  433.      #ifdef Ledstrip
  434.        BlackBarLS(band, barHeight);
  435.        outrunPeakLS(band);
  436.      #endif
  437.      break;
  438.     case 10:
  439.      #ifdef HUB75
  440.      PeakDirection=down;
  441.      centerBars2(band, barHeight);
  442.      #endif
  443.      #ifdef Ledstrip
  444.       BlackBarLS(band, barHeight);
  445.       NormalPeakLS(band, PeakColor5);
  446.      #endif
  447.       break;
  448.     case 11:
  449.      #ifdef HUB75
  450.       PeakDirection=down;
  451.       BlackBars(band, barHeight);
  452.       DoublePeak(band);     
  453.      #endif
  454.      #ifdef Ledstrip
  455.       BlackBarLS(band, barHeight);
  456.       TriPeak2LS(band);
  457.      #endif
  458.       break;
  459.     case 12:
  460.      #ifdef HUB75
  461.       make_fire(); // go to demo mode
  462.      #endif
  463.      #ifdef Ledstrip
  464.       matrix->fillRect(0, 0, matrix->width(), 1, 0x0000); // delete the VU meter
  465.       make_fire();
  466.      #endif
  467.      break;
  468.   }
  469.   
  470.   // Save oldBarHeights for averaging later
  471.   oldBarHeights[band] = barHeight;
  472. }
  473. // for calibration
  474. //bndcounter[h]+=barHeight;
  475. if (loopcounter==256){
  476.   loopcounter=0;
  477. #if CalibratieLog
  478.    Calibration();
  479.    for(int g=0;g<numBands;g++)bndcounter[g]=0;
  480. #endif
  481. }
  482. loopcounter++;
  483. if (buttonPushCounter!=12) DrawVUMeter(0); // Draw it when not in screensaver mode
  484. #if PrintRAWBins
  485.   Serial.printf("\n");
  486.   //delay(10);
  487. #endif
  488.   
  489.    // Decay peak
  490. EVERY_N_MILLISECONDS(Fallingspeed){
  491.    for (byte band = 0; band < numBands; band++){
  492.      if(PeakFlag[band]==1){
  493.        PeakTimer[band]++;
  494.        if (PeakTimer[band]> Peakdelay){PeakTimer[band]=0;PeakFlag[band]=0;}
  495.      }
  496.      else if ((peak[band] > 0) &&(PeakDirection==up)){
  497.        peak[band] += 1;
  498.        if (peak[band]>(kMatrixHeight+10))peak[band]=0;
  499.        } // when to far off screen then reset peak height
  500.      else if ((peak[band] > 0)&&(PeakDirection==down)){ peak[band] -= 1;}
  501.    }   
  502.      colorTimer++;
  503. }
  504. EVERY_N_MILLISECONDS(10)colorTimer++; // Used in some of the patterns
  505. EVERY_N_SECONDS(SecToChangePattern) {
  506.    // if (FastLED.getBrightness() == 0) FastLED.setBrightness(BRIGHTNESSMARK);  //Re-enable if lights are "off"
  507.    if (autoChangePatterns){
  508.     buttonPushCounter = (buttonPushCounter + 1) % 12;
  509.     #ifdef HUB75
  510.     dma_display->clearScreen();
  511.     #endif
  512.    }
  513. }
  514. #ifdef Ledstrip
  515.   delay(1); // needed to give fastled a minimum recovery time
  516.   FastLED.show();
  517. #endif
  518.   
  519. } // loop end
  520.   // BucketFrequency
  521.   //
  522.   // Return the frequency corresponding to the Nth sample bucket.  Skips the first two
  523.   // buckets which are overall amplitude and something else.
  524. int BucketFrequency(int iBucket){
  525. if (iBucket <= 1)return 0;
  526. int iOffset = iBucket - 2;
  527. return iOffset * (samplingFrequency / 2) / (SAMPLEBLOCK / 2);
  528. }
  529. void DrawVUPixels(int i, int yVU, int fadeBy = 0){
  530. CRGB VUC;
  531. if (i>(PANE_WIDTH/3)){
  532.    VUC.r=255;
  533.    VUC.g=0;
  534.    VUC.b=0 ;
  535. }
  536. else if (i>(PANE_WIDTH/5)){
  537.    VUC.r=255;
  538.    VUC.g=255;
  539.    VUC.b=0;
  540. }
  541. else{ // green
  542.    VUC.r=0;
  543.    VUC.g=255;
  544.    VUC.b=0;
  545. }
  546. #ifdef Ledstrip
  547.   int xHalf = matrix->width()/2;
  548. //  matrix->drawPixel(xHalf-i-1, yVU, CRGB(0,100,0).fadeToBlackBy(fadeBy));
  549. //  matrix->drawPixel(xHalf+i,   yVU, CRGB(0,100,0).fadeToBlackBy(fadeBy));
  550.   matrix->drawPixel(xHalf-i-1, yVU, CRGB(VUC.r,VUC.g,VUC.b).fadeToBlackBy(fadeBy));
  551.   matrix->drawPixel(xHalf+i,   yVU, CRGB(VUC.r,VUC.g,VUC.b).fadeToBlackBy(fadeBy));
  552.   
  553. #endif
  554. #ifdef HUB75
  555.   int xHalf = PANE_WIDTH/2;
  556.   dma_display->drawPixelRGB888(xHalf-i-2,yVU,VUC.r,VUC.g,VUC.b); //left side of screen line 0
  557.   dma_display->drawPixelRGB888(xHalf-i-2,yVU+1,VUC.r,VUC.g,VUC.b); //left side of screen line 1
  558.   dma_display->drawPixelRGB888(xHalf+i+1,yVU,VUC.r,VUC.g,VUC.b); // right side of screen line 0
  559.   dma_display->drawPixelRGB888(xHalf+i+1,yVU+1,VUC.r,VUC.g,VUC.b);// right side of screen line 1
  560. #endif
  561. }
  562. void DrawVUMeter(int yVU){
  563. static int iPeakVUy = 0;        // size (in LED pixels) of the VU peak
  564. static unsigned long msPeakVU = 0;       // timestamp in ms when that peak happened so we know how old it is
  565. const int MAX_FADE = 256;
  566. #ifdef HUB75
  567.   for(int x=0; x<PANE_WIDTH;x++){
  568.     dma_display->drawPixelRGB888(x,yVU,0,0,0);
  569.     dma_display->drawPixelRGB888(x,yVU+1,0,0,0);
  570.   }
  571. #endif
  572. #ifdef Ledstrip
  573.   matrix->fillRect(0, yVU, matrix->width(), 1, 0x0000);
  574. #endif
  575. if (iPeakVUy > 1){
  576.    int fade = MAX_FADE * (millis() - msPeakVU) / (float) 1000;
  577.    DrawVUPixels(iPeakVUy,   yVU, fade);
  578. }
  579. int xHalf = (PANE_WIDTH/2)-1;
  580. int bars  = map(gVU, 0, MAX_VU, 1, xHalf);
  581. bars = min(bars, xHalf);
  582. if(bars > iPeakVUy){
  583.    msPeakVU = millis();
  584.    iPeakVUy = bars;
  585. }
  586. else if (millis() - msPeakVU > 1000)iPeakVUy = 0;
  587. for (int i = 0; i < bars; i++)DrawVUPixels(i, yVU);
  588.       
  589. }
  590. void Calibration(void){
  591.   Serial.printf("BandCalibration_XXXX[%1d]=\n{",numBands);
  592.   long Totalbnd=0;
  593.   
  594.   for (int g=0;g<numBands;g++){
  595.     if (bndcounter[g]>Totalbnd)Totalbnd=bndcounter[g];
  596.   }
  597.   
  598.   
  599.   for (int g=0;g<numBands;g++){
  600.     bndcounter[g]=Totalbnd/bndcounter[g];
  601.     Serial.printf(" %2.2f",bndcounter[g]);
  602.     if(g<numBands-1)Serial.printf(",");
  603.     else Serial.print(" };\n");
  604.   }
  605. }
  606. //**************************************************************************************************
  607. //                                          D B G P R I N T                                        *
  608. //**************************************************************************************************
  609. // Send a line of info to serial output.  Works like vsprintf(), but checks the DEBUG flag.        *
  610. // Print only if DEBUG flag is true.  Always returns the formatted string.                         *
  611. // Usage dbgprint("this is the text you want: %d", variable);
  612. //**************************************************************************************************
  613. void dbgprint(const char * format, ...) {
  614.   if (DEBUG) {
  615.     static char sbuf[DEBUG_BUFFER_SIZE]; // For debug lines
  616.     va_list varArgs; // For variable number of params
  617.     va_start(varArgs, format); // Prepare parameters
  618.     vsnprintf(sbuf, sizeof(sbuf), format, varArgs); // Format the message
  619.     va_end(varArgs); // End of using parameters
  620.     if (DEBUG) // DEBUG on?
  621.     {
  622.       Serial.print("Debug: "); // Yes, print prefix
  623.       Serial.println(sbuf); // and the info
  624.     }
  625.    // return sbuf; // Return stored string
  626.   }
  627. }
  628. void make_fire() {
  629.   uint16_t i, j;
  630.   
  631.   if (t > millis()) return;
  632.   t = millis() + (1000 / FPS);
  633.   // First, move all existing heat points up the display and fade
  634.   for (i = rows - 1; i > 0; --i) {
  635.     for (j = 0; j < cols; ++j) {
  636.       uint8_t n = 0;
  637.       if (pix[i - 1][j] > 0)
  638.         n = pix[i - 1][j] - 1;
  639.       pix[i][j] = n;
  640.     }
  641.   }
  642.   // Heat the bottom row
  643.   for (j = 0; j < cols; ++j) {
  644.     i = pix[0][j];
  645.     if (i > 0) {
  646.       pix[0][j] = random(NCOLORS - 6, NCOLORS - 2);
  647.     }
  648.   }
  649.   // flare
  650.   for (i = 0; i < nflare; ++i) {
  651.     int x = flare[i] & 0xff;
  652.     int y = (flare[i] >> 8) & 0xff;
  653.     int z = (flare[i] >> 16) & 0xff;
  654.     glow(x, y, z);
  655.     if (z > 1) {
  656.       flare[i] = (flare[i] & 0xffff) | ((z - 1) << 16);
  657.     } else {
  658.       // This flare is out
  659.       for (int j = i + 1; j < nflare; ++j) {
  660.         flare[j - 1] = flare[j];
  661.       }
  662.       --nflare;
  663.     }
  664.   }
  665.   newflare();
  666.   // Set and draw
  667.   for (i = 0; i < rows; ++i) {
  668.     for (j = 0; j < cols; ++j) {
  669.      // matrix -> drawPixel(j, rows - i, colors[pix[i][j]]);
  670.      CRGB COlsplit=colors[pix[i][j]];
  671.      #ifdef HUB75
  672.       dma_display->drawPixelRGB888(j,rows - i,COlsplit.r,COlsplit.g,COlsplit.b);
  673.      #endif
  674.      #ifdef Ledstrip
  675.       matrix -> drawPixel(j, rows - i, colors[pix[i][j]]);
  676.      #endif
  677.     }
  678.   }
  679. }
  680. void drawLogo(void){
  681. #ifdef HUB75  
  682. // logo is 46 width and 54 high
  683. int i=0;
  684. int xyStart[2]={(kMatrixWidth/2)-23,2};
  685. CRGB pix;
  686. for (int y=0;y<50;y++){
  687.    for (int x=0;x<46;x++){
  688.      pix=logo[i];   
  689.      i++;
  690.      dma_display->drawPixelRGB888(x+xyStart[0],y+xyStart[1],pix.r,pix.g,pix.b);   
  691.    }
  692. }
  693. dma_display->fillRect(5, 53, kMatrixWidth-10, 11, dma_display->color444(0, 0, 0));
  694. delay(1000);
  695. dma_display->setTextSize(1);
  696. dma_display->setTextWrap(false);
  697. dma_display->setCursor(10,55);
  698. dma_display->print("Spectrum FFT  ");  
  699. dma_display->print(VERSION);
  700. delay(2000);
  701. dma_display->fillRect(5, 53, kMatrixWidth-10, 11, dma_display->color444(0, 0, 0));
  702. dma_display->setTextColor(dma_display->color444(15,15,0));
  703. dma_display->setCursor(10,55);
  704. dma_display->print("   Mark Donners   ");
  705. delay(2000);
  706. #endif
  707. }
  708. void DisplayPrint(char * text){
  709. #ifdef HUB75
  710.    dma_display->fillRect(8, 8, kMatrixWidth-16, 11, dma_display->color444(0,0 , 0));
  711.    dma_display->setTextSize(1);
  712.    dma_display->setTextWrap(false);
  713.    dma_display->setCursor(10,10);
  714.    dma_display->print(text);
  715.    delay(1000);
  716.    dma_display->fillRect(8, 8, kMatrixWidth-16, 11, dma_display->color444(0,0 , 0));   
  717. #endif
  718. }
复制代码


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 2025-8-2 09:54:09

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail