"Christmas - the time to fix the computers of your loved ones" « Lord Wyrm

LED mit Mic steuern - Code issue

slateSC 16.08.2020 - 14:12 9726 45
Posts

slateSC

The Suntoucher
Avatar
Registered: Jul 2004
Location: oö
Posts: 1249
Ich nutze einen D1 Mini - ESP8266 mit soundsensor und einem WS2812B adressierbaren LED strip.
Es ist alles verlötet und der Testcode funktioniert auch einwandfrei. (Erster Code)
Nun hätte ich aber gerne eine etwas spannendere Animation wie zB.: in dieser Demo. (Zweiter Code)
Allerdings schaffe ich es nicht die funktionalität des Mikrofon mit dem zweiten Code zu verheiraten.

Und am schönsten wär eigentlich etwas in Richtung Spectrum (zweiter Effekt im Video), aber das funzt anscheinend nur über das W-lan und nen Rechner.
Ich möchte das ganze aber mobil mit nem Akkubar ohne Netzwerkanbindung betreiben.


Code:
#include <FastLED.h>
#define NUM_LEDS 60
#define DATA_PIN 3 //not D3 but 3 for FASTLED!
CRGB leds[NUM_LEDS];

#define ANALOG_SIGNAL_INPUT A0 //for external microphone 13
//#define M5STACKFIRE_SPEAKER_PIN 25 // speaker DAC, only 8 Bit
//uint32_t triggerLevel = 512;
uint32_t triggerLevel = 200;

bool startAnimation = true;
int i = 0;


void showFastLed() {
  static uint8_t hue = 0;

  if(startAnimation == true) {
    i = 0; //start at the beginning
    startAnimation = false;
  }

  leds[i] = CHSV(hue++, 255, 255);
  FastLED.show(); 
  leds[i] = CRGB::Black; // now that we've shown the leds, reset the i'th led to black
  
  if (i < NUM_LEDS) i++;

  /**
   * 10ms ... 20 LEDs
   * 3ms ... 60 LEDs
   */
  delay(5);
}


void setup() {
  Serial.begin(115200);
  pinMode(ANALOG_SIGNAL_INPUT, INPUT);
  //dacWrite(M5STACKFIRE_SPEAKER_PIN, 0); // make sure that the speaker is quiet
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);  // GRB ordering is assumed
  leds[1] = CHSV(2, 255, 255); FastLED.show(); delay(700);
}

void loop() {
  int analogValue = analogRead(ANALOG_SIGNAL_INPUT);

  if (analogValue < triggerLevel) {
    Serial.println(analogValue);
    startAnimation = true;
  }
  showFastLed();
}

----------------------------------------

Code:
#include <FastLED.h>

#define LED_PIN     5
#define NUM_LEDS    14
#define BRIGHTNESS  64
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

#define UPDATES_PER_SECOND 100

// This example shows several ways to set up and use 'palettes' of colors
// with FastLED.
//
// These compact palettes provide an easy way to re-colorize your
// animation on the fly, quickly, easily, and with low overhead.
//
// USING palettes is MUCH simpler in practice than in theory, so first just
// run this sketch, and watch the pretty lights as you then read through
// the code.  Although this sketch has eight (or more) different color schemes,
// the entire sketch compiles down to about 6.5K on AVR.
//
// FastLED provides a few pre-configured color palettes, and makes it
// extremely easy to make up your own color schemes with palettes.
//
// Some notes on the more abstract 'theory and practice' of
// FastLED compact palettes are at the bottom of this file.



CRGBPalette16 currentPalette;
TBlendType    currentBlending;

extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;


void setup() {
    delay( 3000 ); // power-up safety delay
    FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
    FastLED.setBrightness(  BRIGHTNESS );
    
    currentPalette = RainbowColors_p;
    currentBlending = LINEARBLEND;
}


void loop()
{
    ChangePalettePeriodically();
    
    static uint8_t startIndex = 0;
    startIndex = startIndex + 1; /* motion speed */
    
    FillLEDsFromPaletteColors( startIndex);
    
    FastLED.show();
    FastLED.delay(1000 / UPDATES_PER_SECOND);
}

void FillLEDsFromPaletteColors( uint8_t colorIndex)
{
    uint8_t brightness = 255;
    
    for( int i = 0; i < NUM_LEDS; i++) {
        leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
        colorIndex += 3;
    }
}


