Returning values from Sub functions

Can someone tell me why this code will not work, and how I can fix it? The goal of the sketch is to turn 2 LEDs on and off based on the input to the serial monitor. It works if I include the code in function captureChar() in the void loop. But if I separate and simply call it instead, it does not work. I'm not confident with returning values from a function, and I guess that's where my error is. My serial inputs are strings that look like this: "00" or "11" or "01" or "10".


String readString, mot1, mot2;

void setup() {
  Serial.begin(9600);
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
}

int captureChar(int n1,int n2) {

  while (Serial.available()) {
    delay(3);  //delay to allow buffer to fill 
    if (Serial.available() >0) {
      char c = Serial.read();  //gets one byte from serial buffer
      readString += c; //makes the string readString
    } 
  }

  if (readString.length() >0) {
      Serial.println(readString); //see what was received
      
      // expect a string like 01 containing the LED status     
      mot1 = readString.substring(0,1); //get the first character
      mot2 = readString.substring(1,2); //get the second character 
      n1 = mot1.toInt();
      n2 = mot2.toInt();
    readString="";
      return(n1,n2);
    
  } 
}

void loop(){
  int led1, led2;
  captureChar(led1,led2);
  digitalWrite(13,led1);
  digitalWrite(12,led2);
}

You can only return a single value.

If you need to return more, you can return them in a struct, or via pointers or references.

...and you need to assign the returned value to a variable

I have to admit I haven't worked with structs/pointers/references. What would the code to do this look like?

Via reference

void captureChar(int& n1,int& n2) {

Via pointer

void captureChar(int* n1, int* n2) {

I suggest using reference, then all you need to do is remove the "return"

1 Like

@rayk05
1. Since return statement delivers only one value to the calling program, you can declare led1 and led2 as global variables and then assign their values in the called function. Also, you can bring some simplication in your codes with respect to receiving characters from the InputBox of the Serial Monitor.

Your sketch with slight revision (tested on UNO):

String readString, mot1, mot2;
int led1, led2;

void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  digitalWrite(13, LOW);
  digitalWrite(12, LOW);
}

void captureChar() //(int n1, int n2) Edit
{

  /* while (Serial.available())
    {
     delay(3);  //delay to allow buffer to fill
     if (Serial.available() > 0)
     {
       char c = Serial.read();  //gets one byte from serial buffer
       readString += c; //makes the string readString
     }
    }*/

  byte n = Serial.available();
  if (n == 2)
  {
    readString = (char)Serial.read();//1 = 0x31
    readString += (char)Serial.read();//1 = 0x31

    // if (readString.length() > 0)
    //{
    Serial.println(readString); //see what was received 11

    // expect a string like 01 containing the LED status
    mot1 = readString.substring(0, 1); //get the first character
    mot2 = readString.substring(1, 2 ); //get the second character
    led1 = mot1.toInt(); //1
    led2 = mot2.toInt(); //1
    readString = "";
    // return (n1, n2);
  }
}


void loop()
{
  //int led1, led2;
  captureChar();   //(led1, led2); Edit
  digitalWrite(13, led1);
  digitalWrite(12, led2);
}

2. You can use structure type data as shown below to return values for led1 and led2 (tested on UNO).

String readString, mot1, mot2;
//int led1, led2;

struct myTag
{
  int led1, led2;
};

struct myTag led;

void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  digitalWrite(13, LOW);
  digitalWrite(12, LOW);
}

myTag captureChar()
{

  /* while (Serial.available())
    {
     delay(3);  //delay to allow buffer to fill
     if (Serial.available() > 0)
     {
       char c = Serial.read();  //gets one byte from serial buffer
       readString += c; //makes the string readString
     }
    }*/

  byte n = Serial.available();
  if (n == 2)
  {
    readString = (char)Serial.read();//1 = 0x31
    readString += (char)Serial.read();//1 = 0x31

    // if (readString.length() > 0)
    //{
    Serial.println(readString); //see what was received 11

    // expect a string like 01 containing the LED status
    mot1 = readString.substring(0, 1); //get the first character
    mot2 = readString.substring(1, 2 ); //get the second character
    led.led1 = mot1.toInt(); //1
    led.led2 = mot2.toInt(); //1
    readString = "";
    return (led);
  }
}

void loop()
{
  //int led1, led2;
  led = captureChar();
  digitalWrite(13, led.led1);
  digitalWrite(12, led.led2);
}

{Facepalm}

The called function is not returning any value to the calling program; so, the return type should be void. This is my understanding.

And what about n1 and n2?

These are the formal parameters to which the actual paramteers are being copied.

And used for precisely . . . what?

have a look at function parameters pass by value and pass by reference

You mean that the called function should have the following codes instead:

    n1 = mot1.toInt(); //1
    n2 = mot2.toInt(); //1
    led1 = n1;
    led2 = n2;

No, certainly not

int readString[2];

void setup() {
  Serial.begin(9600);
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
}

