Merge branch 'master' of https://git.binary-kitchen.de/buddhabrot/fusion-zauberstab
This commit is contained in:
commit
38cb19e87c
5
firmware/.gitignore
vendored
Normal file
5
firmware/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
10
firmware/.vscode/extensions.json
vendored
Normal file
10
firmware/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
|
// for the documentation about the extensions.json format
|
||||||
|
"recommendations": [
|
||||||
|
"platformio.platformio-ide"
|
||||||
|
],
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
"ms-vscode.cpptools-extension-pack"
|
||||||
|
]
|
||||||
|
}
|
39
firmware/include/README
Normal file
39
firmware/include/README
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
10
firmware/include/app.h
Normal file
10
firmware/include/app.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef ZAUBERSTAB_APP_H
|
||||||
|
#define ZAUBERSTAB_APP_H
|
||||||
|
|
||||||
|
struct App {
|
||||||
|
void (*loop)(void);
|
||||||
|
void (*setup)(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct App
|
||||||
|
#endif
|
12
firmware/include/zauberstab.h
Normal file
12
firmware/include/zauberstab.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <FastLED.h>
|
||||||
|
|
||||||
|
#define LED_PIN 12
|
||||||
|
#define NUM_LEDS 144
|
||||||
|
#define MIC_PIN 15
|
||||||
|
|
||||||
|
extern CRGB leds[NUM_LEDS];
|
||||||
|
|
||||||
|
int zauberstab_init();
|
||||||
|
int32_t get_sample();
|
46
firmware/lib/README
Normal file
46
firmware/lib/README
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
23
firmware/platformio.ini
Normal file
23
firmware/platformio.ini
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:esp32doit-devkit-v1]
|
||||||
|
platform = espressif32
|
||||||
|
board = esp32doit-devkit-v1
|
||||||
|
framework = arduino
|
||||||
|
lib_deps = fastled/FastLED @ ^3.5.0
|
||||||
|
|
||||||
|
monitor_port = COM5
|
||||||
|
monitor_speed = 115200
|
||||||
|
|
||||||
|
upload_protocol = esptool
|
||||||
|
upload_port = COM5
|
||||||
|
|
||||||
|
|
125
firmware/sound_ripple/sound_ripple.cpp
Normal file
125
firmware/sound_ripple/sound_ripple.cpp
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/* soundmems_ripple
|
||||||
|
*
|
||||||
|
* By: Andrew Tuline
|
||||||
|
*
|
||||||
|
* Date: August, 2015
|
||||||
|
*
|
||||||
|
* Updated: January, 2020
|
||||||
|
*
|
||||||
|
* Create a ripple from a calculated peak from a sampled microphone
|
||||||
|
*
|
||||||
|
* Note: If you are using a microphone powered by the 3.3V signal, such as the Sparkfun MEMS microphone, then connect 3.3V to the AREF pin.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
//#define FASTLED_ALLOW_INTERRUPTS 0 // Used for ESP8266.
|
||||||
|
#include <FastLED.h> // FastLED library.
|
||||||
|
#include "zauberstab.h"
|
||||||
|
// Fixed definitions cannot change on the fly.
|
||||||
|
#define LED_DT LED_PIN // Data pin to connect to the strip.
|
||||||
|
#define LED_CK 11 // Clock pin for WS2801 or APA102.
|
||||||
|
#define COLOR_ORDER GRB // It's GRB for WS2812 and BGR for APA102.
|
||||||
|
#define LED_TYPE WS2812 // Using APA102, WS2812, WS2801. Don't forget to modify LEDS.addLeds to suit.
|
||||||
|
|
||||||
|
uint8_t squelch = 7; // Anything below this is background noise, so we'll make it '0'.
|
||||||
|
int sample; // Current sample.
|
||||||
|
float sampleAvg = 0; // Smoothed Average.
|
||||||
|
float micLev = 0; // Used to convert returned value to have '0' as minimum.
|
||||||
|
uint8_t maxVol = 11; // Reasonable value for constant volume for 'peak detector', as it won't always trigger.
|
||||||
|
bool samplePeak = 0; // Boolean flag for peak. Responding routine must reset this flag.
|
||||||
|
|
||||||
|
int max_bright = 255;
|
||||||
|
|
||||||
|
CRGB leds[NUM_LEDS];
|
||||||
|
// Ripple variables
|
||||||
|
uint8_t colour; // Ripple colour is randomized.
|
||||||
|
int center = 0; // Center of the current ripple.
|
||||||
|
int step = -1; // -1 is the initializing step.
|
||||||
|
uint8_t myfade = 255; // Starting brightness.
|
||||||
|
#define maxsteps 16 // Case statement wouldn't allow a variable.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
//analogReference(EXTERNAL); // 3.3V reference for analog input.
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
LEDS.addLeds<LED_TYPE, LED_DT, COLOR_ORDER>(leds, NUM_LEDS); // Use this for WS2812B
|
||||||
|
// LEDS.addLeds<LED_TYPE, LED_DT, LED_CK, COLOR_ORDER>(leds, NUM_LEDS); // Use this for WS2801 or APA102
|
||||||
|
|
||||||
|
FastLED.setBrightness(max_bright);
|
||||||
|
//FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);
|
||||||
|
|
||||||
|
} // setup()
|
||||||
|
|
||||||
|
|
||||||
|
void getSample() {
|
||||||
|
|
||||||
|
int16_t micIn; // Current sample starts with negative values and large values, which is why it's 16 bit signed.
|
||||||
|
static long peakTime;
|
||||||
|
|
||||||
|
micIn = analogRead(MIC_PIN); // Poor man's analog Read.
|
||||||
|
micLev = ((micLev * 31) + micIn) / 32; // Smooth it out over the last 32 samples for automatic centering.
|
||||||
|
micIn -= micLev; // Let's center it to 0 now.
|
||||||
|
micIn = abs(micIn); // And get the absolute value of each sample.
|
||||||
|
sample = (micIn <= squelch) ? 0 : (sample + micIn) / 2; // Using a ternary operator, the resultant sample is either 0 or it's a bit smoothed out with the last sample.
|
||||||
|
sampleAvg = ((sampleAvg * 31) + sample) / 32; // Smooth it out over the last 32 samples.
|
||||||
|
|
||||||
|
if (sample > (sampleAvg+maxVol) && millis() > (peakTime + 50)) { // Poor man's beat detection by seeing if sample > Average + some value.
|
||||||
|
samplePeak = 1; // Then we got a peak, else we don't. Display routines need to reset the samplepeak value in case they miss the trigger.
|
||||||
|
peakTime=millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // getSample()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void ripple() {
|
||||||
|
|
||||||
|
if (samplePeak == 1) {step = -1; samplePeak = 0; } // If we have a peak, let's reset our ripple.
|
||||||
|
|
||||||
|
fadeToBlackBy(leds, NUM_LEDS, 64); // 8 bit, 1 = slow, 255 = fast
|
||||||
|
|
||||||
|
switch (step) {
|
||||||
|
|
||||||
|
case -1: // Initialize ripple variables.
|
||||||
|
center = random(NUM_LEDS);
|
||||||
|
colour = random8(); // More peaks/s = higher the hue colour.
|
||||||
|
step = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
leds[center] = CHSV(colour, 255, 255); // Display the first pixel of the ripple.
|
||||||
|
step ++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case maxsteps: // At the end of the ripples.
|
||||||
|
// step = -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // Middle of the ripples.
|
||||||
|
leds[(center + step + NUM_LEDS) % NUM_LEDS] += CHSV(colour, 255, myfade/step*2); // Simple wrap.
|
||||||
|
leds[(center - step + NUM_LEDS) % NUM_LEDS] += CHSV(colour, 255, myfade/step*2);
|
||||||
|
step ++; // Next step.
|
||||||
|
break;
|
||||||
|
} // switch step
|
||||||
|
|
||||||
|
} // ripple()
|
||||||
|
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
getSample();
|
||||||
|
|
||||||
|
EVERY_N_MILLISECONDS(20) {
|
||||||
|
ripple();
|
||||||
|
}
|
||||||
|
|
||||||
|
FastLED.show();
|
||||||
|
|
||||||
|
} // loop()
|
||||||
|
|
||||||
|
|
163
firmware/sound_wave/sound_wave.ino
Normal file
163
firmware/sound_wave/sound_wave.ino
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
|
||||||
|
/* sound_wave
|
||||||
|
*
|
||||||
|
* By: Andrew Tuline
|
||||||
|
*
|
||||||
|
* Date: February, 2017
|
||||||
|
*
|
||||||
|
* Basic code to read from the Sparkfun INMP401 microphone, and create waves based on sampled input. This does NOT include sensitivity adjustment.
|
||||||
|
*
|
||||||
|
* My hardware setup:
|
||||||
|
*
|
||||||
|
* Arduino Nano & Addressable LED strips
|
||||||
|
* - Powered by USB power bank
|
||||||
|
* - APA102 or WS2812 data connected to pin 12.
|
||||||
|
* - APA102 clock connected to pin 11.
|
||||||
|
* - 5V on APA102 or WS2812 connected to 5V on Nano (good for short strips).
|
||||||
|
* - Gnd to Gnd on Nano.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Sparkfun MEMS microphone
|
||||||
|
* - Vcc on microphone is connected to 3.3V on Nano.
|
||||||
|
* - AREF on Nano connected to 3.3V on Nano.
|
||||||
|
* - Mic out connected to A5.
|
||||||
|
* - Gnd to Gnd on Nano.
|
||||||
|
*
|
||||||
|
* Note: If you are using a microphone powered by the 3.3V signal, such as the Sparkfun MEMS microphone, then connect 3.3V to the AREF pin.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define FASTLED_ALLOW_INTERRUPTS 0 // Used for ESP8266.
|
||||||
|
#include <FastLED.h> // FastLED library.
|
||||||
|
#include "zauberstab.h"
|
||||||
|
|
||||||
|
uint8_t squelch = 7; // Anything below this is background noise, so we'll make it '0'.
|
||||||
|
int sample; // Current sample.
|
||||||
|
float sampleAvg = 0; // Smoothed Average.
|
||||||
|
float micLev = 0; // Used to convert returned value to have '0' as minimum.
|
||||||
|
uint8_t maxVol = 11; // Reasonable value for constant volume for 'peak detector', as it won't always trigger.
|
||||||
|
bool samplePeak = 0; // Boolean flag for peak. Responding routine must reset this flag.
|
||||||
|
|
||||||
|
int sampleAgc, multAgc;
|
||||||
|
uint8_t targetAgc = 60; // This is our setPoint at 20% of max for the adjusted output.
|
||||||
|
|
||||||
|
|
||||||
|
// Fixed definitions cannot change on the fly.
|
||||||
|
#define LED_DT LED_PIN // Data pin to connect to the strip.
|
||||||
|
#define LED_CK 11 // Clock pin for WS2801 or APA102.
|
||||||
|
#define COLOR_ORDER GRB // It's GRB for WS2812 and BGR for APA102.
|
||||||
|
#define LED_TYPE WS2812 // Using APA102, WS2812, WS2801. Don't forget to modify LEDS.addLeds to suit.
|
||||||
|
|
||||||
|
|
||||||
|
struct CRGB leds[NUM_LEDS]; // Initialize our LED array.
|
||||||
|
|
||||||
|
int max_bright = 255;
|
||||||
|
|
||||||
|
CRGBPalette16 currentPalette = OceanColors_p;
|
||||||
|
CRGBPalette16 targetPalette = OceanColors_p;
|
||||||
|
TBlendType currentBlending = LINEARBLEND; // NOBLEND or LINEARBLEND
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
//analogReference(EXTERNAL); // 3.3V reference for analog input.
|
||||||
|
|
||||||
|
Serial.begin(115200); // Initialize serial port for debugging.
|
||||||
|
delay(1000); // Soft startup to ease the flow of electrons.
|
||||||
|
|
||||||
|
LEDS.addLeds<LED_TYPE, LED_DT, COLOR_ORDER>(leds, NUM_LEDS); // Use this for WS2812B
|
||||||
|
// LEDS.addLeds<LED_TYPE, LED_DT, LED_CK, COLOR_ORDER>(leds, NUM_LEDS); // Use this for WS2801 or APA102
|
||||||
|
|
||||||
|
FastLED.setBrightness(max_bright);
|
||||||
|
FastLED.setMaxPowerInVoltsAndMilliamps(5, 500); // FastLED Power management set at 5V, 500mA.
|
||||||
|
|
||||||
|
} // setup()
|
||||||
|
|
||||||
|
|
||||||
|
void getSample() {
|
||||||
|
|
||||||
|
int16_t micIn; // Current sample starts with negative values and large values, which is why it's 16 bit signed.
|
||||||
|
static long peakTime;
|
||||||
|
|
||||||
|
micIn = analogRead(MIC_PIN)>>2; // Poor man's analog Read.
|
||||||
|
micLev = ((micLev * 31) + micIn) / 32; // Smooth it out over the last 32 samples for automatic centering.
|
||||||
|
micIn -= micLev; // Let's center it to 0 now.
|
||||||
|
micIn = abs(micIn); // And get the absolute value of each sample.
|
||||||
|
sample = (micIn <= squelch) ? 0 : (sample + micIn) / 2; // Using a ternary operator, the resultant sample is either 0 or it's a bit smoothed out with the last sample.
|
||||||
|
sampleAvg = ((sampleAvg * 31) + sample) / 32; // Smooth it out over the last 32 samples.
|
||||||
|
|
||||||
|
if (sample > (sampleAvg+maxVol) && millis() > (peakTime + 50)) { // Poor man's beat detection by seeing if sample > Average + some value.
|
||||||
|
samplePeak = 1; // Then we got a peak, else we don't. Display routines need to reset the samplepeak value in case they miss the trigger.
|
||||||
|
peakTime=millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // getSample()
|
||||||
|
|
||||||
|
|
||||||
|
void agcAvg() { // A simple averaging multiplier to automatically adjust sound sensitivity.
|
||||||
|
|
||||||
|
multAgc = (sampleAvg < 1) ? targetAgc : targetAgc / sampleAvg; // Make the multiplier so that sampleAvg * multiplier = setpoint
|
||||||
|
sampleAgc = sample * multAgc;
|
||||||
|
if (sampleAgc > 255) sampleAgc = 255;
|
||||||
|
|
||||||
|
//------------ Oscilloscope output ---------------------------
|
||||||
|
Serial.print(targetAgc); Serial.print(" ");
|
||||||
|
Serial.print(multAgc); Serial.print(" ");
|
||||||
|
Serial.print(sampleAgc); Serial.print(" ");
|
||||||
|
|
||||||
|
Serial.print(micLev); Serial.print(" ");
|
||||||
|
Serial.print(sample); Serial.println(" ");
|
||||||
|
// Serial.print(sampleAvg); Serial.print(" ");
|
||||||
|
// Serial.print(samplePeak); Serial.print(" "); samplePeak = 0;
|
||||||
|
// Serial.print(100); Serial.print(" ");
|
||||||
|
// Serial.print(0); Serial.print(" ");
|
||||||
|
// Serial.println(" ");
|
||||||
|
|
||||||
|
} // agcAvg()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void sndwave() {
|
||||||
|
|
||||||
|
leds[NUM_LEDS/2] = ColorFromPalette(currentPalette, sampleAgc, sampleAgc, currentBlending); // Put the sample into the center
|
||||||
|
|
||||||
|
for (int i = NUM_LEDS - 1; i > NUM_LEDS/2; i--) { //move to the left // Copy to the left, and let the fade do the rest.
|
||||||
|
leds[i] = leds[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_LEDS/2; i++) { // move to the right // Copy to the right, and let the fade to the rest.
|
||||||
|
leds[i] = leds[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // sndwave()
|
||||||
|
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
|
||||||
|
EVERY_N_SECONDS(5) { // Change the palette every 5 seconds.
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
targetPalette[i] = CHSV(random8(), 255, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EVERY_N_MILLISECONDS(100) { // AWESOME palette blending capability once they do change.
|
||||||
|
uint8_t maxChanges = 24;
|
||||||
|
nblendPaletteTowardPalette(currentPalette, targetPalette, maxChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EVERY_N_MILLIS_I(thistimer,20) { // For fun, let's make the animation have a variable rate.
|
||||||
|
uint8_t timeval = beatsin8(10,20,50); // Use a sinewave for the line below. Could also use peak/beat detection.
|
||||||
|
thistimer.setPeriod(timeval); // Allows you to change how often this routine runs.
|
||||||
|
fadeToBlackBy(leds, NUM_LEDS, 16); // 1 = slow, 255 = fast fade. Depending on the faderate, the LED's further away will fade out.
|
||||||
|
getSample();
|
||||||
|
agcAvg();
|
||||||
|
sndwave();
|
||||||
|
}
|
||||||
|
|
||||||
|
FastLED.show();
|
||||||
|
|
||||||
|
} // loop()
|
||||||
|
|
||||||
|
|
54
firmware/src/vumeter.cpp
Normal file
54
firmware/src/vumeter.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include "zauberstab.h"
|
||||||
|
|
||||||
|
unsigned long last_sample_time;
|
||||||
|
static int sample_counter = 0;
|
||||||
|
unsigned int top_led_pos = 0;
|
||||||
|
float rms_avg = 0;
|
||||||
|
float vu_filt = 0.0f;
|
||||||
|
float vu_filt_slow = 0.0f;
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
zauberstab_init();
|
||||||
|
Serial.begin(115200);
|
||||||
|
FastLED.setBrightness(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (micros()-last_sample_time >= 500){
|
||||||
|
last_sample_time = micros();
|
||||||
|
int32_t sample = get_sample();
|
||||||
|
float in = sample*sample;
|
||||||
|
sample_counter++;
|
||||||
|
rms_avg += (in - rms_avg)/(sample_counter + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
EVERY_N_MILLIS(10){
|
||||||
|
|
||||||
|
float vu = 20 * log10f(rms_avg);
|
||||||
|
vu_filt = (vu-vu_filt) * 0.8 + vu_filt;
|
||||||
|
vu_filt_slow = (vu_filt-vu_filt_slow) * 0.01 + vu_filt_slow;
|
||||||
|
//Serial.println(vu);
|
||||||
|
int max_led = vu_filt;
|
||||||
|
int top_led = vu_filt_slow;
|
||||||
|
|
||||||
|
max_led = max_led > 0xFF ? 0xFF : max_led;
|
||||||
|
|
||||||
|
if (top_led < max_led){
|
||||||
|
vu_filt_slow = vu_filt;
|
||||||
|
top_led = max_led;
|
||||||
|
}
|
||||||
|
|
||||||
|
fill_solid(leds, NUM_LEDS, CRGB::Black);
|
||||||
|
for(int i = 0; i < max_led; i++) {
|
||||||
|
int idx = map(i, 0, NUM_LEDS, 0, 0xFF);
|
||||||
|
leds[i] = ColorFromPalette(RainbowColors_p, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
leds[top_led] = CRGB::White;
|
||||||
|
|
||||||
|
FastLED.show();
|
||||||
|
rms_avg = 0.0f;
|
||||||
|
sample_counter = 0;
|
||||||
|
}
|
||||||
|
}
|
31
firmware/src/zauberstab.cpp
Normal file
31
firmware/src/zauberstab.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include "zauberstab.h"
|
||||||
|
|
||||||
|
CRGB leds[NUM_LEDS];
|
||||||
|
static int16_t mic_offset = 0;
|
||||||
|
|
||||||
|
static uint16_t read_mic() {
|
||||||
|
return analogRead(MIC_PIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t get_mic_offset() {
|
||||||
|
float average = 0.f;
|
||||||
|
uint32_t num_samples = 0;
|
||||||
|
|
||||||
|
for(num_samples = 0; num_samples < 10000; num_samples++) {
|
||||||
|
uint16_t sample = read_mic();
|
||||||
|
average += ((float) sample - average) / (num_samples + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (uint16_t)average;
|
||||||
|
}
|
||||||
|
|
||||||
|
int zauberstab_init() {
|
||||||
|
FastLED.addLeds<WS2812, LED_PIN, RGB>(leds, NUM_LEDS);
|
||||||
|
//FastLED.setMaxPowerInVoltsAndMilliamps(5, 300);
|
||||||
|
mic_offset = get_mic_offset();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t get_sample() {
|
||||||
|
return read_mic() - mic_offset;
|
||||||
|
}
|
204
firmware/temp/alex_beat_detect.cpp
Normal file
204
firmware/temp/alex_beat_detect.cpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#include "zauberstab.h"
|
||||||
|
#include "app.h"
|
||||||
|
|
||||||
|
#define SAMPLING_FREQUENCY_BP 40 // number of energy chunks per second
|
||||||
|
#define SAMPLING_FREQUENCY_CONTROL 1 // check number of times per second if the current band pass is the best one
|
||||||
|
#define Q 20. // quality factor of band pass filters
|
||||||
|
#define PI 3.1415926535897932384626433832795
|
||||||
|
#define n_BP 30 //number of band pass filters
|
||||||
|
|
||||||
|
static unsigned long sampling_period_bp = 1000000L / SAMPLING_FREQUENCY_BP;
|
||||||
|
static unsigned long sampling_period_control = 1000000L / SAMPLING_FREQUENCY_CONTROL;
|
||||||
|
static double energy = 0;
|
||||||
|
static unsigned long last_us_bp = 0L;
|
||||||
|
static unsigned long last_us_control = 0L;
|
||||||
|
|
||||||
|
static float a0[n_BP];
|
||||||
|
static float a1[n_BP];
|
||||||
|
static float a2[n_BP];
|
||||||
|
static float b0[n_BP];
|
||||||
|
//static float b1[n_BP];
|
||||||
|
static float b2[n_BP];
|
||||||
|
|
||||||
|
static float a[n_BP];
|
||||||
|
static float w0[n_BP];
|
||||||
|
|
||||||
|
static float yy1[n_BP];
|
||||||
|
static float yy2[n_BP];
|
||||||
|
static float yy3[n_BP];
|
||||||
|
static float yy4[n_BP];
|
||||||
|
static float yy5[n_BP];
|
||||||
|
static float yy6[n_BP];
|
||||||
|
|
||||||
|
static float u1[n_BP];
|
||||||
|
static float u2[n_BP];
|
||||||
|
static float y[n_BP];
|
||||||
|
static float y_fil[n_BP];
|
||||||
|
|
||||||
|
static float angle;
|
||||||
|
static float angle2;
|
||||||
|
|
||||||
|
static double energy_fil = 800.;
|
||||||
|
|
||||||
|
static float pos_target = NUM_LEDS / 2;
|
||||||
|
static float pos_target_filtered = NUM_LEDS / 2;
|
||||||
|
|
||||||
|
static float microphone_offset = 675;
|
||||||
|
static long initial_time;
|
||||||
|
|
||||||
|
static int active = 15;
|
||||||
|
static int candidate = 15;
|
||||||
|
static int rounds = 0;
|
||||||
|
|
||||||
|
static int get_value(int pos, float pos0)
|
||||||
|
{
|
||||||
|
if (abs(pos0 - pos) > 20)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (40 - abs(pos0 - pos) * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_filter()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n_BP; i++)
|
||||||
|
{
|
||||||
|
float frequency = 1.75 + i * (2.4 - 1.75) / n_BP;
|
||||||
|
w0[i] = 2. * PI * frequency / SAMPLING_FREQUENCY_BP;
|
||||||
|
a[i] = sin(w0[i] / (2. * Q));
|
||||||
|
b0[i] = a[i];
|
||||||
|
//b1[i] = 0;
|
||||||
|
b2[i] = -a[i];
|
||||||
|
a0[i] = 1. + a[i];
|
||||||
|
a1[i] = -2. * cos(w0[i]);
|
||||||
|
a2[i] = 1. - a[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void abeat_setup()
|
||||||
|
{
|
||||||
|
zauberstab_init();
|
||||||
|
Serial.begin(115200);
|
||||||
|
set_filter();
|
||||||
|
initial_time = micros();
|
||||||
|
}
|
||||||
|
static void abeat_loop()
|
||||||
|
{
|
||||||
|
|
||||||
|
int sample = int(analogRead(MIC_PIN) - microphone_offset);
|
||||||
|
energy += abs(sample) * abs(sample);
|
||||||
|
|
||||||
|
if (micros() - last_us_bp > sampling_period_bp)
|
||||||
|
{
|
||||||
|
|
||||||
|
Serial.println(sample);
|
||||||
|
|
||||||
|
microphone_offset += (analogRead(MIC_PIN) - microphone_offset) * 0.001;
|
||||||
|
|
||||||
|
//Serial.println(microphone_offset);
|
||||||
|
|
||||||
|
last_us_bp += sampling_period_bp;
|
||||||
|
energy_fil += (energy - energy_fil) * 0.01;
|
||||||
|
//Serial.println(energy);
|
||||||
|
for (int i = 0; i < n_BP; i++)
|
||||||
|
{
|
||||||
|
y[i] = (b0[i] / a0[i]) * energy + 0. + (b2[i] / a0[i]) * u2[i] - (a1[i] / a0[i]) * yy1[i] - (a2[i] / a0[i]) * yy2[i];
|
||||||
|
u2[i] = u1[i];
|
||||||
|
u1[i] = energy;
|
||||||
|
yy6[i] = yy5[i];
|
||||||
|
yy5[i] = yy4[i];
|
||||||
|
yy4[i] = yy3[i];
|
||||||
|
yy3[i] = yy2[i];
|
||||||
|
yy2[i] = yy1[i];
|
||||||
|
yy1[i] = y[i];
|
||||||
|
y_fil[i] += (abs(y[i]) - y_fil[i]) * 0.005; //linie der scheitelpunkte
|
||||||
|
}
|
||||||
|
|
||||||
|
float delays = constrain(SAMPLING_FREQUENCY_BP * 0.25 / (1.75 + active * (2.4 - 1.75) / n_BP), 4., 6.);
|
||||||
|
|
||||||
|
float delayed = 0;
|
||||||
|
if (delays > 5)
|
||||||
|
{
|
||||||
|
delayed = yy5[active] * (1 - delays + 5) + yy6[active] * (delays - 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delayed = yy4[active] * (1 - delays + 4) + yy5[active] * (delays - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
angle = atan2(delayed, y[active]);
|
||||||
|
|
||||||
|
if (PI < abs(angle - angle2) && abs(angle - angle2) < 3 * PI)
|
||||||
|
{
|
||||||
|
angle2 = angle + 2 * PI;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
angle2 = angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos_target = map(angle2, -PI, 3 * PI, -0.3 * NUM_LEDS, NUM_LEDS * 1.5);
|
||||||
|
|
||||||
|
if (pos_target > pos_target_filtered)
|
||||||
|
{
|
||||||
|
pos_target_filtered += (pos_target - pos_target_filtered) * 0.35;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos_target_filtered = pos_target;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial.print(y_fil[active]);
|
||||||
|
// Serial.print(",");
|
||||||
|
// Serial.println(y[active]);
|
||||||
|
|
||||||
|
energy = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_LEDS; i++)
|
||||||
|
{
|
||||||
|
int brightness = get_value(i, pos_target_filtered);
|
||||||
|
//leds[i].setRGB(brightness, brightness, brightness);
|
||||||
|
|
||||||
|
//leds[i].setHSV(160, (rounds == 6) ? 0xFF : 0, brightness);
|
||||||
|
leds[i] = CRGB::White;
|
||||||
|
}
|
||||||
|
FastLED.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (micros() - last_us_control > sampling_period_control)
|
||||||
|
{
|
||||||
|
last_us_control += sampling_period_control;
|
||||||
|
int argmax = -1;
|
||||||
|
float valuemax = 0;
|
||||||
|
for (int i = 0; i < n_BP; i++)
|
||||||
|
{
|
||||||
|
if (y_fil[i] > valuemax)
|
||||||
|
{
|
||||||
|
valuemax = y_fil[i];
|
||||||
|
argmax = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argmax > -1)
|
||||||
|
{
|
||||||
|
if (argmax == candidate)
|
||||||
|
{
|
||||||
|
rounds++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rounds = 0;
|
||||||
|
candidate = argmax;
|
||||||
|
}
|
||||||
|
if (rounds > 6)
|
||||||
|
{
|
||||||
|
rounds = 0;
|
||||||
|
active = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
203
firmware/temp/beat_detect.cpp
Normal file
203
firmware/temp/beat_detect.cpp
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
#include "zauberstab.h"
|
||||||
|
|
||||||
|
#define SAMPLING_FREQUENCY_BP 40 // number of energy chunks per second
|
||||||
|
#define SAMPLING_FREQUENCY_CONTROL 1 // check number of times per second if the current band pass is the best one
|
||||||
|
#define Q 20. // quality factor of band pass filters
|
||||||
|
#define PI 3.1415926535897932384626433832795
|
||||||
|
#define n_BP 30 //number of band pass filters
|
||||||
|
|
||||||
|
static unsigned long sampling_period_bp = 1000000L / SAMPLING_FREQUENCY_BP;
|
||||||
|
static unsigned long sampling_period_control = 1000000L / SAMPLING_FREQUENCY_CONTROL;
|
||||||
|
static double energy = 0;
|
||||||
|
static unsigned long last_us_bp = 0L;
|
||||||
|
static unsigned long last_us_control = 0L;
|
||||||
|
|
||||||
|
static float a0[n_BP];
|
||||||
|
static float a1[n_BP];
|
||||||
|
static float a2[n_BP];
|
||||||
|
static float b0[n_BP];
|
||||||
|
//static float b1[n_BP];
|
||||||
|
static float b2[n_BP];
|
||||||
|
|
||||||
|
static float a[n_BP];
|
||||||
|
static float w0[n_BP];
|
||||||
|
|
||||||
|
static float yy1[n_BP];
|
||||||
|
static float yy2[n_BP];
|
||||||
|
static float yy3[n_BP];
|
||||||
|
static float yy4[n_BP];
|
||||||
|
static float yy5[n_BP];
|
||||||
|
static float yy6[n_BP];
|
||||||
|
|
||||||
|
static float u1[n_BP];
|
||||||
|
static float u2[n_BP];
|
||||||
|
static float y[n_BP];
|
||||||
|
static float y_fil[n_BP];
|
||||||
|
|
||||||
|
static float angle;
|
||||||
|
static float angle2;
|
||||||
|
|
||||||
|
static double energy_fil = 800.;
|
||||||
|
|
||||||
|
static float pos_target = NUM_LEDS / 2;
|
||||||
|
static float pos_target_filtered = NUM_LEDS / 2;
|
||||||
|
|
||||||
|
static float microphone_offset = 675;
|
||||||
|
static long initial_time;
|
||||||
|
|
||||||
|
static int active = 15;
|
||||||
|
static int candidate = 15;
|
||||||
|
static int rounds = 0;
|
||||||
|
|
||||||
|
static int get_value(int pos, float pos0)
|
||||||
|
{
|
||||||
|
if (abs(pos0 - pos) > 20)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (40 - abs(pos0 - pos) * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_filter()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n_BP; i++)
|
||||||
|
{
|
||||||
|
float frequency = 1.75 + i * (2.4 - 1.75) / n_BP;
|
||||||
|
w0[i] = 2. * PI * frequency / SAMPLING_FREQUENCY_BP;
|
||||||
|
a[i] = sin(w0[i] / (2. * Q));
|
||||||
|
b0[i] = a[i];
|
||||||
|
//b1[i] = 0;
|
||||||
|
b2[i] = -a[i];
|
||||||
|
a0[i] = 1. + a[i];
|
||||||
|
a1[i] = -2. * cos(w0[i]);
|
||||||
|
a2[i] = 1. - a[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
zauberstab_init();
|
||||||
|
Serial.begin(115200);
|
||||||
|
set_filter();
|
||||||
|
initial_time = micros();
|
||||||
|
}
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
|
||||||
|
int sample = int(analogRead(MIC_PIN) - microphone_offset);
|
||||||
|
energy += abs(sample) * abs(sample);
|
||||||
|
|
||||||
|
if (micros() - last_us_bp > sampling_period_bp)
|
||||||
|
{
|
||||||
|
|
||||||
|
Serial.println(sample);
|
||||||
|
|
||||||
|
microphone_offset += (analogRead(MIC_PIN) - microphone_offset) * 0.001;
|
||||||
|
|
||||||
|
//Serial.println(microphone_offset);
|
||||||
|
|
||||||
|
last_us_bp += sampling_period_bp;
|
||||||
|
energy_fil += (energy - energy_fil) * 0.01;
|
||||||
|
//Serial.println(energy);
|
||||||
|
for (int i = 0; i < n_BP; i++)
|
||||||
|
{
|
||||||
|
y[i] = (b0[i] / a0[i]) * energy + 0. + (b2[i] / a0[i]) * u2[i] - (a1[i] / a0[i]) * yy1[i] - (a2[i] / a0[i]) * yy2[i];
|
||||||
|
u2[i] = u1[i];
|
||||||
|
u1[i] = energy;
|
||||||
|
yy6[i] = yy5[i];
|
||||||
|
yy5[i] = yy4[i];
|
||||||
|
yy4[i] = yy3[i];
|
||||||
|
yy3[i] = yy2[i];
|
||||||
|
yy2[i] = yy1[i];
|
||||||
|
yy1[i] = y[i];
|
||||||
|
y_fil[i] += (abs(y[i]) - y_fil[i]) * 0.005; //linie der scheitelpunkte
|
||||||
|
}
|
||||||
|
|
||||||
|
float delays = constrain(SAMPLING_FREQUENCY_BP * 0.25 / (1.75 + active * (2.4 - 1.75) / n_BP), 4., 6.);
|
||||||
|
|
||||||
|
float delayed = 0;
|
||||||
|
if (delays > 5)
|
||||||
|
{
|
||||||
|
delayed = yy5[active] * (1 - delays + 5) + yy6[active] * (delays - 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delayed = yy4[active] * (1 - delays + 4) + yy5[active] * (delays - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
angle = atan2(delayed, y[active]);
|
||||||
|
|
||||||
|
if (PI < abs(angle - angle2) && abs(angle - angle2) < 3 * PI)
|
||||||
|
{
|
||||||
|
angle2 = angle + 2 * PI;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
angle2 = angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos_target = map(angle2, -PI, 3 * PI, -0.3 * NUM_LEDS, NUM_LEDS * 1.5);
|
||||||
|
|
||||||
|
if (pos_target > pos_target_filtered)
|
||||||
|
{
|
||||||
|
pos_target_filtered += (pos_target - pos_target_filtered) * 0.35;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos_target_filtered = pos_target;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial.print(y_fil[active]);
|
||||||
|
// Serial.print(",");
|
||||||
|
// Serial.println(y[active]);
|
||||||
|
|
||||||
|
energy = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_LEDS; i++)
|
||||||
|
{
|
||||||
|
int brightness = get_value(i, pos_target_filtered);
|
||||||
|
if (brightness >= 1) {
|
||||||
|
brightness = 10;
|
||||||
|
}
|
||||||
|
//leds[i].setRGB(brightness, brightness, brightness);
|
||||||
|
leds[i].setHSV(160, (rounds == 6) ? 0xFF : 0, brightness);
|
||||||
|
}
|
||||||
|
FastLED.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (micros() - last_us_control > sampling_period_control)
|
||||||
|
{
|
||||||
|
last_us_control += sampling_period_control;
|
||||||
|
int argmax = -1;
|
||||||
|
float valuemax = 0;
|
||||||
|
for (int i = 0; i < n_BP; i++)
|
||||||
|
{
|
||||||
|
if (y_fil[i] > valuemax)
|
||||||
|
{
|
||||||
|
valuemax = y_fil[i];
|
||||||
|
argmax = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argmax > -1)
|
||||||
|
{
|
||||||
|
if (argmax == candidate)
|
||||||
|
{
|
||||||
|
rounds++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rounds = 0;
|
||||||
|
candidate = argmax;
|
||||||
|
}
|
||||||
|
if (rounds > 6)
|
||||||
|
{
|
||||||
|
rounds = 0;
|
||||||
|
active = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
firmware/temp/main.cpp
Normal file
9
firmware/temp/main.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "zauberstab.h"
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
zauberstab_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
}
|
11
firmware/test/README
Normal file
11
firmware/test/README
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
This directory is intended for PlatformIO Test Runner and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
Loading…
Reference in New Issue
Block a user