Author Topic: storing function pointer and calling  (Read 2514 times)

0 Members and 1 Guest are viewing this topic.

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
storing function pointer and calling
« on: June 03, 2021, 07:16:24 am »
Problem in c++ (arduino)  ( my c++ skills are very rusty... )
Simple task : store and retrieve function pointers in a simple table.
Don't focus on what the code does. It is just an example. The part i am interested in is retrieving the address of a function and storing it in a variable, not something defined as pointer or array of pointers. Don't know if that is possible. We may have to make two arrays, one holding the normal integers , one holding the pointers.
Again , don't focus on what the example code does, it is nonsense. i am after how to store the address of a function and then call that function given its address.

Code: [Select]
int functions[10][4];  // an 2 dimensional array that will hold data and function pointers

int somefunction(){return 123;};           // a routine that does something, takes no arguments but returns a value
int someotherfunction (){return 456;}; // a routine that does something else , takes no arguments but returns a value


// a routine that will store the pointer to a given function in a given slot in the array. this is the bit i am fuzzy on. how do i obtain the pointer to the passed function ?
// the routine takes 2 integers and a pointer to a given function
void storefunction (int location , int data1,int data2, int funcpointer)  // <- correct definition needed here so it can take address of function
{
  functions[location][0] = data1;
  functions[location][1] = data2;
  functions[location][2] = funcpointer;
}

// this routine finds a pointer in the array and calls it
int process (int location){
  int result = call functions[location][2];  // <- this bit of code is needed.
  result = result + functions[location][0]
  result = result / functions[location][1]
  return result;
}

storefunction (0,0,1,somefunction);
storefunction (1,123,2,somefunction);
storefunction (2,0,1,someotherfunction);
storefunction (3,0,1,someotherfunction);

int x = process(0);  //  should now contain 123 (we added 0 and divided by 1)
int x = process(1);  //  should now contain 123 (we added 123 and divided by 2)


« Last Edit: June 03, 2021, 07:48:21 am by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 2250
  • Country: nz
  • Formerly SiFive, Samsung R&D
Re: storing function pointer and calling
« Reply #1 on: June 03, 2021, 07:43:30 am »
You're fairly close.

The main problem is your array stores ints not function pointers.

You want

Code: [Select]
typedef int (*func_no_args_returns_int)();

func_no_args_returns_int functions[4];

The main other thing wrong is there is no call keyword. You just put () after the array access: functions[location]().
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #2 on: June 03, 2021, 07:52:33 am »
You're fairly close.

The main problem is your array stores ints not function pointers.

And there is the rub. i want to store this in a multidimensional array that contains other data that are not function pointers.. that is what i am trying to figure out.

Let's say we have an array that is 10 by 3.
Each line contains two integers and a function pointer (which is nothing but an integer).
so:
-  how do i cast such an array ?
- how can i pass both normal values and function pointer to a routine
Code: [Select]

void storefunction (int location_in_array , int integer_data1,int integer_data2, 'functionpointer') {};

storefunction (5,999,111,the_function_that_needs_calling);





« Last Edit: June 03, 2021, 07:57:30 am by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Online newbrain

  • Super Contributor
  • ***
  • Posts: 1145
  • Country: se
Re: storing function pointer and calling
« Reply #3 on: June 03, 2021, 07:54:33 am »
The problem (calling a function based on an index) can be probably tackled in different ways in 'real' C++, but I'll give you some hints for a C-style solution, as that seems to be what you are after.

First, the definition of the table:
Code: [Select]
int functions[4];won't do: this is an array of integers, not of function pointers.
The correct declaration is:
Code: [Select]
int (*functions[4])(void);Reading the declaration we have that:
"functions is an array of 4 pointers to a function taking no arguments and returning int"
This site will do that for you, but IMO it's important to learn the skill.

As this kind of declaration often becomes difficult to eye-parse, I often prefer to split it in a typedef and the actual declaration:
Code: [Select]
typedef int (*funcPtr)(void); // funcPtr is a pointer to a function taking no arguments and returning int
funcPtr functions[4]; // Functions is an array [4] of functPtr

Now, with functions in place the rest is a real breeze!
Filling the array is as simple as assigning its elements.
The name of the function, if not followed by (), will give you the pointer. If you want to make it more explicit, &myfunction has exactly he same meaning:
Code: [Select]
int myfunction(void) { return 42; }
...
functions[1] = myfunction; // or &myfunction

Invoking the function is similarly very simple:
Code: [Select]
int result;
result = functions[1](); // calls myfunction()

Standard references C++17:
Function calls [expr.call]
Conversion to function pointer [conv.func]
& with a function name [expr.unary.op] §3
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #4 on: June 03, 2021, 08:04:54 am »
The problem (calling a function based on an index) can be probably tackled in different ways in 'real' C++, but I'll give you some hints for a C-style solution, as that seems to be what you are after.
nope. I have found thousands of examples like that, but that is NOT what i seek !
the examples all assume either a single dimension and one type (pointer)

if have a two dimensional array that contains both normal data and function pointers.
In essence i need to be able to store the 'function pointer' as a plain integer (the address is nothing but a simple integer). and then have a routine that allows me to pass this integer , converting it back to a pointer and invoking the routine.

thinkof it this way : we have two processors running the same binary. they talk over a serial link.
processor 1 says to processor 2 : please execute your function at location 0x01234
this 0x01234 is sent in plain ascii. the receiver converts this back to an integer, loads a pointer and invokes the routine it is now pointing to. basically a remote procedure call  by passing addresses.

the functions don't return anything because they pick up data from global variables and dump their results back into global variables, they only have an exit value returning an error code. not all functions have the same number of arguments
anyway ,we are diverging.

what is seek is how to create a function that can accept two integers and a function pointer and store that in a row of a two dimensional array
Code: [Select]
void storefunction (int location_in_array , int integer_data1,int integer_data2, 'functionpointer') {};
storefunction (5,999,111,the_function_that_needs_calling);

and then how to do the reverse : given the row number in the array : invoke the function
« Last Edit: June 03, 2021, 08:12:29 am by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Online newbrain

  • Super Contributor
  • ***
  • Posts: 1145
  • Country: se
Re: storing function pointer and calling
« Reply #5 on: June 03, 2021, 08:05:24 am »
so how do i cast such an array ?
Simple: you do not cast it - that's a different thing.

But, you can build a structure containing a pointer to function and the integers, and have an array of those.

As an example, here's the (largely abridged) code I use in the processing pipeline for an SDR hobby project (C11 - not C++):
Code: [Select]
/**Generic step function pointer
 * buf: points the buffer to be processed
 * len: processing length
 * user: step specific parameter,
 *       uintptr_t can be used to safely pass a void pointer in an uint */
typedef void (*ProcStepFunc)(float complex *buf, uint32_t len, uintptr_t user);

/* A structure with the processing step and its arguments */
typedef struct ProcStep_s
{
    ProcStepFunc func;
    float complex *buf;
    uint32_t     len;
    uintptr_t    user;
} ProcStep;

extern ProcStep  pipeline[];

Not really complicated.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 610
Re: storing function pointer and calling
« Reply #6 on: June 03, 2021, 08:05:51 am »
You can play with ideas in the online compiler-
https://godbolt.org/z/hsbxosabj


Code: [Select]
template<typename T, unsigned N>
static constexpr auto
arraySize(T (&v)[N]) { return N; }

using funcT = int(*)();

struct FuncArray {
    funcT func;
    int a;
    int b;
};

FuncArray funcArray[10];

void storefunction (int n, funcT func, int a, int b) {
    if( n >= arraySize(funcArray) ) return;
    funcArray[n].func = func;
    funcArray[n].a = a;
    funcArray[n].b = b;
}

int callfunction (int n){
    if( n >= arraySize(funcArray) or not funcArray[n].func ) return 0;
    return funcArray[n].func();
}


int somefunction(){return 123;}
int someotherfunction (){return 456;}



int main()
{
    storefunction (1,somefunction,123,123);
    storefunction (2,somefunction,456,456);
    storefunction (3,someotherfunction,789,789);
    storefunction (0,someotherfunction,452,452);

    int x = callfunction(0);  //  should now contain 456
    x = callfunction(2);  //  should now contain 123

while(1){}
}
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #7 on: June 03, 2021, 08:14:27 am »
so how do i cast such an array ?
Simple: you do not cast it - that's a different thing.

But, you can build a structure containing a pointer to function and the integers, and have an array of those.

As an example, here's the (largely abridged) code I use in the processing pipeline for an SDR hobby project (C11 - not C++):
Code: [Select]
/**Generic step function pointer
 * buf: points the buffer to be processed
 * len: processing length
 * user: step specific parameter,
 *       uintptr_t can be used to safely pass a void pointer in an uint */
typedef void (*ProcStepFunc)(float complex *buf, uint32_t len, uintptr_t user);

/* A structure with the processing step and its arguments */
typedef struct ProcStep_s
{
    ProcStepFunc func;
    float complex *buf;
    uint32_t     len;
    uintptr_t    user;
} ProcStep;

extern ProcStep  pipeline[];

Not really complicated.

aha ! of course. structures ... and then make an array of structures. how come i didn't think of that ... -facepalm-. it;s been too long since i wrote code...
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Online newbrain

  • Super Contributor
  • ***
  • Posts: 1145
  • Country: se
Re: storing function pointer and calling
« Reply #8 on: June 03, 2021, 08:17:50 am »
I have found thousands of examples like that, but that is NOT what i seek !
the examples all assume either a single dimension and one type (pointer)

if have a two dimensional array that contains both normal data and function pointers.
In essence i need to be able to store the 'function pointer' as a plain integer (the address is nothing but a simple integer). and then have a routine that allows me to pass this integer , converting it back to a pointer and invoking the routine.
Ok, this was definitely not clear from the first post.

If you are sure of the function pointer implementation and that a function pointer can be safely translated back and forth to an integer, you can simply use a cast (now I see why you mentioned it) with the typedef I proposed.
Code: [Select]
int funcs[4]; // this your int array, but they are really function poointers
typedef int (*FuncPtr)(void); // funcPtr is a pointer to a function taking no arguments and returning int
[...]
int result;
FuncPtr f = (FuncPtr)funcs[1];
result = f();

Two or more dimensions do not matter at all.

[good Stuff]
Yes that is a good way to do it in C++!
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: free_electron

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #9 on: June 03, 2021, 08:40:34 am »
rats .. more problems.

Code: [Select]
#define max_daemons 10

int daemoncounter =0;                // keep track of how many we already have

using functionpointer = int(*)();
struct Daemon {
   int counter;              // current counter value
   int interval;             // given interval to reload
   int state;                // runtime state 0 = stop 1 =run
   functionpointer handler;  // function to be called when counter reaches zero
};

Daemon Daemons[max_daemons]; // the array of daemons
 
int CreateDaemon (int interval, functionpointer target_function )
{
   Daemons[daemoncounter].interval = interval;
   Daemons[daemoncounter].handler = target_function;
   daemoncounter++;         
};

void StartDaemon (int daemon)
{
   Daemons[daemon].counter = Daemons[daemon].interval;
   Daemons[daemon].state = 1;
};

void PauseDaemon (int daemon)
{
   Daemons[daemon].state = 0;
};
void ResumeDaemon (int daemon)
{
   Daemons[daemon].state = 1;
};
           
void AbortDaemon (int daemon)
{
   Daemons[daemon].state = 0;
   Daemons[daemon].counter = 0;
};

error 'functionpointer' has not been declared ...
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Online newbrain

  • Super Contributor
  • ***
  • Posts: 1145
  • Country: se
Re: storing function pointer and calling
« Reply #10 on: June 03, 2021, 08:50:41 am »
Type aliases with "using" were introduced in C++11.
You might have an old compiler, or wrong compiler flags.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #11 on: June 03, 2021, 08:51:55 am »
Type aliases with "using" were introduced in C++11.
You might have an old compiler, or wrong compiler flags.

arduino in tinkercad

this is why i hate languages like c and c++. it 's like trying to peel a banana with a hammer. compile -> fix typo's -> compile - > fix flags -> compile -> compiler issues.
i want interpreted basic back. at least i can move forward in that without endless hunting for typos and cryptical error mesages.
i am trying to port a simple basic program to arduino and i've been struggling for hours on this simple bit of code.
all it is is a bit of simple downcounters attached to an interrupt handler , that fire off functions when their count reaches zero.
grrr... frustrated.
« Last Edit: June 03, 2021, 08:57:07 am by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Online newbrain

  • Super Contributor
  • ***
  • Posts: 1145
  • Country: se
