"For loop" interfering with tone function

I’ve got a program that plays a tone and flashes an LED for each button in an 11-button keypad. To be more specific, each key is connected to a sensor: when a key is depressed, the Arduino senses a change in voltage and activates the tone and LED for as long as the button is pressed. I have the code set up using a “for loop” to constantly scan the 11 sensors for changes in voltage, and I use a basic state machine to switch between the two states: triggered and idle.

The problem: When a key is pressed and held, there is distortion in the tone which I believe is caused by reactivating the tone repeatedly while it is depressed. When I take the for loop out of the code and try with just one button, the tone has no distortion. Here’s the code:

#include <toneAC.h>
#include <Adafruit_NeoPixel.h>

// Which pin on the Arduino is connected to the NeoPixels?
#define LED_PIN 10

// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 12

#define GS4 415  //  1*
#define AS4 466  //  2
#define B4  494  //  3
#define CS5 554  //  4
#define DS5 622  //  5* 
#define F5  698  //  6
#define FS5 740  //  7  
#define GS5 831  //  8*
#define AS5 932  //  9*
#define C6  1047 //  10*
#define D6  1175 //  11
#define E6  1319 //  12

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

//states
enum {ST1_idle, ST1_triggered} currentState1 = ST1_idle;
enum {ST2_idle, ST2_triggered} currentState2 = ST2_idle;

const byte sensors[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
const byte numberOfSensors = sizeof(sensors) / sizeof(sensors[0]);
const int speakerPin = 6;
const int notes[] = {GS4, AS4, B4, CS5, DS5, F5, FS5, GS5, AS5, C6, D6, E6};

enum theStates {ST_idle, ST_triggered};
theStates currentState[numberOfSensors] = {ST_idle, ST_idle};

unsigned long lastBlink[numberOfSensors];
int blinkInterval[numberOfSensors] = {480, 520, 450, 510, 380, 490, 500, 475, 415, 390, 465, 500};
unsigned long triggeredAt[numberOfSensors];

void setup()
{
  // initialize serial communication:
  Serial.begin(9600);
  Serial.print("setup() ... ");

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, pulseState);

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'

  Serial.println(" done");
  delay(1000);

  Serial.println(" ");
  for (byte i = 0; i < numberOfSensors; i++) {
    Serial.print(i);
    Serial.println(" is idle");
  }
}   // setup


void loop() {
  manageStates();
}  

void manageStates() {
  for (byte i = 0; i < numberOfSensors; i++) {
    int val = analogRead(i);
    switch (currentState[i]) {    // ST_idle, ST_triggered
      case ST_idle:
        if (millis() - lastBlink[i] >= blinkInterval[i]) {
          lastBlink[i] = millis();
          strip.setPixelColor(random(i, 1), random(i, 50), random(i, 50), random(i, 50));
          strip.show();
          noToneAC();
        }

        if (val < 100) {
          currentState[i] = ST_triggered;
          Serial.print(i);
          Serial.println(" is triggered");
        }
        break;

      case ST_triggered:
        strip.setPixelColor(i, 200, 0, 0); 
        strip.show();
        toneAC(notes[i]);

        if (val >= 100) {   // lost trigger
          currentState[i] = ST_idle;
          Serial.print(i);
          Serial.println(" is idle");
        }
        break;

    }   // switch i
  }   // for
}   // manageStates

How can I remove the distortion caused by the for loop when a key is held in the pressed position? What am I missing here?

How can I remove the distortion caused by the for loop when a key is held in the pressed position?

By having a while loop that waits until no switch is pressed after the for instruction and before you go into the switch statement.

I would use a state variable to monitor the switch. Initialize the tone only as the switch becomes pressed, and cancel the tone when it becomes released.

Grumpy_Mike:
By having a while loop that waits until no switch is pressed after the for instruction and before you go into the switch statement.

Interesting. But I don’t quite see it yet. Do you mean something like this?

while (val < 100) {switch statements}

Surely not? I don’t see how that wold help, so I must be missing something. :slight_smile:

jremington:
I would use a state variable to monitor the switch. Initialize the tone only as the switch becomes pressed, and cancel the tone when it becomes released.

But I would still need a for loop to scan all the sensors, including to see whether the button is still depressed, which would mean the problem remains. Maybe I'm missing something?

No not quite

 while (val < 100) { // read and calculate val again }
// stop any tone
// now look at your switches again

 switch statements

So in other words the while loop will hold the flow of the code until the button is released.

Grumpy_Mike:
No not quite

 while (val < 100) { // read and calculate val again }

// stop any tone
// now look at your switches again

switch statements



So in other words the while loop will hold the flow of the code until the button is released.

That works brilliantly. Thank you!