This commit is contained in:
Alexander Alber 2022-06-17 19:46:50 +02:00
commit 38cb19e87c
15 changed files with 945 additions and 0 deletions

5
firmware/.gitignore vendored Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,10 @@
#ifndef ZAUBERSTAB_APP_H
#define ZAUBERSTAB_APP_H
struct App {
void (*loop)(void);
void (*setup)(void);
};
struct App
#endif

View 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
View 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
View 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

View 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()

View 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
View 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;
}
}

View 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;
}

View 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;
}
}
}
}

View 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
View File

@ -0,0 +1,9 @@
#include "zauberstab.h"
void setup() {
zauberstab_init();
}
void loop() {
}

11
firmware/test/README Normal file
View 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