Multitasking: therimin with a led chase

Hi
I have a project to create a very simple theremin, with the notes activated by an ultrasonic sensor HC-SR04, and a potentiometer for the volume. In parallel, I would like to have a chase led thanks to a shift register 74HC595. I am using an Arduino Uno. I started from the following codes:

-for the theremin:

const int trigger = 19;
const int echo = 18;

const int piezo = 5;

int distance = 0;
int distanceHigh = 0;

int lengthOfScale = 0;

int note = 0;

//A Minor pentatonic scale
int scale[] = {
  147, 165, 196, 220, 262, 294, 330, 392, 440,
  523, 587, 659, 784, 880, 1047, 1175, 1319, 1568,
  1760, 2093, 2349
};

//C Major scale
//int scale[] = {
//  131, 147, 165, 175, 196, 220, 247, 262, 294,
//  330, 349, 392, 440, 494, 523, 587, 659, 698,
//  784, 880, 988, 1047
//};


void setup() {
  pinMode(trigger, OUTPUT);
  pinMode(echo, INPUT);

  while (millis() < 5000) {
    digitalWrite(trigger, HIGH);
    digitalWrite(trigger, LOW);
    distance = pulseIn(echo, HIGH);

    if (distance > distanceHigh) {
      distanceHigh = distance;
    }
  }

  for (byte i = 0; i < (sizeof(scale) / sizeof(scale[0])); i++) {
    lengthOfScale += 1;
  }
}

void loop() {
  digitalWrite(trigger, HIGH);
  digitalWrite(trigger, LOW);

  distance = pulseIn(echo, HIGH);

  note = map(distance, 250, distanceHigh, scale[0], scale[lengthOfScale - 1]);

  for (byte j = 0; j < (lengthOfScale); j++) {

    if (note == scale[j]) {
      tone(piezo, note);
      break;
    }
    else if (note > scale[j] && note < scale[j + 1]) {
      note = scale[j];
      tone(piezo, note);
      break;
    }
  }
  delay(30);
}

-For the led chase:

//constantes pour les broches de commande du 74HC595
int latchPin = 7;
int clockPin = 8;
int dataPin = 6;
int outputEnablePin = 3;




byte leds = 0;

void setup() 
{
  
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);
  pinMode(outputEnablePin, OUTPUT); 
}

void loop() 
{
  setBrightness(255);
  leds = 0;
  updateShiftRegister();
  delay(10);
  for (int i = 0; i < 8; i++)
  {
    bitSet(leds, i);
    updateShiftRegister();
    delay(50);
  }
  for (byte b = 255; b > 0; b--)
  {
    setBrightness(b);
    delay(10);
  }
}

void updateShiftRegister()
{
   digitalWrite(latchPin, LOW);
   shiftOut(dataPin, clockPin, LSBFIRST, leds);
   digitalWrite(latchPin, HIGH);
}

void setBrightness(byte brightness) // 0 to 255
{
  analogWrite(outputEnablePin, 255-brightness);
}

I mixed the two codes, I created this, which compiles:

const int trigger = 19;//pin du hcsr04 pour déclencher piezzo en fonction de la distance
const int echo = 18;//idem

const int piezo = 5;

int distance = 0;
int distanceHigh = 0;

int lengthOfScale = 0;

int note = 0;

//A Minor pentatonic scale
int scale[] = {
  147, 165, 196, 220, 262, 294, 330, 392, 440,
  523, 587, 659, 784, 880, 1047, 1175, 1319, 1568,
  1760, 2093, 2349
};

//C Major scale
//int scale[] = {
//  131, 147, 165, 175, 196, 220, 247, 262, 294,
//  330, 349, 392, 440, 494, 523, 587, 659, 698,
//  784, 880, 988, 1047
//};
void setBrightness(byte brightness);
//constantes pour les broches de commande du 74HC595, chenillard de 8 leds
int latchPin = 7;
int clockPin = 8;
int dataPin = 6;
int outputEnablePin = 3;

byte leds = 0;

// Declaration des fonctions
void commande_piezzo();
void commande_led();

void setup() {

  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(outputEnablePin, OUTPUT);


  pinMode(trigger, OUTPUT);
  pinMode(echo, INPUT);

  while (millis() < 5000) {
    digitalWrite(trigger, HIGH);
    digitalWrite(trigger, LOW);
    distance = pulseIn(echo, HIGH);

    if (distance > distanceHigh) {
      distanceHigh = distance;
    }
  }

  for (byte i = 0; i < (sizeof(scale) / sizeof(scale[0])); i++) {
    lengthOfScale += 1;
  }
}

