BPM Counter / Metronome for triggering a DMX console - need a little help

Hey guys!

I´m Helmuth here. 20 years ago a did some programming in PASCAL and electronic stuff driven by the parallel LPT port. Now I noticed the Arduino and old dreams seem to become true...

After some days of learning basics this is my first Arduino project and my first C code at all. It´s about sending audio impulses to a DMX console in order to control a light show precisely.
That means a perfect synchronisation to the speed of the music and sending the signal before you want to see the change - so that the lights change exactly on the beat and not just while...

Here is the code I wrote. I hope it´s documented well enough. So far the sketch is doing what i expect, except the function of buttonSTART. It is meant to shift the beginning of the loop, in order to deal with the latency of the DMX chain - the goal is to send the impulse before the beat in the music really comes - so, that the light change is visible on the beat.

My question is: Why does buttonSTART not trigger an impulse immediately? It is supposed to restart the loop by calling impulseACTION(). Where is the time wasted?

Any suggestions are welcome. And general critic about the code itself, too.

Thanks to all of you for the nice and helpful forum! :slight_smile:

Greetings

Helmuth

//
// First Arduino Project by Pitt Tesla 2012
//
// BPM counter and metronome for triggering a DMX console with an audio input
// for precise light to sound show
//
// In gratitude for the LCD library and examples.
// 
// ...tested with Arduino Uno v3 and DFRobot LCD Shield
//

#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Configuration of DFRobot LCD Shield

int lcd_key        = 0;    // for button reading
int adc_key_in     = 0;    // for button reading
long x             = 0;    // later used to compare with millis()
long y             = 0;    // used to measure TAP time
int tap_counter    = 0;    // counts the TAPs
int tap_max        = 4;    // number of TAPs used for calculation
float BPM          = 0;    // to display beats/minute
int time           = 500;  // ms between impulses = 120 BPM
int SCHRITT        = 10;   // UP/DOWN steps
int schritt        = 1;    // UP/DOWN steps fine
int shift          = 33;   // delay of LED flash (displays latency of my DMX chain) for better control)
int LEDpin         = 2;    // LED flash 
int AUDIOpin       = 3;    // PWM output with 10k to AUDIO in
int frequency      = 500;  // Audio signal that seems to be easy to detect
int BUTTONpin      = 0;    // Buttons switching resistors from 5V to A0
int flashon        = 10;   // LED flash time
int wait_after_key = 150;  // for debouncing & key autorepeat
int BACKLIGHTpin   = 10;   // why dont´t use the backlight for flashing?!
int backlight      = 50;   // brightness to give a good contrast for flashing
int range          = 0;    // switches for rough and fine tuning

#define buttonSTART 0
#define buttonUP    1
#define buttonDOWN  2
#define buttonRANGE 3
#define buttonTAP   4
#define buttonNONE  5

int read_LCD_buttons()      // reads the LCD shield buttons 
{
  adc_key_in = analogRead(BUTTONpin);      
  if (adc_key_in > 1000) {
    return buttonNONE;
  } 
  if (adc_key_in < 50)   {
    return buttonSTART;
  } 
  if (adc_key_in < 195)  {
    return buttonUP;
  }
  if (adc_key_in < 380)  {
    return buttonDOWN;
  }
  if (adc_key_in < 555)  {
    return buttonRANGE;
  }
  if (adc_key_in < 790)  {
    return buttonTAP;
  }  
  return buttonNONE;  
}

void ledACTION () {
  digitalWrite(LEDpin, HIGH);
  delay(flashon);
  digitalWrite(LEDpin, LOW);
}

void impulseACTION() {
  x = millis();
  tone(AUDIOpin, frequency, 5);          // send audio impulse
  analogWrite(BACKLIGHTpin, 255);        // flash backlight in "realtime"
  delay(flashon);
  analogWrite(BACKLIGHTpin, backlight); 
  delay(shift-flashon);
  ledACTION();                           // flash external LED synchronized to latency of DMX processing
}

