Arduino mega interrupt problem. Code or wiring? [SOLVED]

Hello,

My rotary encoder works fine with nano, but when I switched to Mega I cannot get it to work. I checked all the pins on Mega for interrupt but now In thinking the issue is in the code.

Im trying to get the following Fastled library exaple code working with the Rotary Encoder module for arduino:

Could anyone help me figure it out or guide me in some direction.

I have tried almost all the pins mentioned on the net as arduino interrupt pins, but regardless it should work with the default digital pins 2-3 in code since those are mega interrupt pins too.

OP:s code:

#include "FastLED.h"

FASTLED_USING_NAMESPACE

#define DATA_PIN    10
//#define CLK_PIN   4
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS    6
CRGB leds[NUM_LEDS];

volatile byte BRIGHTNESS = 0;
#define FRAMES_PER_SECOND  120

static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
static int pinC = 4;
volatile long pinCignoreCycles = 0;         // the last time the output pin was sampled
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
volatile bool encoderMode = true; // 0 brightness // 1 pattern
// List of patterns to cycle through.  Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm, white, red,redglitter, green,greenglitter, blue };

uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 0; // rotating "base color" used by many of the patterns


void setup() {
	  delay(3000); // 3 second delay for recovery

  // tell FastLED about the LED strip configuration
  FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  //FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);

  // set master brightness control
  FastLED.setBrightness(BRIGHTNESS);

  pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  attachInterrupt(0,PinA,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
  attachInterrupt(1,PinB,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
  pinMode(pinC, INPUT_PULLUP);
  Serial.begin(115200); // start the serial monitor link
}

void PinA(){
  cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
  if(reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
	encoderPos ++; //decrement the encoder's position count
	bFlag = 0; //reset flags for the next turn
	aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
  sei(); //restart interrupts
}

void PinB(){
  cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
	encoderPos --; //increment the encoder's position count
	bFlag = 0; //reset flags for the next turn
	aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
  sei(); //restart interrupts
}
byte b; // used to detect if brighness have overflowed
void loop(){

	if(oldEncPos != encoderPos) {
		if (encoderMode) {
			if (oldEncPos > encoderPos) {
				b = BRIGHTNESS;
				BRIGHTNESS -= 15;
				if(BRIGHTNESS > b) {
					BRIGHTNESS = 0;
				}
			} else {
				b = BRIGHTNESS;
				BRIGHTNESS += 15;
				if(BRIGHTNESS < b) {
					BRIGHTNESS = 255;
				}
			}
			FastLED.setBrightness(BRIGHTNESS);
		}
		if (!encoderMode) {
			if (oldEncPos > encoderPos) {
				prevPattern();
			} else {
				nextPattern();
			}
		}

		Serial.print("C:");
		Serial.print(BRIGHTNESS);
		Serial.print(",");
		Serial.print(gCurrentPatternNumber);
		Serial.println(".");
		oldEncPos = encoderPos;
	}

	if (pinCignoreCycles > 0) {
		pinCignoreCycles--;
	}

	if(pinCignoreCycles == 0) {
		if(digitalRead(pinC) == LOW) {
			pinCignoreCycles = 25;
			Serial.println("Btn");
			encoderMode = !encoderMode;

		}
	}

	// Call the current pattern function once, updating the 'leds' array
	gPatterns[gCurrentPatternNumber]();

	// send the 'leds' array out to the actual LED strip
	FastLED.show();
	// insert a delay to keep the framerate modest
	FastLED.delay(1000/FRAMES_PER_SECOND);

	// do some periodic updates
	EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow
	EVERY_N_SECONDS(30) {
		if(BRIGHTNESS > 0) {
			BRIGHTNESS--;
			Serial.print("A:");
			Serial.print(BRIGHTNESS);
			Serial.print(",");
			Serial.print(gCurrentPatternNumber);
			Serial.println(".");
			if (BRIGHTNESS == 0) {
				encoderMode = true; //fall back to brightness mode
			}
			FastLED.setBrightness(BRIGHTNESS);
		}
	}
}


#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))

void nextPattern()
{
  // add one to the current pattern number, and wrap around at the end
  gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns);
}
void prevPattern()
{
	gCurrentPatternNumber = (gCurrentPatternNumber - 1);
	if (gCurrentPatternNumber > ARRAY_SIZE(gPatterns)) {
		gCurrentPatternNumber = ARRAY_SIZE(gPatterns)-1;
	}
}

void rainbow()
{
  // FastLED's built-in rainbow generator
  fill_rainbow( leds, NUM_LEDS, gHue, 7);
}

void rainbowWithGlitter()
{
  // built-in FastLED rainbow, plus some random sparkly glitter
  rainbow();
  addGlitter(80);
}

void addGlitter( fract8 chanceOfGlitter)
{
  if( random8() < chanceOfGlitter) {
    leds[ random16(NUM_LEDS) ] += CRGB::White;
  }
}

void confetti()
{
  // random colored speckles that blink in and fade smoothly
  fadeToBlackBy( leds, NUM_LEDS, 10);
  int pos = random16(NUM_LEDS);
  leds[pos] += CHSV( gHue + random8(64), 200, 255);
}

void sinelon()
{
  // a colored dot sweeping back and forth, with fading trails
  fadeToBlackBy( leds, NUM_LEDS, 20);
  int pos = beatsin16(13,0,NUM_LEDS);
  leds[pos] += CHSV( gHue, 255, 192);
}

void bpm()
{
  // colored stripes pulsing at a defined Beats-Per-Minute (BPM)
  uint8_t BeatsPerMinute = 62;
  CRGBPalette16 palette = PartyColors_p;
  uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
  for( int i = 0; i < NUM_LEDS; i++) { //9948
    leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10));
  }
}

