Calling function in response to receiving serial data

Hello,

I am trying to decipher some code that I've received, and I'm wondering if there is a way call a function at run-time in response to receiving serial data? (i.e., Upon compiling, serial Monitor says "Enter a valid function to call", and I want to send it a function to "execute" said desired function).

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

In particular, the code includes a setup(), loop(), checkserial(), readserial() functions, and then a set of functions that I'd like to "execute," called a(), b(), and c().

How can I go about executing, for example, function a()? Is it possible to send a command to the Serial Monitor to achieve such a result?

Yes. Check function pointers, for example, but you can do only a simple switch/case.

Hi myom

It would be useful to see the whole program, but looking at the extract you posted ...

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

Caller is being defined as the type "pointer to function". FuncCall is then declared as an array of these pointers, and is initialised to point to the three functions a(), b() and c().

The last statement suggests that the program will look for the characters a, b or c to be received to decide which function to execute. You could use any characters; it is the position in the array func_list[] which makes the connection with the functions pointed to by FuncCall[].

Regards

Ray

Hackscribble:
Hi myom

It would be useful to see the whole program, but looking at the extract you posted ...

typedef void (* Caller)();

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




`Caller` is being defined as the type "pointer to function". `FuncCall` is then declared as an array of these pointers, and is initialised to point to the three functions `a()`, `b()` and `c()`. 

The last statement suggests that the program will look for the characters a, b or c to be received to decide which function to execute. You could use any characters; it is the position in the array `func_list[]` which makes the connection with the functions pointed to by `FuncCall[]`.

Regards

Ray

Thanks Ray. Here is a chunk of the program regarding functionA. I am trying to execute "functionA," and I'm wondering what string(s) I need to send the serial monitor to get that to happen.

float change_temp = 10.0;
float temp = 70.0;
boolean running = false;
boolean paused = false;
boolean power = false;
float start_time = 0.0;
float pause_start = 0.0;
float pause_time = 0.0;
unsigned long change_time = 10;
unsigned long ONSTART;
typedef void (* Caller)();
Caller FuncCall[] = {functionA, functionB, functionC};
String func_list[] = {"functionA","functionB", "functionC"}; //correspond to functions in FuncCall, order matters.

void functionA(){
          if(!power){
            start();}
          float target_temp = 70.0;
          power = true;
          Serial.println("starting 'functionA'");
          if(!running){
            Serial.println("Couldn't start. Error powering up");
          return;}
            
          start_time = float(millis());
          
          float start_temp = CheckTemp();
          if(start_temp < 0){
            running = false;
            Serial.println("Couldn't start. Error taking temp.");
            return;
          }
          
          while(running){
            float tick = millis();
            //*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*> CHECK TEMP, TEC CONTROL <*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<
            target_temp = (start_temp - (change_temp * ((tick-start_time-pause_time)/(change_time))));
            Serial.print("TARGET >>>>>>>>>>>>>>>>>>>>>>>> ");
            Serial.println(target_temp);
            temp = CheckTemp();
            if(temp < 0 ){return;}
            ONSTART = millis();
            while(temp > (target_temp + bound)){
              digitalWrite(TEC, HIGH);
              temp =  CheckTemp();
              if(temp < 0){return;}
              if(millis() > ONSTART + TIMEOUT){
                Serial.println("Temp timeout. TEC module defective.");
                running = false;
                finish();
                return;}
            }
            digitalWrite(TEC, LOW);
            //*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*> CHECK TEMP, TEC CONTROL <*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<
            
            
            //*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*> SERIAL CHECKS, FUNCTION CALLS <*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<
            if(CheckSerial()){//check for new commands
            int call = -1;
            call = ReadSerial();
            if(call >= 0){
              FuncCall[call]();
              }
            }
                 
            if(paused){//if new call was to pause
              pause_start = millis();
              boolean hold = holding();
            while(hold){
              hold = holding();
              if(CheckSerial()){//check serial and call function if necessary
                  int call = -1;
                  call = ReadSerial();
                  if(call == (1) ){
                  FuncCall[call]();
                  }
                }
              }//end while holding
           }//end pause
           //*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*> SERIAL CHECKS, FUNCTION CALLS <*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<
            xSerial.print(dataTag);
            xSerial.print(delimeter);
            xSerial.print(millis());
            xSerial.print(delimeter);
            xSerial.print(temp);
            xSerial.print(endSig);
            
           if(temp <= (start_temp-change_temp) || millis() > (start_time + change_time + pause_time )){
             running = false;
             finish();
             
           } 
          }//end while running
}//END FUNCTIONA

