Go Down

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

Aaronkoe

so this is as close as i have gotten. It actually works and the proper drink is selected, (if i start from 0) but i can't get it to stop looping. I need it to re-prompt for the next drink.

Code: [Select]
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() {
     
getSelection();            //Accept multiple digits function
showNewData();                  //Display Input on Serial

char buffer[2];
buffer[0] = receivedChars[0];
buffer[1] = receivedChars[1];

int drinkSelection;
drinkSelection = atoi(buffer);

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,
};
recipes[drinkSelection]();
delay(2000);

}



void getSelection() {
    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;
    }
}

sterretje

#16
Mar 21, 2017, 04:12 am Last Edit: Mar 21, 2017, 04:13 am by sterretje
You need to have a nul-terminated buffer
Code: [Select]
  char buffer[3];
  buffer[0] = receivedChars[0];
  buffer[1] = receivedChars[1];
  buffer[2] = '\0';

  int drinkSelection;
  drinkSelection = atoi(buffer);

Or even better, directly convert the receivedChars (without using the additional buffer) as indicated by @boolrules
Code: [Select]
  int drinkSelection;
  drinkSelection = atoi(receivedChars);


The latter also has the advantage that it does not limit you to 99 (if you ever need that).
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

can you tell me what is it about this loop that wont end?

Code: [Select]
void loop() {
  lcd.clear();
  lcd.setCursor(7,1);           //Greeting on LCD
  lcd.print("Hello");
  lcd.setCursor(3,2);
  lcd.print("Select a drink");

  Serial.println("Hello");      //Greeting on Serial
  delay(500);
  Serial.println(" ");
  Serial.println("Please enter your drink selection number");
  Serial.println(" ");
  delay(500);
   
getSelection();            //Accept multiple digits function
showNewData();                  //Display Input on Serial

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,
};
  int drinkSelection = 0;
  drinkSelection = atoi(receivedChars);
recipes[drinkSelection-1]();                  //Start drinks at #1
delay(500);

}



void getSelection() {
    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;
    }
}

GoForSmoke

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
You could make one general function that reads and acts on stored stepper and motor data. The data can be stored in flash memory if it can fit -- on an UNO there is 32K flash and your sketch probably won't use 10K.

Serial reads one char at a time. If the drinks are chosen by letter-number combinations you can show 3 columns (1, 2, and 3) of 20 rows ( A through T ) to get A1 to A3, B1 to B3, etc. This will provide a crude error check on entry, first must be a letter A-T and next must be a number 1-3 and newline must follow (set Serial Monitor to add newline to every entry).

A simple state machine can track user entries, do you know state machines at all? The first state waits for a letter, the next waits for a number and the next waits for newline. Any deviation puts the state back to the first.
The last state is for when the others are all good, then the letter-number is turned into where the drink data is in flash (PROGMEM) and that fed to the single make-drink function.

If you have an SD card adapter, the data and drink name list can be stored there instead of flash. Changes to the drinks then becomes change the SD file, no need to ever change the sketch again once debugged.
Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

PaulS

Why is the array of function pointers local to loop()? You do not save any SRAM doing that. Make the array global, so you are not cluttering loop() up with irrelevant data.

Why does getSelection() not return a value?

Making getSelection() return an int, and the array of function pointers global, loop() then looks like this:
Code: [Select]
void loop() {
  lcd.clear();
  lcd.setCursor(7,1);           //Greeting on LCD
  lcd.print("Hello");
  lcd.setCursor(3,2);
  lcd.print("Select a drink");

  Serial.println("Hello");      //Greeting on Serial
  delay(500);
  Serial.println(" ");
  Serial.println("Please enter your drink selection number");
  Serial.println(" ");
  delay(500);
   
  int drinkSelection = getSelection();            //Accept multiple digits function

  recipes[drinkSelection-1]();                  //Start drinks at #1
  delay(500);
}


