Help with function oder help

Hi! I would like to create a code that in the loop function, using millis, turns on one of the 5 functions (function1(); [...]) every 1500ms and so on in a circle. It uses the millis function because it also reads the state of the IR receiver. When I press the SET button on the remote control (0xE31CFF00) the program enters the menu. I would like to make it possible to change the order of function execution in the menu, or you can reject function so that it is skipped in oder. I wanted the program to support a 128 x 32 OLED display on the I2C bus. Can anyone help and write a sample code?

Welcome to the forum!

I will probably not be of much assistance myself, but from what I've seen in this forum, you are much more likely to get useful help if you follow the guidance provided in the pinned post "How to get the best out of this forum", especially the directions about providing detailed descriptions of your hardware, code, and circuits.

You may want to edit your top post accordingly.

Good luck!

Since you do not seem to have a program right now that attempts do what you want, are you asking someone to spend their time trying to figure out what you want and what equipment you have for free, or are you wanting to pay someone to do this for you?

1 Like

It looks like a line 42 mistake.

1 Like

What is you knowledge of programming. The, in my opinion, correct approach is to have

  1. An array of function pointers to point to the functions; this is fixed.
  2. An array of indices that determines the sequence in which you walk through the array of function pointers; this one is not fixed and you can modify on the fly using your menu.

Function pointers is not a beginners item although it's not difficult once you understand.

Note:
You will also need to make sure that your functions are non-blocking, else your system will still be non-responsive.

Hello slimakxx

Check this code example for function pointers to get started.

void (*function[])() {
  function1, function2, function3, function4, function5
};
void function1()
{
  Serial.println(__func__);
}
void function2()
{
  Serial.println(__func__);
}
void function3()
{
  Serial.println(__func__);
}
void function4()
{
  Serial.println(__func__);
}
void function5()
{
  Serial.println(__func__);
}
void setup()
{
  Serial.begin(115200);
  Serial.println("off we go");
}
void loop()
{
  uint32_t currentMillis = millis();
  constexpr uint32_t interval = 1500;
  static uint32_t previousMillis = currentMillis;

  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    static uint8_t index = 0;
    function[index]();
    index = (index + 1) % (sizeof(function) / sizeof(function[0]));
  }
}

Have a nice day and enjoy coding in C++.

p.s. enums, arrays and structs are your friends.

Since OP wants only 5 functions, a switch case could be used instead of an array of function pointers.

1 Like

paulpaulson already gave you a quick example. I did work things out a little more in detail.

I created three functions; they are placed after loop().

void setup()
{
}

void loop()
{
}

void func1()
{
  Serial.println(F("executing function 1"));
}

void func2()
{
  Serial.println(F("executing function 2"));
}

void func3()
{
  Serial.println(F("executing function 3"));
}

When you compile the sketch the IDE does some things behind the scenes. One is the adding function prototypes; in this demo I did manually add them because the stuff behind the scenes does not always work properly resulting in compiler erros.

// function prototypes
void func1();
void func2();
void func3();

void setup()
{
  ...
  ...
}

Now we can add the array of function pointers. A function pointer is declared like void (*funcPtr)(). This would be a pointer to a function ((*funcPtr)) that does not return anything (void) and takes no arguments (()).
As we need an array of them (one for each function), it becomes void (*funcPtrs[3])(). This is for an array with 3 function pointers; I did add the 's' to indecate that it is an array of multiple function pointers.
And you initialise it as below

// array of function pointers
// functions don't return anything and don't take arguments
void (*funcPtrs[3])(){
  func1,
  func2,
  func3,
};

Below loops through the array of function pointers

void setup()
{
  Serial.begin(115200);

  // loop through functions
  for (uint8_t cnt = 0; cnt < 3; cnt++)
  {
    funcPtrs[cnt]();
  }
}

Note that there are two places where the number 3 occurs. This is a maintenance nightmare because you have (now) two places where you need to modify this number for your 5 functions.

Add the following macro to the top of the code

// macro to calculate number of elements in any type of array
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

The array can now be as below; note the omissionof the number 3).

// array of function pointers
// functions don't return anything and don't take arguments
void (*funcPtrs[])(){
  func1,
  func2,
  func3,
};

The full code now becomes

////////////////////////////////////////
// improved demonstration of array of function pointers
// size of array does not need to be specified
////////////////////////////////////////

// macro to calculate number of elements in any type of array
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

// function prototypes
void func1();
void func2();
void func3();

// array of function pointers
// functions don't return anything and don't take arguments
void (*funcPtrs[])(){
  func1,
  func2,
  func3,
};

void setup()
{
  Serial.begin(115200);

  Serial.print(F("There are "));
  Serial.print(NUMELEMENTS(funcPtrs));
  Serial.println(F(" functions to be executed"));

  // loop through functions
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(funcPtrs); cnt++)
  {
    funcPtrs[cnt]();
  }
}

void loop()
{
}

void func1()
{
  Serial.println(F("executing function 1"));
}

void func2()
{
  Serial.println(F("executing function 2"));
}

void func3()
{
  Serial.println(F("executing function 3"));
}

Note that the for-loop now uses NUMELEMENTS. This way it does not matter of the array has one element, 3 elements, 5 elements or 100 elements.

In the next step we add an array that determines the sequence in which the steps are executed.

// array with indices; determines the sequence
uint8_t sequence[] = {
  1,
  0,
  2,
};

Instead of looping directly through the array of function pointers we loop through the sequence and use the number to select the function pointer as shown in the setup() function in below code.

////////////////////////////////////////
// improved demonstration of array of function pointers
// adding sequence
////////////////////////////////////////