void juggle() {
  // eight colored dots, weaving in and out of sync with each other
  fadeToBlackBy( leds, NUM_LEDS, 20);
  byte dothue = 0;
  for( int i = 0; i < 8; i++) {
    leds[beatsin16(i+7,0,NUM_LEDS)] |= CHSV(dothue, 200, 255);
    dothue += 32;
  }
}
void white() {
	for(int i=0; i < NUM_LEDS; i++) {
		leds[i] = 0xFFFFFF;
	}
}
void red() {
	for(int i=0; i < NUM_LEDS; i++) {
		leds[i] = 0x00FF00;
	}
}
void redglitter() {
	for(int i=0; i < NUM_LEDS; i++) {
		leds[i] = 0x00FF00;
	}
	addGlitter(80);
}
void green() {
	for(int i=0; i < NUM_LEDS; i++) {
		leds[i] = 0xFF0000;
	}
}
void greenglitter() {
	for(int i=0; i < NUM_LEDS; i++) {
		leds[i] = 0xFF0000;
		addGlitter(80);
	}
}
void blue() {
	for(int i=0; i < NUM_LEDS; i++) {
		leds[i] = 0x0000FF;
	}
}

On a Mega pins 2 and 3 are PINE bits 4 and 5.

void PinA(){
  cli(); //stop interrupts happening before we read pin values
...
  sei(); //restart interrupts
}

These are unnecessary. Interrupts are already disabled when the interrupt handler is called and enabled after it returns.

On mega2560:

INT0 -> pin21
INT1 -> pin20
INT2 -> pin19
INT3 -> pin18

INT4 -> pin2
INT5 -> pin3

You need to change either your connections to pin 21/20 along with your pinA/B declarations, or attachInterrupt in setup to 4/5.

DKWatson:
On mega2560:
...
You need to change either your connections to pin 21/20 along with your pinA/B declarations, or attachInterrupt in setup to 4/5.

No.

The interrupt number given to attachInterrupt() does not match INTx on the Mega.

pin             INTx    interrupt number for attachInterrupt()
                
3       PE5     INT5    1
2       PE4     INT4    0               
18 TX1  PD3     INT3    5
19 RX1  PD2     INT2    4
20 SDA  PD1     INT1    3
21 SCL  PD0     INT0    2

(nc)    PE6     INT6    -
(nc)    PE7     INT7    -