The code you need to look for is somewhere else in the program.

Look for where something is being read from serial port and being compared with the elements of func_list[].

String func_list[] = {"functionA","functionB", "functionC"}; //correspond to functions in FuncCall, order matters.

But I would guess you need to enter "functionA".

EDIT
Look for a function called ReadSerial.

            if(CheckSerial()){//check for new commands
            int call = -1;
            call = ReadSerial();
            if(call >= 0){
              FuncCall[call]();
              }
            }

Yep, it does seem that I need to enter "functionA," but it doesn't seem to do anything. I believe that this may be the result of an error in my circuitry (because this code is supposed to work). But then an error message should appear..

Here is the function ReadSerial

int ReadSerial(){
        char inData[30] = {};
        int index = 0;
        Serial.println("Got Data...");
        delay(5);
        while(xSerial.available()>0){
          inData[index] = xSerial.read();
          if(inData[index] == '\n'){
            inData[index] = '\0';
            while(xSerial.read()>=0){}
          }
          else{
          index++;
          inData[index] = '\0';
        }
        }
        String together = "";
        for(int j = 0; j < (index + 1); j++){
          if(inData[j] == '\0'){}
          else{
            together += inData[j];
          }
        }
        for(int k = 0; k < sizeof(func_list); k++){
          if((together.equals(func_list[k]))){
            Serial.println("Data match!");
            return k;
          }
        }
       return -1;
  
  
}//END READSERIAL

Do you see any output from this statement in ReadSerial()?

Serial.println("Got Data...");

That would indicate this function is being called, at least.

Presumably you do not see output from this?

Serial.println("Data match!");

Could you post your complete program. I'm assuming that ReadSerial() is called somewhere outside of functionA() etc, otherwise no function will be executed to start things off.

I do not see those Got Data / Data matched outputs..
Note: i've given functions A, B, and C names

#include <math.h>
#include <SoftwareSerial.h>

const int therm = A0;//thermistor
const int VDD_GOOD = A1;//logic voltage check
const int VM_GOOD = A3;//motor voltage check
const int VD = 10;//digital 5V for logic level and thermistor
const int VM = 9;//Power 12V for TEC
const int TEC = 11;//TEC power mosfet
const char delimeter = ',';
const String msgTag = "m";//to write messages to tablet
const String dataTag = "s";//to write data to tablet
const String endSig = "\n";//to end a transmission to tablet
const float bound = .3;// +/- overshoot on temp
const float A = .001129148; //thermistor constants
const float B = .000234125;
const float C = .0000000876741;
const float R = 9890.0; // Resistor value used in thermistor circuit
const float Temp_Limit[] = {48.0,90.0};
const float TIMEOUT = 10000.0;// Timeout for TEC when it's trying too hard and not getting anywhere.
SoftwareSerial xSerial(3,4); //Rx, Tx
int userData[20] = {};

float change_temp = 10.0;
float temp = 70.0;
boolean running = false;
boolean paused = false;
boolean power = false;
float start_time = 0.0;
float pause_start = 0.0;
float pause_time = 0.0;
unsigned long change_time = 10;
unsigned long ONSTART;
typedef void (* Caller)();
Caller FuncCall[] = {rash, pause, start};
String func_list[] = {"rash","pause", "start"}; //correspond to functions in FuncCall, order matters.


void setup(){
    for(int i = 8; i<13; i++){
    pinMode(i,OUTPUT);
    digitalWrite(i,LOW);
    delay(2);
    }

  Serial.begin(9600);
  xSerial.begin(9600);
  delay(10);
  
  
  TCCR1B = TCCR1B & 0xF8 | 0x02;//set pins 9&10 to 122.55Hz
                         //0x05;//set to 30.64 Hz
                         //0x04;//set pins 9&10 to 122.55Hz
                         //0x03;//set to 490.2 Hz
                         //0x02;//set to 3921.16 Hz
                         //0x01;//set to 31372.55 Hz
                         
                        
  Serial.println("Enter a valid func to call");
}//END SETUP