// macro to calculate number of elements in any type of array
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

// function prototypes
void func1();
void func2();
void func3();

// array of function pointers
// functions don't return anything and don't take arguments
void (*funcPtrs[])(){
  func1,
  func2,
  func3,
};

// array with indices; determines the sequence
uint8_t sequence[] = {
  1,
  0,
  2,
};

void setup()
{
  Serial.begin(115200);

  Serial.println(F("The sequence is"));
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(sequence); cnt++)
  {
    Serial.println(sequence[cnt]);
  }

  // loop through functions in order indicated by sequence
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(sequence); cnt++)
  {
    funcPtrs[sequence[cnt]]();
  }
}

void loop()
{
}

void func1()
{
  Serial.println(F("executing function 1"));
}

void func2()
{
  Serial.println(F("executing function 2"));
}

void func3()
{
  Serial.println(F("executing function 3"));
}

There is one massive risk and that is that you can accidentally specify an index (in the sequence arraY that is outside the bounds of the array of function pointers. You will need to harden your code to prevent that from happening as it can result in crashes. You also have the requirement that you might want to skip a function.

In the below code the index 255 is used as an indication that a function must be skipped. It also contains the hardening and the code will stop (hang forever) if that invalid index is detected.
The full code

////////////////////////////////////////
// improved demonstration of array of function pointers
// implement skip and hardening
////////////////////////////////////////

// macro to calculate number of elements in any type of array
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

// array of function pointers
// functions don't return anything and don't take arguments
void (*funcPtrs[])(){
  func1,
  func2,
  func3,
};

// array with indices; determens the sequence
uint8_t sequence[] = {
  255,
  0,
  3,
};

void setup()
{
  // error flag
  bool error = false;

  Serial.begin(115200);

  Serial.println(F("The sequence is"));
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(sequence); cnt++)
  {
    Serial.print(sequence[cnt]);
    if (sequence[cnt] == 255)
    {
      Serial.print(F(" skip"));
    }
    else
    {
      if (sequence[cnt] >= NUMELEMENTS(funcPtrs))
      {
        Serial.print(F(" is invalid"));
        error = true;
      }
    }
    Serial.println();
  }

  if (error == true)
  {
    // hang forever
    for (;;) {}
  }

  // loop through functions in order indicated by sequence
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(sequence); cnt++)
  {
    if (sequence[cnt] != 255)
    {
      funcPtrs[sequence[cnt]]();
    }
  }
}

void loop()
{
}

void func1()
{
  Serial.println(F("executing function 1"));
}

void func2()
{
  Serial.println(F("executing function 2"));
}

void func3()
{
  Serial.println(F("executing function 3"));
}

The example contains one sequence element with index 255 and one sequence element with index 3. You will need to change that to a valid index.
The last for-loop skips entries in the sequence that have the value 255.

Output in serial monitor (after fixing the incorrect index in the sequence array

08:35:06.706 -> The sequence is
08:35:06.706 -> 255 skip
08:35:06.706 -> 0
08:35:06.706 -> 2
08:35:06.706 -> executing function 1
08:35:06.706 -> executing function 3

For you to implement a shuffling of the sequence using a menu.

1 Like

You mentioned IR, so you will need to include the IRremote library. One that I know of is IRremote.hpp

#include <IRremote.hpp>

... and you will need to define a pin to use for the IR receiver.

#define IR_RECEIVE_PIN 2   // Signal Pin of IR receiver

You said you will have a button, so you will need to define a button...

#define BUTTON_PIN 3 // wired: pin .. button .. grounc

... and then configure the button pin and initialize the IR instance (inside setup())...

void setup() {
  pinMode(buttonPin, INPUT_PULLUP); // nominal HIGH, pressed LOW
  IrReceiver.begin(IR_RECEIVE_PIN); // initialize IR instance
}

For the IR receiver, inside loop() or any function called by loop() you will use the IR receiver instance to (1) decode the incoming data, (2) translate the data to the function to perform, and (3) resume reading the IR receiver.

You also mentioned using the millis() function as a timer, so you will need to define variables to hold two millis() size values, and the timeout you mentioned is 1500 milliseconds, so...

unsigned long timer, timeout = 1500;

I struggle with pointers, so I make clumsy code that gets close. An array can be used in this example of calling functions to add, remove or change function order.

// calling functions

unsigned long timer, interval = 1500; // timing variables in milliseconds
int counter = 0; // a counter
byte functions = 5; // the number of functions available (f0 through f4)

void setup() {
  Serial.begin(115200);
}

void loop() {
  if (millis() - timer > interval) { // program running longer than "interval"
    timer = millis(); // store new "time zero"

    if (counter > functions - 1) { // check counter
      Serial.println(); // new line
      counter = 0; // reset counter
    }

    switch (counter) { // check value
      case (0): { // if 0
          function_0(); // call function
          break; // exit this case
        }
      case (1): {
          function_1();
          break;
        }
      case (2): {
          function_2();
          break;
        }
      case (3): {
          function_3();
          break;
        }
      case (4): {
          function_4();
          break;
        }
      default: break;
    }
    counter++; // increment function counter
  }
}

void function_0() {
  Serial.print("f0 Never ");
}
void function_1() {
  Serial.print("f1 gonna ");
}
void function_2() {
  Serial.print("f2 give ");
}
void function_3() {
  Serial.print("f3 you ");
}
void function_4() {
  Serial.print("f4 up ");
}

I tried to use arrays in the code, but I don't really know how to use them at all

I really thank you for your help, in my free time I will try to write a sensible code based on your help, but I will definitely want to delve into arrays and if I manage to do something I will write about it. Thank you one more time.

1 Like

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