Re: storing function pointer and calling
« Reply #12 on: June 03, 2021, 09:09:32 am »
Type aliases with "using" were introduced in C++11.
You might have an old compiler, or wrong compiler flags.

arduino in tinkercad

this is why i hate languages like c and c++.
Typos: get a good IDE/programming editor, it's difficult nowadays to slip in typos in variable names or syntax errors. I use Visual Studio and VS Code, but even Eclipse (barf) is  decent at it.
Compiler issue: know your compiler and how you use it - Aduino makes a good job of mangling and hiding what the compiler is actually doing. Good for simple stuff, dreadful for (even slightly) advanced one.
Languages: C and C++ are rich complex languages - especially modern C++ - it takes time and a lot of patience and dedication... :blah:
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: agehall

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #13 on: June 03, 2021, 09:11:53 am »
Type aliases with "using" were introduced in C++11.
You might have an old compiler, or wrong compiler flags.

arduino in tinkercad

this is why i hate languages like c and c++.
Typos: get a good IDE/programming editor, it's difficult nowadays to slip in typos in variable names or syntax errors. I use Visual Studio and VS Code, but even Eclipse (barf) is  decent at it.
Compiler issue: know your compiler and how you use it - Aduino makes a good job of mangling and hiding what the compiler is actually doing. Good for simple stuff, dreadful for (even slightly) advanced one.
Languages: C and C++ are rich complex languages - especially modern C++ - it takes time and a lot of patience and dedication... :blah:

ok, but that doesn't solve my problem at hand : how do i get this thing to run in arduino ? it is supposed to be c++
neither typedef nor using works.
i need to get this thing running. proof fo concept. things that take 5 minutes in VB take 5 days in c. i have no patience. i'm trying to help someone write a simple task scheduler.

« Last Edit: June 03, 2021, 09:13:42 am by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Online newbrain

  • Super Contributor
  • ***
  • Posts: 1145
  • Country: se
Re: storing function pointer and calling
« Reply #14 on: June 03, 2021, 09:32:41 am »
it is supposed to be c++
neither typedef nor using works.
Arduino is a bastardized version of some C++ (I have no idea which standard and I'm not looking it up).
That said, I can't see any reason the correct typedef should not work.

BTW: you have undefined behaviour: int function that falls off the end. The compiler is allowed to do whatever, including spurious error messages (no, it's 99.9% not the case here).
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #15 on: June 03, 2021, 09:38:42 am »
code compiles and runs in arduino ide. does not compile in tinkercad.  Typical Autocrap product.
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 4123
  • Country: fi
Re: storing function pointer and calling
« Reply #16 on: June 03, 2021, 10:56:48 am »
Arduino is supposed to be used by artists, not programmers, to write code like

blink_led();
delay(100);
blink_led();

It's just not usable in real professional work or more complex projects, get rid of it.

Looking at your preferred function pointer approach, it also looks like you are "a C guy", and there is nothing wrong with it. I guess it would be helpful to limit yourself to C as you are not using (or don't want to use) C++ features. So compile with a C compiler. This way, the language gets approximately two orders of magnitude smaller, with well specified freestanding environment, meaning it is possible for a human being to understand everything going on.
 
The following users thanked this post: newbrain

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 2250
  • Country: nz
  • Formerly SiFive, Samsung R&D
Re: storing function pointer and calling
« Reply #17 on: June 03, 2021, 11:01:31 am »
i need to get this thing running. proof fo concept. things that take 5 minutes in VB take 5 days in c. i have no patience. i'm trying to help someone write a simple task scheduler.

I find the opposite. C is an extremely simple and rather stable language. I struggle to think of the last time something that didn't have obvious typos failed to work as expected.

There is no way that typedef won't work.
 
The following users thanked this post: newbrain

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 2731
  • Country: fi
    • My home page and email address
Re: storing function pointer and calling
« Reply #18 on: June 03, 2021, 11:39:12 am »
There are two main use cases for use with indirect functions: closures and callbacks.

A typical example of callbacks is how the standard C qsort() function takes the (pointer to the) function used to compare elements as a parameter.

In this case, free_electron has a closure: a function pointer, and an environment (here, parameters) the function is called in.  Unlike some other programming languages, C and C++ do not have a native way to describe arbitrary closures; this is why structures (or classes in C++) are used to describe them.  In C++, subclasses can be used to describe function calls with different types and/or number of arguments.  Unions can be used in both C and C++ (especially in code that compiles as either C or C++ cleanly), but the aliasing rules does impose certain constraints on how this should be done: commonly used code works but may not be portable (see e.g. POSIX struct sockaddr, which only works with compilers and C libraries that implement the stricter POSIX rules, and not just standard C) – and obviously that is also the kinda sorta the situation we have here.

The Arduino environment is a freestanding GNU C/C++ environment, with a custom system library (the base Arduino library).  The standard C or C++ libraries are not available.  Because the compiler used is GNU gcc, the rules it implements do differ a bit from the standard C and C++ specifications.  Language lawyers can discuss whether it conforms to which version of which standard, but basically, C++ freestanding environment leaves a lot for the implementation to decide, whereas the C freestanding environment is a bit better specified; and the freestanding environment GNU C and C++ compilers provide is pretty well described here and piecewise in the run time options.  In particular, there are built in functions that although nominally part of the C or C++ library, are by default implemented by the compiler instead, unless directed otherwise; and that the prototype int func() can actually mean either "function with arbitrary arguments" or "function with no arguments", depending on the exact details.

Wait, keep reading: I do have a point.  You see, because of the above, the solution here is to use the minimum intersection of all of the above, to get it to work in real life in different environments.  (In language lawyer terms, this is about picking the most likely subset of the implementation defined behaviour, plus assumed workarounds for bugs in non-conforming compilers.)
  • Use exact function pointer declaration in the structure, not via a typedef
  • Use explicit function prototypes
  • For different types of functions, use an initial common member in the structure to describe the type via an enum, and an union of the structures containing the parameters passed to each type of function
The first one is arguably a bug in a compiler, but :-//.  The second avoids the issue with ambiguity in a function prototype with empty parameter list (whether that means "arbitrary parameters" or "no parameters" or something else, depends; and we want to avoid any ambiguity to get the code to work).  The third one works in both C and C++ even with strict aliasing rules.  But if you only need closures of one specific function prototype type, you don't need to bother.

Let's say you want to support closures that have a context of two integer parameters, and a third one supplied at call time, so something that is ambiguous wrt. the above terminology just to keep things fun; and returns an int:
Code: [Select]
struct afterthought {
    int  (*callback)(int a, int b, int c);
    int  a;
    int  b;
};

#define  AT_NUM_X  5
#define  AT_NUM_Y  6

static struct afterthought  at_array[AT_NUM_Y][AT_NUM_X];

int set_afterthought(int x, int y, int (*callback)(int a, int b, int c), int a, int b) {
    if (x < 0 || x >= AT_NUM_X || y < 0 || y >= AT_NUM_Y) {
        /* Index [y][x] is out of bounds */
        return -1;
    }

    at_array[y][x].callback = callback;
    at_array[y][x].a = a;
    at_array[y][x].b = b;
    return 0;
}

int run_afterthought(int x, int y, int c) {
    if (x < 0 || x >= AT_NUM_X || y < 0 || y >= AT_NUM_Y) {
        /* Index [y][x] is out of bounds */
        return -1;
    }
    return at_array[y][x].callback(at_array[y][x].a, at_array[y][x].b, c);
}
The index check is not an assert(), because assert() is not available in freestanding C or C++.  We could write a macro, or use #ifndef NDEBUG ... #endif around the index bounds check, so it is only compiled in when NDEBUG is NOT defined, just like assert()s.

When we supply the function to use to set_afterthought(), you may or may not need to use ampersand (address-of operator).  In C, the two are equivalent, but I'm not sure how C-and-C++ compilers (like the Microsoft compiler) treat it.  You could work around this via a macro (like Glib uses G_CALLBACK(function)), so you only need to pick it once based on the compiler used (per macros that each compiler defines).
« Last Edit: June 03, 2021, 11:44:31 am by Nominal Animal »
 

Online langwadt

  • Super Contributor
  • ***
  • Posts: 2486
  • Country: dk
Re: storing function pointer and calling
« Reply #19 on: June 03, 2021, 11:41:50 am »
Type aliases with "using" were introduced in C++11.
You might have an old compiler, or wrong compiler flags.

arduino in tinkercad

this is why i hate languages like c and c++. it 's like trying to peel a banana with a hammer. compile -> fix typo's -> compile - > fix flags -> compile -> compiler issues.
i want interpreted basic back. at least i can move forward in that without endless hunting for typos and cryptical error mesages.
i am trying to port a simple basic program to arduino and i've been struggling for hours on this simple bit of code.
all it is is a bit of simple downcounters attached to an interrupt handler , that fire off functions when their count reaches zero.
grrr... frustrated.

well, if you have no idea what your are doing and just throw random stuff at the compiler hoping it will guess what you mean, ....



 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 2731
  • Country: fi
    • My home page and email address
Re: storing function pointer and calling
« Reply #20 on: June 03, 2021, 11:52:52 am »
i am trying to port a simple basic program to arduino and i've been struggling for hours on this simple bit of code.
That right there is the root cause of your problem.

You are, essentially, trying to bolt on wings to a van, and get it to fly supersonic.

What I mean is that each language – and here, we really need to consider Arduino a separate "language" because basically none of the standard C or C++ library is available, and is a very limited, low-level programming environment – has its own approach to solving the problems.  (I call this the paradigm.  Yes, C, C++, and Basic are all imperative languages, but that's just one aspect of the overall approach to problem solving the languages implement.) "Simple basic" is actually rather high level language environment, with lots of abstractions (like "wings" and "fuselage" and "jet engine" in my above analog).  This means that the design, the structure, of the basic program does not map at all to the design or structure of an Arduino sketch.



It sounds like your code implements timeouts.  I could help you with that.  So, why not ask the underlying question (in the programming section) – describing what you want the code to achieve, instead of asking about a minute implementation detail of your already chosen solution?
« Last Edit: June 03, 2021, 11:55:38 am by Nominal Animal »
 
The following users thanked this post: newbrain

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 610
Re: storing function pointer and calling
« Reply #21 on: June 03, 2021, 12:24:14 pm »
I don't use arduino, but maybe your source file is .c and arduino may be adding an -xc option when it sees a .c file. It may be using g++ for all files but tacking in a -xc for what it thinks are c files. That would be one thing to check.

I use the gcc-avr 7.3.0 toolchain from arduino for avr0/1 and use c++ most of the time. 7.3.0 is c++17 'compatible' so you also get some nice features if you are into c++. I would figure out what commands are going to the toolchain from whatever your environment is so you can see what is wrong.


edit-

and as long you are using c++, may as well use it like c++
https://godbolt.org/z/jT1Mvj4n5
https://godbolt.org/z/MvbhsKjn6  (my 'style' which keeps important names on the left)
Not sure if you have a need to reuse these array members at runtime, and if not I would just populate the array one time as needed and skip dealing with a fixed array size and figuring out if there is any room left, etc. (remove daemoncounter things, change create() to a Daemon constructor instead).

Just like c, you keep things as private as you can and in places they 'belong'. A class/struct is a good place to handle the Daemon functions and vars.
« Last Edit: June 03, 2021, 02:13:01 pm by cv007 »
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #22 on: June 03, 2021, 02:53:36 pm »
Type aliases with "using" were introduced in C++11.
You might have an old compiler, or wrong compiler flags.

arduino in tinkercad

this is why i hate languages like c and c++. it 's like trying to peel a banana with a hammer. compile -> fix typo's -> compile - > fix flags -> compile -> compiler issues.
i want interpreted basic back. at least i can move forward in that without endless hunting for typos and cryptical error mesages.
i am trying to port a simple basic program to arduino and i've been struggling for hours on this simple bit of code.
all it is is a bit of simple downcounters attached to an interrupt handler , that fire off functions when their count reaches zero.
grrr... frustrated.

well, if you have no idea what your are doing and just throw random stuff at the compiler hoping it will guess what you mean, ....
the issue is that the code works in the real arduino ide , but does not work in tinkercad simulator ...
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 2731
  • Country: fi
    • My home page and email address
Re: storing function pointer and calling
« Reply #23 on: June 03, 2021, 03:27:59 pm »
the issue is that the code works in the real arduino ide , but does not work in tinkercad simulator ...
Um.

Tinkercad simulator is based on Code::Blocks simulator, which is an API-level simulator. It simulates based on the function calls, not based on what the hardware register values et cetera are.  As far as I know, it does not support timer interrupts, because the base API does not have such functions; they vary too much between different hardware.

So, let me guess, you're poking at the hardware registers to manipulate the timer, and are not seeing the interrupts fire, ever? Or such code does not compile at all?  Even using structures causes the simulator to b0rk? That is because the simulator does not support that, it's too simple of a simulator for that sort of stuff.

In fact, I don't even believe there is a proper compiler used for the simulation at all.  Just a lexer/parser that converts the code to an abstract syntax model (a directed graph of operations) – that's how it can convert between the different forms, from graphical to text – and a state machine that acts on that abstract syntax model.  It is a toy.
« Last Edit: June 03, 2021, 03:33:14 pm by Nominal Animal »
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #24 on: June 03, 2021, 03:56:26 pm »
ok guys, all comments aside , Below is the code that compiles in the official arduino ide. it does not compile in tinkercad.
I always assumed arduino was plain c++ , but apparently it is not.
I grabbed the damn thing because i wanted to show proof of concept on how to make a simple cooperative threading application. I have used this mechanism on many applications ( 8051 in pl/m or asm ) and even in other compilers on arm and other machines.
The arduino world is indeed full of code containing blocking 'sleep' statements. So that is why my little microthreader solves : you can set up a process that will fire at a given interval without blocking others. It is not time-deterministic and yu need to make sure you complete all before the next tick but it works.

i normally do the following:
1) i set  up a table containing 4 parameters per line : the current count value, the interval , the run state and the pointer to what needs to be called. Since we run on 8/16 bitters there are all cast as 16 bit integers (we dont have more than 64k code memory)
2) if i need to handle 10 processes the table is  10 lines by 4 words or 80 bytes of memory. So my overhead is very low.
3) i set up a hardware timer to fire an interrupt every 1 second ( or 1 millisecond or 100 milliseconds , play at will here, just make sure to be able to complete all before next tick )
4) at interrupt, the handler decrements all the count values in the table where the run state is set to 1.
    if the count value hits zero :
    we call the function (we have the pointer ). the return value is stored in the state. this allows a function to terminate itself. if it returns 0 it stops itself.
    we reload the count value with the interval