void loop(){
  int call = -1;
  if(CheckSerial()){
    call = ReadSerial();
    Serial.println(call);
    if(call >= 0){
      FuncCall[call]();}
    else{}
  }
   delay(9);
}//END LOOP

boolean CheckSerial(){ //check and see if the user tried to do something
        if(xSerial.available()>0){
            return true;
        }//end if
        else{
          return false;
        }//end else
}//END CHECKSERIAL

int ReadSerial(){
        char inData[30] = {};
        int index = 0;
        Serial.println("Got Data...");
        delay(5);
        while(xSerial.available()>0){
          inData[index] = xSerial.read();
          if(inData[index] == '\n'){
            inData[index] = '\0';
            while(xSerial.read()>=0){}
          }
          else{
          index++;
          inData[index] = '\0';
        }
        }
        String together = "";
        for(int j = 0; j < (index + 1); j++){
          if(inData[j] == '\0'){}
          else{
            together += inData[j];
          }
        }
        for(int k = 0; k < sizeof(func_list); k++){
          if((together.equals(func_list[k]))){
            Serial.println("Data match!");
            return k;
          }
        }
       return -1;
  
  
}//END READSERIAL

Edit: I added an error check to void loop(), and it seems that the first if(CheckSerial()) loop doesn't run through.. any ideas?

Part 2:

void rash(){
          if(!power){
            start();}
          float target_temp = 70.0;
          power = true;
          Serial.println("starting 'rash'");
          if(!running){
            Serial.println("Couldn't start. Error powering up");
          return;}
            
          start_time = float(millis());
          
          float start_temp = CheckTemp();
          if(start_temp < 0){
            running = false;
            Serial.println("Couldn't start. Error taking temp.");
            return;
          }
          
          while(running){
            float tick = millis();
            //*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*> CHECK TEMP, TEC CONTROL <*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<
            target_temp = (start_temp - (change_temp * ((tick-start_time-pause_time)/(change_time))));
            Serial.print("TARGET >>>>>>>>>>>>>>>>>>>>>>>> ");
            Serial.println(target_temp);
            temp = CheckTemp();
            if(temp < 0 ){return;}
            ONSTART = millis();
            while(temp > (target_temp + bound)){
              digitalWrite(TEC, HIGH);
              temp =  CheckTemp();
              if(temp < 0){return;}
              if(millis() > ONSTART + TIMEOUT){
                Serial.println("Temp timeout. TEC module defective.");
                running = false;
                finish();
                return;}
            }
            digitalWrite(TEC, LOW);
            //*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*> CHECK TEMP, TEC CONTROL <*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<
            
            
            //*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*> SERIAL CHECKS, FUNCTION CALLS <*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<
            if(CheckSerial()){//check for new commands
            int call = -1;
            call = ReadSerial();
            if(call >= 0){
              FuncCall[call]();
              }
            }
                 
            if(paused){//if new call was to pause
              pause_start = millis();
              boolean hold = holding();
            while(hold){
              hold = holding();
              if(CheckSerial()){//check serial and call function if necessary
                  int call = -1;
                  call = ReadSerial();
                  if(call == (1) ){
                  FuncCall[call]();
                  }
                }
              }//end while holding
           }//end pause
           //*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*>*> SERIAL CHECKS, FUNCTION CALLS <*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<*<
            xSerial.print(dataTag);
            xSerial.print(delimeter);
            xSerial.print(millis());
            xSerial.print(delimeter);
            xSerial.print(temp);
            xSerial.print(endSig);
            
           if(temp <= (start_temp-change_temp) || millis() > (start_time + change_time + pause_time )){
             running = false;
             finish();
             
           } 
          }//end while running
}//END RASH


void pause(){
            paused = !paused;
}