// There are several different palettes of colors demonstrated here.
//
// FastLED provides several 'preset' palettes: RainbowColors_p, RainbowStripeColors_p,
// OceanColors_p, CloudColors_p, LavaColors_p, ForestColors_p, and PartyColors_p.
//
// Additionally, you can manually define your own color palettes, or you can write
// code that creates color palettes on the fly.  All are shown here.

void ChangePalettePeriodically()
{
    uint8_t secondHand = (millis() / 1000) % 60;
    static uint8_t lastSecond = 99;
    
    if( lastSecond != secondHand) {
        lastSecond = secondHand;
        if( secondHand ==  0)  { currentPalette = RainbowColors_p;         currentBlending = LINEARBLEND; }
        if( secondHand == 10)  { currentPalette = RainbowStripeColors_p;   currentBlending = NOBLEND;  }
        if( secondHand == 15)  { currentPalette = RainbowStripeColors_p;   currentBlending = LINEARBLEND; }
        if( secondHand == 20)  { SetupPurpleAndGreenPalette();             currentBlending = LINEARBLEND; }
        if( secondHand == 25)  { SetupTotallyRandomPalette();              currentBlending = LINEARBLEND; }
        if( secondHand == 30)  { SetupBlackAndWhiteStripedPalette();       currentBlending = NOBLEND; }
        if( secondHand == 35)  { SetupBlackAndWhiteStripedPalette();       currentBlending = LINEARBLEND; }
        if( secondHand == 40)  { currentPalette = CloudColors_p;           currentBlending = LINEARBLEND; }
        if( secondHand == 45)  { currentPalette = PartyColors_p;           currentBlending = LINEARBLEND; }
        if( secondHand == 50)  { currentPalette = myRedWhiteBluePalette_p; currentBlending = NOBLEND;  }
        if( secondHand == 55)  { currentPalette = myRedWhiteBluePalette_p; currentBlending = LINEARBLEND; }
    }
}

// This function fills the palette with totally random colors.
void SetupTotallyRandomPalette()
{
    for( int i = 0; i < 16; i++) {
        currentPalette[i] = CHSV( random8(), 255, random8());
    }
}

// This function sets up a palette of black and white stripes,
// using code.  Since the palette is effectively an array of
// sixteen CRGB colors, the various fill_* functions can be used
// to set them up.
void SetupBlackAndWhiteStripedPalette()
{
    // 'black out' all 16 palette entries...
    fill_solid( currentPalette, 16, CRGB::Black);
    // and set every fourth one to white.
    currentPalette[0] = CRGB::White;
    currentPalette[4] = CRGB::White;
    currentPalette[8] = CRGB::White;
    currentPalette[12] = CRGB::White;
    
}

// This function sets up a palette of purple and green stripes.
void SetupPurpleAndGreenPalette()
{
    CRGB purple = CHSV( HUE_PURPLE, 255, 255);
    CRGB green  = CHSV( HUE_GREEN, 255, 255);
    CRGB black  = CRGB::Black;
    
    currentPalette = CRGBPalette16(
                                   green,  green,  black,  black,
                                   purple, purple, black,  black,
                                   green,  green,  black,  black,
                                   purple, purple, black,  black );
}


// This example shows how to set up a static color palette
// which is stored in PROGMEM (flash), which is almost always more
// plentiful than RAM.  A static PROGMEM palette like this
// takes up 64 bytes of flash.
const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM =
{
    CRGB::Red,
    CRGB::Gray, // 'white' is too bright compared to red and blue
    CRGB::Blue,
    CRGB::Black,
    
    CRGB::Red,
    CRGB::Gray,
    CRGB::Blue,
    CRGB::Black,
    
    CRGB::Red,
    CRGB::Red,
    CRGB::Gray,
    CRGB::Gray,
    CRGB::Blue,
    CRGB::Blue,
    CRGB::Black,
    CRGB::Black
};



// Additionl notes on FastLED compact palettes:
//
// Normally, in computer graphics, the palette (or "color lookup table")
// has 256 entries, each containing a specific 24-bit RGB color.  You can then
// index into the color palette using a simple 8-bit (one byte) value.
// A 256-entry color palette takes up 768 bytes of RAM, which on Arduino
// is quite possibly "too many" bytes.
//
// FastLED does offer traditional 256-element palettes, for setups that
// can afford the 768-byte cost in RAM.
//
// However, FastLED also offers a compact alternative.  FastLED offers
// palettes that store 16 distinct entries, but can be accessed AS IF
// they actually have 256 entries; this is accomplished by interpolating
// between the 16 explicit entries to create fifteen intermediate palette
// entries between each pair.
//
// So for example, if you set the first two explicit entries of a compact 
// palette to Green (0,255,0) and Blue (0,0,255), and then retrieved 
// the first sixteen entries from the virtual palette (of 256), you'd get
// Green, followed by a smooth gradient from green-to-blue, and then Blue.
Bearbeitet von slateSC am 16.08.2020, 14:28