there are a number of housekeeping commands
pause and resume : alter the run state (pause = 0 , resume = 1) without modifying the count value.
stop : set runstate to zero but reset the count value to interval, when start comes it begins from interval
start : set runstate to 1

int createprocess (interval, function to be called ) : loads the table with the data , returns the index the process is stored in in the table

example :
blinky_led1 = createprocess (5, led1handler)    // this fires every 5 seconds led1handler is responsible to do something with led1
blinky_led2 = createprocess (1, led2handler)    // this fires every 1 second
flasher = createprocess (10,flasherhandler)


startprocess (blinky_led1)
startprocess (blinky_led2)
startprocess(flasher)

main {
//i can do whatever here , even sleep. the interrupts will keep running
   if some_event then pause (blinky_led1)
  if some_other event then stop (blinky_led2)

};

The actual handlers can take 1 argument.
the argument tells the individual functions to execute the normal runcode or a different chunk of code. for example :
0 : do the normal process , for example toggle the led pin
1 : set the led pin high
2 : set the led pin low
255 : emergency stop : safe the i/o (whatever that means for your application ) and terminate yourself

a helper function (Rundaemon) allows you to do a force call to the handlers.


Code: [Select]

typedef int (* functionpointer) (int);
#define max_daemons 10
struct Daemon {
   int counter;              // current counter value
   int interval;             // given interval to reload
   int state;                // runtime state 0 = stop 1 =run
   functionpointer handler;  // function to be called when counter reaches zero
};
int daemoncounter =0;                // keep track of how many we already have
Daemon Daemons[max_daemons]; // the array of daemons
 
int CreateDaemon (int interval, functionpointer handler )
{
   Daemons[daemoncounter].interval = interval;
   Daemons[daemoncounter].handler = handler;
   daemoncounter++;         
};

void StartDaemon (int daemon)
{
   Daemons[daemon].counter = Daemons[daemon].interval;
   Daemons[daemon].state = 1;
};

void PauseDaemon (int daemon)
{
   Daemons[daemon].state = 0;
};
void ResumeDaemon (int daemon)
{
   Daemons[daemon].state = 1;
};
           
void AbortDaemon (int daemon)
{
   Daemons[daemon].state = 0;
   Daemons[daemon].counter = 0;
};

void RunDaemon (int daemon, int command)
{
  Daemons[daemon].state = Daemons[daemon].handler(command);
};


// some dummy routines
int led1 (int command){
  static int x;
  int exitcode = 1;
  pinMode(3,OUTPUT);
  if (command == 0){x = ~x;};             // toggle x
  if (command == 1){x = 1 ;};             // make high
  if (command == 2){x = 0 ;};             // make low
  if (command == 255){                    // emergency stop
    x=0;           // turn the led off (safe)
    exitcode =0;   // kill ourselves
  } 
  digitalWrite(3,x);
 
  return exitcode;                        // signal we want to keep running
};

int led2 (int command){
  static int x;
  pinMode(4,OUTPUT);
  x = ~(x);
  digitalWrite(4,x);
  return 1;
};

int led3 (int command){
  static int x;
  pinMode(5,OUTPUT);
  x = ~(x);
  digitalWrite(5,x);
  return 1;
};


// the interrupt handler. this fires at a 1 second interval
ISR(TIMER1_COMPA_vect)
{ for (int x = 0; x <daemoncounter; x++){
    if (Daemons[x].state=1){                       // if in run state
      if (Daemons[x].counter ==0) {                // and we hit zero
       
        //Daemons[x].handler();
        Daemons[x].state = Daemons[x].handler(0);   // -> execute the handler and store return value in state
                                                    //    this allows a function to stop itself by returning 0
                                                    //    we call with argument 0 : normal operation
        Daemons[x].counter = Daemons[x].interval;   // -> reload the counter
       
      }
      else {
        Daemons[x].counter--;                    // else decrement the counter
      };
    };
  };
}

void setup() {
    CreateDaemon (1, led1(0));  // blink once a second
    CreateDaemon (2, led2(0));  // blink every two second
    CreateDaemon (3, led3(0));  // blink every three seconds
     
    // Set up the hardware countern
    // - mode 4 (CTC)
    // - set prescaler to 1024
    // - using TCCR1A and TCCR1B registers
    // - see datasheet starting on page 170
       TCCR1A = 0b00000000; // set WGM11 and WGM10 bits (table 20-6, p171)
       TCCR1B = 0b00001101; // set WGM13, WGM12 (table 20-6), CS12, CS11, and CS10 bits (table 20-7, p 173)
       OCR1A = 15625;       // set the output compare value (timer interrupt will trigger when the timer reaches this value)
                            // one clock tick is 62.5 nanoseconds @ 16MHz
                            // prescaler * OCR1A * 62.5nS = seconds.
       TIMSK1 = 0b00000010; // enable the interrupt - p184
       sei();               // allow interrupts
}

void loop() {
  // do whatever logic here 
}

Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #25 on: June 03, 2021, 04:02:54 pm »
Tinkercad simulator is based on Code::Blocks simulator, which is an API-level simulator. It simulates based on the function calls, not based on what the hardware register values et cetera are.  As far as I know, it does not support timer interrupts, because the base API does

Tinkercad simulator works perfectly fine. it actually blinks the led driven by the timer.
but it does not understand the typedef for a pointer. it throws a compile error.
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 2731
  • Country: fi
    • My home page and email address
Re: storing function pointer and calling
« Reply #26 on: June 03, 2021, 04:46:30 pm »
Tinkercad simulator works perfectly fine. it actually blinks the led driven by the timer.
but it does not understand the typedef for a pointer. it throws a compile error.
"It works perfectly fine.  It's just that it throws a compile error when I do (a very normal thing)."

Don't you understand how self-contradictory that statement is?  By definition, it does not work perfectly fine, because it apparently does not support function pointers at all (at least via a typedef).

You claim the problem must be in Arduino or C or C++, when it is in your beloved Tinkercad simulator; but because you love it, you refuse to acknowledge that that is where the problem lies.  This is unbelievably frustrating.  I'm out.
 
The following users thanked this post: newbrain

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #27 on: June 03, 2021, 07:15:06 pm »
Tinkercad simulator works perfectly fine. it actually blinks the led driven by the timer.
but it does not understand the typedef for a pointer. it throws a compile error.
"It works perfectly fine.  It's just that it throws a compile error when I do (a very normal thing)."

Don't you understand how self-contradictory that statement is?  By definition, it does not work perfectly fine, because it apparently does not support function pointers at all (at least via a typedef).

You claim the problem must be in Arduino or C or C++, when it is in your beloved Tinkercad simulator; but because you love it, you refuse to acknowledge that that is where the problem lies.  This is unbelievably frustrating.  I'm out.
OK,  i should have formulated that better

- tinkercad can simulate the hardware timer and interrupt code. (tested in separate program, works correct. simple blinky led program)
- tinkercad can NOT handle typedef or 'using' for function pointers. it throws a compilation error. i found one other recent report of someone having issues with typedef. same code compiles without error/warning in arduino ide.

for the record:
- i do NOT like Tinkercad. it's just the only simulator i could find. nothing worse than a simulator that has bugs in it, except silicon with bugs in it.
- i do not like Arduino or its IDE. Visual studio is way better.

Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #28 on: June 03, 2021, 08:10:18 pm »
found a better simulator : wokwi
this one integrates with a web based GDB so i can monitor and trace code as it executes.

so far i can trace into the interrupt handler
but the function does not get called :
Code: [Select]
Daemons[x].state = Daemons[x].handler(0);
this does nothing..
« Last Edit: June 03, 2021, 08:15:55 pm by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Online nctnico

  • Super Contributor
  • ***
  • Posts: 21259
  • Country: nl
    • NCT Developments
Re: storing function pointer and calling
« Reply #29 on: June 03, 2021, 08:21:09 pm »
The best way forward is to test & debug your code on a PC (=compile for and run on a PC) and then transfer to an embedded platform. There is nothing in your code preventing you from doing that. Running on a PC will allow for much easier debugging.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 6948
  • Country: fr
Re: storing function pointer and calling
« Reply #30 on: June 03, 2021, 08:47:34 pm »
- tinkercad can NOT handle typedef or 'using' for function pointers. it throws a compilation error. i found one other recent report of someone having issues with typedef. same code compiles without error/warning in arduino ide.