The safest way is to use digitalPinToInterrupt():

 attachInterrupt(digitalPinToInterrupt(pinA),PinA,RISING);

I changed

attachInterrupt(0, PinA, RISING);

to

attachInterrupt(digitalPinToInterrupt(pinA),PinA,RISING);

and kept the pins as

static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3

but still didnt work.

also tried to attach pins 20, 21.

Interestingly this example below works when I connect the module as usual as:

GND GND

  • +5V
    SW PIN 4
    DT PIN 3
    CLK PIN2
#define encoder0PinA 2
#define encoder0PinB 3
#define encoder0Btn 4
int encoder0Pos = 0;
void setup() {
  Serial.begin(57600);
  pinMode(encoder0PinA, INPUT_PULLUP);
  pinMode(encoder0PinB, INPUT_PULLUP);
  pinMode(encoder0Btn, INPUT_PULLUP);
  attachInterrupt(0, doEncoder, CHANGE);
}
int valRotary, lastValRotary;
void loop() {
  int btn = digitalRead(encoder0Btn);
  Serial.print(btn);
  Serial.print(" ");
  Serial.print(valRotary);
  if (valRotary > lastValRotary)
  {
    Serial.print("  CW");
  }
  if (valRotary)  {

  Serial.print("  CCW");
  }
  lastValRotary = valRotary;
                  Serial.println(" ");
                  delay(250);
}
void doEncoder()
{
  if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB))
  {
    encoder0Pos++;
  }
  else
  {
    encoder0Pos--;
  }
  valRotary = encoder0Pos / 2.5;
}

I will have to abandon this code I believe,
As mentioned here https://www.instructables.com/id/Improved-Arduino-Rotary-Encoder-Reading/
where the rotary encoder part originated from:

The code in the my Instructables uses some language elements that are specific to the ATMEGA328P (which is why you need different code for the MEGA2561). You might be able to spot how to adapt the external interrupts to work with the Wemos D1 mini from this blog post ESP8266: External interrupts - techtutorialsx

Refer back to #2 and #5, your masks in PinA() and PinB() need to be changed from reading = PIND & 0xC to reading = PINE & 0x30 and then the bit tests against aFlag and bFlag need to reflect the bit positions of 4 and 5.

You could generalize it by,

#ifdef __AVR_ATmega2560__
    #define	PIN		PINE
	#define	ABMASK	0b00110000
	#define AMASK	0b00010000
	#define BMASK	0b00100000
#endif

#ifdef __AVR_ATmega328P__
    #define	PIN		PIND
	#define	ABMASK	0b00001100
	#define AMASK	0b00000100
	#define BMASK	0b00001000
#endif

and then your code would read

void PinA()
{
	cli();
	reading = PIN & ABMASK;
	if(reading == ABMASK && aFlag) 
	{
		encoderPos ++;
		bFlag = 0;
		aFlag = 0;
	}
	else if (reading == AMASK) bFlag = 1;
	sei();
}

void PinB()
{
	cli();
	reading = PIN & ABMASK;
	if (reading == ABMASK && bFlag) 
	{
		encoderPos --;
		bFlag = 0;
		aFlag = 0;
	}
	else if (reading == BMASK) aFlag = 1;
	sei();
}

Thank you so much! Finally works and perfectly generalized!

Will let folks at instructables know the Mega2560 code too.

#include "FastLED.h"

FASTLED_USING_NAMESPACE

#define DATA_PIN    10
//#define CLK_PIN   4
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS    6
CRGB leds[NUM_LEDS];

volatile byte BRIGHTNESS = 0;
#define FRAMES_PER_SECOND  120

#ifdef __AVR_ATmega2560__
    #define  PIN   PINE
  #define ABMASK  0b00110000
  #define AMASK 0b00010000
  #define BMASK 0b00100000
#endif

#ifdef __AVR_ATmega328P__
    #define PIN   PIND
  #define ABMASK  0b00001100
  #define AMASK 0b00000100
  #define BMASK 0b00001000
#endif