Vinci

hatin' on summer
Registered: Jan 2003
Location: Wien
Posts: 5774
Owei... Ich mag dir jetzt nicht den Spaß verderben, aber das ist ein ziemlich ambitioniertes Projekt. Gar nicht von der eigentlichen Aufgabenstellung her (die auch nicht ganz ohne is), sondern weil deine Hardware dafür hint und vorn nicht ausgelegt ist. Ich versuch mal meine Bedenken ein wenig zu sortieren.

1.) Analog
Dem Code nach zu urteilen ist dein Audioeingang analog. Das ist zwar prinzipiell einmal nicht verkehrt, jedoch ist der 10Bit ESP8266 ADC eher ein "Gimmick", aber keine ernst zu nehmende Signalquelle. Im offiziellen Datenblatt finden sich bis heute weder Eingangsimpedanz noch Abtastrate von dem Ding. Aktiviert man WLAN oder Bluetooth, dann kann man von den angeblichen 10Bit (die eh nie stimmen) gleich nochmal ~3 abziehen. Im Vergleich zu echten modernen ADCs besitzt der ESP also eher einen "Digitaleingang mit ein paar Stufen." Das mag jetzt für so ein wenig Lichtspielerei noch keine Hürde sein, sinnvolle Audio-Abtastung fangt nach Nyquist aber quasi erst ab 40kHz an... das trau ich dem ADC nicht zu. Hohe Frequenzen werden also schon mal unter den Tisch fallen.

2.) Rechenleistung
Wie dir vielleicht aufgefallen ist übernimmt bei dem Projekt eigentlich der Raspberry die Signalverarbeitung. Nach kurzem drüberschaun im Code seh ich:
- Exponentielles Filter
- FFT
- Gauß-Filter
Bei +1GHz alles kein Problem. Der ESP8266 hat aber im besten Fall 160MHz und(!) keine FPU. Das heißt also du müsstest diese ganzen Filter via Ganzzahl-Algorithmen Implementieren und kannst keine Gleitkommazahlen verwenden. Ich kenn leider die Tensilica Architektur nicht sehr gut, rein gefühlsmäßig bewegt sich das aber alles sehr hart an der Grenze zum Machbaren. Ich habe seit Jahren tagtäglich mit solchen Aufgabenstellungen zu tun und könnt salopp nicht sagen obs überhaupt möglich wär...

slateSC

The Suntoucher
Avatar
Registered: Jul 2004
Location: oö
Posts: 1249
Hallo Vinci,

erst mal danke für deinen sehr ausführlichen Response!
Ich nehme mal an du sprichst in erster Linie von dem Code und der Umsetzung in dem verlinkten YouTube Video?

Die beiden Codes, die ich direkt in den Post kopiert habe, funktionieren von der Performance her ausgezeichnet.
Für mich ist halt die frage ob man das analoge mic mit ein bisschen mehr bling bling aus dem zweiten code kombinieren kann?

Das ganze muss nicht jeden Ton abbilden, sondern soll in erster Linie nur grob zur abgespielten musik/lautstärke passen.
Ich will das Ding nur so kompakt wie möglich unter mein Audiorig basteln, deshalb fallen für mich raspberry und konsorten sowieso flach.
Bearbeitet von slateSC am 16.08.2020, 16:04

Vinci

hatin' on summer
Registered: Jan 2003
Location: Wien
Posts: 5774
Ja ich red von der Umsetzung im Link. Da sind sehr rechenintensive Filter drin die alle in Echtzeit laufen müssen wenn das 1:1 so umgesetzt werden soll.

Dass dein Code funktioniert glaub ich schon, der hat aber auch momentan quasi keine Echtzeitanforderung. FastLED buttert nehm ich an via SPI oder ähnlichem Daten an den WS2812 raus, dabei kann der Core ja quasi schlafen. ;)

Wenn alles "ein wenig gröber" auch geht, dann könnt ma da schon hinkommen. Die entscheidende Frage für den Anfang ist jetzt erstmal was der ADC als Samplerate schafft. Beim googlen hab ich zwischen 200Hz bis 20kHz nämlich grad so gut wie alle Angaben gefunden...

Kannst du das vielleicht mal messen?

slateSC