I don't know what Tinkercad uses as a compiler. I find that odd. For a simulator, I would expect a compiler such as GCC compiling to object code, and then the object code to be simulated. I dunno how Tinkercad does this. Because unless there is something we haven't seen from your code, a compiler having problems with basic typedef declarations would be pretty nasty, and I also wouldn't quite get why Tinkercad would implement their own compiler (if that's the case) while there are good open-source compilers out there.
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #31 on: June 03, 2021, 08:52:26 pm »
The best way forward is to test & debug your code on a PC (=compile for and run on a PC) and then transfer to an embedded platform. There is nothing in your code preventing you from doing that. Running on a PC will allow for much easier debugging.
sure, please tell, how do i attach 3 led's to my pc so i can see the i/o ? and what about the inputs ?
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 610
Re: storing function pointer and calling
« Reply #32 on: June 03, 2021, 08:56:25 pm »
Code: [Select]
if (Daemons[x].state=1){
you want ==


You can also give you handlers access to the daemon, so they can control their own interval/status-
https://godbolt.org/z/GGdrqPeq3
« Last Edit: June 03, 2021, 08:59:04 pm by cv007 »
 

Online nctnico

  • Super Contributor
  • ***
  • Posts: 21259
  • Country: nl
    • NCT Developments
Re: storing function pointer and calling
« Reply #33 on: June 03, 2021, 09:26:06 pm »
The best way forward is to test & debug your code on a PC (=compile for and run on a PC) and then transfer to an embedded platform. There is nothing in your code preventing you from doing that. Running on a PC will allow for much easier debugging.
sure, please tell, how do i attach 3 led's to my pc so i can see the i/o ? and what about the inputs ?
Simple: printf("led 1 on\n");
Input are best simulated (don't forget to include invalid values so you can test the code for that as well).

Again: there is nothing in your software that needs the actual hardware in the loop and with testing on a PC you can go through a large number of input combinations and check the output whether it is valid. This is called unit testing which is extremely useful for the kind of problem you are trying to tackle.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #34 on: June 03, 2021, 09:49:18 pm »
Code: [Select]
if (Daemons[x].state=1){

you want ==


You can also give you handlers access to the daemon, so they can control their own interval/status-
https://godbolt.org/z/GGdrqPeq3
yup, i found that.
but i still can't get this to work.

if i call the functions directly from the ISR they work ( led toggles)
if i call the function by functionpointer the behavior is total whack.

Code: [Select]
// working
ISR(TIMER1_COMPA_vect)
{
   led2(0);
  };


Code: [Select]
// not working
ISR(TIMER1_COMPA_vect)
{
   Daemons[1].handler(0);
};


it calls all 3 daemons and called function loses its internal data... it's like the processor restarts .
another thing is , when i call createdaemon the function executes

i can not figure this out ... is it the compiler ? is it the simulator ? is it the code ? what the hell is going on ? i am using wokwi now as tinkercad cannot handle the typedef.
Code: [Select]

typedef int (*functionpointer) (int);
#define max_daemons 10
struct Daemon {
   volatile int counter;              // current counter value
   volatile int interval;             // given interval to reload
   volatile int state;                // runtime state 0 = stop 1 =run
   functionpointer handler;  // function to be called when counter reaches zero
};
int daemoncounter =0;                // keep track of how many we already have
volatile Daemon Daemons[max_daemons]; // the array of daemons
 
int CreateDaemon (int interval, functionpointer handler )
{
   Daemons[daemoncounter].interval = interval;
   Daemons[daemoncounter].handler = handler;
   daemoncounter++;         
   return daemoncounter -1;
};

void StartDaemon (int daemon)
{
   Daemons[daemon].counter = Daemons[daemon].interval;
   Daemons[daemon].state = 1;
};

void PauseDaemon (int daemon)
{
   Daemons[daemon].state = 0;
};
void ResumeDaemon (int daemon)
{
   Daemons[daemon].state = 1;
};
           
void AbortDaemon (int daemon)
{
   Daemons[daemon].state = 0;
   Daemons[daemon].counter = 0;
};

void RunDaemon (int daemon, int command)
{
  Daemons[daemon].state = Daemons[daemon].handler(command);
};


// some dummy routines
int led1 (int command){
  static int l1 ;
  int exitcode = 1;
  pinMode(3,OUTPUT);
  if (command == 0){l1 = ~l1;};             // toggle x
  if (command == 1){l1 = 1 ;};             // make high
  if (command == 2){l1 = 0 ;};             // make low
  if (command == 255){                    // emergency stop
    l1=0;           // turn the led off (safe)
    exitcode =0;   // kill ourselves
  } 
  if (l1 ==0){
    digitalWrite(3,HIGH);
  }
  else {
    digitalWrite(3,LOW);
  }
  Serial.println("l1");
  return exitcode;                        // signal we want to keep running
};


int led2 (int command){
  static int l2 ;
  //pinMode(4,OUTPUT);
  if (l2 == 1 ){
    //digitalWrite(4,HIGH);
    l2 = 0;
  }
  else {
    //digitalWrite(4,LOW);
    l2 = 1;
  }
  Serial.print("led2 :");
  Serial.println(l2);
  return 1;
};

int led3 (int command){
  static volatile int l3 ;
  //pinMode(5,OUTPUT);
  l3 = ~(l3);
  if (l3 ==0){
    digitalWrite(5,HIGH);
  }
  else {
    digitalWrite(5,LOW);
  }
  Serial.println("l3");
  return 1;
};

volatile int dummy;
// the interrupt handler. this fires at a 1 second interval
ISR(TIMER1_COMPA_vect)
{
  //led1(0);
  led2(0);
  //led3(0);
  // Daemons[0].handler(0);
  // Daemons[1].handler(0);
   //Daemons[2].handler(0);
  // for (int x = 0; x <daemoncounter; x++){
    //if (Daemons[x].state==1){                       // if in run state
    //  if (Daemons[x].counter == 0) {                // and we hit zero
    //    dummy = Daemons[x].handler(0);   // -> execute the handler and store return value in state
                                                    //    this allows a function to stop itself by returning 0
                                                    //    we call with argument 0 : normal operation
    //    Daemons[x].counter = Daemons[x].interval;   // -> reload the counter
    //  }
    //  else {
    //    Daemons[x].counter --;                    // else decrement the counter 
    //  };
    //};
 // };
 
}

int led1_daemon =0;
int led2_daemon =0;
int led3_daemon =0;

void setup() {
    Serial.begin(115200);
    led1_daemon = CreateDaemon (1, led1(0));  // blink once a second
    led2_daemon = CreateDaemon (2, led2(0));  // blink every two second
  //  led3_daemon = CreateDaemon (5, led3(0));  // blink every three seconds

    StartDaemon (led1_daemon)  ;
    StartDaemon (led2_daemon)  ;
    StartDaemon (led3_daemon)  ;
    // Set up the hardware countern
    // - mode 4 (CTC)
    // - set prescaler to 1024
    // - using TCCR1A and TCCR1B registers
    // - see datasheet starting on page 170
       TCCR1A = 0b00000000; // set WGM11 and WGM10 bits (table 20-6, p171)
       TCCR1B = 0b00001101; // set WGM13, WGM12 (table 20-6), CS12, CS11, and CS10 bits (table 20-7, p 173)
       OCR1A = 15625;       // set the output compare value (timer interrupt will trigger when the timer reaches this value)
                            // one clock tick is 62.5 nanoseconds @ 16MHz
                            // prescaler * OCR1A * 62.5nS = seconds.
       TIMSK1 = 0b00000010; // enable the interrupt - p184
       sei();               // allow interrupts
}

void loop() {
  // do whatever logic here 
 // led1 (0);
  //led2(0);
  //led3(0);
 // delay(1000);
}
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #35 on: June 03, 2021, 09:55:04 pm »
yup. confirmed. the fucker just restarts when calling the function pointer ...
so the function pointers do not work.
another thing i cannot figure out is why the passed fucntion executes when calling CreateDaemon.

Code: [Select]
typedef int (*functionpointer) (int arg1);

#define max_daemons 10
struct Daemon {
   volatile int counter;              // current counter value
   volatile int interval;             // given interval to reload
   volatile int state;                // runtime state 0 = stop 1 =run
   functionpointer handler;           // function to be called when counter reaches zero
};
int daemoncounter =0;                // keep track of how many we already have
volatile Daemon Daemons[max_daemons]; // the array of daemons
 
int CreateDaemon (int interval, functionpointer handler )
{
   Daemons[daemoncounter].interval = interval;
   Daemons[daemoncounter].handler = handler;
   daemoncounter++;         
   return daemoncounter -1;
};

void StartDaemon (int daemon)
{
   Daemons[daemon].counter = Daemons[daemon].interval;
   Daemons[daemon].state = 1;
};

void PauseDaemon (int daemon)
{
   Daemons[daemon].state = 0;
};
void ResumeDaemon (int daemon)
{
   Daemons[daemon].state = 1;
};
           
void AbortDaemon (int daemon)
{
   Daemons[daemon].state = 0;
   Daemons[daemon].counter = 0;
};

void RunDaemon (int daemon, int command)
{
  Daemons[daemon].state = Daemons[daemon].handler(command);
};


// some dummy routines
int led1 (int command){
  static int l1 ;
  int exitcode = 1;
  pinMode(3,OUTPUT);
  if (command == 0){l1 = ~l1;};             // toggle x
  if (command == 1){l1 = 1 ;};             // make high
  if (command == 2){l1 = 0 ;};             // make low
  if (command == 255){                    // emergency stop
    l1=0;           // turn the led off (safe)
    exitcode =0;   // kill ourselves
  } 
  if (l1 ==0){
    digitalWrite(3,HIGH);
  }
  else {
    digitalWrite(3,LOW);
  }
  Serial.println("l1");
  return exitcode;                        // signal we want to keep running
};


int led2 (int command){
  static int l2 ;
  //pinMode(4,OUTPUT);
  if (l2 == 1 ){
    //digitalWrite(4,HIGH);
    l2 = 0;
  }
  else {
    //digitalWrite(4,LOW);
    l2 = 1;
  }
  Serial.print("led2 :");
  Serial.println(l2);
  return 1;
};

int led3 (int command){
  static volatile int l3 ;
  //pinMode(5,OUTPUT);
  l3 = ~(l3);
  if (l3 ==0){
    digitalWrite(5,HIGH);
  }
  else {
    digitalWrite(5,LOW);
  }
  Serial.println("l3");
  return 1;
};

volatile int dummy;
// the interrupt handler. this fires at a 1 second interval
ISR(TIMER1_COMPA_vect)
{
   Serial.println("START interrupt handler");
   Daemons[0].handler(0); // <- this crashes the processor. it restarts
   Daemons[1].handler(0);
   Daemons[2].handler(0);
  //led1(0);
  //led2(0);
  //led3(0);
   Serial.println("END   interrupt handler");
  // for (int x = 0; x <daemoncounter; x++){
    //if (Daemons[x].state==1){                       // if in run state
    //  if (Daemons[x].counter == 0) {                // and we hit zero
    //    dummy = Daemons[x].handler(0);   // -> execute the handler and store return value in state
                                                    //    this allows a function to stop itself by returning 0
                                                    //    we call with argument 0 : normal operation
    //    Daemons[x].counter = Daemons[x].interval;   // -> reload the counter
    //  }
    //  else {
    //    Daemons[x].counter --;                    // else decrement the counter 
    //  };
    //};
 // };
 
}

int led1_daemon =0;
int led2_daemon =0;
int led3_daemon =0;

void setup() {
    Serial.begin(115200);
    Serial.println ("Entering setup");
    Serial.println ("Creating Daemons");
    led1_daemon = CreateDaemon (1, led1(0));  // blink once a second
    led2_daemon = CreateDaemon (2, led2(0));  // blink every two second
    led3_daemon = CreateDaemon (5, led3(0));  // blink every three seconds
    Serial.println ("Starting Daemons");
    StartDaemon (led1_daemon)  ;
    StartDaemon (led2_daemon)  ;
    StartDaemon (led3_daemon)  ;
    Serial.println ("Starting Hardware Timer");
    // Set up the hardware countern
    // - mode 4 (CTC)
    // - set prescaler to 1024
    // - using TCCR1A and TCCR1B registers
    // - see datasheet starting on page 170
       TCCR1A = 0b00000000; // set WGM11 and WGM10 bits (table 20-6, p171)
       TCCR1B = 0b00001101; // set WGM13, WGM12 (table 20-6), CS12, CS11, and CS10 bits (table 20-7, p 173)
       OCR1A = 15625;       // set the output compare value (timer interrupt will trigger when the timer reaches this value)
                            // one clock tick is 62.5 nanoseconds @ 16MHz
                            // prescaler * OCR1A * 62.5nS = seconds.
       TIMSK1 = 0b00000010; // enable the interrupt - p184
       sei();               // allow interrupts
}

void loop() {
  // do whatever logic here 
 // led1 (0);
  //led2(0);
  //led3(0);
 // delay(1000);
}
« Last Edit: June 03, 2021, 10:11:06 pm by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #36 on: June 03, 2021, 10:13:29 pm »
success !

the problem lies here :
when Creating the Daemon , the passed function must be called without arguments.
Code: [Select]
led1_daemon = CreateDaemon (1, led1);   // <- this works correctly
Code: [Select]
led1_daemon = CreateDaemon (1, led1(0));   // <- this crashes the cpu when attempting to call the pointer
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Online langwadt

  • Super Contributor
  • ***
  • Posts: 2486
  • Country: dk
Re: storing function pointer and calling
« Reply #37 on: June 03, 2021, 10:27:41 pm »
success !

the problem lies here :
when Creating the Daemon , the passed function must be called without arguments.
Code: [Select]
led1_daemon = CreateDaemon (1, led1);   // <- this works correctly
Code: [Select]
led1_daemon = CreateDaemon (1, led1(0));   // <- this crashes the cpu when attempting to call the pointer

ofcourse as expected, the one that works sets your function pointer to the address of led1, the one that doesn't work sets you function pointer to the return value of led1(0)

the second one should also produce a warning like "makes pointer from integer without a cast"


 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #38 on: June 03, 2021, 10:35:52 pm »
success !

the problem lies here :
when Creating the Daemon , the passed function must be called without arguments.
Code: [Select]
led1_daemon = CreateDaemon (1, led1);   // <- this works correctly
Code: [Select]
led1_daemon = CreateDaemon (1, led1(0));   // <- this crashes the cpu when attempting to call the pointer

ofcourse as expected, the one that works sets your function pointer to the address of led1, the one that doesn't work sets you function pointer to the return value of led1(0)

the second one should also produce a warning like "makes pointer from integer without a cast"

hindsight is always 20:20 ... no warnings or errors were thrown.
this is why i hate these kind if systems. you spend hours on a tiny little detail and the IDe is not very forthcoming with what is wrong.

if the language had two simple operators : address_of  and call_by_reference  or something similar and a proper debugger and a correct simulator. and an ide where you could set watchpoints and properly trace through interrupts ... and and and.

Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Online nctnico

  • Super Contributor
  • ***
  • Posts: 21259
  • Country: nl
    • NCT Developments
Re: storing function pointer and calling
« Reply #39 on: June 03, 2021, 10:45:08 pm »
hindsight is always 20:20 ... no warnings or errors were thrown.
this is why i hate these kind if systems. you spend hours on a tiny little detail and the IDe is not very forthcoming with what is wrong.
That is why you develop on a PC first and then move to an embedded platform. On a PC you could have single stepped through the code and spot the problem right away. But you'd likely got a warning during compilation which hinted towards the problem. Instead you tried to run before you can walk.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Online langwadt

  • Super Contributor
  • ***
  • Posts: 2486
  • Country: dk
Re: storing function pointer and calling
« Reply #40 on: June 03, 2021, 11:51:33 pm »
success !

the problem lies here :
when Creating the Daemon , the passed function must be called without arguments.
Code: [Select]
led1_daemon = CreateDaemon (1, led1);   // <- this works correctly
Code: [Select]
led1_daemon = CreateDaemon (1, led1(0));   // <- this crashes the cpu when attempting to call the pointer

ofcourse as expected, the one that works sets your function pointer to the address of led1, the one that doesn't work sets you function pointer to the return value of led1(0)

the second one should also produce a warning like "makes pointer from integer without a cast"

hindsight is always 20:20 ... no warnings or errors were thrown.
this is why i hate these kind if systems. you spend hours on a tiny little detail and the IDe is not very forthcoming with what is wrong.

if the language had two simple operators : address_of  and call_by_reference  or something similar

and it does, & and *

and a proper debugger and a correct simulator. and an ide where you could set watchpoints and properly trace through interrupts ... and and and.

run it on a PC or pick a tool and mcu that isn't stuck in 80's

 
The following users thanked this post: newbrain

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #41 on: June 04, 2021, 12:27:32 am »
hindsight is always 20:20 ... no warnings or errors were thrown.
this is why i hate these kind if systems. you spend hours on a tiny little detail and the IDe is not very forthcoming with what is wrong.
That is why you develop on a PC first and then move to an embedded platform. On a PC you could have single stepped through the code and spot the problem right away. But you'd likely got a warning during compilation which hinted towards the problem. Instead you tried to run before you can walk.
no warnings during compilation. the arduino ide said : compilation complete and spat out a binary.

I don't like the your approach of having to emulate the i/o code. i want to see the led turn on and the display display what it needs. How do you emulate things like rotary encoders or hardware timers and interrupts ? or a control loop that interacts with the analog world ?
That becomes very difficult to emulate. That's where you hook up a board to a JTAG probe and have a true debugger that lets you snoop around while the machine is running. alas. this arduino stuff is braindead in that world... the second best is a system emulator , but those are also braindead as they are not true machine simulators as i foudn out. although this WokWi seems to be the real thing , it lets you connect to a GDB session and you can actually set breakpoints and trace the program with all bells and whistles of GDB, that's how i found out most of the issues i had.

But you are right, i am used to having a real IDE like visual Basic (visual studio) or the IAR/ Keil tools in combination with hardware emulators.

This whole arduino ecosystem is like being in the 60's .. debugging using printf. and then simulators that don't work right.
i don't have patience for this kind of stuff anymore. All these little intricacies. i want simple things that work.  i'm going back to Keil and Cortex processors.
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 9939
  • Country: us
Re: storing function pointer and calling
« Reply #42 on: June 04, 2021, 12:37:15 am »
This whole arduino ecosystem is like being in the 60's .. debugging using printf. and then simulators that don't work right.
i don't have patience for this kind of stuff anymore. All these little intricacies. i want simple things that work.  i'm going back to Keil and Cortex processors.

There's more to the Arduino ecosystem than the basic Arduino IDE.

Here's a video with some pointers to better tools:
https://youtu.be/sm6QxJkWcSc
I'm not an EE--what am I doing here?
 
The following users thanked this post: free_electron

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #43 on: June 04, 2021, 12:38:51 am »

and it does, & and *
and they are a pain to get working... typedefs and other chinese ...

Quote
]

run it on a PC or pick a tool and mcu that isn't stuck in 80's
running on a pc : how ? the problem is the i/o emulation. i want to test this attached to a bunch of switches , leds' and displays. and analog stuff. The final function of this code a solar thermostat. it needs to monitor boundaries coming from analog world, timers telling it what to do when. it's all interrupt driven. emulating that on a pc is impossible.

As for stuck in 80's: The cpu is not the issue. The development environment is ! i can develop code for 8051 faster because i have proper tools and correctly working simulators/jtag probes. the Avr processor is not the issue, the environment is the problem.
in that aspect the Wokwi compiler is much more informative about typo's and bugs than the arduino ide. it really helped me finding the issues.



Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 610
Re: storing function pointer and calling
« Reply #44 on: June 04, 2021, 01:22:50 am »
Turn on -Werror, so you cannot proceed until any/all problems are resolved. There are no warnings that are ok, because they all have a solution. Also not a bad idea to turn on -Warray-bounds.

your code with -Werror
https://godbolt.org/z/5z1Grc71z

There is also a possibility your handlers may not be set, so only call them when not null unless you want to be back at the reset vector someday.

I have a number of links posted in this thread, so it should now be obvious I use that online compiler to test out ideas all the time (avr, cortex-m, pc).
Here is what I would do to figure out what is going on in function pointers for an avr-
https://godbolt.org/z/n8EvxPETT
You can see the asm output and figure out what is going on. If you do not know avr asm, you can learn as you go.

Probably easier than segfaulting you way through a pc version, or resetting your way on the avr. If you can understand the connection from your code to the asm output, that goes a long way to solving problems that may be from something that is unclear to you in the language. Maybe 5 minutes in the online compiler will make it clear what you get when you do x.
 
The following users thanked this post: free_electron

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #45 on: June 04, 2021, 01:59:45 am »
Turn on -Werror, so you cannot proceed until any/all problems are resolved. There are no warnings that are ok, because they all have a solution. Also not a bad idea to turn on -Warray-bounds.

your code with -Werror
https://godbolt.org/z/5z1Grc71z

the online compiler is nice but: it can't compile the real code. There are too many arduino specific functions and constants that it does not know. It's good for trying little things left and right but not really usefull for large code blocks. it is more a tool to see what the compiler produces and compare compiler output based on the flags.
That is the issue using something else than the actual system. Same reason i don't want to emulate on a pc.

For me the only acceptable solution is either a true simulator with watchpoints , breakpoints and hardware emulation , or an actual board with a jtag debugger.
I would prefer an emulator where you can step forward AND backward and do inline code edits without requiring recompilation. Have an immediate mode where you can try out simple statements, call functions, examine variables etc. like what you can do in visual basic.
You can develop code interactively and step forward, backward inject lines , alter data at will. That WOKWI allows me part of it. i can at least see what is going on. Just the GDB user interface is cranky. Too much keyboard pounding to examine variables. It needs a memory viewer, register viewer, stack viewer, watchpoints. variable watchpoints. kind of like DDD .. if Wokwi would be able to connect to DDD that would be a huge step forward to trace and analyse code. It would be even better if you had a debugger like visual studio. Stop the code hover mouse over variable and you can see the contents. click the variable and a input box comes up where you can alter the contents. Have an instruction pointer you can move. But then on these little microcontroller that would be hard. it's not like they can do JIT. At least interactively invoking a routine should be possible. Put the thing in break and simply go "print led(1);" and see what the return code is and be able to trace the function.
Those are simple , but extremely helpful to debug a block of code. interactively launch a routine and trace it out while being able to see and modify all internal variables used. The IAR/Keil  tools can do that. I woudl have thought that Arduino woudl have evolved to such capabilities by now.

i wonder if that PlatformIO system can do such things ? it is visual studio based. But then i think it does not have simulation abilities... but if it can do it via a JTAG connection then it would be golden.

At this point that Labcenter electronics VSM is starting to look attractive ... you can draw a schematic including parts like displays and whatnot , hook up virtual logic analyser and oscilloscope and actually run/trace the code. i need to look into that.
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 2250
  • Country: nz
  • Formerly SiFive, Samsung R&D
Re: storing function pointer and calling
« Reply #46 on: June 04, 2021, 02:39:04 am »
PlatformIO is not Visual Studio based.

It can use VSCode (a different, cross-platform, thing) as the IDE. It can use many other things too. Eclipse, for example. Or Atom.

It does debugging using JTAG, gdb and a GUI.
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #47 on: June 04, 2021, 05:19:01 am »
PlatformIO is not Visual Studio based.

It can use VSCode (a different, cross-platform, thing) as the IDE. It can use many other things too. Eclipse, for example. Or Atom.

It does debugging using JTAG, gdb and a GUI.
oh ? i saw in that video he was installing visual studio code and then installing platformio plugin. so i thought it was visual studio based.
i need to look into those things. i'm getting out of touch with what is out there.

I was just trying to help someone out with a proof of concept based on an arduino cause that is what they had laying around ( student in india...  ).

This microscheduler is something i have used many many times but written in PL/M on 8051 or Basic on various other processors ( Mikroe basic for pic, avr , arm  , Bascom avr , oshonsoft basic, swordfish basic for pic f18 ,  B4R  , Great Cow Basic , Annex on ESP32 and ESP8266

Porting it to Arduino C proved to be a painful learning curve. First because of the C compiler idiosyncrasies (tinkercad can't compile it at all , Arduino IDE compiles without error/warning but the binary doesn't work (i initially did not have an arduino around so i was banking on the simulators, this afternoon i went out and bought a board to try it on) , Wokwi finally let me trace it using GDB ) and second because of the bad simulators. I'm not a c wizard. i can read it , but writing it and knowing all the intimate details is not for me. (like knowing how to create a function pointer and then store that in a struct. A function pointer is nothing but an address , which in this case is a 16 bit number so it can be stored in an u_int16. i first attempted to simply store that as an integer but that didn't work. C deos't work that way.
in PL/M anything is passed by reference . whether variable or function.

if can do
Code: [Select]

Procedure Createdaemon (word interval , word *function_to_be _called)  // the * signifies what follows is not a variable but a function so it stores addressof
  table (1,1) = interval
  table(1,2) = function_to_be_called
end procedure

...
call table(1,2)  : calls the stored address
of course call table (1,1) would crash the program as that is a regular value. basically the 'call operator' moves the value stored at (1,1) onto the instruction pointer. if you put arguments , it performs push and pop operations on the stack
Code: [Select]
result = call table(1,2)(some_argument, some_other_argument)

compiles to
push some_other_argument
push some_argument
push ip+4 ' push return address
mov ip @table(1,2)  ' move content of table 1,2 to instruction pointer
pop result;

the code of the function is
[code]
pop return_address
pop arg1
pop arg2
..... do what needs doing
push result
mov ip return_address
as simple as can be.

It irritates me that a platform that is so popular is in such a poor state and so difficult to get started with. A little bit from here, a bit from there , futz it together , drop some more gobbledegook from somewhere else. I'm used to tools that just work. Launch setup and start coding. No need to puzzle it together.

Anyway, got it running, student understand the logic of a scheduler.
No more delay and sleep operations that block the CPU. you can create little tasks , set an interval and start/stop/pause/resume them and the tasks can stop themselves and react to emergency signals. that was the goal. non concurrent , cooperative multitasking micro threads on small microcontrollers.

The task was to write a small monitor for a water boiler.
if the boiler is on : blink an led at 1 second interval and count up two counters (one lifetime run time , one current run time)
display lifetime and current runtime on lcd
also display temperature (read from analog input)

so we have a simple function that , upon calling , toggles an i/o and increments 2 counters.
in the main program we simply control the runstate of that routine. the hardware timer does the rest.
basically the main program reads a digital input which directly controls the run state of the routine.
the main program then displays the data.

int temperature, totalrun, currentrun;
createdaemon (1,turn_off_all_leds)
forever :{
  DaemonState(counters) = digitalread(3)
  daemonstate(runled) = digitalread(3)
  daemonstate(overtemp) = (temperature >60)   // fire his as long as we are too hot  , sets the overtemp led to on
  daemonstate(toocold) = (temperature < 10)    // sets the toocold led to on
  daemonstate(frostprotection) = (temperature <1) // opens a bleedvalve
  lcdprint totalrun , currentrun,temperature
  if digitalread(3) = 0 then currentrun=0;
 }

since it runs on a hardware timer i don't care how long the lcd processing routine really takes. it will fire at exactly 1 second interval. as long as the called routines can all complete within the timer interval this program will run correctly. and 1 second is a very generous interval.
« Last Edit: June 04, 2021, 05:43:07 am by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 17420
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: storing function pointer and calling
« Reply #48 on: June 04, 2021, 06:39:34 am »
Some more lecturing/philosophizing -- if it helps:

- C isn't BASIC.  Why are you trying to use it as something it's not?

- Have you considered putting BASIC on it and just doing that?

Example:
https://hackaday.com/2011/08/28/basic-programming-on-an-arduino/
TinyBasic maybe too simple/trivial for your purposes, or doesn't expose enough (any) Arduino or hardware resources, fair.

Looks like there's a more fleshed out version here?  Or maybe it's pretty similar, Idunno.
https://github.com/robinhedwards/ArduinoBASIC
It's probably? easy enough to add wrapper functions around Arduino built-ins?  Doesn't exactly avoid working in C, in a project much more complicated than the original problem, of course...

I should note, it's classic BASIC, not QBASIC and the like, there's no SUB or FUNCTION in there.  So you have to define new keywords, by the looks of it, not just create your own subroutines/functions and call into them (whether declared in the program or preexisting in the environment).

If you can define your platform (AVR, ARM, which model?), there may also be a more bare-metal version available too.  Of course then you'll have to import Arduino libs entirely, YMMV.

- Since you're stuck with C (the above options notwithstanding?), and not especially well versed in it... why get frustrated at it?  It's a huge messy pile of grammar, syntax and logic.  I wouldn't expect to get very far in something like that, myself.  (I think I've taken a good month or a few at a time, at various stages through my life, learning the language.)  Take it slow, read up, find all the manuals, find relevant StackOverflow articles, the works.  Compilers are horrendously complex tools, and their documentation is only slightly less inscrutable.  Draw on a mix of resources until you're comfortable with how things are described (e.g. often in terms of other objects or relationships, which are described in terms of..).

- If you just want to get some stupid thing done, don't be afraid to give the specific example and ask about it.  (This is a bit redundant I suppose, as this has more or less already happened in this thread.)

- As for the actual problem, as has been more or less covered already -- as you can see, C doesn't really like not knowing what it's doing.  You need correct types at all times, or else you're just casting everything to void and praying...  Getting complex types right is  PITA, but it's still a bit better than the alternative.

As far as dynamic execution goes, I don't know that there's really a good or standard way to do that, and it'll be platform-dependent to some extent.  Like on AVR, you need to flash a payload to PROGMEM before you can execute it.  If you had an operating system, you could use whatever features it provides -- you might load a DLL on the PC, a surprisingly simple operation actually, from the program's perspective.  But even with an RTOS, you're likely calling static (defined at compile time) pointers, or not even that but just hard-wiring all the required function calls into the main() loop or whatever.

An architectural point: why make something code-dependent, like passing an actual (physical) pointer over serial?  Besides errors (one bit off and it's probably a crash), this exposes way too much implementation over the interface.  Nevermind possible vulnerabilities (an actual arbitrary-call op is a hacker's wet dream; is it even a zero-day if it's part of the spec!?).  A better option would be a function table (maybe this is what you were after in the first place and I've skimmed over that?) so you just say, "give me operation 12", instead of "execute memory location 0x0BADF00D".  With bounds checking of course, so you aren't executing pointers that don't exist.  Most importantly, each binary can then be updated by itself, without the other side having to know that.  In short, creating an API.

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 2250
  • Country: nz
  • Formerly SiFive, Samsung R&D
Re: storing function pointer and calling
« Reply #49 on: June 04, 2021, 06:51:45 am »
Porting it to Arduino C proved to be a painful learning curve. First because of the C compiler idiosyncrasies (tinkercad can't compile it at all , Arduino IDE compiles without error/warning but the binary doesn't work

C is a very very simple language. It maps directly to assembly language, with just the addition of a little syntax and a little type checking. But you can always cast a value to any other type of the same size if you know what it really is and the compiler doesn't. In particular casting between functions or pointers and integers is trivial and done all the time.

It would be well worth your time to learn it properly.

"Arduino C" is entirely standard gcc. The only difference is a preprocessor finds your global declarations and automatically prefixes your code with forward declarations for everything, so you can put your code in any order (like in Java and others) and it works. This doesn't affect standard C code.

Of course Arduino provides their own library to hide differences between different hardware, and to enable its use on machines with often only 2 KB of RAM and 32 KB of flash (and I've used it on AVRs with a quarter of that). A standard C printf() implementation would blow out the program ROM on many devices.

Quote
I'm not a c wizard. i can read it , but writing it and knowing all the intimate details is not for me. (like knowing how to create a function pointer and then store that in a struct. A function pointer is nothing but an address , which in this case is a 16 bit number so it can be stored in an u_int16. i first attempted to simply store that as an integer but that didn't work. C deos't work that way.

C is very simple, but you do have to actually read a manual, not just try to intuit it.

Quote
in PL/M anything is passed by reference . whether variable or function.

Ouch!

In C everything is passed by value, whether variable or function.

If you mention a function or array just by the name, rather than calling it or indexing it, then the value is a pointer to it.

Quote
if can do
Code: [Select]

Procedure Createdaemon (word interval , word *function_to_be _called)  // the * signifies what follows is not a variable but a function so it stores addressof
  table (1,1) = interval
  table(1,2) = function_to_be_called
end procedure

...
call table(1,2)  : calls the stored address

It's very little different in C.

Here's a complete example.

Code: [Select]
#include <stdio.h>
#include <inttypes.h>

typedef void (*fn_t)(void);

#define NDAEMONS 10
#define NDAEMON_ATTRS 2

intptr_t table[NDAEMONS][NDAEMON_ATTRS];

void CreateDaemon(int daemon, intptr_t interval, fn_t fn){
  table[daemon][0] = interval;
  table[daemon][1] = (intptr_t)fn;
}

void call(int daemon){
  ((fn_t)table[daemon][1])();
}

void foo(){
  printf("hi from daemon\n");
}

int main(){
  CreateDaemon(7, 13, foo);
  call(7);
  return 0;
}

Quote
of course call table (1,1) would crash the program as that is a regular value. basically the 'call operator' moves the value stored at (1,1) onto the instruction pointer. if you put arguments , it performs push and pop operations on the stack

Stack? Arguments on the stack?

None of the common Arduino CPU types do that. AVR, ARM, RISC-V ... all pass arguments in registers.

Here's the previous program compiled for 32 bit RISC-V...

Code: [Select]
00010106 <foo>:
   10106:       1141                    addi    sp,sp,-16
   10108:       c606                    sw      ra,12(sp)
   1010a:       6549                    lui     a0,0x12
   1010c:       50450513                addi    a0,a0,1284 # 12504 <__errno+0x8>
   10110:       2acd                    jal     10302 <puts>
   10112:       40b2                    lw      ra,12(sp)
   10114:       0141                    addi    sp,sp,16
   10116:       8082                    ret

00010118 <CreateDaemon>:
   10118:       00351793                slli    a5,a0,0x3
   1011c:       07418513                addi    a0,gp,116 # 13d9c <table>
   10120:       953e                    add     a0,a0,a5
   10122:       c10c                    sw      a1,0(a0)
   10124:       c150                    sw      a2,4(a0)
   10126:       8082                    ret

00010128 <call>:
   10128:       1141                    addi    sp,sp,-16
   1012a:       c606                    sw      ra,12(sp)
   1012c:       00351793                slli    a5,a0,0x3
   10130:       07418513                addi    a0,gp,116 # 13d9c <table>
   10134:       953e                    add     a0,a0,a5
   10136:       415c                    lw      a5,4(a0)
   10138:       9782                    jalr    a5
   1013a:       40b2                    lw      ra,12(sp)
   1013c:       0141                    addi    sp,sp,16
   1013e:       8082                    ret

00010140 <main>:
   10140:       1141                    addi    sp,sp,-16
   10142:       c606                    sw      ra,12(sp)
   10144:       6641                    lui     a2,0x10
   10146:       10660613                addi    a2,a2,262 # 10106 <foo>
   1014a:       45b5                    li      a1,13
   1014c:       451d                    li      a0,7
   1014e:       37e9                    jal     10118 <CreateDaemon>
   10150:       451d                    li      a0,7
   10152:       3fd9                    jal     10128 <call>
   10154:       4501                    li      a0,0
   10156:       40b2                    lw      ra,12(sp)
   10158:       0141                    addi    sp,sp,16
   1015a:       8082                    ret

Quote
It irritates me that a platform that is so popular is in such a poor state and so difficult to get started with. A little bit from here, a bit from there , futz it together , drop some more gobbledegook from somewhere else. I'm used to tools that just work. Launch setup and start coding. No need to puzzle it together.

Arduino is *extremely* easy to get started with. Literally millions of people have done so. Before Arduino microcontrollers were completely unapproachable.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 4123
  • Country: fi
Re: storing function pointer and calling
« Reply #50 on: June 04, 2021, 10:03:45 am »
gcc -Wall

Also you look and sound like a professional and are doing beyond-artist-beginner stuff like a simple scheduler. Don't use any "for artists" tools. Just say no to tinkercad and arduino. If you stick to C and basic GNU tools, you'll be able to learn most of the language and the tools in a matter of a few intensive months (or a few less intensive years).

Also do what nctnico said, when unsure, try things out on a PC. I have this habit of running

nano t.c; gcc -Wall t.c; ./a.out

and just write a few lines to test how uint16_t wraps, what is sizeof(something) or how that function pointer syntax worked again.

Or you can test your complete "logic" by printing things you want to output, and inputting things from command line arguments, and so on. The rest, what you can't simulate like this, leave it for testing on actual hardware.

If you want to see an actual LED blink, why not skip all unnecessary layers and test it on real hardware? Different methods go hand-in-hand, you can still test other parts on your PC much faster.
« Last Edit: June 04, 2021, 10:10:11 am by Siwastaja »
 

Online nctnico

  • Super Contributor
  • ***
  • Posts: 21259
  • Country: nl
    • NCT Developments
Re: storing function pointer and calling
« Reply #51 on: June 04, 2021, 10:07:59 am »
hindsight is always 20:20 ... no warnings or errors were thrown.
this is why i hate these kind if systems. you spend hours on a tiny little detail and the IDe is not very forthcoming with what is wrong.
That is why you develop on a PC first and then move to an embedded platform. On a PC you could have single stepped through the code and spot the problem right away. But you'd likely got a warning during compilation which hinted towards the problem. Instead you tried to run before you can walk.
no warnings during compilation. the arduino ide said : compilation complete and spat out a binary.

I don't like the your approach of having to emulate the i/o code. i want to see the led turn on and the display display what it needs. How do you emulate things like rotary encoders or hardware timers and interrupts ? or a control loop that interacts with the analog world ?
Model it and simulate. The hardware layer should be very thin so it is not prone to errors and when you put known-good code on top of it you know a problem is in the hardware. Divide and conquer otherwise you'll driving blind. I'd never start developing something like your code on an embedded platform. I want to test the sh#t out of it before committing it to a embedded platform. In some cases you can't even test with real hardware before having a certain degree of certainty the software does wreck the hardware. A while ago I made a controller for a system with extreme pressures. I have modelled the hardware and used simulations to get the control software right. As a bonus I can test & debug software without having the actual hardware available.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 
The following users thanked this post: free_electron

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #52 on: June 04, 2021, 01:22:39 pm »
- C isn't BASIC.  Why are you trying to use it as something it's not?
true, but that is not my point.

the logic principle remains the same, just a different language . i have coded this in several machine languages (6802, 6805 , 6809, 8051 ,) and higher level languages (pl/m , basic, pascal).
I'm not so much frustrated at c , as to the issues i ran into with the environment i was using.

Trying to debug code in an unfamiliar language based off good meaning suggestions from people on a forum. (thank the universe for at least that)

but ... let's see ... it compiles in tool A , but the binary doesn't work. It does not compile in tool B. It emulates in tool C but crashes randomly. Tool C has  a debugger but its user interface is a box of hammers and i'm trying to peel a banana.
Tools A, B and C all pretend to be toolchains for the same thing.
Other people suggest trying to run load it on a pig and see if it can walk there.

So what problem am i solving ? is it my understanding of the syntax of the language that is flawed ? is it a logic error in the program ? or is it the toolchain that is fucking up ? what am i trying to debug here ?
Then i get very cranky... like when using a scope probe with a bad contact in it... or worse, a scope that lies at random showing data that doesn't exist or is randomly time skewed between channels ( hello LeCrap ? fixed your Z7 logic analyzer module yet ?).

Quote
An architectural point: why make something code-dependent, like passing an actual (physical) pointer over serial?
1) fast (small payloads to transport)
2) the comms channel never leaves the board.
3) the functions do not return any values.

A simple (but silly) example. 1000 processors , all running the same binary. one broadcasts : execute address 0xdeadbeef. why do you need a lookup table ? instead of saying " go look in the phone book for johns smiths number and call him ". i simply give you the number.
That function performs startdaemon(1) and exits. the attached handler blinks an led at 1 second interval.
My packets can all be a standard fixed size format. Example 1 start byte, 2 bytes address to execute and 1 byte for a possible argument. The receiver routine is very simple. No need for lookup tables or any other processing. Put the argument in a global variable and perform a hard call to the given address. Also for the sender it is easy. He knows what will be called , fetches the address of the function and sends the same address to its siblings and they all do the same.
such a system can run on very memory restricted machines. like small processors with only 1k rom and 32 bytes ram. you don't have much wiggle room to start creating a high level api , you are going to bust the code memory.

Think about those art installations that british guy does Mike Mike mike ..... mikes electric stuff ? , with all the leds. that sort of thing. but then cheaper :) one cheap processor per led, distributed system and all that jazz.
transport packet could be 2 bytes target , 2 bytes opcode (address to execute) and 2 bytes optional arguments. if target is 00 that is a broadcast. if MSB is set : that is now a group number stuff along those lines. the transport code is extremely small.

