Hilfe für LED-Bastelei gesucht

Hallo zusammen,

ich suche Hilfe für ein Bastelprojekt.

Ich möchte mir für ein Musik-Festival eine Art tanztauglichen Rucksack basteln in dem ich meine Wasserflasche transportieren kann. Der Rucksack-Körper besteht aus einem PVC-Fallrohr (ca. 50cm hoch und 10cm Druchmesser). Daran befestige ich mit Kabelbindern einen alten Fahrradschlauch um 2 Schultergurte zu erhalten. Etwas unterhalb der Mitte kommt eine Gewindestange durch das Rohr, die die Flasche am durchrutschen hindert.

Im Querschnitt sieht das dann so aus.

xport

Im unteren Teil des Rohrs ist dann noch genug Platz für eine Powerbank um etwas Dekobeleuchtung zu betreiben, die aussen an das Rohr kommen soll.

Für diesen Zweck habe ich mir erstmal eine Komplettlösung bestellt, einen 2m 5V-LED-Streifen der über ein eingebautes Mikrophon ein paar Musik-Sync-Effekte kann. Diese Effekte gefallen mir aber nicht und jetzt will ich versuchen mir eine eigene Lösung, die über einen Mirocontroller gesteuert wird zu erarbeiten.

Erfahrung mit Arduino, Raspberry & Co hab ich keine und meine Lötskills und Stromkenntnisse sind ausbaufähig, darum wende ich mich an Euch :slight_smile:

Was ich gerne hätte sind 2-3 LED-Streifen a 30-40cm, mit einzeln ansteuerbaren LEDs, die senkrecht außen am Rohr verlaufen und ein paar Musik-synchrone Effekte (VU-Meter o,ä,) abbilden können.

Die Anzahl und Helligkeit der LEDs ist dabei zweitrangig. Zum einen sollen die Menschen hinter mir nicht genervt von zu hellem Geblinke sein und zum anderen soll der Stromverbrauch nicht zu hoch sein.

Für die Stromversorgung habe ich diese Powerbank ins Auge gefasst, in der Hoffnung damit den Controller (9V-Ausgang) und die Stripes (5V-Ausgang) gleichzeitig betreiben zu können.

Als Gehäuse würde sich z.B. eine 250g-Kaffeedose anbieten, die formschlüssig in das PVC-Rohr passt.

xport2

Für die Bedienung hätte ich gerne nicht mehr als 1-2 Taster und ggf 1 Potientometer für die Empfindlichkeit (falls es nicht schon möglich ist, dass sich das Programm selber einpegelt).

Von dem was ich bisher bei Youtube so gefunden habe, kommt folgendes Video meiner Vorstellung recht nahe, da es sehr nach Plug&Play aussieht und ohne feine Bauteile wie Widerstände und Transistoren auskommt.

Leider ist da wenig Erklärung dabei, umso mehr freue ich mich über Eure Ideen & Anregungen.

Viele Grüße und herzlichen Dank im Voraus :slight_smile:

Die Inspiration ist klar erkennbar :crazy_face:

Ich würde dir vorschlagen da einen ESP8266 zu nehmen, da du schon 5V LED stripes nimmst. Dann kannst du alles mit 5V betreiben und bist bei der Powerbank deutlich weniger eingeschränkt. Die dinger gibts für 5-10 euro und die haben gleich WLAN an Bord.

Wenn es um SW mit LEDs angeht wäre ich sofort bei WLED gewesen. Das kenne ich allerdings bisher nur nicht reaktiv. Es scheint da aber bereits einen Fork zu geben:

Vielleicht hilft dir das ja als Inspiration :smile:

1 „Gefällt mir“

Vielen Dank für den Tipp! Leider ist ne Wifi-Bedienung für meine Zwecke eher unpassend, weil ich auf dem Festival ohne Handy unterwegs sein will.

Ich bin gestern noch auf dieses Video gestossen:

…und hab mir jetzt mal die Bauteile für diese Lösung bestellt.

Das ganze soll nach diesem Schaltplan aufgebaut werden

Falls das so funktioniert, käme ich auch mit einem einzelnen 5V-Powerbank-Ausgang aus.
Was mich ein wenig irritiert ist, dass das Netzteil sowohl beim 5V Eingang Ausgang als auch beim 3,3V Eingang Ausgang angeschlossen werden soll. Falls jemand dazu Ideen, Einwände oder Erklärungen hat, freu ich mich sehr :slight_smile:

Hier noch zum Vergleich ein Arduino-Uno mit lesbarer Beschriftung

