forked from buddhabrot/fusion-zauberstab
Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2f6da3c531 | ||
|
7e2a287786 | ||
|
dbb9da1e4f |
@ -13,6 +13,13 @@ struct BeatDetectApp : public App {
|
||||
};
|
||||
|
||||
|
||||
struct QuarterApp : public App {
|
||||
void init();
|
||||
void deinit();
|
||||
void loop();
|
||||
};
|
||||
|
||||
|
||||
struct VuMeterApp: public App {
|
||||
void init();
|
||||
void deinit();
|
||||
@ -31,8 +38,10 @@ struct FackelApp: public App {
|
||||
void loop();
|
||||
};
|
||||
|
||||
struct ImageDisplayApp: public App {
|
||||
/**
|
||||
* struct ImageDisplayApp: public App {
|
||||
void init();
|
||||
void deinit();
|
||||
void loop();
|
||||
};
|
||||
**/
|
364
firmware/src/applications/quarter.cpp
Normal file
364
firmware/src/applications/quarter.cpp
Normal file
@ -0,0 +1,364 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "app.h"
|
||||
#include "biquad.h"
|
||||
#include "pt1.h"
|
||||
#include "zauberstab.h"
|
||||
|
||||
#undef NUM_LEDS
|
||||
#define NUM_LEDS 46
|
||||
|
||||
#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 30. // quality factor of band pass filters
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
#define n_BP 40 // number of band pass filters
|
||||
|
||||
static const unsigned long sampling_period_bp = 1000000L / SAMPLING_FREQUENCY_BP;
|
||||
static const unsigned long sampling_period_control = 1000000L / SAMPLING_FREQUENCY_CONTROL;
|
||||
static float energy = 0;
|
||||
static unsigned long last_us_bp = 0L;
|
||||
static unsigned long last_us_control = 0L;
|
||||
|
||||
static Biquad<float> bp_filters[n_BP];
|
||||
static Pt1<float> y_filter[n_BP];
|
||||
static Pt1<float> pos_filter{1.f, 1.f};
|
||||
|
||||
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 y[n_BP];
|
||||
static float y_fil[n_BP];
|
||||
|
||||
static float angle;
|
||||
static float angle_pre = 0.1;
|
||||
static long initial_time;
|
||||
|
||||
static int active = 15;
|
||||
static int rounds = 0;
|
||||
static int n_samples = 0;
|
||||
|
||||
static int beat_count = 1;
|
||||
static int quarter = 0;
|
||||
|
||||
static int quarter_color_h1 = 30;
|
||||
static int quarter_color_s = 255;
|
||||
static int quarter_color_v = 50;
|
||||
|
||||
|
||||
//modus 0: farbverlauf, farben wechseln alle 4 beats
|
||||
//modus 1: konstante farbe, wechselt bei jedem beat
|
||||
//modus 2: weiß, zählt binärzahlen hoch
|
||||
static int modus = 0;
|
||||
static int modus_count = 0;
|
||||
|
||||
|
||||
|
||||
static int get_value(int pos, int quarter, int beat_count, char component)
|
||||
{
|
||||
|
||||
|
||||
if (modus == 3)
|
||||
{
|
||||
if (component == 'h'){return 255;}
|
||||
if (component == 's'){return 0;}
|
||||
if (component == 'v')
|
||||
{
|
||||
if ((quarter+pos)%2==0)
|
||||
{return 50;}
|
||||
else
|
||||
{return 0;}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
else if (modus == 2)
|
||||
{
|
||||
if (component == 'h'){return 255;}
|
||||
if (component == 's'){return 0;}
|
||||
if (component == 'v')
|
||||
{
|
||||
int v = 50;
|
||||
int s1=0, s2=0, s3=0, s4=0;
|
||||
switch (beat_count)
|
||||
{
|
||||
case 0:
|
||||
s1 = 0, s2 = 0, s3 = 0, s4 = 0;
|
||||
break;
|
||||
case 1:
|
||||
s1 = 0, s2 = 0, s3 = 0, s4 = 1;
|
||||
break;
|
||||
case 2:
|
||||
s1 = 0, s2 = 0, s3 = 1, s4 = 0;
|
||||
break;
|
||||
case 3:
|
||||
s1 = 0, s2 = 0, s3 = 1, s4 = 1;
|
||||
break;
|
||||
case 4:
|
||||
s1 = 0, s2 = 1, s3 = 0, s4 = 0;
|
||||
break;
|
||||
case 5:
|
||||
s1 = 0, s2 = 1, s3 = 0, s4 = 1;
|
||||
break;
|
||||
case 6:
|
||||
s1 = 0, s2 = 1, s3 = 1, s4 = 0;
|
||||
break;
|
||||
case 7:
|
||||
s1 = 0, s2 = 1, s3 = 1, s4 = 1;
|
||||
break;
|
||||
case 8:
|
||||
s1 = 1, s2 = 0, s3 = 0, s4 = 0;
|
||||
break;
|
||||
case 9:
|
||||
s1 = 1, s2 = 0, s3 = 0, s4 = 1;
|
||||
break;
|
||||
case 10:
|
||||
s1 = 1, s2 = 0, s3 = 1, s4 = 0;
|
||||
break;
|
||||
case 11:
|
||||
s1 = 1, s2 = 0, s3 = 1, s4 = 1;
|
||||
break;
|
||||
case 12:
|
||||
s1 = 1, s2 = 1, s3 = 0, s4 = 0;
|
||||
break;
|
||||
case 13:
|
||||
s1 = 1, s2 = 1, s3 = 0, s4 = 1;
|
||||
break;
|
||||
case 14:
|
||||
s1 = 1, s2 = 1, s3 = 1, s4 = 0;
|
||||
break;
|
||||
case 15:
|
||||
s1 = 1, s2 = 1, s3 = 1, s4 = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos < NUM_LEDS/4) {return s1*v;}
|
||||
else if (pos < NUM_LEDS/2) {return s2*v;}
|
||||
else if (pos < 3*NUM_LEDS/4) {return s3*v;}
|
||||
else {return s4*v;}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
else if (pos < NUM_LEDS*quarter/3)
|
||||
{
|
||||
if (component == 'h')
|
||||
{
|
||||
if (modus == 0)
|
||||
{
|
||||
|
||||
int color = quarter_color_h1 + 50*pos/NUM_LEDS;
|
||||
return color%255;
|
||||
}
|
||||
return quarter_color_h1;
|
||||
|
||||
}
|
||||
if (component == 's')
|
||||
{
|
||||
return quarter_color_s;
|
||||
}
|
||||
if (component == 'v')
|
||||
{
|
||||
return quarter_color_v;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
set_filter()
|
||||
{
|
||||
for (int i = 0; i < n_BP; i++)
|
||||
{
|
||||
float frequency = 1.75 + i * (2.5 - 1.75) / n_BP;
|
||||
float a, a0, a1, a2, b0, b1, b2, w0;
|
||||
w0 = 2. * PI * frequency / SAMPLING_FREQUENCY_BP;
|
||||
a = sin(w0 / (2. * Q));
|
||||
b0 = a;
|
||||
b1 = 0.f;
|
||||
b2 = -a;
|
||||
a0 = 1.f + a;
|
||||
a1 = -2.f * cos(w0);
|
||||
a2 = 1.f - a;
|
||||
bp_filters[i] = Biquad<float>{a0, a1, a2, b0, b1, b2};
|
||||
y_filter[i] = Pt1<float>{1.f, 1.f};
|
||||
}
|
||||
}
|
||||
|
||||
void QuarterApp::init()
|
||||
{
|
||||
set_filter();
|
||||
initial_time = micros();
|
||||
active = 15;
|
||||
rounds = 0;
|
||||
n_samples = 0;
|
||||
|
||||
pos_filter.reset();
|
||||
|
||||
for (int i = 0; i<n_BP; i++){
|
||||
bp_filters[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
void QuarterApp::deinit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void QuarterApp::loop()
|
||||
{
|
||||
float sample = get_sample();
|
||||
|
||||
energy += std::abs(sample) * std::abs(sample);
|
||||
n_samples++;
|
||||
|
||||
if (micros() - last_us_bp > sampling_period_bp)
|
||||
{
|
||||
|
||||
n_samples = 0;
|
||||
last_us_bp = micros();
|
||||
// energy_fil += (energy - energy_fil) * 0.01;
|
||||
|
||||
for (int i = 0; i < n_BP; i++)
|
||||
{
|
||||
y[i] = bp_filters[i].update(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] = y_filter[i].update(std::abs(y[i]),
|
||||
0.005f); // linie der scheitelpunkte
|
||||
// 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.5 - 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]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//every beat
|
||||
if (angle > 0 and angle_pre <= 0)
|
||||
{
|
||||
quarter = beat_count%4;
|
||||
|
||||
|
||||
//mode 0 logic if active
|
||||
if (modus == 0 & quarter == 0)
|
||||
{
|
||||
quarter_color_h1 += 10;
|
||||
quarter_color_h1 = quarter_color_h1%255;
|
||||
}
|
||||
|
||||
//mode 1 logic if active
|
||||
else if (modus == 1)
|
||||
{
|
||||
quarter_color_h1 += 15;
|
||||
quarter_color_h1 = quarter_color_h1%255;
|
||||
quarter_color_s = 255;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//every 4 beats
|
||||
else if (quarter == 0 & modus == 2)
|
||||
{
|
||||
quarter_color_s = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
quarter_color_s = 255;
|
||||
}
|
||||
|
||||
// state machine
|
||||
beat_count++;
|
||||
if (beat_count > 15)
|
||||
{
|
||||
beat_count = 0;
|
||||
modus_count++;
|
||||
|
||||
if (modus_count > 7)
|
||||
{
|
||||
modus_count = 0;
|
||||
modus++;
|
||||
if (modus > 3)
|
||||
{
|
||||
modus = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; i++)
|
||||
{
|
||||
|
||||
int h = get_value(i, quarter, beat_count, 'h');
|
||||
int s = get_value(i, quarter, beat_count, 's');
|
||||
int v = get_value(i, quarter, beat_count, 'v');
|
||||
|
||||
leds[i].setHSV(h, s, v);
|
||||
|
||||
}
|
||||
FastLED.show();
|
||||
|
||||
angle_pre = angle;
|
||||
energy = 0;
|
||||
}
|
||||
|
||||
if (micros() - last_us_control > sampling_period_control)
|
||||
{
|
||||
last_us_control = micros();
|
||||
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 != active)
|
||||
{
|
||||
rounds ++;
|
||||
}
|
||||
|
||||
if (rounds > 5)
|
||||
{
|
||||
active = argmax;
|
||||
rounds = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -6,19 +6,19 @@
|
||||
struct BeatDetectApp beat_detect_app
|
||||
{
|
||||
};
|
||||
struct VuMeterApp vu_meter_app
|
||||
{
|
||||
};
|
||||
struct FFTTestApp fft_test_app
|
||||
|
||||
struct QuarterApp quarter_app
|
||||
{
|
||||
};
|
||||
|
||||
struct FackelApp fackel_app
|
||||
{
|
||||
};
|
||||
struct ImageDisplayApp image_display {};
|
||||
|
||||
|
||||
|
||||
std::vector<std::reference_wrapper<App>> apps = {
|
||||
std::ref<App>(quarter_app),
|
||||
std::ref<App>(beat_detect_app),
|
||||
std::ref<App>(fackel_app),
|
||||
//std::ref<App>(image_display),
|
||||
|
BIN
inventor/OldVersions/lasercutter.0003.dwg
Normal file
BIN
inventor/OldVersions/lasercutter.0003.dwg
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
inventor2/OldVersions/lasercutter.0003.dwg
Normal file
BIN
inventor2/OldVersions/lasercutter.0003.dwg
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
inventor2/zubehoer/OldVersions/batteriehalter.0005.ipt
Normal file
BIN
inventor2/zubehoer/OldVersions/batteriehalter.0005.ipt
Normal file
Binary file not shown.
BIN
inventor2/zubehoer/batteriehalter.ipt
Normal file
BIN
inventor2/zubehoer/batteriehalter.ipt
Normal file
Binary file not shown.
BIN
inventor2/zubehoer/batteriehalter.stl
Normal file
BIN
inventor2/zubehoer/batteriehalter.stl
Normal file
Binary file not shown.
BIN
inventor2/zubehoer/batteriehalter2.stl
Normal file
BIN
inventor2/zubehoer/batteriehalter2.stl
Normal file
Binary file not shown.
Binary file not shown.
BIN
make_artikel/fritzing/fritzing_bb.png
Normal file
BIN
make_artikel/fritzing/fritzing_bb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 168 KiB |
@ -45,7 +45,7 @@ Der zweite Trick ist eine kreative Verwendung des Vasenmodus. Auf den ersten Bli
|
||||
|
||||
[Erklärbox] Beim 3D-Druck gibt es eine spezielle Druckmethode namens "Vase Mode", die es ermöglicht, hohle Objekte zu drucken. Der Vasenmodus funktioniert so, dass der Drucker nur die äußere Kontur des zu druckenden Objekts aufbaut. Dabei bewegt sich der Druckkopf in einer Spirale nach oben. Dadurch entsteht eine hohle Form, die oft einem Gefäß oder einer Vase ähnelt. Der Vorteil dieser Methode ist, dass sie sehr schnell ist, da nur die äußere Schicht des Objekts gedruckt werden muss. Außerdem spart es Material, da das Innere des Objekts leer bleibt. Das macht den Vasenmodus ideal für den Druck von dekorativen Objekten wie Vasen oder Lampenschirmen. Es gibt jedoch auch Nachteile: Da das Objekt hohl ist, kann es schwierig sein, es stabil genug zu drucken, besonders wenn es groß ist. Außerdem ist das Objekt nur von einer Seite zugänglich, was die Reinigung und Nachbearbeitung erschweren kann.
|
||||
|
||||
Wir nutzen hier die Definition einer Vase aus. Der Slicer fährt auf jeder Lage sturr die äußere Kontur ab. "Außere Kontur" ist hier rein mathematisch definiert. Indem wir einen Volumenkörper von außen dünn einschlitzen, können wir also auch im Innenraum des Eis einen Tunnel für die LED-stripes erzeugen. Das ist in den Abbildungen (vase mode 1) und (vase mode 2) illustriert.
|
||||
Wir nutzen hier die Definition einer Vase aus. Der Slicer fährt auf jeder Lage stur die äußere Kontur ab. "Außere Kontur" ist hier rein mathematisch definiert. Indem wir einen Volumenkörper von außen dünn einschlitzen, können wir also auch im Innenraum des Eis einen Tunnel für die LED-stripes erzeugen. Das ist in den Abbildungen (vase mode 1) und (vase mode 2) illustriert.
|
||||
|
||||
![vase mode 1](grafiken/illustrationen/vase_cad.PNG "Geometrie wie sie im CAD definiert ist")
|
||||
|
||||
@ -136,7 +136,10 @@ Wenn der schnelle Tiefpass mindestens 15% über dem langsamen Tiefpass liegt, da
|
||||
![elektronik 2](grafiken/kitchen_bilder/DSC07982.JPG "Einschieben der LED-Streifen")
|
||||
|
||||
12) Spiele die Software auf den ESP, z.B. mit Visual Studio Code und der Erweiterung PlatformIO.
|
||||
13) Verlöte Mikrofon, ESP, Beschleunigungssensor, Spannungsversorgung und die Anschlussleitungen für die LEDs gemäß der Darstellung (Todo).
|
||||
13) Verlöte Mikrofon, ESP, Beschleunigungssensor, Spannungsversorgung und die Anschlussleitungen für die LEDs gemäß der Darstellung.
|
||||
|
||||
![elektronik 3](fritzing/fritzing_bb.png "Fritzing")
|
||||
|
||||
14) Montiere die gelöteten Teile auf dem gelochten Holzbrett, z.B. mit Hilfe von dünnen Kabelbindern und Heißkleber.
|
||||
15) Lade den Akkupack, schließe und schalte ihn an.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user