The Suntoucher
Avatar
Registered: Jul 2004
Location: oö
Posts: 1249
Ich hab leider nichts zum Messen da.
In der Beschreibung vom Reichelt ist von 160 MHz die Rede?

Von dort hab ich den zweiten code her - link - da sieht man im gif was die fastLED library so zaubert.
Bearbeitet von slateSC am 16.08.2020, 16:32

Viper780

Er ist tot, Jim!
Avatar
Registered: Mar 2001
Location: Wien
Posts: 48898
Mehr als 80Mhz kann nicht gehen.
Der Timer hat 160Mhz/2

Nur dass der trotzdem ned recht genau ist.
Was in Deinem Fall egal ist. Ich würd mal auf ein paar kHz hin arbeiten (aliasing Filter ned vergessen)

Vinci

hatin' on summer
Registered: Jan 2003
Location: Wien
Posts: 5774
160MHz ist die Geschwindigkeit des Tensilica Cores, nicht aber die Samplerate des ADCs. Zu der find ich im Netz keinerlei Angaben (wohl aus gutem Grund).

Verschiedene schwindlige Foreneinträge suggerieren dass man mit der Arduino API (analogRead) nicht mal auf 1ksps kommt. Damit wäre die Idee, zumindest mit dem internen ADC, gestorben.

Das Nyquist-Theorem sagt uns, dass wir eigentlich ~40ksps bräuchten um Musik im Bereich von 0-20kHz vernünftig abtasten zu können. Umso weniger das wird, umso mehr verliert man hohe Frequenzen.
Und ja... wie gesagt, bei den Werten die ich da in den Foren finde bräucht ma da gar nicht weiter machen.

Nur mal zum Vergleich wie schlecht der ADC des ESP8266 tatsächlich ist: Ein STM32F407 der 2011 auf den Markt kam schafft bei 12Bit 2.4Msps, sprich 2.4 Millionen Abtastungen pro Sekunde! (ja... i know, Eingangsimpedanz und so, aber trotzdem)

/edit
Sofern der Arduino Layer da bremst könnte man auch mal probieren direkt die Espressif Toolchain zu nutzen. Die ist aber leider weitaus weniger bequem als Arduino und erfordert weitaus tiefere Kenntnisse.

Viper780

Er ist tot, Jim!
Avatar
Registered: Mar 2001
Location: Wien
Posts: 48898
Arduino Layer ist sowieso Müll.

Würd das einfach mal mit 6bit und so nativ wie möglich umsetzen.

Über 8kHz kannst bei Musik getrost weg lassen, da passiert nichts mehr relevantes und in unserem Alter ist bei 12-14kHz sowieso Schluss. Soll ja nur ein Fun Projekt sein das cool ausschaut wenn ich das richtig versteh.
Tiefpass vorn ADC und gut ist.

Der STM32F hat kein WLAN. Für mich ganz eine andere Anwendung.
Wenn dann würd ich entsprechende Equalizer Chips verwenden die das meiste schon integriert haben

Vinci

hatin' on summer
Registered: Jan 2003
Location: Wien
Posts: 5774
Zitat aus einem Post von Viper780
Arduino Layer ist sowieso Müll.

Würd das einfach mal mit 6bit und so nativ wie möglich umsetzen.

Über 8kHz kannst bei Musik getrost weg lassen, da passiert nichts mehr relevantes und in unserem Alter ist bei 12-14kHz sowieso Schluss. Soll ja nur ein Fun Projekt sein das cool ausschaut wenn ich das richtig versteh.
Tiefpass vorn ADC und gut ist.

Der STM32F hat kein WLAN. Für mich ganz eine andere Anwendung.
Wenn dann würd ich entsprechende Equalizer Chips verwenden die das meiste schon integriert haben

Du meinst des WLAN dass a eh ned braucht? :D
Ja eh klar, war ja nur Beispiel um aufzuzeigen wie "gut" der ADC is...

slateSC

The Suntoucher
Avatar
Registered: Jul 2004
Location: oö
Posts: 1249
Zitat aus einem Post von Vinci
Umso weniger das wird, umso mehr verliert man hohe Frequenzen.
kein Thema, in erster Linie gehts um den Bass :D

Zitat aus einem Post von Viper780
Soll ja nur ein Fun Projekt sein das cool ausschaut wenn ich das richtig versteh.
Genau das! Ohne W-lan und schnick schnack.

Wie gsagt, mein erster Ansatz war es mal die beiden Codes (der erste funktioniert schon mit dem mic) miteinander zu kombinieren, sodass sich ein bisschen mehr tut, als nur ein paar Pixel die vom Anfang zum Ende schießen.
Aber ich bekommdas irgendwie nicht zum Laufen...