i come from a time where microcontrollers had 4k rom and 64 bytes ram. simple does it. you can't spend 2k of overhead code on a transport protocol.

Today they run a python interpreter on a virtual processor architecture in a virtual machine on a processor with a totally different architecture in an emulated environment under a hypervisor. Communicate between boards uses a HTTP post over ethernet broadcasts  and pass json packaged payloads to each other ... to turn on an led. And you wonder why it takes 3 seconds between pushing the button and the light turning on... if it turns on at all. Sometimes it turns on all by itself. Sometimes , someone in china futzes with a server and your house becomes a christmas tree, or worse. They switch off the server cause they milked all the money they could, and now you can throw away all that hardware.(sonos anyone ? roku soundbridge anyone? bueller ? bueller ? )

Over the years i find myself growing more and more cranky at all these things. Stuff is too complicated for simple things. And most the stuff out there is only half baked. Nobody understands how anything works anymore.
And don't give me that open source sauce. What was the latest big open sauce stink we had a couple of months ago ?  Some bug that went undetected for 5 years. Doesn't anyone read the code ? It's there after all for the world to see.
Maybe that's the problem. We suffer from bookcase syndrome : i have a book case full of books. i've never read them ... it looks impressive to an outsider, i can just show off how knowledgeable i am by the amount of paper i posses... like that diploma on to the wall.

Apple has a great one in their M1 processor... oh it's super safe with physically isolated cores. Except somebody figured out there are two status bits that are shared between the cores (all cores can read and write these bits, they are basically semaphores used for bus access or something) . Some smartass implemented something similar to the i2c protocol over those two bits, one is used as a clock , the other as data. They wrote a small driver and they can transport data between processes that should be isolated . The entire system security sank over 2 fucking "flifplops". And it can't be resolved because the two bits are needed for housekeeping.
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #53 on: June 04, 2021, 01:50:11 pm »
hindsight is always 20:20 ... no warnings or errors were thrown.
this is why i hate these kind if systems. you spend hours on a tiny little detail and the IDe is not very forthcoming with what is wrong.
That is why you develop on a PC first and then move to an embedded platform. On a PC you could have single stepped through the code and spot the problem right away. But you'd likely got a warning during compilation which hinted towards the problem. Instead you tried to run before you can walk.
no warnings during compilation. the arduino ide said : compilation complete and spat out a binary.

I don't like the your approach of having to emulate the i/o code. i want to see the led turn on and the display display what it needs. How do you emulate things like rotary encoders or hardware timers and interrupts ? or a control loop that interacts with the analog world ?
Model it and simulate. The hardware layer should be very thin so it is not prone to errors and when you put known-good code on top of it you know a problem is in the hardware. Divide and conquer otherwise you'll driving blind. I'd never start developing something like your code on an embedded platform. I want to test the sh#t out of it before committing it to a embedded platform. In some cases you can't even test with real hardware before having a certain degree of certainty the software does wreck the hardware. A while ago I made a controller for a system with extreme pressures. I have modelled the hardware and used simulations to get the control software right. As a bonus I can test & debug software without having the actual hardware available.
That's a different use case. i would do that too. Like a processor controlling a 20 kilowatt inverter. There is no way you debug that on the board. You don't go "let's put a breakpoint right after we switched on the mosfets in the H-bridge and see what happens."

