Go Down

Topic: Calling functions through serial? (Read 371 times) previous topic - next topic

Aaronkoe

I am making a bunch of functions , ill call them "recipes" that are a different combination of stepper and dc motor movements.

How could i write each of these recipes as their own function, and then have the serial monitor waiting for me to call one of those functions to begin the stepper and dc motor movements?

BTW, i am making a robot bartender.


THANK YOU

saximus

Will you be using a PC based serial monitor like the one in the IDE? For simplicity, I would probably have it print a "menu" to the screen in table form with a single input letter corresponding to each drink e.g.:

Code: [Select]
Rum and Coke           r
Shirley Temple         s
Vodka and Orange       v
etc...


That would simplify your code a little bit because you only need to make your decision based on a single character.

In Loop() you would just need to do a Serial.read() and call your recipe functions from there.

sterretje

Code: [Select]

void loop()
{

  char recipeNumber = 0;

  if(Serial.available())
  {
    recipeNumber = Serial.read();
  }

  switch (recipeNumber)
  {
    case '1':
      recipeOne();
      break;
    case '2':
      recipeTwo();
      break;
    case 'A':
      recipeA();
      break;

  }

  recipeNumber = 0;

}

This will be a very basic sketch that reacts on a single character received on the serial port. You can very well imagine that this switch becomes massively big when you have more than e.g. 10 recipes.

In that case you can make use of an array of function pointers to select a recipe. You probably also want to read Serial Input Basics - updated to handle more complex serial input (e.g. 2 or 3 digit recipe numbers).
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

Aaronkoe

Thank you very much for the assistance.  Is there any limit to the switch? I'm looking at 60+ recipes.

Im using a Mega to give me enough memory so that shouldn't be a problem

sterretje

It's just difficult to maintain with 60 recipes. Use of an array function pointers is far cleaner.

Write your functions (just give them better names than the ones below).
Code: [Select]
void recipe1()
{
  Serial.println("recipe 1");
}

void recipe2()
{
  Serial.println("recipe 2");
}

void recipe5()
{
  Serial.println("recipe 5");
}

Create an array of function pointers
Code: [Select]
// an array of function pointers
void (*recipes[])() =
{
  recipe1,
  recipe2,
  NULL,
  NULL,
  recipe5,
};

Note that the sequence is important; the recipes need to be known before you can add them to the array. So you can either define them before the array (as done above) or use a forward declaration.

And you can basically execute the function by using
Code: [Select]
void loop()
{
  int recipeNumber = -1;
  if (Serial.available() > 0)
  {
    recipeNumber = Serial.read() - '0';

    /*
      subtract 1 from the recipe number to get the index in the array
    */
    recipes[recipeNumber - 1]();
    delay(1000);
  }
}

Nice, clean and small

However, you have to make sure that a recipe is implemented (value in array not NULL) and that you have a valid index. That will nearly triple the number of lines but validation always requires a lot of code.
Code: [Select]
void loop()
{
  int recipeNumber = -1;
  if (Serial.available() > 0)
  {
    recipeNumber = Serial.read() - '0';

    // check if it's valid (1 .. number of recipes in arrray)
    if (recipeNumber > 0 && recipeNumber <= sizeof(recipes) / sizeof(recipes[0]))
    {
      /*
        subtract 1 from the recipe number to get the index in the array
      */

      // if no function specified
      if (recipes[recipeNumber - 1] == NULL)
      {
        Serial.println("recipe not implemented yet");
      }
      else
      {
        // execute recipe
        recipes[recipeNumber - 1]();
        delay(1000);
      }
    }
    // no valid recipe number received
    else
    {
      Serial.println("unknown recipe");
    }
  }
}

The only thing left for you is to get a proper implementation of the reading of the serial port to get the number (see the thread that I linked earlier) so you can enter multiple digits. You also might want to add some confirmation of the selected recipe before brewing it ;)
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

Aaronkoe

that was very helpful sterretje, thank you

Aaronkoe

#6
Mar 20, 2017, 08:48 pm Last Edit: Mar 20, 2017, 08:54 pm by Aaronkoe
okay so after adjusting my code for your suggestions, i get a weird result.

If i enter 1-9 ...  works great


if i enter for example,  25  i get recipe 2 and recipe 5

any thoughts?

Groove

Quote
Only registering the first digit. Thoughts?
Fix, or post, your code.
Per Arduino ad Astra

saximus

Sterretje's code only reads one digit. Have a look at the link he posted formhow to deal with multi digit inputs

LarryD

#9
Mar 20, 2017, 09:05 pm Last Edit: Mar 20, 2017, 09:06 pm by LarryD
You could use Serial.parseInt() to convert 2 received characters, ex: 02 or 45.
https://www.arduino.cc/en/Reference/parseInt 

Then use the int in your switch.
.
No technical PMs.
The last thing you did is where you should start looking.

Aaronkoe

This is my code. The error is trying to reference my recipe array with my receivedChars array. I can accept multiple digits through serial monitor now but i cannot use that number to reference my array.

Code: [Select]
#include <AccelStepper.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"

LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 20 chars and 4 line display

Adafruit_MotorShield AFMStop(0x60);  //0000 No Jumpers Closed
//Adafruit_MotorShield AFMSmid(0x61);  //0001 First Jumper Closed
//Adafruit_MotorShield AFMSbot(0x62);  //0010 Second Jumper Closed


/* Steppers can use Port (1) (M1, M2) and Port (2) (M3, M4)
 * DC Motors can use Port (1) -> Port (4) (M1, M2, M3, M4)
 */

//Connect two steppers to the top shield
Adafruit_StepperMotor *stepper1 = AFMStop.getStepper(200, 1);
Adafruit_StepperMotor *stepper2 = AFMStop.getStepper(200, 2);

