Communication between .ino and .cpp with functions [SOLVED]

I am using the library "rfid-master" by miguelbalboa on github, which contains the file MFRC522.cpp. My sketch calls a function within this file, PICC_DumpToSerial(/arguments/), which calls PICC_DumpMifareClassicToSerial(/arguments/), which finally calls PICC_DumpMifareClassicSectorToSerial(/arguments/) where I have modified the code to copy an existing buffer (byte buffer[18]:wink: to a new variable instead of posting it to Serial Monitor. I now need that variable's value to be sent back to my sketch where I will be converting it to an integer value and sending it to a servo.

I've never been exceptionally good at understanding/remembering how to send values between functions, especially across files. Normally I'd just set the function to have a return type and call the function to receive the resulting value, but the functions are already declared as void and I'd rather not change that unless I know what I'm doing. I'm assuming I can just send the value using arguments, but then comes the use of addresses and pointers, which is my main weakness (especially since I'm not used to using the byte variable type).

Any assistance/guidance with this would be greatly appreciated. (I also need to convert the received value to an integer, which I will probably do by copying it over to a String and then typecasting that to an integer...unless someone knows how to do this more efficiently lol)

Thanks!

Image attached to give visual outline of situation.

DumpToSerial.png

u might try something like this--

void PICC_DumpToSerial(/arguments/)

change that to:

int PICC_DumpToSerial(/arguments/)

then make ur int conversion in that function and return that value

backwoodsjack:
u might try something like this--

void PICC_DumpToSerial(/arguments/)

change that to:

int PICC_DumpToSerial(/arguments/)

then make ur int conversion in that function and return that value

True, that could work. I'm still not sure how to pass the variable from PICC_DumpMifareClassicSectorToSerial to PICC_DumpToSerial though...where/how do I use pointers, if at all...?

Declare the buffer in your sketch and then pass it to the function as an argument. It will be passed by reference which means that any modifications to the buffer made in the functions will be present in the buffer in the sketch after the function returns. This way you can keep the function return type as void. For more information on pass by reference see http://www.cplusplus.com/doc/tutorial/functions/

void setup() {
  Serial.begin(115200);
  byte g[18];
  g[0] = 0;
  x(g);
  Serial.print(g[0]);
}

void loop() {}

void x(byte g[]) {
  g[0] = 42;
}

edit: actually I think the right term is pass by pointer but the general concept remains the same.

pert:
actually I think the right term is pass by pointer but the general concept remains the same.

Yes, in the declaration below 'g' is a pointer:

void x(byte g[]) {
  g[0] = 42;
}

For it to be a reference, it could look like this:

void x(byte (&g)[18]) {
  g[0] = 42;
}

The difference is, the reference preserves the length of the array, whereas a pointer does not.

Thanks guys! I will try that!

Small question:
I'm already using a buffer called "buffer" which I did not create and is from other existing code that I have modified, and this buffer called "buffer" shows up orange. Is that any different from a buffer called "mybuffer123" for example? If not, can I safely rename the original to "writebuffer" and name my new one "readbuffer"? Or does the fact that "buffer" is orange mean something important and thus I shouldn't change it?

My wording sounds slightly confusing even to me; I hope you get what I mean lol.

EDIT:
Additionally, when I try to pass by pointer

mfrc522.PICC_DumpToSerial(&(mfrc522.uid), servodata[]);

I get the error:
"expected primary-expression before ']' token"

I'm assuming it wants to see something within the brackets, but that would then be passing by reference, no?

EDIT2:
My function declarations in MFRC522.h have my buffer, "servodata[18]", in the arguments. Should it be "servodata[]" instead? Same thing for the definitions, or no?

The reason the word buffer is orange is because it's in the keywords.txt of the Bridge library, it's a function in that library. So the orange coloration is meaningless in this application. You can change the name to anything you like.

XORduino:

mfrc522.PICC_DumpToSerial(&(mfrc522.uid), servodata[]);

should be
mfrc522.PICC_DumpToSerial(&(mfrc522.uid), servodata);

XORduino:
My function declarations in MFRC522.h have my buffer, "servodata[18]", in the arguments. Should it be "servodata[]" instead?

The short answer is either one will work if that's the size of the array. I don't have time to write the long answer right now. I'm sure pYro_65 can explain it much better than me but I'll try to get back to it when I get off work if someone else hasn't answered first.

As pYro_65 pointed out previously there is a difference between the pass by pointer approach and the pass by reference approach.

