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!
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;
}
}
}