//Connect one stepper & 2 motors to the middle shield
//Adafruit_StepperMotor *stepper3 = AFMSmid.getStepper(200, 1);
//Adafruit_DCMotor        *motor1 = AFMSmid.getMotor(3);
//Adafruit_DCMotor      *motor2 = AFMSmid.getMotor(4);         

//Connect four DC motors to the bottom shield
//Adafruit_DCMotor *motor3 = AFMSbot.getMotor(1);           
//Adafruit_DCMotor *motor4 = AFMSbot.getMotor(2);
//Adafruit_DCMotor *motor5 = AFMSbot.getMotor(3);
//Adafruit_DCMotor *motor6 = AFMSbot.getMotor(4);

const byte numChars = 32;
char receivedChars[numChars];   // an array to store the received data
int recipeNumber = 0;

boolean newData = false;

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

  lcd.init();                      // initialize the lcd
  lcd.backlight();                 // turn on backlight
 
  //AFMSbot.begin();              //Start the bottom shield
 // AFMSmid.begin();              //Start the middle shield
  AFMStop.begin();              //Start the top shield

  Serial.println("Please enter drink selection");
  Serial.println(" ");
}

void loop() {


void (*recipes[])() ={
adiosSucka,
alpineLemonade,
baileysMadras,
bayBreeze,
blackWidow,
blueCosmo,
blueLagoon,
blueMoon,
blueShark,
carolinaBlueWhale,
chelseaSidecar,
countryClubLemonade, 
deathValleySunrise,
dosAmigosChicorita,
electricTea,
ghostGoblet,
ginAndSin,
hawaiianCocktail,
holeInOne,
hoolaHoop,
judgeJr,
ladysGentlemen,       
littleDevil,
longIsland,
maidensBlush,
margarita,
margaritaLaBamba,       
orangeCrush,
quicky,
rumCoke,
screwdriver,
soylentGreen,
tequilaSunrise,       
texasTea,
TGV,
theAbyss,
theOllie,
whiskeySeven,
whiskeySour,   
wyooterHooter,
punch,
rumPunch,
};

     
recvWithEndMarker();            //Accept multiple digits function
showNewData();



recipes[receivedChars]();
delay(1000);
}

void recvWithEndMarker() {
    static byte ndx = 0;
    char endMarker = '\n';
    char rc;
   
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            receivedChars[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
    }
}

void showNewData() {
    if (newData == true) {
        Serial.println(receivedChars);
        newData = false;
    }
}

// DRINK MENU
//(Drink#) Drink Name [(Stepper Ingredients) (Pump Ingredients)]

//(1) Adios Sucka [(30mL Vodka, Gin, Rum, Tequila, Curacao; 60mL Sprite) (60mL Sour)]
void adiosSucka()
{ Serial.println("You have Chosen Adios Sucka");
  //Motor Movements Here
}

//(2) Alpine Lemonade [(30mL Vodka, Gin, Rum) (60mL Cranberry, Lemonade)]
void alpineLemonade()
{ Serial.println("You have chosen Alpine Lemonade");
  //Motor Movements Here
}

//(3) Bailey's Madras [(60mL Tequila) (30mL Cranberry, Orange, Pineapple)]
void baileysMadras()
{ Serial.println("You have chosen Bailey's Madras");
  //Motor Movements Here
}

//(4) Bay Breeze [(60mL Vodka) (60mL Cranberry, Pineapple)]
void bayBreeze()
{ Serial.println("You have chosen Bay Breeze");
  //Motor Movements Here
}

//(5) Black Widow [(60mL Vodka) (60mL Cranberry, Lemonade)]
void blackWidow()
{ Serial.println("You have chosen Black Widow");
  //Motor Movements Here
}

//(6) Blue Cosmo [(60mL Vodka, Curacao) (60mL Cranberry)]
void blueCosmo()
{Serial.println("You have chosen Blue Cosmo");
  //Motor Movements Here
}

//(7) Blue Lagoon [(60mL Vodka, Curacao) (60mL Lemonade)]
void blueLagoon()
{Serial.println("You have chosen Blue Lagoon");
  //Motor Movements Here
}

//(8) Blue Moon [(90mL Gin; 30mL Curacao) (null)]
void blueMoon()
{Serial.println("You have chosen Blue Moon");
  //Motor Movements Here
}

//(9) Blue Shark [(60mL Vodka; 30mL Tequila, Curacao) (null)]
void blueShark()
{Serial.println("You have chosen Blue Shark");
  //Motor Movements Here
}

//(10) Carolina Blue Whale [(60 mL Rum; 30mL Curacao) (90mL Pineapple)]
void carolinaBlueWhale()
{Serial.println("You have chosen Carolina Blue Whale");
  //Motor Movements Here
}
...

boolrules

Where do you convert receivedChars[ ] into an integer?

Aaronkoe

Where do you convert receivedChars[ ] into an integer?
something like this?

Code: [Select]
char buffer[2];
buffer[0] = receivedChars[0];
buffer[1] = receivedChars[1];

int drinkSelection;
drinkSelection = atoi(buffer);

recipes[drinkSelection]();

LarryD

#13
Mar 21, 2017, 03:05 am Last Edit: Mar 21, 2017, 03:07 am by LarryD
What happens if: buffer[0] was '4' and buffer[1] was '0' and buffer[2] was '4' and buffer[3] was 0?
What would drinkSelection =
No technical PMs.
The last thing you did is where you should start looking.

boolrules

Aaronkoe --- Why are you creating a new buffer? Especially one that is not NULL terminated?
                            Just give atoi receivedChars.

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy