Create a sub timer in Free-running mode

Hi all,

I’ve been struggling with some algorithm for the past few days and I feel it’s time for me to ask for some help after I went through the forum and couldn’t find any answer.

My set up is an Arduino Uno + Relay (and this is important) + microphone. I’m using all of that to switch ON/OFF the circuits of the 3 lights of a traffic light which follow the music beat. Everything works great ! Problem : I want a button to switch between different mode (always ON, always OFF, random, follow the music beat, etc…).
Also, and that’s important, I’m in free running mode (which makes everything harder).

The button works perfectly when connected to LED while using the attachInterrupt function (I can’t find a button library which runs under free-running).
Once it’s connected to the relay, the attachInterrupt function is triggered everytime a relay open/close. Relay are super noisy and that’s another side effect…
I’m using “state_id” (from 0 to 5) to switch between the different function.

To avoid that I’ve been trying different technics and my last idea is to have a timer running while attachedInterrupt function is triggered. Like : if(state change for 0.5s), then state_id++

I have this code based on interrupt demo. My main issue is that the “while(state == LOW)” is triggered randomly anytime a relay is open. I’ve thought about using while((state == LOW) & (timer % 500)==0) but I get out of the while loop very quickly of course.

Any idea ? suggestion ? On how I can “filter” the noise from the relay

//Delcaring the button and LED Pins
#define button1Pin 2
#define led1Pin 5
#define led2Pin 6
#define led3Pin 7

volatile byte state = HIGH;

byte state_id = 0;


// the setup function runs once when you press reset or power the board
void setup() {
  pinMode(led1Pin, OUTPUT);
  pinMode(led2Pin, OUTPUT);
  pinMode(led3Pin, OUTPUT);
  pinMode(button1Pin, INPUT_PULLUP);
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(button1Pin), blink1, CHANGE);
  
}
void blink1() {
  state = !state;
 
}

// the loop function runs over and over again forever
void loop() {

Serial.println(state);

Serial.println(state_id);
//Serial.println(state);


int time_state=0;
while(state == LOW){
  Serial.println(time_state);
  //Serial.println(state);
  if((time_state % 1000) == 0) {
    state_id++; 
    state = HIGH;
  }
  if(state_id == 6) state_id = 0;

  time_state++;
}

  
  if (state == LOW) {//using the state variable that gets changed in the interrupt function instead of a digitalRead()
    // turn LEDS on:
    Serial.println("on");
    digitalWrite(led1Pin, HIGH); 
    digitalWrite(led2Pin, HIGH);
    digitalWrite(led3Pin, HIGH);  
    onoff=1; 
  } else if(state == HIGH) {//using the state variable that gets changed in the interrupt function instead of a digitalRead()
    // turn LEDs off:
      digitalWrite(led1Pin, LOW);
      digitalWrite(led2Pin, LOW);
      digitalWrite(led3Pin, LOW);   
    }
      
}

Hello,

The button works perfectly when connected to LED while using the attachInterrupt function (I can’t find a button library which runs under free-run).

Interrupts are the wrong tool for reading the state of a button, you should be polling buttons every, maybe 1 to 20ms. I’ve pasted below my general demonstration code for reading buttons, as presented it reads 4 buttons, but you can easily change that. My code isn’t the only way, there are other examples if you search for them.

You might also find the following tutorials helpfull:
See Demonstration for several things at the same time
Using millis for timing, which is also relevant as frequently millis timing and a state machine are used together.

/* Simple button debounce for 4 buttons. Increments a count and sends the updated count to the serial monitor once per button press */
/* Tested on an Uno */
/* Connect simple push to make buttons between 0V and pin 2, 0V and pin 3, 0V and pin 4 and between 0V and pin 5 */

#define noOfButtons 4     //Exactly what it says; must be the same as the number of elements in buttonPins
#define bounceDelay 20    //Minimum delay before regarding a button as being pressed and debounced
#define minButtonPress 3  //Number of times the button has to be detected as pressed before the press is considered to be valid

const int buttonPins[] = {2, 3, 4, 5};      // Input pins to use, connect buttons between these pins and 0V
uint32_t previousMillis[noOfButtons];       // Timers to time out bounce duration for each button
uint8_t pressCount[noOfButtons];            // Counts the number of times the button is detected as pressed, when this count reaches minButtonPress button is regared as debounced 
uint8_t testCount[noOfButtons];             //Test count, incremented once per button press

void setup() {
  uint8_t i;
  uint32_t baudrate = 115200;
  Serial.begin(baudrate);
  Serial.println("");
  Serial.print("Serial port connected: ");
  Serial.println(baudrate);
  for (i = 0; i < noOfButtons; ++i) {
    pinMode(buttonPins[i], INPUT_PULLUP);
    Serial.print("Testcount ");
    Serial.print(i);
    Serial.print(" = ");
    Serial.println(testCount[i]);
  }
}

void loop() {
  debounce();
  delay(10);     //Your other code goes here instead of this delay. DO NOT leave this delay here, it's ONLY for demonstration.
}

