Detecting period of a pulse, and assigning that period to a timer using micros()

I have the following working code which I am running on an Arduino Uno R3, and also on an Arduino Nano, which illuminates the LEDs on an Adafruit Neopixel ring (16 LEDs) and a Neopixel Strip (8 LEDs).

At the moment, the period of the timer is adjusted by a potentiometer.

Basically I would like to modify this code so that the software detects the frequency (period) of a 5v voltage applied to a digital pin - this voltage switches on and off at a fixed frequency (around once a second), and I would like the software to transfer this detected period to my ‘interval’ value in the code. Can this be done using just one Uno or Nano, or would it require more than one? Any help would be much appreciated!

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1
#define PIN            6

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS      24
#define RINGSIZE      16

// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);


#define MAIN_COLOR pixels.Color(255, 127, 0)
#define NEXT_COLOR_2 pixels.Color(96, 48, 0)
#define NEXT_COLOR_1 pixels.Color(48, 24, 0)
#define NEXT_COLOR_0 pixels.Color(6, 3, 0)
#define DIMMEST_COLOR pixels.Color(3, 1, 0)
#define OFF_COLOR pixels.Color(0, 0, 0)




unsigned long previousMicros=0;
unsigned long interval=20000; // the time we need to wait

int potentiometer = A0;
int potRead = 0;
 

bool ledState = false; // state variable for the LED
int count = 0;
i
bool anticlockwise;
bool shouldRunLEDs;
bool started;