static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
static int pinC = 4;
volatile long pinCignoreCycles = 0;         // the last time the output pin was sampled
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
volatile bool encoderMode = true; // 0 brightness // 1 pattern
// List of patterns to cycle through.  Each is defined as a separate function below.
typedef void (*SimplePatternList[])();


uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 0; // rotating "base color" used by many of the patterns


void setup() {
    delay(3000); // 3 second delay for recovery

  // tell FastLED about the LED strip configuration
  FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  //FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);

  // set master brightness control
  FastLED.setBrightness(BRIGHTNESS);

  pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  attachInterrupt(0,PinA,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
  attachInterrupt(1,PinB,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
  pinMode(pinC, INPUT_PULLUP);
  Serial.begin(115200); // start the serial monitor link
}


void PinA()
{
//  cli();
  reading = PIN & ABMASK;
  if(reading == ABMASK && aFlag) 
  {
    encoderPos ++;
    bFlag = 0;
    aFlag = 0;
  }
  else if (reading == AMASK) bFlag = 1;
//  sei();
}

void PinB()
{
  cli();
  reading = PIN & ABMASK;
  if (reading == ABMASK && bFlag) 
  {
    encoderPos --;
    bFlag = 0;
    aFlag = 0;
  }
  else if (reading == BMASK) aFlag = 1;
  sei();
}
SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm, white, red,redglitter, green,greenglitter, blue };

byte b; // used to detect if brighness have overflowed
void loop(){

  if(oldEncPos != encoderPos) {
    if (encoderMode) {
      if (oldEncPos > encoderPos) {
        b = BRIGHTNESS;
        BRIGHTNESS -= 15;
        if(BRIGHTNESS > b) {
          BRIGHTNESS = 0;
        }
      } else {
        b = BRIGHTNESS;
        BRIGHTNESS += 15;
        if(BRIGHTNESS < b) {
          BRIGHTNESS = 255;
        }
      }
      FastLED.setBrightness(BRIGHTNESS);
    }
    if (!encoderMode) {
      if (oldEncPos > encoderPos) {
        prevPattern();
      } else {
        nextPattern();
      }
    }

    Serial.print("C:");
    Serial.print(BRIGHTNESS);
    Serial.print(",");
    Serial.print(gCurrentPatternNumber);
    Serial.println(".");
    oldEncPos = encoderPos;
  }

  if (pinCignoreCycles > 0) {
    pinCignoreCycles--;
  }

  if(pinCignoreCycles == 0) {
    if(digitalRead(pinC) == LOW) {
      pinCignoreCycles = 25;
      Serial.println("Btn");
      encoderMode = !encoderMode;

    }
  }

  // Call the current pattern function once, updating the 'leds' array
  gPatterns[gCurrentPatternNumber]();

  // send the 'leds' array out to the actual LED strip
  FastLED.show();
  // insert a delay to keep the framerate modest
  FastLED.delay(1000/FRAMES_PER_SECOND);

  // do some periodic updates
  EVERY_N_MILLISECONDS( 20 ) { gHue++; } // slowly cycle the "base color" through the rainbow
  EVERY_N_SECONDS(30) {
    if(BRIGHTNESS > 0) {
      BRIGHTNESS--;
      Serial.print("A:");
      Serial.print(BRIGHTNESS);
      Serial.print(",");
      Serial.print(gCurrentPatternNumber);
      Serial.println(".");
      if (BRIGHTNESS == 0) {
        encoderMode = true; //fall back to brightness mode
      }
      FastLED.setBrightness(BRIGHTNESS);
    }
  }
}


#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))

void nextPattern()
{
  // add one to the current pattern number, and wrap around at the end
  gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns);
}
void prevPattern()
{
  gCurrentPatternNumber = (gCurrentPatternNumber - 1);
  if (gCurrentPatternNumber > ARRAY_SIZE(gPatterns)) {
    gCurrentPatternNumber = ARRAY_SIZE(gPatterns)-1;
  }
}

void rainbow()
{
  // FastLED's built-in rainbow generator
  fill_rainbow( leds, NUM_LEDS, gHue, 7);
}