void debounce() {
  uint8_t i;
  uint32_t currentMillis = millis();
  for (i = 0; i < noOfButtons; ++i) {
    if (digitalRead(buttonPins[i])) {             //Input is high, button not pressed or in the middle of bouncing and happens to be high
        previousMillis[i] = currentMillis;        //Set previousMillis to millis to reset timeout
        pressCount[i] = 0;                        //Set the number of times the button has been detected as pressed to 0
      } else {
      if (currentMillis - previousMillis[i] > bounceDelay) {
        previousMillis[i] = currentMillis;        //Set previousMillis to millis to reset timeout
        ++pressCount[i];
        if (pressCount[i] == minButtonPress) {
          doStuff(i);                             //Button has been debounced. Call function to do whatever you want done.
        }
      }
    }
  }
}

// Function to do whatever you want done once the button has been debounced.
// In this example it increments a counter and send the count to the serial monitor.
// Put your own functions here to do whatever you like.
void doStuff(uint8_t buttonNumber) {
  ++testCount[buttonNumber];
  Serial.print("Button ");
  Serial.print(buttonNumber);
  Serial.print(" testcount = ");
  Serial.println (testCount[buttonNumber]);
}

Hi Perry, thank you for your answer. I agree that the way you're providing would be easier but I haven't manage to make millis() works under free-running mode of the arduino. In this case millis(), delay() or any time related function are not working (at least for what I understood so far)

So I'm trying to find a way to timer without these functions

Avoid delay(), or at least, if you do use it don't ask why your code has become slow and unresponsive; the answer is because you are using delay.

I don't know what you mean by 'free running mode of the Arduino', please explain. By all means use interrupts for buttons; you will learn from the problems you will have :o . I strongly suggest you get your head around using millis() for timing, it is useful for so many things, you need it and you need to understand it. It is well worth the effort of learning to use it. What did you try and what did it do that was different to what you expected?

I’m still new in the field of free-run mode of the chip (which is why I’m struggling), but if I understood well, reading the chip in free-running allows to bypass the timer of the Arduino core library and get the frequency of the chip itself. Thus, any time related function are gone but chip is way faster which is necessary if I want to get a very fast FFT of the signal from the microphone.

Maybe I should post the whole code with the free-running mode part to make it more clear

:

#include <avr/pgmspace.h>
#include <ffft.h>
#include <math.h>
#include <Wire.h>


// Microphone connects to Analog Pin 0.  Corresponding ADC channel number
// varies among boards...it's ADC0 on Uno and Mega, ADC7 on Leonardo.
// Other boards may require different settings; refer to datasheet.
#ifdef __AVR_ATmega32U4__
 #define ADC_CHANNEL 7
#else
 #define ADC_CHANNEL 0
#endif

//Delcaring the button and LED Pins
#define button1Pin 2
#define led1Pin 5
#define led2Pin 6
#define led3Pin 7

volatile byte state = HIGH;

byte state_id = 0;


// the setup function runs once when you press reset or power the board
void setup()
{

   // Init ADC free-run mode; f = ( 16MHz/prescaler ) / 13 cycles/conversion 
  ADMUX  = ADC_CHANNEL; // Channel sel, right-adj, use AREF pin
  ADCSRA = _BV(ADEN)  | // ADC enable
           _BV(ADSC)  | // ADC start
           _BV(ADATE) | // Auto trigger
           _BV(ADIE)  | // Interrupt enable
           _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz
  ADCSRB = 0;                // Free run mode, no high MUX bit
  DIDR0  = 1 << ADC_CHANNEL; // Turn off digital input for ADC pin
  TIMSK0 = 0;                // Timer0 off

  sei(); // Enable interrupts (free-run mode)

  
  pinMode(led1Pin, OUTPUT);
  pinMode(led2Pin, OUTPUT);
  pinMode(led3Pin, OUTPUT);
  pinMode(button1Pin, INPUT_PULLUP);
  Serial.begin(115200);
  attachInterrupt(digitalPinToInterrupt(button1Pin), blink1, CHANGE);
  
}




void blink1()
{
  state = !state;
}




// the loop function runs over and over again forever
void loop() 
{

  Serial.println(state);
  
  Serial.println(state_id);
  //Serial.println(state);
  
  
  int time_state=0;
  while(state == LOW){
    Serial.println(time_state);
    //Serial.println(state);
    if((time_state % 1000) == 0) {
      state_id++; 
      state = HIGH;
    }
    if(state_id == 6) state_id = 0;
  
    time_state++;
  }

  
  if (state == LOW) {//using the state variable that gets changed in the interrupt function instead of a digitalRead()
    // turn LEDS on:
    Serial.println("on");
    digitalWrite(led1Pin, HIGH); 
    digitalWrite(led2Pin, HIGH);
    digitalWrite(led3Pin, HIGH);  
  } else if(state == HIGH) {//using the state variable that gets changed in the interrupt function instead of a digitalRead()
    // turn LEDs off:
      digitalWrite(led1Pin, LOW);
      digitalWrite(led2Pin, LOW);
      digitalWrite(led3Pin, LOW);   
    }
      
}

I'm still new in the field of free-run mode of the chip (which is why I'm struggling), but if I understood well, reading the chip in free-running allows to bypass the timer of the Arduino core library and get the frequency of the chip itself.

I think you mean using the A2C in free run mode. I am going to stop advising you at this point because I don't have enough experience of using the A2D on an Arduino. There are others here better able to help and I invite one of them to take over. Good luck with your project.