void setup() {
  // put your setup code here, to run once:
pinMode(flasherInPin, INPUT);
pinMode(rearLightInPin, INPUT);
//pinMode(brakeLightInPin, INPUT);
 anticlockwise = false;
 pixels.begin(); // This initializes the NeoPixel library.
 for (int i=0; i<NUMPIXELS;i++){
   pixels.setPixelColor(i, OFF_COLOR);
 }
 pixels.show();
 Serial.begin(9600);
 shouldRunLEDs = true;

 pinMode(potentiometer,INPUT);
}
void runLEDs(){
  if (shouldRunLEDs){
  unsigned long currentMicros = micros(); // grab current time
  if ((unsigned long)(currentMicros - previousMicros) >= interval) {
    if (anticlockwise){
    pixels.setPixelColor(count+5-RINGSIZE*(count+5>=RINGSIZE), MAIN_COLOR);
    pixels.setPixelColor(count+5+RINGSIZE/2-RINGSIZE*(count+5+RINGSIZE/2>=RINGSIZE), MAIN_COLOR);
    pixels.setPixelColor(count+4-RINGSIZE*(count+4>=RINGSIZE), NEXT_COLOR_2);
    pixels.setPixelColor(count+4+RINGSIZE/2-RINGSIZE*(count+4+RINGSIZE/2>=RINGSIZE), NEXT_COLOR_2);
    pixels.setPixelColor(count+3-RINGSIZE*(count+3>=RINGSIZE), NEXT_COLOR_1);
    pixels.setPixelColor(count+3+RINGSIZE/2-RINGSIZE*(count+3+RINGSIZE/2>=RINGSIZE), NEXT_COLOR_1);
    pixels.setPixelColor(count+2-RINGSIZE*(count+2>=RINGSIZE), NEXT_COLOR_0);
    pixels.setPixelColor(count+2+RINGSIZE/2-RINGSIZE*(count+2+RINGSIZE/2>=RINGSIZE), NEXT_COLOR_0);
    pixels.setPixelColor(count+1-RINGSIZE*(count+1>=RINGSIZE), DIMMEST_COLOR);
    pixels.setPixelColor(count+1+RINGSIZE/2-RINGSIZE*(count+1+RINGSIZE/2>=RINGSIZE), DIMMEST_COLOR);
    pixels.setPixelColor(count, OFF_COLOR);
    pixels.setPixelColor(count+RINGSIZE/2-RINGSIZE*(count+RINGSIZE/2>=RINGSIZE), OFF_COLOR);

    if (8-count+RINGSIZE > 15){
    pixels.setPixelColor(8-count+RINGSIZE, MAIN_COLOR);
    }
    if (8-count+RINGSIZE+7 > 15){
    pixels.setPixelColor(8-count+RINGSIZE+7, OFF_COLOR);
    }
  //pixels.setPixelColor(-count%16 + NUMPIXELS, MAIN_COLOR);
  //pixels.setPixelColor(-count%16 + 8 + NUMPIXELS, OFF_COLOR);
  //pixels.setPixelColor(-count%16 - 8 + NUMPIXELS, OFF_COLOR);
      
    }
   else
   {
    pixels.setPixelColor(RINGSIZE-count+5-RINGSIZE*(RINGSIZE-count+5>=RINGSIZE), OFF_COLOR);
    pixels.setPixelColor(RINGSIZE-count+5+RINGSIZE/2-RINGSIZE*(RINGSIZE-count+5+RINGSIZE/2>=RINGSIZE), OFF_COLOR);
    pixels.setPixelColor(RINGSIZE-count+4-RINGSIZE*(RINGSIZE-count+4>=RINGSIZE), DIMMEST_COLOR);
    pixels.setPixelColor(RINGSIZE-count+4+RINGSIZE/2-RINGSIZE*(RINGSIZE-count+4+RINGSIZE/2>=RINGSIZE), DIMMEST_COLOR);
    pixels.setPixelColor(RINGSIZE-count+3-RINGSIZE*(RINGSIZE-count+3>=RINGSIZE), NEXT_COLOR_0);
    pixels.setPixelColor(RINGSIZE-count+3+RINGSIZE/2-RINGSIZE*(RINGSIZE-count+3+RINGSIZE/2>=RINGSIZE), NEXT_COLOR_0);
    pixels.setPixelColor(RINGSIZE-count+2-RINGSIZE*(RINGSIZE-count+2>=RINGSIZE), NEXT_COLOR_1);
    pixels.setPixelColor(RINGSIZE-count+2+RINGSIZE/2-RINGSIZE*(RINGSIZE-count+2+RINGSIZE/2>=RINGSIZE), NEXT_COLOR_1);
    pixels.setPixelColor(RINGSIZE-count+1-RINGSIZE*(RINGSIZE-count+1>=RINGSIZE), NEXT_COLOR_2);
    pixels.setPixelColor(RINGSIZE-count+1+RINGSIZE/2-RINGSIZE*(RINGSIZE-count+1+RINGSIZE/2>=RINGSIZE), NEXT_COLOR_2);
    pixels.setPixelColor(RINGSIZE-count, MAIN_COLOR);
    pixels.setPixelColor(RINGSIZE-count+RINGSIZE/2-RINGSIZE*(RINGSIZE-count+RINGSIZE/2>=RINGSIZE), MAIN_COLOR);

  if (count+RINGSIZE > 15){
    pixels.setPixelColor(count+RINGSIZE, MAIN_COLOR);
    }
    if (count+RINGSIZE-7 > 15){
    pixels.setPixelColor(count+RINGSIZE-7, OFF_COLOR);
    }
   }
    
       

 // pixels.show(); // This sends the updated pixel color to the hardware.
 
 
   // save the "current" time
   
   if (count < RINGSIZE-1){
   count++;
   previousMicros = micros();
   }
   else
   {
    count = 0;
    ledState = !ledState;  
   }
  }
}
}
void killLEDs(){
  for (int n = 0; n < RINGSIZE;n++){
   pixels.setPixelColor(n, OFF_COLOR); 
   }
   
}
void loop() {
  // put your main code here, to run repeatedly:
  potRead = analogRead(potentiometer);
  interval = potRead * 60 +20000;
 
 runLEDs();
pixels.show();
}

Your problem statement needs a bit more definition before it's entirely clear to me what it is you're trying to accomplish. Before that though, have a look at the pulseIn() function and see if that can be of use.