boolean holding(){
  
            float hold_temp = CheckTemp();
            
            if((temp > (hold_temp + bound )) && paused){//if too warm and paused 
                                                  
            while(temp > (hold_temp - bound) && paused){    
                digitalWrite(TEC,HIGH);
                temp = CheckTemp();
                 if(CheckSerial()){//check serial and call function if necessary
                    int call = -1;
                    call = ReadSerial();
                    if(call == (1) ){
                    digitalWrite(TEC, LOW);
                    FuncCall[call]();
                    }
                  }
            }//end while
            digitalWrite(TEC, LOW);
          }
        
            else if(paused){
              return true;}
              
            else{
              pause_time += (millis() - pause_start);
              return false;}
  
}//END HOLDING




float CheckTemp(){
            temp = analogRead(therm);
            temp = R * (( 1/(temp/(1023*(4.78/5)))) - 1);
            temp = 1/(A + (B * log(temp)) + (C * (pow(log(temp),3))));//in Kelvin
            temp = ((temp - 273.15) * 1.8) + 32; // in Farenheit
            
            if(temp < Temp_Limit[0] || temp > Temp_Limit[1]){
              Serial.println("Temp out of bounds.");
              Serial.println("If this keeps occuring, check module wires.");
              digitalWrite(TEC,LOW);
              finish();
              return -1;}
            else{
              Serial.println(temp);
            return temp;}                 
  }//END CHECKTEMP
  
void start(){//wait for/take simulation parameters
            power = !power;
            if(power){//we're turning on.
            int h = 0;
            Serial.println("here in start...");
            xSerial.print(msgTag);
            xSerial.print(delimeter);
            xSerial.print("Enter runtime, change temp: (time,Temp)");
            xSerial.print(endSig);
            while(!xSerial.available()){
            }
            delay(10);
            while(xSerial.available()>0){
            userData[h] = xSerial.parseInt();
            h++;
            
            if(xSerial.read() == '\n'){
              while(xSerial.read()>=0){}
            }
            delay(5);
              }
              change_time = float(userData[0])*1000;
              Serial.println(change_time);
              change_temp = float(userData[1]);
              Serial.println(change_temp);
              if(change_temp <= 0 || change_time <= 0){
                  power = false;
                  running = false;
                  finish();
                  return;}
              xSerial.print(msgTag);
              xSerial.print(delimeter);
              xSerial.print("starting up");
              xSerial.print(endSig);
              
              Serial.println("Turning on Digital Power...");
              digitalWrite(VD,HIGH);
              unsigned long timer = millis();
              while(!(digitalRead(VDD_GOOD))){
              Serial.println("...");
              delay(500);
              if(millis() > timer + 5000){
              Serial.println("Timeout occured. Check power in.");
              power = false;
              running = false;
              finish();
              return;}
              }
              Serial.println("Digital Power Good.");
//              delay(250);
//              Serial.println("Turning on Motor Power...");
//              timer = millis();
//              while(!(digitalRead(VM_GOOD))){
//              Serial.println("...");
//              delay(500);
//              if(millis() > timer + 5000){
//              Serial.println("Timeout occured. Check power in.");
//              power = false;
//              running = false;
//              finish();
//              return;}
//              }
//              Serial.println("Motor Power Good.");
              delay(250);
              running = true;
              rash();
            }
            else{
              Serial.println("Powering down...");
              running = false;
              power = false;
              finish();
            
            }
}//END START


void finish(){
            digitalWrite(TEC,LOW);
            xSerial.print(msgTag);
            xSerial.print(delimeter);  
            xSerial.println("Wait for cooldown...");
            xSerial.print(endSig);
            delay((.25 * change_time)/(.01 * change_temp)); // (W[gen by TEC] * time)/(dx therm. Si * 2W/mK * temp chng)
            while(Serial.read()>=0){}
            Serial.println("cooled down.");
            Serial.println("Enter a valid func to call");
            power = false;
            return;
}//END FINISH

Your problem is here:

    if(call >= 0){
      FuncCall[call]();}
    else{}

With the this:

    call = ReadSerial();

is easy call some function, like:

switch (call) {
   case 1: 
      rash();
      break;
   case 2:
      pause();
      break;
   case 3:
      start();
      break;
}