Pass by pointer:

void setup() {
  Serial.begin(115200);
  byte g[18];
  g[0] = 0;
  x(g);
  Serial.print(g[0]); //42
}

void loop() {}

void x(byte g[]) {
  Serial.println(sizeof(g));  //2
  g[0] = 42;
}

As you can see, the length of the array is not known to the function x(), only the size of the pointer.

An equivalent way to write the x() function in the above code would be:

void x(byte *g) {

the array parameter is automatically converted to a pointer. For this reason I don't think specifying an array size in the parameter when doing a pass by pointer has any effect so I'd leave it off.

Pass by reference:

void x(byte (&g)[18]);  //x() function prototype

void setup() {
  Serial.begin(115200);
  byte g[18];
  g[0] = 0;
  x(g);
  Serial.print(g[0]); //42
}

void loop() {}

void x(byte (&g)[18]) {
  Serial.println(sizeof(g));  //18
  g[0] = 42;
}

In this case the length of the array is known to x() but the specific length had to be specified in the parameter. Trying to pass an array of any other length or unknown length to x() will cause a compile error. This could be useful if you wanted to ensure that only a certain length of array was allowed. Note that Arduino IDE 1.6.9 didn't correctly generate the x() function prototype in this case so I had to do it manually.

XORduino:
I am using the library "rfid-master" by miguelbalboa on github, which contains the file MFRC522.cpp. My sketch calls a function within this file, PICC_DumpToSerial(/arguments/), which calls PICC_DumpMifareClassicToSerial(/arguments/), which finally calls PICC_DumpMifareClassicSectorToSerial(/arguments/) where I have modified the code to copy an existing buffer (byte buffer[18]:wink: to a new variable instead of posting it to Serial Monitor. I now need that variable's value to be sent back to my sketch where I will be converting it to an integer value and sending it to a servo.

I'm kind of confused how anyone can read this and not think of the X-Y problem. It seems extrememly unwise to me to modify a library to make the DumpToSerial function not dump to serial.

Assuming that this is what you actually want to do, the least janky way of doing this without modifying function signatures is using a global variable with external linkage. Here is an example.

test.ino

char lol[10];
void rewrite_string();

void setup() {
  Serial.begin(9600);
  strcpy(lol, "Hello");
  Serial.println(lol);
  rewrite_string();
  Serial.println(lol);
}

void loop() {
}

yolo.cpp

extern char lol[10];
#include <string.h>

void rewrite_string()
{
  strcpy(lol, "World");
}

Note the extern keyword applied to the global variable lol in the test.cpp file. This means it's referencing another global variable with the given name from a different file (in this case, from test.ino).

Running this sketch produced the following output in Serial Monitor:

Hello
World

Notice that the contents of the lol string buffer have been modified, even though no values are passed to or returned from the rewrite_string function (it is declared void with no parameters). This appears to be exactly what you want to do.

Jiggy-Ninja:
I'm kind of confused how anyone can read this and not think of the X-Y problem.

You're probably right. XORduino hasn't given enough information on their goal to know what the best way of doing it is. The question asked was "how do I return an array from a function". That's valuable information to understand even if not necessarily relevant to this application. I tend to think the best way to teach is to actually answer the question asked instead of immediately saying "no no, that's all wrong, do it like this".

Jiggy-Ninja:
It seems extrememly unwise to me to modify a library to make the DumpToSerial function not dump to serial.

Why? Arduino is about learning. Digging into the source of a library and trying some modifications is a great way to do that. Of course the function name should match what the function actually does but that's a simple change.

Jiggy-Ninja:
Assuming that this is what you actually want to do, the least janky way of doing this without modifying function signatures is using a global variable with external linkage.

Why is modifying the function signatures such a problem? I think using a global is the most janky way to do this. The library is being modified either way, why not do it right?

Jiggy-Ninja:
I'm kind of confused how anyone can read this and not think of the X-Y problem. It seems extrememly unwise to me to modify a library to make the DumpToSerial function not dump to serial.

Assuming that this is what you actually want to do, the least janky way of doing this without modifying function signatures is using a global variable with external linkage. Here is an example.

test.ino

char lol[10];

void rewrite_string();

void setup() {
 Serial.begin(9600);
 strcpy(lol, "Hello");
 Serial.println(lol);
 rewrite_string();
 Serial.println(lol);
}

void loop() {
}



yolo.cpp


extern char lol[10];
#include <string.h>

void rewrite_string()
{
 strcpy(lol, "World");
}



Note the *extern* keyword applied to the global variable *lol* in the test.cpp file. This means it's referencing another global variable with the given name from a different file (in this case, from test.ino).

Running this sketch produced the following output in Serial Monitor:


Hello
World



Notice that the contents of the *lol* string buffer have been modified, even though no values are passed to or returned from the *rewrite_string* function (it is declared *void* with no parameters). This appears to be exactly what you want to do.

I used DumpToSerial, DumpMifareClassicToSerial, and DumpMifareClassicSectorToSerial because, out of all of that code, only 1 line of code actually does any Dump[ing]ToSerial. The other 99.9% is identifying the card type, authenticating the data, and reading the data, which is what I need. With this in mind, I figured that it'd be worth the effort to redirect the location of the output instead of write new code from scratch considering I would not have time to do that with my lack of experience.

On the topic of your example code, would that mean that if lol's value were read again in test.ino, it would be "World"? If not, I don't think that would solve my problem since I need the value to be read/used in the sketch (.ino) after being obtained in the .cpp file. If it does update in the sketch, then yeah that'd be perfect.

Pert,
Thanks for your input! I tried making the described changes and it compiles without any errors, however, servodata seems to be empty. What might cause this?

UPDATE:
I have added some Serial.print and Serial.println commands into the code at various locations to check what's happening, and the data is successfully being copied to my buffer within the .cpp file but apparently this buffer's value is not being sent back to the sketch. How would I go about solving this?

pert:
Why? Arduino is about learning. Digging into the source of a library and trying some modifications is a great way to do that. Of course the function name should match what the function actually does but that's a simple change.

The function name being wrong is precisely the problem. If it's installed in the libraries folder like that then any other code using that library is going to get the modified functions and won't work like expected.

If I write a function that trims leading and trailing whitespace from a string, and call it makeLowercase(), that's bad practice.

Why is modifying the function signatures such a problem? I think using a global is the most janky way to do this. The library is being modified either way, why not do it right?

Compatibility with other sketches is broken if the signatures are modified.

There's multiple ways to do anything of course, and digging into a library's source code to dissect and hack around with is is always something that should be encouraged. I'm not saying it can't be done without modifying the library this way, or even that it's always a bad way. A well designed library would have ways to expose this kind of information to a string variable instead of just Serial. A poorly designed library might not, and would require exactly this kind of workaround to make work. Good class design is difficult, and is probably not something most hobbyists will be considering when trying to make their project work (I certainly don't always).

At this point, I think that this topic screams "X-Y problem" so strongly that we we need the OP to post a full sketch of what they're trying to do, a description of their goal (and not simply this specific method they're using), and a link to the RFID library.