For your input, use Pin 2 or Pin 3 of an Arduino UNO and attach an interrupt handler for whichever edge you like (RISING or FALLING). In the ISR, note the time in micros(), Subtract the current sample from the previous sample. If the result is greater than 30000 (30 milliseconds for debounce) store the value as the new period and record the current time to use as the previous time next time.

Since time values are 'unsigned long' you should declare the period 'volatile' and make a local copy with the interrupts disabled before you act on it.

@johnwasser - Thank you so much for the swift reply! This looks to be exactly what I'm seeking! Is there any sample code or a tutorial you can recommend describing how to use interrupt handlers? Sorry for my ignorance, but whats the ISR that you mention? Any pointers in the right direction would be much appreciated! Does an interrupt handler run on a separate thread? (ie will the main 'loop' be tied up at all?
Looking forward to your reply...

John M

ISR means "Interrupt Service Routine", the function you 'attach' to an interrupt.

Here is the documentation for 'attachInterrupt()' which contains an example.
https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

Here is the basic example from the documentation:

const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;


void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
}


void loop() {
  digitalWrite(ledPin, state);
}


void blink() {
  state = !state;
}

And this is how it might look after you adapt it to your needs:

const byte PulseInputPin = 2;
const byte OutputPin = 5;


// Mark as 'volatile' any variables used both in ISR and outside the ISR
volatile unsigned long PulsePeriod = 0;


unsigned long PulseStartTime = 0;
unsigned long LastInterruptTime = 0;
boolean OutputState = false;


void setup()
{
  pinMode(OutputPin, OUTPUT);
  pinMode(PulseInputPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(PulseInputPin), PulseISR, RISING);
}


void loop()
{
  unsigned long period;


  noInterrupts(); // Keep the value from changing while we read it
  period = PulsePeriod;
  interrupts();


  // Generate a square wave output with the same period.
  if (micros() - PulseStartTime > (period / 2))
  {
    OutputState = !OutputState;
    digitalWrite(OutputPin, OutputState);
    PulseStartTime = micros();
  }
}


// Interrupt Service Routine.  Must take no args and return void.
void PulseISR(void)
{
  unsigned long currentTime = micros();
  unsigned long period = currentTime - LastInterruptTime;
  if (period > 30000) // Debounce input
  {
    PulsePeriod = period;
    LastInterruptTime = currentTime;
  }
}

@johnwasser - Thanks so much for your help! I’ve managed to achieve what I wanted - I didn’t tell you that I need to ‘switch off’ after a certain time period has elapsed… I’ve done this by using a second input pin (I tried using interrupts, but it wasn’t working smoothly for some reason…). For your interest, and anyone else who might be interested, here is the ‘working’ code:-

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1
#define PIN            6

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS      24
#define RINGSIZE      16

// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);


#define MAIN_COLOR pixels.Color(255, 127, 0)
#define NEXT_COLOR_2 pixels.Color(96, 48, 0)
#define NEXT_COLOR_1 pixels.Color(48, 24, 0)
#define NEXT_COLOR_0 pixels.Color(6, 3, 0)
#define DIMMEST_COLOR pixels.Color(3, 1, 0)
#define OFF_COLOR pixels.Color(0, 0, 0)
#define RED_COLOR pixels.Color(192, 0, 0)
#define BLUE_COLOR pixels.Color(0, 0, 192)

const byte pulseInputPin = 2;
const byte pulseInputPin2 = 7;
const byte LEDOutputPin = 5;
boolean outputState = false;
unsigned long pulseInterval = 800; //set according to flasher speed
unsigned long previousReadMillis=0;
unsigned long previousMicros=0;
unsigned long interval=40000; // the time we need to wait
volatile unsigned long eventCount;

volatile unsigned long pulsePeriod = 0;
volatile unsigned long period;
volatile unsigned long lastPeriod;

unsigned long pulseStartTime = 0;
unsigned long lastInterruptTime = 0;
unsigned long lastEventTime;


int potRead = 0;
 

bool ledState = false; // state variable for the LED
int count = 0;