Now, since the whole purpose of loop() is to loop, I can't understand why you are confused when the code loops.

getSelection() is not a blocking function, although it appears as though you think that it is. That is, you seem to think that getSelection() waits for serial data to arrive. It does not.

If you want it to wait, you need to make changes to getSelection().

If you want the waiting to happen in loop(), then you need to pay attention to the value in newData, which tells you whether or not you can use the data collected by getSelection().

Of course, you could make no changes to getSelection() or to loop() to make either one block. But, then, you would convert receivedChars to an int, and call the function whose pointer is in the array, ONLY when newData is true.
The art of getting good answers lies in asking good questions.

Aaronkoe

Why is the array of function pointers local to loop()? You do not save any SRAM doing that. Make the array global, so you are not cluttering loop() up with irrelevant data.
If i put my array of functions before or inside void setup() it complains that each item in the array is not declared.

if i put the array of functions after the void loop() it complains that 'recipes' was not declared.

I'm sure there is a better way but i finally got it all working late last night. This is what i came up with and i really appreciate all the help.

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 char setupArray = 32;
char input[setupArray];   // an array to store the received data
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
};
  
void loop() {

    lcd.clear();
  lcd.setCursor(7,1);           //Greeting on LCD
  lcd.print("Hello");
  lcd.setCursor(3,2);
  lcd.print("Select a drink");
  
convertByte();                  //Accept multiple digits function
displayByte();                  //Display Input on Serial
 
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,
};
  
   int drinkSelection = 0;
   drinkSelection = atoi(input);
 
    if (drinkSelection > 0 & drinkSelection <= sizeof(recipes) / sizeof(recipes[0]))
    {
        recipes[drinkSelection - 1]();      // execute recipe
        delay(2000);
      }
    
    // no valid recipe number received
    else
    {
    Serial.println("Please enter a valid drink number");
    Serial.println(" ");
    delay(200);
      while (Serial.available()==0) { }
      }
  
  drinkSelection = 0;                           //Reset variables after drink made
  input[0] = 0;
  const char setupArray = 0;
  
} // End of loop

char convertByte() {
    static byte ndx = 0;
    char endMarker = '\n';
    char rc = 0;

   // while (Serial.available()==0) { }      
    while (Serial.available() > 0 && newData == false) {
 
        rc = Serial.read();

        if (rc != endMarker) {
            input[ndx] = rc;
            ndx++;
            if (ndx >= setupArray) {
                ndx = setupArray - 1;
            }
        }
        else {
            input[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
    }
}
void displayByte() {
    if (newData == true) {
        Serial.println(input);
        Serial.println(" ");
        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");
  Serial.println(" ");
  lcd.clear();
  lcd.setCursor(3,1);
  lcd.print("You have chosen");
  lcd.setCursor(5,2);
  lcd.print("Adios Sucka");
  delay(4000);
  //Motor Movements Here
...

GoForSmoke

#21
Mar 21, 2017, 08:06 pm Last Edit: Mar 21, 2017, 08:07 pm by GoForSmoke
You can store pointers in flash but it would be easier and faster to put them in a switch-case as function calls.

It would be smarter to store the 'recipes' as data that drives a state machine.
Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

sterretje

If i put my array of functions before or inside void setup() it complains that each item in the array is not declared.

if i put the array of functions after the void loop() it complains that 'recipes' was not declared.
See below
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.
So
Code: [Select]
...
...

void recipeOne()
{
  ...
  ...
}

void recipeOne()
{
  ...
  ...
}

void (*recipes[])() = {
  ...
  ...
};

...
...

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

void loop()
{
  ...
  ...
}

or using forward declarations
Code: [Select]
...
...

void recipeOne();
void recipeTwo();

void (*recipes[])() = {
  ...
  ...
};


...
...

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

void loop()
{
  ...
  ...
}


void recipeOne()
{
  ...
  ...
}

void recipeOne()
{
  ...
  ...
}




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.

Go Up