XORduino, please do that.

On the topic of your example code, would that mean that if lol's value were read again in test.ino, it would be "World"? If not, I don't think that would solve my problem since I need the value to be read/used in the sketch (.ino) after being obtained in the .cpp file. If it does update in the sketch, then yeah that'd be perfect.

I posted the output from the Serial Monitor; that's exactly what it did. Make a sketch with my code to try it out for yourself.

With the extern keyword in the second file, it's linked to the variable with the same name and type in the ino file where it is declared without extern. It is a single global variable shared between the two files.

Jiggy-Ninja:
The function name being wrong is precisely the problem. If it's installed in the libraries folder like that then any other code using that library is going to get the modified functions and won't work like expected.

Yes, I agree. I would retain the signatures of the original functions and add new function(s) with appropriate names that provide the extra functionality required. I just assumed by modified XORduino meant basing new functions on the dump to serial functions.

The other problem with modifying a library is you have to merge your changes every time you want to update to a new version of the original library

XORduino:
servodata seems to be empty.

Please post your code and I'll look into it.

The function name issue is not as bad in this case as you think, lol; it still dumps to serial, I've just added code to copy a portion of the data to a buffer. Either way, I could easily just rename the functions involved and the issue would be gone :P. That's not a priority for me at this time though, especially since none of this is being redistributed anyways.

As for the example code: I'm not questioning the output provided, but it is not proven by the outout that the value of the variable in the .cpp file is/can be accessed by the sketch. That is why I was asking. You have stated in your recent post though that it indeed does get updated in the sketch as well, so that answers the question :slight_smile:

Sketch:

/*
   FleetID
   by Sean Sciberras
*/