bool anticlockwise;
bool shouldRunLEDs;
bool started;
bool oneShotHigh;
bool oneShotLow;

void setup() {
  // put your setup code here, to run once:
  pinMode(LEDOutputPin, OUTPUT);
  pinMode(pulseInputPin, INPUT_PULLUP);
  pinMode(pulseInputPin2, INPUT);
  attachInterrupt(digitalPinToInterrupt(pulseInputPin), PulseISR, CHANGE);

 anticlockwise = false;
 pixels.begin(); // This initializes the NeoPixel library.
 for (int i=0; i<NUMPIXELS;i++){
   pixels.setPixelColor(i, OFF_COLOR);
 }
 pixels.show();
 Serial.begin(9600);
 


}
void runLEDs(){
  if (shouldRunLEDs){
  unsigned long currentMicros = micros(); // grab current time
  if ((unsigned long)(currentMicros - previousMicros) >= interval) {
    if (anticlockwise){
    pixels.setPixelColor(count+5-RINGSIZE*(count+5>=RINGSIZE), MAIN_COLOR);
    pixels.setPixelColor(count+5+RINGSIZE/2-RINGSIZE*(count+5+RINGSIZE/2>=RINGSIZE), MAIN_COLOR);
    pixels.setPixelColor(count+4-RINGSIZE*(count+4>=RINGSIZE), NEXT_COLOR_2);
    pixels.setPixelColor(count+4+RINGSIZE/2-RINGSIZE*(count+4+RINGSIZE/2>=RINGSIZE), NEXT_COLOR_2);
    pixels.setPixelColor(count+3-RINGSIZE*(count+3>=RINGSIZE), NEXT_COLOR_1);
    pixels.setPixelColor(count+3+RINGSIZE/2-RINGSIZE*(count+3+RINGSIZE/2>=RINGSIZE), NEXT_COLOR_1);
    pixels.setPixelColor(count+2-RINGSIZE*(count+2>=RINGSIZE), NEXT_COLOR_0);
    pixels.setPixelColor(count+2+RINGSIZE/2-RINGSIZE*(count+2+RINGSIZE/2>=RINGSIZE), NEXT_COLOR_0);
    pixels.setPixelColor(count+1-RINGSIZE*(count+1>=RINGSIZE), DIMMEST_COLOR);
    pixels.setPixelColor(count+1+RINGSIZE/2-RINGSIZE*(count+1+RINGSIZE/2>=RINGSIZE), DIMMEST_COLOR);
    pixels.setPixelColor(count, OFF_COLOR);
    pixels.setPixelColor(count+RINGSIZE/2-RINGSIZE*(count+RINGSIZE/2>=RINGSIZE), OFF_COLOR);

    if (8-count+RINGSIZE > 15){
    pixels.setPixelColor(8-count+RINGSIZE, MAIN_COLOR);
    }
    if (8-count+RINGSIZE+7 > 15){
    pixels.setPixelColor(8-count+RINGSIZE+7, OFF_COLOR);
    }
  //pixels.setPixelColor(-count%16 + NUMPIXELS, MAIN_COLOR);
  //pixels.setPixelColor(-count%16 + 8 + NUMPIXELS, OFF_COLOR);
  //pixels.setPixelColor(-count%16 - 8 + NUMPIXELS, OFF_COLOR);
      
    }
   else
   {
    pixels.setPixelColor(RINGSIZE-count+5-RINGSIZE*(RINGSIZE-count+5>=RINGSIZE), OFF_COLOR);
    pixels.setPixelColor(RINGSIZE-count+5+RINGSIZE/2-RINGSIZE*(RINGSIZE-count+5+RINGSIZE/2>=RINGSIZE), OFF_COLOR);
    pixels.setPixelColor(RINGSIZE-count+4-RINGSIZE*(RINGSIZE-count+4>=RINGSIZE), DIMMEST_COLOR);
    pixels.setPixelColor(RINGSIZE-count+4+RINGSIZE/2-RINGSIZE*(RINGSIZE-count+4+RINGSIZE/2>=RINGSIZE), DIMMEST_COLOR);
    pixels.setPixelColor(RINGSIZE-count+3-RINGSIZE*(RINGSIZE-count+3>=RINGSIZE), NEXT_COLOR_0);
    pixels.setPixelColor(RINGSIZE-count+3+RINGSIZE/2-RINGSIZE*(RINGSIZE-count+3+RINGSIZE/2>=RINGSIZE), NEXT_COLOR_0);
    pixels.setPixelColor(RINGSIZE-count+2-RINGSIZE*(RINGSIZE-count+2>=RINGSIZE), NEXT_COLOR_1);
    pixels.setPixelColor(RINGSIZE-count+2+RINGSIZE/2-RINGSIZE*(RINGSIZE-count+2+RINGSIZE/2>=RINGSIZE), NEXT_COLOR_1);
    pixels.setPixelColor(RINGSIZE-count+1-RINGSIZE*(RINGSIZE-count+1>=RINGSIZE), NEXT_COLOR_2);
    pixels.setPixelColor(RINGSIZE-count+1+RINGSIZE/2-RINGSIZE*(RINGSIZE-count+1+RINGSIZE/2>=RINGSIZE), NEXT_COLOR_2);
    pixels.setPixelColor(RINGSIZE-count, MAIN_COLOR);
    pixels.setPixelColor(RINGSIZE-count+RINGSIZE/2-RINGSIZE*(RINGSIZE-count+RINGSIZE/2>=RINGSIZE), MAIN_COLOR);

  if (count+RINGSIZE > 15){
    pixels.setPixelColor(count+RINGSIZE, MAIN_COLOR);
    }
    if (count+RINGSIZE-7 > 15){
    pixels.setPixelColor(count+RINGSIZE-7, OFF_COLOR);
    }
   }
      

 // pixels.show(); // This sends the updated pixel color to the hardware.
 
 
   // save the "current" time
   
   if (count < RINGSIZE-1){
   count++;
   previousMicros = micros();
   }
   else
   {
    count = 0;
    ledState = !ledState;  
   }
  }
}
}
void killLEDs(){
  for (int n = 0; n < NUMPIXELS;n++){
   pixels.setPixelColor(n, OFF_COLOR); 
   }
   
}
void loop() {
  // put your main code here, to run repeatedly:
  
  
 noInterrupts(); // Keep the value from changing while we read it
  period = pulsePeriod;
  
 interrupts();

  interval = period/7.5;
 //unsigned long currentMillis = millis();
 /*
 // Generate a square wave output with the same period.
  if (micros() - PulseStartTime > (period / 2))
  {
    OutputState = !OutputState;
    digitalWrite(LEDOutputPin, OutputState);
    PulseStartTime = micros();
  }
 */
if (digitalRead(pulseInputPin2) == HIGH){
  started = true;
  
  shouldRunLEDs = true;
  digitalWrite(LEDOutputPin, HIGH);
  previousReadMillis = millis();
 }
 else if (digitalRead(pulseInputPin2) == LOW && started == true){
  unsigned long currentMillis = millis(); // grab current time
  if ((unsigned long)(currentMillis - previousReadMillis) >= pulseInterval) {
    
   shouldRunLEDs = false;
   digitalWrite(LEDOutputPin, LOW);
   killLEDs(); 
  }
  else
  {
   shouldRunLEDs = true;
   digitalWrite(LEDOutputPin, HIGH);
  }
  
 }
 runLEDs();
pixels.show();

}
// Interrupt Service Routine.  Must take no args and return void.
void PulseISR(void)
{
  
  lastEventTime = micros();
  eventCount++;
  
  Serial.println(eventCount);
  unsigned long currentTime = micros();
  unsigned long period = currentTime - lastInterruptTime;
  if (period > 30000) // Debounce input
  {
    pulsePeriod = period;
    lastInterruptTime = currentTime;
    Serial.println(period);
  }
  
}