Das höchste der Gefühle wäre es dann noch, wenn sich das Visual von der Mitte aus in beide Richtungen ausbreitet.

Edit:
So sieht das Rig aus.
Hatte da vorher die Ikea LED drunter gehängt, aber die ist nicht so der bringer ;)
click to enlarge
Bearbeitet von slateSC am 16.08.2020, 20:26

Vinci

hatin' on summer
Registered: Jan 2003
Location: Wien
Posts: 5774
Das funktioniert alles nicht so wie du dir vorstellst glaub ich. Der Code im ersten Schnipsel liefert dir einen einzigen Abtastwert. Nachdem du eh einiges von Audio zu verstehen scheinst muss dir doch klar sein, dass man mit einem einzelnen Messpunkt nicht "grob auf die abgespielte Musik" reagieren kann? Wenn du an Hand eines einzigen analogen Wertes irgendwas blinkst, dann wird das ziemlich random aussehen.

Es fängt also alles mal damit eine Messreihe aufzunehmen, am besten mit einer konstanten Periode. Die Arduino API ist für solche Anwendungen leider reichlich unzulänglich...

Code: C++
#include <cstdint>

void measure() {
  // Measure ADC
  auto const val{analogRead()};
  // Push to buffer, array or whatever
}

constexpr auto period_us{200u};
uint32_t then{};

// Measure ADC every 200us
void loop() {
  auto const now{micros()};
  if (now > then) {
    then = now + period_us;
    measure();
  }
}

Ob der Code funktioniert? Keine Ahnung, ich weiß nicht wie lang eine ADC Messung über "analogRead()" dauert...
Bearbeitet von Vinci am 17.08.2020, 08:43

matiss

Chaos Maestro
Avatar
Registered: Dec 2007
Location: Vienna
Posts: 695
Kann leider nicht viel beitragen da ich sowas seit sehr langer Zeit nicht mehr gemacht habe und nur noch Bruchstücke verstehe. Ich bin jedoch gerade auf YouTube über folgendes gestolpert und vielleicht hilft es dir.

slateSC

The Suntoucher
Avatar
Registered: Jul 2004
Location: oö
Posts: 1249
Zitat aus einem Post von Vinci
Das funktioniert alles nicht so wie du dir vorstellst glaub ich.

Da ich für mehr keinen Platz habe ist die Prio 1, dass das ganze mit den ausgewählten komponenten umgesetzt wird. (Ich werd auch die LED noch etwas kürzen müssen)
Demnach reduziere ich meine Ansprüche soweit es sein muss. Auch wenn das heisst, das nur Random was passiert sobald die Musik losgeht.
Die Effekte ausm 2ten Code schauen per default schon nicht schlecht aus und es tut sich trotzdem schon mal viel mehr als bei ner einfachen Farbüberblendung ala Ikea.
Und der Erste Code funktioniert trotz seiner Einfachheit erstaunlich gut mit zB.: Drum & Base.

Alles andere wie - Effekte breiten sich von der Mitte in beide Richtungen aus (sollte zB.: mit einem einfachen glow ja ebenso möglich sein?)
sind nur nice to haves.

Wenn ich den Code von dir compile kommt die Meldung: "too few arguments to function 'int analogRead(uint8_t)'"

@matiss: Das hab ich auch schon gesehen. Da bekommst ja nen epileptischen Anfall :D
Bearbeitet von slateSC am 17.08.2020, 20:20

wergor

connoisseur de mimi
Avatar
Registered: Jul 2005
Location: graz
Posts: 4027
Zitat aus einem Post von slateSC
"too few arguments to function 'int analogRead(uint8_t)'"
der code ist auch nicht dazu gedacht compiliert zu werden ;)
analogRead will den pin als argument, z.b.
Code:
analogRead(A0);

slateSC

The Suntoucher
Avatar
Registered: Jul 2004
Location: oö
Posts: 1249
Zitat aus einem Post von wergor
der code ist auch nicht dazu gedacht compiliert zu werden ;)
analogRead will den pin als argument, z.b.
Code:
analogRead(A0);
Okay ich checks nicht - wenn er nicht compiliert werden kann, kann ich ihn ja auch nicht raufspielen?
Und wenn ich A0 Eintrage, was auch korrekt ist, kommt "no match for'operator>' (operand...."
Kontakt | Unser Forum | Über overclockers.at | Impressum | Datenschutz