void rainbowWithGlitter()
{
  // built-in FastLED rainbow, plus some random sparkly glitter
  rainbow();
  addGlitter(80);
}

void addGlitter( fract8 chanceOfGlitter)
{
  if( random8() < chanceOfGlitter) {
    leds[ random16(NUM_LEDS) ] += CRGB::White;
  }
}

void confetti()
{
  // random colored speckles that blink in and fade smoothly
  fadeToBlackBy( leds, NUM_LEDS, 10);
  int pos = random16(NUM_LEDS);
  leds[pos] += CHSV( gHue + random8(64), 200, 255);
}

void sinelon()
{
  // a colored dot sweeping back and forth, with fading trails
  fadeToBlackBy( leds, NUM_LEDS, 20);
  int pos = beatsin16(13,0,NUM_LEDS);
  leds[pos] += CHSV( gHue, 255, 192);
}

void bpm()
{
  // colored stripes pulsing at a defined Beats-Per-Minute (BPM)
  uint8_t BeatsPerMinute = 62;
  CRGBPalette16 palette = PartyColors_p;
  uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
  for( int i = 0; i < NUM_LEDS; i++) { //9948
    leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10));
  }
}

void juggle() {
  // eight colored dots, weaving in and out of sync with each other
  fadeToBlackBy( leds, NUM_LEDS, 20);
  byte dothue = 0;
  for( int i = 0; i < 8; i++) {
    leds[beatsin16(i+7,0,NUM_LEDS)] |= CHSV(dothue, 200, 255);
    dothue += 32;
  }
}
void white() {
  for(int i=0; i < NUM_LEDS; i++) {
    leds[i] = 0xFFFFFF;
  }
}
void red() {
  for(int i=0; i < NUM_LEDS; i++) {
    leds[i] = 0x00FF00;
  }
}
void redglitter() {
  for(int i=0; i < NUM_LEDS; i++) {
    leds[i] = 0x00FF00;
  }
  addGlitter(80);
}
void green() {
  for(int i=0; i < NUM_LEDS; i++) {
    leds[i] = 0xFF0000;
  }
}
void greenglitter() {
  for(int i=0; i < NUM_LEDS; i++) {
    leds[i] = 0xFF0000;
    addGlitter(80);
  }
}
void blue() {
  for(int i=0; i < NUM_LEDS; i++) {
    leds[i] = 0x0000FF;
  }
}

Share the love. (That sounds so lame)

One thing more. Can you edit your original post and add [SOLVED] to the subject line please. Saves myself (and others) from checking back periodically to see how you're progressing.

trying to understand this code so I can port it to Nodemcu esp8266 board, if possible.
using interrupt pins d1 and d2 as interrupt on nodemcu.

This was Solved and I'm ashamed to come back to it after spending so much time trying to grasp the previous issue but I'm working on NodeMcu board at the moment and is there a way to port this code into nodemcu?

I'm getting a whole lot of issues especially with the mask values.

#define PIN PIND
#define ABMASK 0b00001100
#define AMASK 0b00000100
#define BMASK 0b00001000

'PIND' was not declared in this scope

got some clues to port registers here
https://www.esp8266.com/viewtopic.php?f=13&t=9407

And this code seems to work ok esp8266 - How to use a rotary encoder on NodeMCU with Arduino code? - Arduino Stack Exchange

but no info on what mask values can be.

a little trick to play into a delay() function so as to create interrupt() , not that real but it works especially if your sketch has bunch of delays

you need to define your own delay_function and call it delay1() for example, defined by original source code found in your Arduino IDE ie:

void delay1(unsigned long ms)

{       
       
    uint32_t start = micros(); 

        while (ms > 0) 
        
        {
                   
                  
                while ( ms > 0 && (micros() - start) >= 1000)
                
                {
                        ms--;   
                        start += 1000;  my_routine();
                }
        }

        
}

then you can invoke your my_routine() just like that,

Thanks, since I'm running a ws2812b led strip on this code I cannot have any delays otherwise the animation on the strip stalls.