//Servo
#include <Servo.h>

//Writing
#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN 9
#define SS_PIN 10

MFRC522 mfrc522(SS_PIN, RST_PIN);   //Create MFRC522 instance

Servo servo1; //create servo object to control a servo

const int buttonPin = 2; //the number of the pushbutton pin

int potpin1 = 0; //analog pin used to connect the potentiometer
int analog1; //variable to read the value from the analog pin
int fromAnalog_servo1; //analog value scaled for servo
int buttonState = 0; //variable for reading the pushbutton status
int servodataInt;

byte buffer[34];
byte block;
byte len;
byte servodata[18];

MFRC522::StatusCode status;
MFRC522::MIFARE_Key key;

String fromAnalog_servo1_string;
String servo1_string;

char message;

void setup() {
  Serial.begin(9600); //Initialize serial communications with the PC
  SPI.begin(); //Init SPI bus
  mfrc522.PCD_Init(); //Init MFRC522 card
  servo1.attach(9); //attaches the servo on pin 9 to the servo object
  pinMode(buttonPin, INPUT); //initialize the pushbutton pin as an input:
}

void loop() {
  buttonState = digitalRead(buttonPin); //read the state of the pushbutton value:
  Serial.setTimeout(20000L);     // wait until 20 seconds for input from serial
  //WRITE MODE
  if (buttonState == HIGH) { //check if the pushbutton is pressed.
    //write to tag
    if (message != 'w')
    {
      Serial.println("Scan a PICC to write...");
      message = 'w';
    }
    // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory.

    for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;

    // Look for new cards
    if ( ! mfrc522.PICC_IsNewCardPresent()) {
      return;
    }

    // Select one of the cards
    if ( ! mfrc522.PICC_ReadCardSerial())    return;

    Serial.print(F("Card UID:"));    //Dump UID
    for (byte i = 0; i < mfrc522.uid.size; i++) {
      Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
      Serial.print(mfrc522.uid.uidByte[i], HEX);
    }
    Serial.print(F(" PICC type: "));   // Dump PICC type
    MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
    Serial.println(mfrc522.PICC_GetTypeName(piccType));


    //Get sensor1 readings ==================================================================
    analog1 = analogRead(potpin1); //read sensor positions
    fromAnalog_servo1 = map(analog1, 0, 1023, 0, 180); //scale sensor positions (0 to 1023) to be usable by servo (0 to 180)
    fromAnalog_servo1_string = String(fromAnalog_servo1);
    len = sizeof(fromAnalog_servo1_string);
    for (byte fillbuffer = 0; fillbuffer < len; fillbuffer++)
    {
      buffer[fillbuffer] = fromAnalog_servo1_string[fillbuffer];
      Serial.print(F("buffer["));
      Serial.println(fillbuffer);
      Serial.print(F("] = "));
      Serial.println(buffer[fillbuffer]);
    }
    Serial.print(F("len = "));
    Serial.println(len);
    Serial.print(F(" analog1 = "));
    Serial.println(analog1);
    Serial.print(F(" fromAnalog_servo1 = "));
    Serial.println(fromAnalog_servo1);
    Serial.print(F(" fromAnalog_servo1_string = "));
    Serial.println(fromAnalog_servo1_string);
    for (byte i = len; i < 3; i++) {
      buffer[i] = ' '; // pad with spaces
    }
    block = 1;
    Serial.println(F("Authenticating using key A..."));
    status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
    if (status != MFRC522::STATUS_OK) {
      Serial.print(F("PCD_Authenticate() failed: "));
      Serial.println(mfrc522.GetStatusCodeName(status));
      return;
    }
    else Serial.println(F("PCD_Authenticate() success: "));

    // Write block
    status = mfrc522.MIFARE_Write(block, buffer, 16);
    if (status != MFRC522::STATUS_OK) {
      Serial.print(F("MIFARE_Write() failed: "));
      Serial.println(mfrc522.GetStatusCodeName(status));
      return;
    }
    else Serial.println(F("MIFARE_Write() success: "));

    block = 2;
    //Serial.println(F("Authenticating using key A..."));
    status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
    if (status != MFRC522::STATUS_OK) {
      Serial.print(F("PCD_Authenticate() failed: "));
      Serial.println(mfrc522.GetStatusCodeName(status));
      return;
    }

    // Write block
    status = mfrc522.MIFARE_Write(block, &buffer[16], 16);
    if (status != MFRC522::STATUS_OK) {
      Serial.print(F("MIFARE_Write() failed: "));
      Serial.println(mfrc522.GetStatusCodeName(status));
      return;
    }
    else Serial.println(F("MIFARE_Write() success: "));

    Serial.println(" ");
    mfrc522.PICC_HaltA(); // Halt PICC
    mfrc522.PCD_StopCrypto1();  // Stop encryption on PCD

    //READ MODE
  } else {
    //read from tag
    if (message != 'r')
    {
      Serial.println("Scan a PICC to read...");
      message = 'r';
    }
    // Look for new cards
    if ( ! mfrc522.PICC_IsNewCardPresent()) {
      return;//go to start of loop if there is no card present
    }

    // Select one of the cards
    if ( ! mfrc522.PICC_ReadCardSerial()) {
      return;//if ReadCardSerial returns 1, the "uid" struct (see MFRC522.h lines 238-45)) contains the ID of the read card.
    }

    // Dump debug info about the card. PICC_HaltA() is automatically called.
    mfrc522.PICC_DumpToSerial(&(mfrc522.uid), servodata);
    for (int extract = 0; extract < 3; extract++)
    {
     servo1_string[extract] = servodata[extract];
     Serial.print(F("servo1_string["));
     Serial.println(extract);
     Serial.print(F("] = "));
     Serial.println(servo1_string[extract]);
    }
    Serial.print(F("servo1_string: "));
    Serial.println(servo1_string);
    //servo1.write(int(servo1_string));
    /*
    block = 1;
    Serial.println(F("Authenticating using key A..."));
    status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid));
    if (status != MFRC522::STATUS_OK) {
      Serial.print(F("PCD_Authenticate() failed: "));
      Serial.println(mfrc522.GetStatusCodeName(status));
      return;
    }
    else Serial.println(F("PCD_Authenticate() success: "));
    // Read block
    byte byte_count = sizeof(buffer);
    status = mfrc522.MIFARE_Read(block, buffer, &byte_count);
    if (status != MFRC522::STATUS_OK) {
      Serial.print(F("MIFARE_Read() failed: "));
      Serial.println(mfrc522.GetStatusCodeName(status));
      return;
    }
    else Serial.println(F("MIFARE_Read() success: "));
    for (byte emptybuffer = 0; emptybuffer < 16; emptybuffer++)
    {
      fromAnalog_servo1_string[emptybuffer] = buffer[emptybuffer];
    }
    fromAnalog_servo1 = fromAnalog_servo1_string.toInt();
    servo1.write(fromAnalog_servo1);
    */
  }
}

Library (without my modifications):

Attached are my modified versions of MFRC522.cpp and MFRC522.h

Thanks for your continued assistance!

EDIT:
By the way, I have heard of and read about the X-Y problem via the forum rules and I understand the issues behind it. My reason for falling into this problem is that I try to focus on precisely what (I think) my issue is so that I don't feel like I'm getting people to do my work for me lol. Of course, it is easier for others who are more experienced to solve an issue effectively and efficiently if they can see the whole picture, but I want to avoid looking like one of those "Here's everything I've got, make it work please!" posters, haha :).

MFRC522.cpp (70.7 KB)

MFRC522.h (23.3 KB)

Here's your problem

XORduino:

     servo1_string[extract] = servodata[extract];

Strings(with a capital S) don't work that way, it should be:

    servo1_string += servodata[extract];

After that change this code:

    Serial.print(F("servo1_string["));
     Serial.println(extract);
     Serial.print(F("] = "));
     Serial.println(servo1_string[extract]);

still won't work but this code:

   Serial.print(F("servo1_string: "));
    Serial.println(servo1_string);

does work.

I actually recommend avoiding the use of String unless you have a good reason. If you're trying to convert bytes to text you can use itoa() with a char array: http://www.cplusplus.com/reference/cstdlib/itoa/.

It does seem that it should be possible to do what you're trying to achieve from the sketch using the unmodified functions of the library but I haven't looked over the library very thoroughly.

Thanks, pert! I forget what my/the reason was for using String, but it was a solution provided by another member and it worked so I stuck with it. Initially, I did try to simply use the MIFARE_Read command since I had used MIFARE_Write to do my writing, but for some reason MIFARE_Read doesn't function if authentication hasn't been done (which would have taken me far too long to learn in the time constraint that I have), so I resorted to modifying something where the required authentication is already present.