void loop() {
  // Programme principal
  commande_piezzo();
  commande_led();
}

void commande_piezzo() {
  digitalWrite(trigger, HIGH);
  digitalWrite(trigger, LOW);

  distance = pulseIn(echo, HIGH);

  note = map(distance, 250, distanceHigh, scale[0], scale[lengthOfScale - 1]);

  for (byte j = 0; j < (lengthOfScale); j++) {

    if (note == scale[j]) {
      tone(piezo, note);
      break;
    }
    else if (note > scale[j] && note < scale[j + 1]) {
      note = scale[j];
      tone(piezo, note);
      break;
    }
    delay(30);
  }
}

void commande_led() {
    setBrightness(255);
    leds = 0;
    updateShiftRegister();
    delay(10);
    for (int i = 0; i < 8; i++)
    {
      bitSet(leds, i);
      updateShiftRegister();
      delay(50);
    }
    for (byte b = 255; b > 0; b--)
    {
      setBrightness(b);
      delay(10);
    }
  }

  void updateShiftRegister()
  {
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, LSBFIRST, leds);
    digitalWrite(latchPin, HIGH);
  }

  void setBrightness(byte brightness) // 0 to 255
  {
    analogWrite(outputEnablePin, 255 - brightness);
  }

and I noticed several problems:
-that the notes only changed after a complete revolution of the led chase, and not instantly as in the initial code of the Theremin;
-and in the following simulation on Tinkercad:

The 74HC595 chip received too much amperage: 90.6ma instead of 50ma

After researching, I found that the problem was with the delay, which interrupts the program, and the inherent inability of the arduino to perform simultaneous tasks.
My question is: should i better schedule with the millis () function, Or create classes like in this tutorial:

Since my project is very basic, what would be the easiest solution to have the attention of the arduino focused only on the notes, and a kind of infinite loop without modification for the leds ... Thank you for your advices.

You need to get rid of the delays and for loops, and implement the code as a state machine.

Because your theremin is a real-time device, you need to determine the fundamental timebase of the whole process to provide adequate sampling of the inputs.

Tone() might not be the best approach, and you’re right delay() are unsuitable for this type of project.

I found this topic

which coud help me, i guess i could link one note to a distance then to a led pattern.
I'll let you know if i work it out!

This code works

/*
 * created by Rui Santos, http://randomnerdtutorials.com
 * Ultrasonic Sensor with LED's bar graph and buzzer 
*/
int tonePin = 5;    //Tone - Red Jumper
int trigPin = 19;    //Trig - violet Jumper
int echoPin = 18;   //Echo - yellow Jumper
int clockPin = 8;  //IC Pin 11 - white Jumper
int latchPin = 7;  //IC Pin 12 - Blue Jumper
int dataPin = 6;   //IC Pin 14 - Green Jumper
 
byte possible_patterns[9] = {
B00000000, 
B00000001,
B00000011,
B00000111,
B00001111,
B00011111,
B00111111, 
B01111111, 
B11111111,
};
int proximity=0;
int duration;
int distance;
 
void setup() {
  //Serial Port
  Serial.begin (9600);
  
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(tonePin, OUTPUT);
}
 
void loop() {
  digitalWrite(latchPin, LOW);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(1000);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = (duration/2) / 29.1;
  
  /*if (distance >= 45 || distance <= 0){
    Serial.println("Out of range");
  }
  else {
    Serial.print(distance);
    Serial.println(" cm");
  }*/
  
  proximity=map(distance, 0, 45, 8, 0);
  //Serial.println(proximity);
  
  if (proximity <= 0){
    proximity=0;
  }
  else if (proximity >= 3 && proximity <= 4){
    tone(tonePin, 200000, 200);
  }
  else if (proximity >= 5 && proximity <= 6){
     tone(tonePin,5000, 200);
  }
  else if (proximity >= 7 && proximity <= 8){
    tone(tonePin, 1000, 200);
  }
  shiftOut(dataPin, clockPin, MSBFIRST, possible_patterns[proximity]);
  digitalWrite(latchPin, HIGH);
 
  
  delay(50);
  noTone(tonePin);
}

But now i have to improve it.
I'll post the result soon.
Thank you for the help.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.