This case ? show a student how a simple scheduler can be created quickly using simple and cheap stuff she has laying around. It's not easy in india to get your hands on real hardware. There's no digikey ( there is but it costs an arm and a leg ).
So i thought, hey, i've done this many times, let me write this for arduino. It's only a few lines of code... how hard can it be ? i've done it on many platforms in many languages. And arduino is so simple it can be used by artists, so this will be a doddle. After all it's nothing but a simple 8 bit machine with an ide and framework used by millions. Should be easy.

Apparently much harder cause i was trying to do too many things at the same time :

- code it in an unfamiliar language
- struggle with different compilers ,  (some compile it, but it does nothing ; some do not compile it ,giving error, some cannot handle the language )
- struggle with simulators. Some simulate it wrong; some simulate it right but it doesn't work because of a logic error. )
- not having a real board in front of me and be stuck with the above.

So what was i trying to debug ? syntax error ? logic error ? compiler error ? or simulator error ? or a combination of all...
Now i understand why most arduino enthusiasts never make it past blinking an led.

It's Like learning french from a badly cross translated dictionary, trying to talk to a guy that only speaks "patois". His neighbour speaks creole so he's useless as well. They both are 'basically' french but different enough it doesn't work. Meanwhile some canadian is trying to be helpful telling you to add 'eh' to the end of the sentence.
We're not moving forward very fast here. it's not "allez vite vite, sur votre bicyclette et roulez !"  (quickly, get on your bike and go. that idiom is not translateable)

Next time someone mentions arduino i will just walk away. Give them one of my working sources (available for many processors and languages, just not c ) and they can port it. Let them figure out if it is the arduino compiler/simulator/debugger that does not cooperate or their code.

But then ... they are family... you can't really do that.

Anyway, thanks to all for the help. even though i sound cranky , it is appreciated. got there in the end , but not an exercise i am looking to do ever again. (unless i can't weasel out of it , for like .. family ..)
« Last Edit: June 04, 2021, 01:52:55 pm by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #54 on: June 04, 2021, 01:58:52 pm »
For the record : THANKS TO ALL ! it is appreciated ! Got there in the end.

i may sound cranky and that's cause i am. Cranky cause there is nothing worse than trying to get something working with stuff you can't trust works right.
Same crankiness if you have a bad contact in your scope probe. that sort of thing.
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline agehall

  • Frequent Contributor
  • **
  • Posts: 306
  • Country: se
Re: storing function pointer and calling
« Reply #55 on: June 04, 2021, 04:41:00 pm »
it is supposed to be c++
neither typedef nor using works.

And this is why I stay far away from Arduino if at all possible if we are talking anything more complicated than blinking an LED on a breadboard.

PlatformIO+VSCode and bare metal is so much easier since you have control over what is going on. And if you need to, use libraries that are not tied to Arduino to speed up the development.
 

Offline T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 17420
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: storing function pointer and calling
« Reply #56 on: June 04, 2021, 08:52:53 pm »
but ... let's see ... it compiles in tool A , but the binary doesn't work. It does not compile in tool B. It emulates in tool C but crashes randomly. Tool C has  a debugger but its user interface is a box of hammers and i'm trying to peel a banana.
Tools A, B and C all pretend to be toolchains for the same thing.
Other people suggest trying to run load it on a pig and see if it can walk there.

I can't speak for the tools you're using (or, trying to); I would suggest looking at the backend.  Likely the GUI is just a layer on top and it's using something in the background.  Arduino?  GCC in a hidden process.  Tinkercad?  No idea, maybe it's GCC and actually emulating a CPU, more likely (from what others have said here) it's something much higher level, and therefore you should beware of shortcuts and inconsistencies.  Find what it really is and see what its capabilities are.

Nothing worse than an underspecified tool.  You don't go rummaging around someone's toolbox and pick up the swiss-army-knife-looking grinder that's also a drill, sander, exfoliator and cocktail mixer, and start using it without understanding what each of those functions does.

Well.. you wouldn't ordinarily, but such is the "adventure" of using these, "artist" tools as you would say... :D


Quote
1) fast (small payloads to transport)
2) the comms channel never leaves the board.
3) the functions do not return any values.

Well-- there's probably only a few dozen, even a few hundred, pointers; a byte index would suffice.  Compression! ;D

And if there really are approx. 64k options to choose from, well for one thing your pointers won't be 64k anymore, you'll have to use a >128k AVR with long pointers to hold even a fraction of that many functions; but more importantly, you're probably better off mixing and matching those operations in a much more free-form manner, for example transmitting bytecode of a scripting language.  Maybe it's just a sequence of ops, maybe it's a proper conditional language, maybe it's a whole-ass VM.

Put another way: it goes against the principle of least data.  Why do you need 64k options when there will only be a few hundred, or thousand even?  It's not fail-safe over that set.  Use only what you need.  Same as bandwidth in analog circuits, filter off what you don't need, and you're saved some noise, immunity, etc.

And what about when a bit error does finally happen, and the call does suddenly return a lot of values? -- a missing bit, it jumps into the preamble, and now the stack is garbage.  (avr-gcc normally makes nearly bare functions when they are simple, but when more than some registers are required, it will push them onto the stack.  Or if a lot is needed, a block is allocated on the stack -- including anything that needs pointer access, like local scoped arrays.)  Impossible to debug, you'll never catch the error in action, it'll just randomly crash some day.  Quality design! :P

Alternately, could add some verification -- checking against a known list for example (reminiscent of "COMEFROM" in certain languages*), or using ECC or CRC or hash or what have you; which is a good idea on a serial link anyway, even on board.

Or make sure that every operation can fail, with no consequence, and be restarted with relative ease.  Enable watchdog so it always restarts in a known state.  Blinking lights is fail safe, who cares.  Running machinery, though, I sincerely hope you use a different approach!


*Not... that one.  Not sure what else has it, but the example I remember reading about is Etherium smart contracts; it's a VM, which has to be secure of course.  It has branch instructions, but will only branch to COMEFROM tokens.


Quote
Think about those art installations that british guy does Mike Mike mike ..... mikes electric stuff ? , with all the leds. that sort of thing. but then cheaper :) one cheap processor per led, distributed system and all that jazz.
transport packet could be 2 bytes target , 2 bytes opcode (address to execute) and 2 bytes optional arguments. if target is 00 that is a broadcast. if MSB is set : that is now a group number stuff along those lines. the transport code is extremely small.

I think Mike does more networking oriented solutions, passing command or data packets sort of thing.  Though I don't recall if he's gone into much detail over the comms he likes to use (understandable, that sort of stuff may be proprietary, and is very boring as video content goes) so if he's watching maybe he can offer some ideas here...

The higher level solution really does get useful though.  Once everything is enumerated with unique addresses, just send a packet with a header "addressed to mr. 2304" and pass it along until received.  Best of all, numerous solutions already exist -- stick it on Modbus for example.

Or hook up an Ethernet PHY and do like Profinet or Ethernet IP, or just plain old TCP/IP.  Leverage cheap cables and switches to extend your network as far as you like!  It's a heavy weight protocol to be sure, but it's popular for good reason!


Quote
i come from a time where microcontrollers had 4k rom and 64 bytes ram. simple does it. you can't spend 2k of overhead code on a transport protocol.

Ah, self-flagellation is a hard habit to break!

You're more than welcome to plop assembly into your .ino sketch, if you must... I'm sure Tinkercad would savor every character of that... :-DD


Quote
Today they run a python interpreter on a virtual processor architecture in a virtual machine on a processor with a totally different architecture in an emulated environment under a hypervisor. Communicate between boards uses a HTTP post over ethernet broadcasts  and pass json packaged payloads to each other ... to turn on an led. And you wonder why it takes 3 seconds between pushing the button and the light turning on... if it turns on at all. Sometimes it turns on all by itself. Sometimes , someone in china futzes with a server and your house becomes a christmas tree, or worse. They switch off the server cause they milked all the money they could, and now you can throw away all that hardware.(sonos anyone ? roku soundbridge anyone? bueller ? bueller ? )