luisilva:
Your problem is here:

    if(call >= 0){

FuncCallcall;}
   else{}




With the this:


call = ReadSerial();




is easy call some function, like:

Actually, it fails to even reach that line of code; it doesn't run through the if(CheckSerial()) within void loop()

luisilva:

switch (call) {

case 1:
      rash();
      break;
  case 2:
      pause();
      break;
  case 3:
      start();
      break;
}

Where are you suggesting I use this switch-case?

CheckSerial() and ReadSerial() are expecting commands to arrive on the software serial connection, rather than from the serial monitor. What do you have connected on pins 3 and 4?

Hackscribble:
CheckSerial() and ReadSerial() are expecting commands to arrive on the software serial connection, rather than from the serial monitor. What do you have connected on pins 3 and 4?

Could you clarify what exactly that means? I currently have nothing connected on pins 3 and 4.

SoftwareSerial xSerial(3,4); //Rx, Tx
boolean CheckSerial(){ //check and see if the user tried to do something
        if(xSerial.available()>0){
...
int ReadSerial(){
...
        Serial.println("Got Data...");
...        while(xSerial.available()>0){
          inData[index] = xSerial.read();
...

Thanks, I see where the issue lies. I'm confused as to which might belong in pins 3 and 4.

I'm wondering if perhaps you could take a look at a wiring diagram and see which inputs on my circuit belong in which pins on my arduino board (based on the comments in the code and the below diagram).

I'm pretty certain about all the inputs on the right (they are connected to the analog in pins), but a little confused about the bottom row. (Output to TEC(+) / Return from TEC(-) is a sensor so they won't be going anywhere). I was told the control signal belongs in pin 11. That leaves control ground, power ground, and power positive; Why would any of these be going to pins 3 and 4?

I can't see anything that looks like serial connections in the photo.

What is odd is that all the prompts and status messages go to serial monitor, but the commands are read from software serial.

Try changing the references to xSerial to be Serial and see if you can control the board solely from serial monitor.

Ohh I see why xSerial was used.. this was intended to be controlled by a tablet, but I am currently controlling through the serial monitor.

Hackscribble:
Try changing the references to xSerial to be Serial and see if you can control the board solely from serial monitor.

That worked (but I didn't change SoftwareSerial xSerial(3,4); //Rx, Tx because of an error.)

I'm wondering if you might know which pins the "Power ground" and "Power positive" leads connect to, considering that I already have +5V feeding the thermistor component. Or rather, how can I power the TEC in addition to the Thermistor?

const int VD = 10;//digital 5V for logic level and thermistor
const int VM = 9;//Power 12V for TEC
const int TEC = 11;//TEC power mosfet

The comments may be inaccurate, but it suggests that you need a 12V supply for the TEC and somewhere in the code this is being controlled. Are there any references to VM or TEC later in the program?

Hackscribble:

const int VD = 10;//digital 5V for logic level and thermistor

const int VM = 9;//Power 12V for TEC
const int TEC = 11;//TEC power mosfet




The comments may be inaccurate, but it suggests that you need a 12V supply for the TEC and somewhere in the code this is being controlled. Are there any references to VM or TEC later in the program?

VM is not referenced, but TEC is.

myom:

luisilva:
Your problem is here:

    if(call >= 0){

FuncCallcall;}
    else{}




With the this:


call = ReadSerial();




is easy call some function, like:

Actually, it fails to even reach that line of code; it doesn't run through the if(CheckSerial()) within void loop()

luisilva:

switch (call) {

case 1:
      rash();
      break;
  case 2:
      pause();
      break;
  case 3:
      start();
      break;
}

Where are you suggesting I use this switch-case?

I'm telling you to use the switch case instead of the if that you have in your original code:

    if(call >= 0){
      FuncCall[call]();}
    else{}

Before you read the serial command and you know what you mus call, that is stored in the call variable, you must put the switch/case that I give you.
Look it this way: To the Arduino, after the compiling process the word "rash" don't will mean anything. If you want to call this function like you are trying to call it:

FuncCall[call]()

you must provide it the address here the function is stored in the code memory, and not this way.