void setup()
{
//  Serial.begin(9600);                  // just for debugging...
  lcd.begin(16, 2);                      // init LCD
  lcd.setCursor(0,0);
  lcd.print(" /  Range   ");
  lcd.setCursor(2,0);
  lcd.print(tap_max);
  lcd.setCursor(0,1);
  lcd.print("   ms        BPM");
  pinMode(LEDpin, OUTPUT);               // init pins
  pinMode(AUDIOpin, OUTPUT);
  pinMode(BACKLIGHTpin, OUTPUT);
  analogWrite(BACKLIGHTpin, backlight);  // adjust brightness
}

void loop()
{
  float BPM = ((float)1000/(float)time) * (float)60;  //calculate BPM
  lcd.setCursor(0,1);           
  lcd.print(time);              // ms between impulses
  lcd.setCursor(6,1);           
  lcd.print(BPM);               // impulses/minute
  lcd.setCursor(13,0);           
  lcd.print(millis()/60000);    // power on time in minutes
  lcd.setCursor(0,0);           
  lcd.print(tap_counter);       // count of taps
  lcd.setCursor(10,0);           
  lcd.print(range);             // big or small steps to adjust

  lcd_key = read_LCD_buttons(); // perform the action when the time is right
  if (millis() - x > time) {   
    impulseACTION();
  }

  switch (lcd_key)              // in order to pressed button do:         
  {
  case buttonSTART:             // start an impulse immediately
    {                         
      impulseACTION;
      break;
    }
  case buttonRANGE:             // select range of adjustion
    {
      range = range + 1;
      if (range == 2) { 
        range = 0;
      }
      delay(wait_after_key); 
      break;
    }
  case buttonUP:                 // adjust
    {
      if (range == 0) {          // big step up
        time = time - SCHRITT;
      } 
      else {                     // small step up
        time = time - schritt;
      } 
      delay(wait_after_key); 
      break;
    }
  case buttonDOWN:
    {
       if (range == 0) {      
        time = time + SCHRITT;
      } 
      else {
        time = time + schritt;
      }
      delay(wait_after_key); 
      break;
    }
  case buttonTAP:                // synchronizing speed to music
    {
      tap_counter = tap_counter + 1;

      if (tap_counter == 1) {
        y = millis();
      }
      if (tap_counter == tap_max) {
        time = (millis() - y) / (tap_max - 1);
        tap_counter = 0;
        impulseACTION();
      }
      delay(wait_after_key); 
      break;
    }
  case buttonNONE:
    {
      break;
    }
  }
}

Hi Helmuth,

i have not read the hole code.
but here are some ideas about timing:
lcds uses normally 'really much time' :slight_smile:
and also float calculation is slow:
Reference:Float

Arduino Reference - Float:
Floating point math is also much slower than integer math in performing calculations, so should be avoided if, for example, a loop has to run at top speed for a critical timing function. Programmers often go to some lengths to convert floating point calculations to integer math to increase speed.

as example - if you need some accuracy like 1.3 you can use 13 as int and know that you have to divide by 10...
so in the last step before printing it on serial or lcd you can print the right value :

13/10 = 1  ( 13 divided by 10 in integer math gives 1)
            .
13%10 = 3  ( 13 modulo 10 (the rest of the division) is 3)

it would help if you just write up a smal overview about how you think it should work - something like an 'flowchart'
then it is faster for others to look through the code.
just write something like:
read input of Pushbutton1
count up
write lcd message

  • status
  • name of the day
    make a tone
    ...

its just an idea-
i will look into your code in the next days-

some general thing - you could give your variables some cleaner naming scheme:
i name them something like this:

// Pin Definitions
byte pin_PushButton_Up		= 1;
byte pin_PushButton_Down	= 2;
byte pin_LightSensor		= A1;

// Light Sensor
word wLightSensor_Raw		= 0;
word wLightSensor_Filtert	= 0;

// PushButton States
byte bPushButton_State_Up	= 0;	// is > 1 if Button is Active; is >2 if Button is Pressed a long time
byte bPushButton_State_Down	= 0;	// is > 1 if Button is Active; is >2 if Button is Pressed a long time

and hey - thats just how i like it^^
there will be others that hate so long variable names :wink:

i think there will be some nice 'code style guides' out there in the net..

for me its easy to understand what i can do with the thing i just ride down..

sunny greetings

stefan