Bzgl. Netzteil und 5V, 3,3V ich glaube da musst Du aufpassen, dass du da nix kurzschließt. Das dürften für mein Verständnis 3 verschiedene Stromkreise sein, auch wenn sie sich in der Zeichnung berühren und die gleiche Farbe haben.

Nach meinem Verstädnis (ist aber auch schon ewig her das ich mit einem Arduino mal was gebastelt habe) ist es so, dass du deine (unregulierte) Spannungsquelle (Batterie, Netzteil, …) auf Vin rein gibst und der Arduino das dann intern auf 5V und 3,3V reguliert und Du es dann von dort für die Bauteile nutzen kannst, die eine regulierte Spannungsquelle brauchen.

Aus der Zeichnung würde ich jetzt schätzen, dass die LEDs mit 3,3V und das Soundmodul mit 5V versorgt werden sollen, das wirst Du aber sicherlich deren Spezifikationen entnehmen können.

1 „Gefällt mir“

Ohne deinen Elan dämpfen zu wollen, aber auf den Festivals, die ich so kenne (zugegebenermaßen nur recht große), darf man heutzutage nicht mal mehr nen Trinkrucksack mit Wasserbeutel mit aufs Gelände nehmen. Safety/Security und 10 Euro fürs Bier spielen da ne Rolle… :face_with_diagonal_mouth:

Also Du hast natürlich Recht, der 5V- und der 3,3V-Pin sind Ausgänge und nicht wie von mir oben geschrieben Eingänge :man_facepalming:.

Bezüglich eines Kurzschlusses war ich zwar auch skeptisch, hab das ganze allerdings jetzt doch mal stumpf so aufgebaut wie es der Schaltplan zeigt. Also alles was die gleiche Farbe hat verbunden.
Und es funktioniert :slight_smile: Allerdings reagieren die LEDs nur in einem Modus auch wirklich auf Geräusche die das Mikrofon aufnimmt.

In diesem Video sieht man zuerst den reaktiven Modus (Centre out) und nach 10Sekunden schalte ich auf den nächsten Modus (Centre inwards), der allerdings nicht auf Geräusche reagiert.

Hier der Code für den centre out Effekt

void vu1() {
  
  uint8_t  i;
  uint16_t minLvl, maxLvl;
  int      n, height;
 
 
 
  n   = analogRead(MIC_PIN);                        // Raw reading from mic 
  n   = abs(n - 512 - DC_OFFSET); // Center on zero
  n   = (n <= NOISE) ? 0 : (n - NOISE);             // Remove noise/hum
  lvl = ((lvl * 7) + n) >> 3;    // "Dampened" reading (else looks twitchy)
 
  // Calculate bar height based on dynamic min/max levels (fixed point):
  height = TOP * (lvl - minLvlAvg) / (long)(maxLvlAvg - minLvlAvg);
 
  if(height < 0L)       height = 0;      // Clip output
  else if(height > TOP) height = TOP;
  if(height > peak)     peak   = height; // Keep 'peak' dot at top
 
 
  // Color pixels based on rainbow gradient
  for(i=0; i<N_PIXELS_HALF; i++) {
    if(i >= height) {              
      strip.setPixelColor(N_PIXELS_HALF-i-1,   0,   0, 0);
      strip.setPixelColor(N_PIXELS_HALF+i,   0,   0, 0);
    }
    else {
      uint32_t color = Wheel(map(i,0,N_PIXELS_HALF-1,30,150));
      strip.setPixelColor(N_PIXELS_HALF-i-1,color);
      strip.setPixelColor(N_PIXELS_HALF+i,color);
    }
    
  } 
 
  // Draw peak dot  
  if(peak > 0 && peak <= N_PIXELS_HALF-1) {
    uint32_t color = Wheel(map(peak,0,N_PIXELS_HALF-1,30,150));
    strip.setPixelColor(N_PIXELS_HALF-peak-1,color);
    strip.setPixelColor(N_PIXELS_HALF+peak,color);
  }
  
   strip.show(); // Update strip
 
// Every few frames, make the peak pixel drop by 1:
 
    if(++dotCount >= PEAK_FALL) { //fall rate 
      
      if(peak > 0) peak--;
      dotCount = 0;
    }
 
 
 
  vol[volCount] = n;                      // Save sample for dynamic leveling
  if(++volCount >= SAMPLES) volCount = 0; // Advance/rollover sample counter
 
  // Get volume range of prior frames
  minLvl = maxLvl = vol[0];
  for(i=1; i<SAMPLES; i++) {
    if(vol[i] < minLvl)      minLvl = vol[i];
    else if(vol[i] > maxLvl) maxLvl = vol[i];
  }
  // minLvl and maxLvl indicate the volume range over prior frames, used
  // for vertically scaling the output graph (so it looks interesting
  // regardless of volume level).  If they're too close together though
  // (e.g. at very low volume levels) the graph becomes super coarse
  // and 'jumpy'...so keep some minimum distance between them (this
  // also lets the graph go to zero when no sound is playing):
  if((maxLvl - minLvl) < TOP) maxLvl = minLvl + TOP;
  minLvlAvg = (minLvlAvg * 63 + minLvl) >> 6; // Dampen min/max levels
  maxLvlAvg = (maxLvlAvg * 63 + maxLvl) >> 6; // (fake rolling average)
 
}

…und hier der Code für centre inwards


void vu2() 
    {
      unsigned long startMillis= millis();  // Start of sample window
      float peakToPeak = 0;   // peak-to-peak level
     
      unsigned int signalMax = 0;
      unsigned int signalMin = 1023;
      unsigned int c, y;
     
      
      while (millis() - startMillis < SAMPLE_WINDOW)
      {
        sample = analogRead(MIC_PIN);
        if (sample < 1024)  
        {
          if (sample > signalMax)
          {
            signalMax = sample;  
          }
          else if (sample < signalMin)
          {
            signalMin = sample;  
          }
        }
      }
      peakToPeak = signalMax - signalMin;  
     
      // Serial.println(peakToPeak);
     
     
      
      for (int i=0;i<=N_PIXELS_HALF-1;i++){
        uint32_t color = Wheel(map(i,0,N_PIXELS_HALF-1,30,150));
        strip.setPixelColor(N_PIXELS-i,color);
        strip.setPixelColor(0+i,color);
      }

      
      c = fscale(INPUT_FLOOR, INPUT_CEILING, N_PIXELS_HALF, 0, peakToPeak, 2);
     
      if(c < peak) {
        peak = c;        // Keep dot on top
        dotHangCount = 0;    // make the dot hang before falling
      }
      if (c <= strip.numPixels()) { // Fill partial column with off pixels
        drawLine(N_PIXELS_HALF, N_PIXELS_HALF-c, strip.Color(0, 0, 0));
        drawLine(N_PIXELS_HALF, N_PIXELS_HALF+c, strip.Color(0, 0, 0));
      }
     
      


      y = N_PIXELS_HALF - peak;
      uint32_t color1 = Wheel(map(y,0,N_PIXELS_HALF-1,30,150));
      strip.setPixelColor(y-1,color1);
      //strip.setPixelColor(y-1,Wheel(map(y,0,N_PIXELS_HALF-1,30,150)));

      y = N_PIXELS_HALF + peak;
      strip.setPixelColor(y,color1);
      //strip.setPixelColor(y+1,Wheel(map(y,0,N_PIXELS_HALF+1,30,150)));
     
      strip.show();
     
      // Frame based peak dot animation
      if(dotHangCount > PEAK_HANG) { //Peak pause length
        if(++dotCount >= PEAK_FALL2) { //Fall rate 
          peak++;
          dotCount = 0;
        }
      } 
      else {
        dotHangCount++; 
      }
    }

Vielleicht hat ja jemand eine Idee woran es liegen könnte oder ne Idee mit welchen Variablen ich mal rumspielen sollte.

Danke für den Hinweis, aber bei dem Festival zu dem ich fahre ist das glücklicherweise kein Problem :slight_smile:

Manchmal geht´s doch ganz schnell :smile:

Dank dem Einwurf von @michael kam mir der Verdacht, dass die Werte die das Mikrofon ausgibt vielleicht zu unsauber sind.

Verkabelt habe ich es jetzt nach diesem Schema

Das Mikrofon ist also nur mit dem 3,3V- und dem AREF-PIN des Arduinos verbunden.
Zusätzlich habe ich in dem Code die Zeile gefunden

 #define DC_OFFSET  0  // DC offset in mic signal - if unusure, leave 0

und da ich noch auf dieses Video augestossen bin

…habe ich den DC_OFFSET-Wert auch mal auf 510 gesetzt.
Und siehe da, nun funktionieren all Modi des Codes :grinning:

Was nun noch fehlt ist die Einbindung eines Potentiometers. So einer ist zwar auf dem Mikrofon-Modul vorhanden, aber die Bedienung mit einem Kreuzschlitz-Schraubendreher ist für ein Festival natürlich keine Option.

Im Code schon vorhanden, aber sonst nicht weiter beschrieben, ist

 #define POT_PIN    4

also werde ich das jetzt mal ausprobieren und gucken ob ich über diesen Pin die Empfindlichkeit steuern kann.

