2

I'm new to Arduino, and I'm trying to decipher this code. There are various void functions that I am trying to call in response to receiving serial data (the Monitor outputs "Please enter valid function to call"), but I suppose I don't really know how.

typedef void (* Caller)();
Caller FuncCall[] = {a, b, c};
String func_list[] = {"a","b","c"}; //correspond to functions in FuncCall, order matters.

"a", "b", and "c" are 3 separate functions that are written out later in the code.

Am I missing something here? I can elaborate if needed.

M.Y.
  • 93
  • 1
  • 3
  • 9

4 Answers4

2

The Serial Monitor is nothing more than a window that displays the serial data sent to the PC. Are you saying that you are passing characters to the ATMega328 and calling certain functions based on what you send?

But as for your question:

typedef void (* Caller)()

Is a typedef for a void* function pointer (with void type) with the name of Called.

Caller FuncCall[] = {a, b, c);

Will evaluate at compile-time to:

void (* Caller)() FuncCall[] = {a, b, c);

Which is essentially an array of void* function pointers.

I am not sure of what you mean by void a(), but to use function pointers you must initialize them to point to functions. Consider the following example:

void printFunc(){
    printf("Print Test\n");
}

int main(){
    void (*functionPtr)();
    functionPtr = &printFunc; //functionPtr points to the address of printFunc()
    functionPtr();
    return 0;
}

Consider the following code:

#include <SoftwareSerial.h>

/* These are function prototypes - required by the compiler */
void a();
void b();
void c();

typedef void (* Caller)();
Caller FuncCall[] = {&a, &b, &c}; //initialize addresses for pointers 
//String func_list[] = {"a","b","c"}; //this is a terrible way to do this
char func_list[] = {'a', 'b', 'c'};   //use this instead - if you wish


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

void a(){
    Serial.println("Called Function: a\n");
}

void b(){
    Serial.println("Called Function: b\n");
}

void c(){
    Serial.println("Called Function: c\n");
}

void loop(){
    if(Serial.available())
        switch(Serial.read()){
            case 'a':           //this could also be func_list[0], if you want
                FuncCall[0]();
                break;

            case 'b':
                FuncCall[1]();
                break;

            case 'c':
                FuncCall[2]();
                break;

            default:
                Serial.println("There was an error\n");
                break;
    }
}

Where you simply send the Arduino a character (a, b, or c) through the Serial Monitor. You can make the implementation as complicated you want. You could, for example, have string parsing and indexing into the array as suggested - but the above implementation could not get any more simple to the best of my knowledge.

sherrellbc
  • 3,431
  • 6
  • 35
  • 62
  • Yes, I am trying to call certain functions based on what I send when I go to the Serial Monitor. In particular, the Serial Monitor outputs, "Enter a valid function to call." I'm just having trouble figuring out how exactly I can do so. – M.Y. Jun 30 '14 at 20:13
  • You can simply pass in a constant that is defined at the top of your program: `#define FUNCTION_A 0xA5` for example. Loop infinintely and check the serial buffer for content and the use a `switch-case`, for example, to determine which code was passed in. Then call the appropriate function by using the function-pointer sytnax. – sherrellbc Jun 30 '14 at 20:17
  • I understand that pointers must be initialized to point to functions, but I am trying to call the functions outside of the code (i.e., in the text entry space in the serial monitor) – M.Y. Jun 30 '14 at 20:19
  • You are not understanding how the serial communication works. You are not interacting directly with your code, you are passing in data that must be interpreted at run-time *by* your code. – sherrellbc Jun 30 '14 at 20:20
  • I see. I guess my question is how can I "pass in" the data at run-time? The serial monitor tells me to "Enter a valid function to call". I apologize for being completely clueless... – M.Y. Jun 30 '14 at 20:23
  • @MichaelYom, see the above edits. I have not written an Arduino program in ages - but that would be my implementation of what you are talking about. You simply type in a character (a, b, or c) and press "Send". This will transmit (over serial - UART) to the Arduino. The code then reads the serial buffer and determines which function to call. If any other character is sent it just reports an error. – sherrellbc Jun 30 '14 at 21:14
  • ^that's exactly what I was looking for, thanks so much. I guess I am just confused because my program isn't calling the function based on the character (a,b, or c) that I entered through the Serial Monitor. – M.Y. Jun 30 '14 at 21:22
  • @MichaelYom, No problem. Don't forget to up-vote helpful answers. – sherrellbc Jun 30 '14 at 21:24
1

You need to indicate that you want to access what the pointer points to.

Add an asterisk in front of the pointer and parenthesises around it.

 (*FuncCall[0])();
Bence Kaulics
  • 6,353
  • 12
  • 33
  • 60
CountZero
  • 11
  • 1
0

It looks like the intent is to look up an incoming string in func_list[], turning into an index value if found, and then use that index to get the corresponding entry in FuncCall[], and call that function.

Once you have i, the index to the name of the function, the call would look like:

(FuncCall[i])();

Also, note that you have a typo in your initializer for FuncCall — the closing paren should be a brace.

Dave Tweed
  • 168,369
  • 17
  • 228
  • 393
  • Let's say I want to call the void function "a" which is within FuncCall[] - is this possible? – M.Y. Jun 30 '14 at 20:38
0

From a comment:

I am trying to call the functions outside of the code (i.e., in the text entry space in the serial monitor)

Whoa there... you can't collect functions written in C into a buffer and then "execute" them. You'd have to build a compiler, asembler, linker etc into the Arduino program to perform their functions at runtime. C is a compiled (not an interpeted) language.

The constructs in your question merely allow you to call functions, which were available at compile time, with compliant signatures "dynamically" at runtime, by accessing them through an array. This is one way that "callback" functions are sometimes implemented.

vicatcu
  • 22,499
  • 13
  • 79
  • 155
  • I see. I'm wondering why then the Serial Monitor asks me to "Enter a valid function to call". I was hoping there was a way for me to "execute" a function of interest by sending the Serial Monitor a command. – M.Y. Jun 30 '14 at 20:39
  • @MichaelYom that's why there's that other array func_list... so you can compare the string you enter to a list of known functions that can be called – vicatcu Jun 30 '14 at 20:48
  • What do you mean by "comparing" to a list of known functions that can be called? So the commands that I'd enter are the strings within "func_list[]"? I am so sorry for being pretty much clueless... – M.Y. Jun 30 '14 at 20:52
  • Yes as in using something the standard strcmp function to look for string equality between the text you enter at the serial terminal and a string entry in the func_list. Your code would then call the function at the same index of the FuncCall array. – vicatcu Jun 30 '14 at 21:01
  • Ah that makes sense. Then wouldn't entering "a" execute "a" within FuncCall? Or do I need to consider all indices of func_list to call the functions at FuncCall? – M.Y. Jun 30 '14 at 21:05
  • @MichaelYom if you use a switch/case in your loop to decode the serial data that makes the use case somewhat contrived. If I was doing it that way, I would just call a(), b() and c() directly from each case. Generally you would search the named array and find the index, the call the function in the "adjacent" array. It's just a different pattern. – vicatcu Jul 01 '14 at 13:30