bool captureChar() {
  if (Serial.available()) {
    delay(3);  //delay to allow buffer to fill
    int c = Serial.read();  //gets one byte from serial buffer
    if (c >= '0' && c <= '1') {
      int x = c - '0';
      c = Serial.read();
      if (c >= '0' && c <= '1') {
        readString[0] = x;
        readString[1] = c - '0';
        while(Serial.available()) Serial.read();//flush buffer
        return true;
      }
    }
    while(Serial.available()) Serial.read();//flush buffer
  }
  return false;
}

void loop(){
  if(captureChar()) {
    digitalWrite(13,readString[0]);
    digitalWrite(12,readString[1]);
  }
}

Then, let me do the followings which are simple as led1 and led2 are global:

void captureChar()
{

And also:

captureChar();

Why are you flushing the serial buffer? Two characters have arrived and you have read them already. Now, the buffer is empty.

In case in the 3 ms more were sent than 2. for a hobby project, it is probably optional.

Thank you for all the input everyone. @anon73444976 's suggestion to use references worked out for me. To be honest, this sketch was for me to test this method to see if I could use it as a button, and I chose to do it with an LED. I have another sketch where I am using this bit of code. This other sketch is used to smooth out the motion of a servo motor in a simple way and it uses a button to do so. However, moving forward into my project, my button is replaced by a virtual one, which I have set up to send "1" when pushed and "0" when not continuously via the serial monitor. This button is on NodeRed, a

web browser-based flow editor, which can be used to create JavaScript functions.

The reason I wanted to use multiple arguments was that at some point I am going to be using 6 servos, which basically means I would need 6 switches. Now, this sketch worked out great using the references, for the LEDs. However, when I use it to run a single servo, my servo is basically going haywire. It goes back and forth at a single spot as if the button were being pushed and released very fast, i.e., 1010101010. And I'm not sure why this happening.

Here's the code for smoothing out my servo using a physical button:

#include<Servo.h>
int switch1;
float switch1Smoothed;
float switch1Prev;
Servo myservo1;
float a=1200.0;

void setup() {

  Serial.begin(9600);
  pinMode(A1,INPUT_PULLUP);
  myservo1.attach(9);
  myservo1.writeMicroseconds(a);
  switch1Prev=a;
}

void loop() {
//Motor 1
  switch1 = digitalRead(A1);
  Serial.println(switch1);// read switch
  switch1 = switch1 * 2400;        // multiply by 10

  // *** smoothing ***

  switch1Smoothed = (switch1 * 0.05) + (switch1Prev * 0.95);
  if(switch1Smoothed>=a){
  switch1Prev = switch1Smoothed;
  }
  else{
    switch1Prev = a;
  }
  // *** end of smoothing ***

//  Serial.print(switch1);                  // print to serial terminal/plotter
//  Serial.print(" , ");   
//  Serial.println(switch1Smoothed);
  myservo1.writeMicroseconds(switch1Smoothed);
  delay(20);
}

I've tested this code with a physical button and it works fine.
And here's the code after I merge it with the one I started this post with:


#include <Servo.h> 
int switch1;
float switch1Smoothed;
float switch1Prev;
Servo myservo1;
float a=1200.0;
String readString, mot1, mot2;

void setup() {
  Serial.begin(9600);
  pinMode(13,OUTPUT);
  myservo1.attach(9);
  myservo1.writeMicroseconds(a);
  switch1Prev=a;
}

void captureChar(int& n1){

  while (Serial.available()) {
    delay(3);  //delay to allow buffer to fill 
    if (Serial.available() >0) {
      char c = Serial.read();  //gets one byte from serial buffer
      readString += c; //makes the string readString
    } 
  }

  if (readString.length() >0) {

      
    
      mot1 = readString.substring(0,1); 
      mot2 = readString.substring(1,2); 
      n1 = mot1.toInt();
      //n2 = mot2.toInt();
    readString="";
    
  } 
}

void loop(){
  captureChar(switch1);
  switch1 = switch1 * 2400;        // multiply by 2400

  // *** smoothing ***

  switch1Smoothed = (switch1 * 0.05) + (switch1Prev * 0.95);
  if(switch1Smoothed>=a){
  switch1Prev = switch1Smoothed;
  }
  else{
    switch1Prev = a;
  }
  // *** end of smoothing ***

//  Serial.print(switch1);                  // print to serial terminal/plotter
//  Serial.print(" , ");   
//  Serial.println(switch1Smoothed);
  myservo1.writeMicroseconds(switch1Smoothed);
  delay(15);
}

Please let me know if you think I should make a separate post about this since I do realize that my question has now kind of changed.

Thank you.

should the two statemets be

     mot1 = readString.substring(0,1); //get the first four characters
      mot2 = readString.substring(2,3); //get the next four characters 

I think the substring syntax calls it to be (0,1) for the first character and (1,2) for the second, and it seemed to be working when I was testing manual inputs into the serial monitor as well. So I would think the statements are correct.