Cool, dass das geklappt hat. Ich habe aktuell kein stabiles Internet und eiere nur am Handy herum, aber hast du mal geschaut ob die eingebundenen Bibliotheken den POT_PIN referenzieren?

Wenn nicht, kannst du den auch selbst mit
analogRead(POT_PIN); auslesen (dann muss das Signal vom Poti an den Analogeingang 4.

Ansonsten könnte INPUT_CEILING von der Beschreibung her die passende Stellschraube für die Empfindlichkeit sein (niedriger = sensibler). Der erwartete Datenbereich liegt hier zwischen 0 und 1023, dürfte also genau der Wertebereich sein, den die Analogue Inputs zurückgeben.

1 „Gefällt mir“

In den Bibliotheken habe ich nicht geguckt, weil ich außer dem Keyword-Dokument nicht weiß wo ich da gucken muss. :see_no_evil:

Mit „nicht weiter beschrieben“ meinte ich, dass dort kein //Kommentar dahinter steht. Im code selbst wird sich noch bei 4 Effekten auf den Pot_Pin bezogen. Ich habe mal einen Poti-Signal-Ausgang mit Pin4 (sowie die anderen Poti-Pins mit 5V bzw. Ground) verbunden, aber noch nicht herausgefunden, was das bewirken soll. Sprich keine Veränderung festgestellt.

Momentan habe ich den Poti (10kOHM) mit dem Signal-Ausgang des Mikrofons verbunden und kann so die Empfindlichkeit justieren.

Das funktioniert auch bei manchen Effekten ganz gut, bei anderen habe ich den Eindruck ich könnte über den Poti auf dem Mikrofon-Modul bessere Einstellungen vornehmen. Keine Ahnung ob die beiden Potis sich da in die Quere kommen (das kenne ich zumindest von Glühlampen-Dimmern).

Deine Idee @michael mit dem INPUT_CEILING-Wert herumzuspielen war übrigens goldwert :heart: und hat viel gebracht. Leider kann ich die Lautstärke die es auf dem Festival hat überhaupt nicht einschätzen und sie schwankt ja dann je nach Position auch stark. Zum Testen jetzt spiele ich Musik über ne Bluetooth-Box, die ich mal näher und mal weiter weg vom Mikrofon postiere und da sind die Auswirkungen schon bei ein paar cm enorm.

Meine Idee wäre jetzt den Code so umzuschreiben, dass ich den INPUT_CEILING-Wert auch über einen Poti steuern kann, neben einem weiteren Poti der Spannung des Mikrofonsignals regelt.
Meint Ihr das ist sinnvoll oder ist da etwas doppeltgemoppelt und eher kontraproduktiv?

Die Idee mit dem zweiten Poti habe ich doch wieder verworfen, das erschien mir nicht so ganz praktikabel.

Dafür habe ich das Schaltbild etwas sauberer gestaltet, den Poti wieder herausgenommen und einen Widerstand in die Datenleitung zu den LED-Streifen eingebaut, der die erste LED schützt. Das sieht jetzt so aus

Außerdem habe ich den Code etwas entschlackt un ein paar Effekte die mir nicht gefallen haben herausgenommen.

Momentan beschäftigt mich einer der verbliebenen Effekte, bei dem die LEDs zwar blinken, aber nicht die Farbe wechseln. Das sollten sie nach meinem Verständnis des Codes aber tun.
Mir würde es schon reichen wenn ich die Farbe in der sie blinken (Blau) fest in eine andere Farbe ändern könnte, aber ich entdecke in dem Code keinen Hinweis warum sie blau blinken.
Ich vermute dass der Fehler in diesem Abschnitt liegt, weil da etwas mit color steht

void updateGlobals() {
  uint16_t minLvl, maxLvl;
    
  //advance color wheel
  color_wait_count++;
  if (color_wait_count > COLOR_WAIT_CYCLES) {
    color_wait_count = 0;
    scroll_color++;
    if (scroll_color > COLOR_MAX) {
      scroll_color = COLOR_MIN;
    }
  }

Aber auch an anderer Stelle taucht color auf (z.B. strip.setPixelColor), deswegen hier zur Vollständigkeit der ganze Code dieses Effekts:

void vu7() {
  int intensity = calculateIntensity();
  updateOrigin(intensity);
  assignDrawValues(intensity);
  writeSegmented();
  updateGlobals();
}

int calculateIntensity() {
  int      intensity;
  
  reading   = analogRead(MIC_PIN);                        // Raw reading from mic 
  reading   = abs(reading - 512 - DC_OFFSET); // Center on zero
  reading   = (reading <= NOISE) ? 0 : (reading - NOISE);             // Remove noise/hum
  lvl = ((lvl * 7) + reading) >> 3;    // "Dampened" reading (else looks twitchy)

  // Calculate bar height based on dynamic min/max levels (fixed point):
  intensity = DRAW_MAX * (lvl - minLvlAvg) / (long)(maxLvlAvg - minLvlAvg);

  return constrain(intensity, 0, DRAW_MAX-1);
}

void updateOrigin(int intensity) {
  // detect peak change and save origin at curve vertex
  if (growing && intensity < last_intensity) {
    growing = false;
    intensity_max = last_intensity;
    fall_from_left = !fall_from_left;
    origin_at_flip = origin;
  } else if (intensity > last_intensity) {
    growing = true;
    origin_at_flip = origin;
  }
  last_intensity = intensity;
  
  // adjust origin if falling
  if (!growing) {
    if (fall_from_left) {
      origin = origin_at_flip + ((intensity_max - intensity) / 2);
    } else {
      origin = origin_at_flip - ((intensity_max - intensity) / 2);
    }
    // correct for origin out of bounds
    if (origin < 0) {
      origin = DRAW_MAX - abs(origin);
    } else if (origin > DRAW_MAX - 1) {
      origin = origin - DRAW_MAX - 1;
    }
  }
}

void assignDrawValues(int intensity) {
  // draw amplitue as 1/2 intensity both directions from origin
  int min_lit = origin - (intensity / 2);
  int max_lit = origin + (intensity / 2);
  if (min_lit < 0) {
    min_lit = min_lit + DRAW_MAX;
  }
  if (max_lit >= DRAW_MAX) {
    max_lit = max_lit - DRAW_MAX;
  }
  for (int i=0; i < DRAW_MAX; i++) {
    // if i is within origin +/- 1/2 intensity
    if (
      (min_lit < max_lit && min_lit < i && i < max_lit) // range is within bounds and i is within range
      || (min_lit > max_lit && (i > min_lit || i < max_lit)) // range wraps out of bounds and i is within that wrap
    ) {
      draw[i] = Wheel(scroll_color);
    } else {
      draw[i] = 0;
    }
  }
}

void writeSegmented() {
  int seg_len = N_PIXELS / SEGMENTS;

  for (int s = 0; s < SEGMENTS; s++) {
    for (int i = 0; i < seg_len; i++) {
      strip.setPixelColor(i + (s*seg_len), draw[map(i, 0, seg_len, 0, DRAW_MAX)]);
    }
  }
  strip.show();
}

uint32_t * segmentAndResize(uint32_t* draw) {
  int seg_len = N_PIXELS / SEGMENTS;
  
  uint32_t segmented[N_PIXELS];
  for (int s = 0; s < SEGMENTS; s++) {
    for (int i = 0; i < seg_len; i++) {
      segmented[i + (s * seg_len) ] = draw[map(i, 0, seg_len, 0, DRAW_MAX)];
    }
  }
  
  return segmented;
}

void writeToStrip(uint32_t* draw) {
  for (int i = 0; i < N_PIXELS; i++) {
    strip.setPixelColor(i, draw[i]);
  }
  strip.show();
}

void updateGlobals() {
  uint16_t minLvl, maxLvl;
    
  //advance color wheel
  color_wait_count++;
  if (color_wait_count > COLOR_WAIT_CYCLES) {
    color_wait_count = 0;
    scroll_color++;
    if (scroll_color > COLOR_MAX) {
      scroll_color = COLOR_MIN;
    }
  }
  
  vol[volCount] = reading;                      // Save sample for dynamic leveling
  if(++volCount >= SAMPLES) volCount = 0; // Advance/rollover sample counter

  // Get volume range of prior frames
  minLvl = maxLvl = vol[0];
  for(uint8_t i=1; i<SAMPLES; i++) {
    if(vol[i] < minLvl)      minLvl = vol[i];
    else if(vol[i] > maxLvl) maxLvl = vol[i];
  }
  // minLvl and maxLvl indicate the volume range over prior frames, used
  // for vertically scaling the output graph (so it looks interesting
  // regardless of volume level).  If they're too close together though
  // (e.g. at very low volume levels) the graph becomes super coarse
  // and 'jumpy'...so keep some minimum distance between them (this
  // also lets the graph go to zero when no sound is playing):
  if((maxLvl - minLvl) < N_PIXELS) maxLvl = minLvl + N_PIXELS;
  minLvlAvg = (minLvlAvg * 63 + minLvl) >> 6; // Dampen min/max levels
  maxLvlAvg = (maxLvlAvg * 63 + maxLvl) >> 6; // (fake rolling average)
}

Vielleicht hat ja jemand eine Idee dazu :slight_smile: