Drumleds

From RevSpace
Jump to navigation Jump to search
Project Drumleds
Drumleds merethan.jpg
Een project wat ik voor een bekende van mij gedaan heb, tevens mijn eerste project bij revspace. Het betreft een kastje wat leds laat flitsen wanneer de drummer de basskick intrapt. Die bekende van mij heeft dit kastje momenteel in gebruik op een drumstel wat hij geheel zelf gemaakt heeft.
Status Completed
Contact merethan
Last Update 2017-05-25

Project

Dit project is begonnen toen iemand die ik kende via een muzikantencentrum mij benaderde met de vraag of ik hem kon helpen. Hij was een eigen drumstel aan het bouwen en als show-element wilde hij een ledstrip met witte powerleds in de bassdrum laten knipperen op het ritme van de kick. Zelf had hij al een paar onderdelen (voeding, ledstrip, sensor). Aan mij de vraag of ik iets kon maken om het geheel aan elkaar te knopen.

Onderdelen

Behuizing

Een onderdeel waar je gemakkelijk overheen kijkt is de behuizing. Dat alles veilig zit opgeborgen is een must voor iets wat van optreden naar optreden gaat, daarom heb ik gekozen voor een project box van Hammond Manufacturing. Doet gewoon wat het moet doen. Gewoon goed. Alle componenten zitten in de behuizing bevestigd met klittenband strips, waardoor ze gemakkelijk uit en weer ingebouwd kunnen worden, mochten er modificaties nodig zijn.

Voedingen

De grote voeding (het witte blok) gaat van 230v naar 12v, met een vermogen van 2.5A (volgens mij). Genoeg prik om de powerleds van stroom te voorzien in elk geval. Diezelfde voeding wordt ook voor de schakel-elektronica gebruikt via een DC-DC converter (rode print), die van de 12v naar 5v gaat. Deze 5v voed het relais-board en de Arduino Pro mini.

Arduino

Tja wat anders. Een Arduino. In dit project heb ik een Arduino Pro mini 5v model gebruikt. Het is weinig meer dan een ATmega328, wat voltage regeling, een lampje en een knopje. Met 32KBytes flash en 2KBytes RAM een tamelijk kleine computer, maar ruim voldoende.

Relais-board

Voor dit project heb ik een print met twee relais gebruikt, ook al gebruik ik nu slecht een enkele relais. Ik voorzie dat de beste man binnenkort meer lampjes op zijn drumstel wil, vandaar dat die tweede er maar alvast zit. (En het was de kleinste print die ik had liggen.) De relais worden door een opto-coupler aangestuurd die van 3.3v (het voltage op de GPIO's van de Arduino) gelijk ook naar 5v gaan, en de relais activeren. De relais zouden tot 250v AC kunnen hebben, nu schakelen ze slechts 12v DC.

Sensor

Een RT-10K van Roland. Gewoon een goede drumtrigger, verder niets speciaals aan. Het is in feite een microfoontje met een andere spoelwikkeling waardoor het een relatief hoog voltage genereert. Ook hebben drumtriggers geen membraam wat mee beweegt op trillingen in de lucht maar een schuimpje wat tegen het vel van een tom of kick moet zitten. Er zit ook een mooi beugeltje bij om het stevig aan de ketel van de trommel vast te maken.

Werking

Circuit

Elektrisch is het apparaat vrij simpel. Er komt 12v uit het witte voeding blok, die de witte powerleds in de bassdrum voed. Van de stroomdraden richting de powerleds is de plus onderbroken door een relais. Dit relais kan geschakeld worden door de Arduino. De Arduino en het relais-board worden ook vanuit het 12v blok gevoed, via een 5.5v-30v naar 5v DC-DC converter (de rode print). De Arduino luistert op een analog-in pin naar het signaal van de drumtrigger, en schakelt a.d.h. daarvan via een digital-out de relais open of dicht. En zo kan de Arduino de leds laten oplichten of doven.
Er zit ook nog een handmatige schakelaar in het circuit (links-onder op de foto). Wanneer deze wordt omgezet dan wordt de alalog-in (de drumtrigger) met de gnd (0v) verbonden. Hierdoor heeft de Arduino geen input signaal meer waardoor de automatische schakeling even pauze heeft. Ook wordt de door het relais onderbroken stroomkring door de schakelaar gesloten, waardoor de ledstrip continue blijft branden.

Code

De beste beschrijving is de code zelf lezen. Ik heb gepoogd die zo simpel en begrijpbaar mogelijk op te stellen. Zie einde van de pagina.
Globaal werkt het zo: De Arduino luistert naar een analog-in pin. Is het voltage op de analog-in een bepaalde grenswaarde gepasseerd, dan worden de bijbehorende leds voor een x aantal miliseconden ingeschakeld. De grenswaarde is een bepaalde fractie van het maximum voltage wat het programma tot dan toe gezien heeft.
Dit maximum wat de code gezien heeft (en zo indirect de grenswaarde) wordt langzaam naar beneden bijgesteld, indien het nominale (gesmoothe) voltage op de analog-in minder dan de helft is van het huidige bekende maximum. Zo wordt het systeem zelf-balancerend en zelf-kalibrerend gemaakt. Dit is nodig omdat het inpluggen van de drumtrigger een piekvoltage kan geven waardoor de grenswaarde heel hoog komt te liggen. Als het programma daar niet zelf van herstelt kan het zijn dat de leds niet meer aan gaan daarna, omdat de grenswaarde nooit meer bereikt wordt. Ook als de drummer zachter gaat spelen moet het systeem daar in mee gaan met de kalibratie.

Verbeterpunten

Er zijn een tweetal zaken die in een eventuele volgende versie anders worden. Als eerste de relais: Dit hadden gewoon mosfets moeten zijn. Relais zijn onderhevig aan mechanische slijtage en reageren met een kleine vertraging. Ook staan ze volledig open of dicht. Bij mosfets zou het via pwm mogelijk zijn om de ledstrips te laten faden. Verminderde slijtage, hogere reactiesnelheid, fade effecten; wellicht in een Drumleds v2
Ook moet er een circuit in komen om analoog het signaal uit de drumtrigger te kunnen afknijpen. Iets van een weerstand of voltagedeler ofzo. De Arduino Pro mini 5v kan waardes van 0v tot 5v waarnemen, welke vertaald worden van 0 tot 1023 in een gpio register. De Roland drumtrigger produceert bij elke goede kick een voltage van meer dan 5v en ook het ruisniveau van de natrillingen ligt vrij hoog. Hierdoor staat de gpio een heel groot deel van de tijd op 1023, wat het stukje smoothing code veel te weinig ruimte geeft om aan effectieve ruisafvlakking te doen.

Arduino code

Dit is de code zoals die uiteindelijk het apparaat in gegaan is. Dit was de eerste keer dat ik met de Arduino IDE gewerkt heb overigens.

/*
   Drumkit lights is a custom piece of code written
   to switch on/off a LED strip in a drumkit.
   Essentially, it is a simple system listening on
   analog inputs and switching digital ones accordingly.
   The analog inputs are to be wired to drumtriggers
   (those are basically very weird microphones) and
   the outputs to mosfets or relay boards, controlling LEDs.
   Currently it is written to run on a Pro mini board.
*/

void setup()
{
  // IO setup
  pinMode(2, OUTPUT); // First relay
  pinMode(3, OUTPUT); // Second relay
  pinMode(4, OUTPUT); // Third relay
  pinMode(5, OUTPUT); // Fourth relay
  pinMode(13, OUTPUT); // Arduino PCB LED

  // Initialize outputs
  digitalWrite(2, HIGH); // Open the first relay
  digitalWrite(3, HIGH); // Open the second relay
  digitalWrite(4, HIGH); // Open the third relay
  digitalWrite(5, HIGH); // Open the fourth relay
  digitalWrite(13, LOW); // Turn off the PCB LED
}

// Default values
const unsigned short flashduration = 42; // Duration, in miliseconds
const unsigned int calibrationinterval = 250; // Minimum interval in between threshold calibrations, in miliseconds

// Global vars
const unsigned short iocount = 4; // The number of drumtrigger input and led output pairs this piece of software has to process
unsigned short current[iocount] = {0, 0, 0, 0}; // The last measured value on an drumtrigger's analog in port
unsigned short peakvalue[iocount] = {0, 0, 0, 0}; // The highest current value seen so far on the drumtrigger's port
unsigned short threshold[iocount] = {0, 0, 0, 0}; // The analog input value we consider a strike for this port
unsigned long lasttrigger[iocount] = {0, 0, 0, 0}; // The last moment a strike was detected on this port (in miliseconds, counted since the start of the program)
unsigned long lastcalcycle = 0; // The last moment a threshold calibration cycle was performed (in miliseconds, counted since the start of the program)
const unsigned short smoothcount = 128; // The number of samples the smoothing algorithm operates with
unsigned short smoothiter[iocount] = {0, 0, 0, 0}; // The iterator for each input, used for cycling trough the history array used for smoothing each input
unsigned short currenthistory[iocount][smoothcount]; // A two-dimensional array holding a history for each analog input, where the history is $smoothcount in size

void loop()
{
  // Each input is processed in a sequential fashion
  for (int i = 0; i < iocount; i++) {
    // The first part is about establishing whether a piece of the drumkit was struck or not
    current[i] = analogRead(i); // Read the input on analog pin i. Goes from 0-1023, representing 0-5v for this model Arduino

    if (current[i] > threshold[i]) {
      lasttrigger[i] = millis(); // Write time of last trigger
    }

    // The second part is about turning lights on or off, depending on how long ago a hit was detected
    if ((millis() - lasttrigger[i]) < flashduration) {
      digitalWrite(i + 2, LOW); // Close the relay
      digitalWrite(13, HIGH); // Activate the LED
    } else {
      digitalWrite(i + 2, HIGH); // Open the relay
      digitalWrite(13, LOW); // Turn off the LED
    }

    // The third part is about self-calibrating threshold values
    if (current[i] > peakvalue[i]) {
      peakvalue[i] = current[i]; // Save the highest detected value so far
    }

    threshold[i] = peakvalue[i] / 1.2; // ~83% of the highest peak so far is considered the threshold

    // Lowering the threshold is based on current value. But since it is noise, smoothing is needed. Therefore, a history needs to be kept
    currenthistory[i][smoothiter[i]] = current[i];
    if (smoothiter[i] < (smoothcount - 1)) {
      smoothiter[i]++;
    } else {
      smoothiter[i] = 0;
    }

    unsigned long totalcurr = 0;
    for (int j = 0; j < smoothcount; j++) {
      totalcurr += currenthistory[i][j];
    }
    unsigned short smoothed = totalcurr / smoothcount;

    if ((millis() - lastcalcycle) > calibrationinterval) {
      lastcalcycle = millis(); // Write time of last threshold calibration

      if (smoothed < (peakvalue[i] / 2)) { // Only reduce the peakvalue if the noise level is less than 50% of the peakvalue
        peakvalue[i]--;
      }
    }
  }
}