You say that like it's a bad thing!

rPi's are popular for good reason -- they have everything you need, they're a whole PC on a tiny board, plus it's got the low level crap like GPIO, I2C and SPI.  You can access all of it from the shell, no programming needed!

You can pipe it over Ethernet if you like, and control it anywhere in the world.  If you're smart, you'll even turn on TLS so you don't have China flicking your light switches. :)

Delays? Unreliable communications?  These aren't problems with the system per se, they're problems with the fact that you're opening up such powerful connections.  Sometimes the internet is slow, sometimes packets get dropped.  A basic PoC might not include mitigations for those events, so it looks janky as hell.  Especially just for blinking lights.  But that's not the limit of what you can do, it's the simplest possible beginning.

Python is heavyweight, sure, but it's unimaginably powerful, if all you know is tiny constrained machines.  You can effortlessly log and trace whole comm channels, or program executions, or emulate a zillion other systems and do the same with all of them.  You can manage a zillion connected LED-blinkers and alert when any one has a frequency variance.

And best of all, we can learn lessons from developing on such systems -- by having such tools, and such computational excess, available, we can literally throw everything at the wall and see what sticks.  Then decide what parts we want to include in some project and strip it down and optimize it for a cheaper platform, like say an STM32 or something.  Which even then, STM32F4+ can run Linux, so you still have a lot of power available on such platforms.  Maybe you can even bring some such services down to an AVR.

Like there's graphical canvas for Arduino, needs more than your basic AVR but it does indeed fit into just a few k of RAM (8k+ I think?).  That's a very powerful graphical environment, something we take for granted on PCs but can still harness on even such simple systems.  Is it really the best tool to use for building an HMI or whatever?  Depends.  It's going to be relatively slow.  But then, no graphics are going to be real-time on AVR, it just doesn't have that much bandwidth.  Would you sacrifice smooth animations for rapid development?  A lot of people do!


Quote
Over the years i find myself growing more and more cranky at all these things. Stuff is too complicated for simple things. And most the stuff out there is only half baked. Nobody understands how anything works anymore.

I used to feel that way, but I've gotten used to the fact that, despite all the jankiness, the leaky abstractions, the inscrutability -- not only is it still possible to do work in such environments, but it's possible to push the envelope of what used to be possible, to bootstrap new and better things into existence.

And if you want to backport those inventions to earlier systems, that's the incredible value of highly portable languages (like C, "if it exists it has a C compiler" just about), you can do that.  And sure, portability is a lie, but it's a soft lie.  Well written code abstracts core functionality away from hardware interfaces, so you only need to patch the gaps around the edges.  Poorly written code, that's tightly integrated with the hardware, or highly optimized to it, is poorly portable.  Which is one of the pitfalls of constrained systems, they're simply harder to build upon.  We'd get nowhere if we had to integrate every new piece of hardware with every single thing we write, that's why we have operating systems, drivers and multiple levels of abstraction.  It's slower in strict terms, but it's far, far more productive.  Transistors are essentially free, don't worry about the computational burden.

There are better things than DOS (or other shell / CLI environment).  There are better things than mouse or touch GUIs.  And most of all, none of those things lose their usefulness in light of each latest advance -- we aren't required to use just one, all the time.  "Better" works both ways, forwards and backwards across the history of user interfaces; there is no true perfect interface mode.  Being able to select interfaces appropriate for tasks, is the true best meta-interface as it were.

That's the thing about technology: the more we move forward, the more we leave behind in our wake -- sometimes yes, in the standard meaning of "left behind", discarded to history -- vacuum tubes are nearly such an example, horses for transportation another.  But also just more plainly, left there in our wake, sitting there, available for use -- and indeed more often than not, continuing to be useful.  Like, I don't think CD4000 logic is ever going away, nor 8-bit micros (or even 4-bit for that matter).  32-bit micros are easily as cheap and plentiful (heh, well, this year excepted..), but there will always be a place for both.  Those places may become more obscure and special over time (e.g. on-chip state machines for specific applications), but they aren't going away completely.  (Tubes haven't gone away, though that doesn't happen to be for any technical reason -- we can quite fully emulate their characteristics of course.  It's more of a...human thing.  And hey, relay computers haven't gone away either, entirely for human reasons again, AFAIK.)


Quote
And don't give me that open source sauce. What was the latest big open sauce stink we had a couple of months ago ?  Some bug that went undetected for 5 years. Doesn't anyone read the code ? It's there after all for the world to see.
Maybe that's the problem. We suffer from bookcase syndrome : i have a book case full of books. i've never read them ... it looks impressive to an outsider, i can just show off how knowledgeable i am by the amount of paper i posses... like that diploma on to the wall.

I will however give a more cynical opinion on FOSS... (Compared to my preceding paragraph, I mean.)  Small projects are, take it or leave it -- the author made it to satisfy some pet problem, and that's that.  Bug fixes, expanded features, just not being a pile of janky interfaces -- what's that?

It's only in the big projects, with huge numbers of users, strong donations, lots of volunteers -- that you get to a scale where things work.  Linux, Firefox, Chromium, etc. might be held up as shining jewels of the FOSS ecosystem, but literally millions of other projects languish in some state between these two extremes.  You get what you pay for, basically, and the same is true of commercial software just as well, and more broadly, products in general.

Everything has bugs; "software" is a verb: it simply changes over time as bug fixes are incorporated, and new (buggy) features are added in turn.  Only trivially simple projects are done, top to bottom, fully considered, with no bugs whatsoever; or they're done by prodigies of the art.  (Knuth's projects come to mind.  Mind, LaTeX for example is janky as hell, but at least it's not buggy.  Though it's also set up so that, when he passes away, it's no longer a bug, but a feature not necessarily written into the documentation.  So there's that.)

We are, slowly, pushing the envelope of what is provable, what is computable -- correctness proofs are something that's expanded into a number of areas, and we have a few languages and hardware designs actually formally proven.  Among informal tools, every time a new exploit or pattern is found, its analysis can be applied to existing codebases or binaries to find new bugs (or rather, the same bugs in new places).  Software keeps getting piled on top, true, but the foundations are also becoming stronger and stronger.  Nobody's broken hard crypto (...that we know of, and other than using plain brute force, which will always be an option), all the exploits have been in the tools written around the core functionality.  (Or poorly written cores, that failed the spec in various and often subtle ways.  Sometimes hardware-dependent ways, like with caching...)


Quote
Apple has a great one in their M1 processor... oh it's super safe with physically isolated cores. Except somebody figured out there are two status bits that are shared between the cores (all cores can read and write these bits, they are basically semaphores used for bus access or something) . Some smartass implemented something similar to the i2c protocol over those two bits, one is used as a clock , the other as data. They wrote a small driver and they can transport data between processes that should be isolated . The entire system security sank over 2 fucking "flifplops". And it can't be resolved because the two bits are needed for housekeeping.

Yeah, that's a neat one... :-DD

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline IanB

  • Super Contributor
  • ***
  • Posts: 9939
  • Country: us
Re: storing function pointer and calling
« Reply #57 on: June 04, 2021, 09:03:17 pm »
I think Mike does more networking oriented solutions, passing command or data packets sort of thing.  Though I don't recall if he's gone into much detail over the comms he likes to use (understandable, that sort of stuff may be proprietary, and is very boring as video content goes) so if he's watching maybe he can offer some ideas here...

If I recall correctly, I think I have seen videos where Mike talks about using RS-485 to network things together.
I'm not an EE--what am I doing here?
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #58 on: June 04, 2021, 09:17:59 pm »
I think Mike does more networking oriented solutions, passing command or data packets sort of thing.  Though I don't recall if he's gone into much detail over the comms he likes to use (understandable, that sort of stuff may be proprietary, and is very boring as video content goes) so if he's watching maybe he can offer some ideas here...

If I recall correctly, I think I have seen videos where Mike talks about using RS-485 to network things together.
which is .. basically a serial port over current loop. possibly modbus.
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 17420
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: storing function pointer and calling
« Reply #59 on: June 04, 2021, 09:33:44 pm »
Yeah I recall he's mentioned RS-485 before.  The comm hardware doesn't matter much... note that RS-485 doesn't even say how you use bits, just what the levels of those bits shall be.  Let alone how you should manage multi-mastering.  It's the protocol that's "draw the rest of the fucking owl" here... ;D

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #60 on: June 04, 2021, 09:39:32 pm »
I can't speak for the tools you're using (or, trying to); I would suggest looking at the backend


yes and no. this was supposed to be a half hour project. and i can't be arsed digging in the underpinnings of these tools. I never dig into how keil works , or the firmware of my oscilloscope.
I don't have time nor desire for that. it should work. period. if i buy a microwave it works. i don't need to check of the magnetron is compatible with the transformer used. Same thing for devtools. My screwdriver works. my scope works. my IDE should also work.

Quote
Well-- there's probably only a few dozen, even a few hundred, pointers; a byte index would suffice.  Compression! ;D

LOL, yes compression but 40 lines of code to compress and decompress and some ram.

Quote
Quality design! :P
my board layouts do not suffer from signal integrity problems  >:D There is no such thing as noise , ESD and comic radiation events.  ;D . just like that coronavirus. doesn't exist.

Quote
Ah, self-flagellation is a hard habit to break!
nope ,  a matter of KISS. keep it simple. no need for billions of lines of overhead nobody understands.

Quote
You say that like it's a bad thing!
it is ! it's full of holes, trouble and untraceable. turning on an LED need wading through 50 billion lines of code when it (doesn't always) work.

Quote
rPi's are popular for good reason -- they have everything you need,

They are also power hungry, overheat frequently , have piss poor designed PoE shields ... i do not want to see a house with every lightbulb and power socket having a rpi attached to it... the vampire drain will be astronomical.

Quote
And best of all, we can learn lessons from developing on such systems
let's first learn how to crawl before we sit down in the cockpit of a hypersonic jet ... I too frequently encounter people that don't know you can make an AND gate with only NOR's ... they need a simulator to check that and will write one if needed.

Quote
- philosophical paragaphs -

Sometimes all you need is a nail. And sometimes all you have is a nail. And that should be fine too.
Forcing people to buy an expensive , need to read manual , screw driving machine and using tape fed screws is not right. not everyone can afford that. or build one from open source plans. or have time for that. Picture, meet nail . Nail, meet wall. -BAM-

On the other hand , i expect the hammer to work.... if you are going to give me a hammer where the head flies of the handle at the first strike ... prepare for a load of vitriol. If i miss the nail and strike my thumb , that's on me. if the head flies off : someone will get tarred and feathered over that. i'm too old to deal with such crap.

I needed to turn an arduino screw. unfortunately the quality of the arduino compatible screwdrivers is such that they break, bend , or have such tolerances they plainly don't fit and you spend hours mucking to find one, or modify one,  than can sort of get the job done.
« Last Edit: June 04, 2021, 09:47:06 pm by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 3462
  • Country: us
Re: storing function pointer and calling
« Reply #61 on: June 04, 2021, 11:12:42 pm »
Quote
no warnings during compilation.
Did you turn on warnings in the Arduino "Preferences" panel?  You can...

(not that the Arduino IDE will make it clear that warnings have occurred - they're certain to have scrolled out of the ~3-line window that they give you into the compiler output, and you might have to hunt for them, and then copy&paste them into something else so that you can actually read what they say..)

 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 610
Re: storing function pointer and calling
« Reply #62 on: June 05, 2021, 08:59:16 am »
Quote
Did you turn on warnings in the Arduino "Preferences" panel?  You can..
Would help if one pays attention to the output, but warnings scrolling by in your little 10 line high output window probably not a big help when you see the last 10 lines showing you the compiler is all happy.

The 'Default' setting would already be showing the warnings he was producing (not sure if 'Default' is default, sounds like it should be). What would be helpful is to get -Werror into the options, but does not appear to be a way other than finding/changing a settings file. I guess the arduino creators are happy if the arduino users make it to the end of the compile process, including warnings. That works if arduino user is aware that warnings are not really a good thing to ignore, and seeks them out in the output window.
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #63 on: June 05, 2021, 02:36:09 pm »
Quote
no warnings during compilation.
Did you turn on warnings in the Arduino "Preferences" panel?  You can...

(not that the Arduino IDE will make it clear that warnings have occurred - they're certain to have scrolled out of the ~3-line window that they give you into the compiler output, and you might have to hunt for them, and then copy&paste them into something else so that you can actually read what they say..)
the compiler report showed compilation complete , code size, ram size and it spat out a binary. i am (was) using the web based ide.

i am using wokwi right now as it has GDB integration and allows me to graphically simulate what the thing is doing.
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf