0

I'm super confused right about now!

I've been working on project involving Xbees in API mode, and I need to send different arrays of bytes to the Xbee depending on which buttons I press. Mostly what I have is working fine, but I'm having a lot of issues with the following questions; I figured I'd summarize all my questions first, then further explain them with code examples.

  1. Arduino requires arrays contain to a size. How can I declare byte data[] without a size?
  2. Arrays sizes must be declared with a number, or a const integer. So how come I can declare byte packet[arraySize]; when arraySize is being passed the integer ledOn_len that isn't constant?
  3. How can I create a global array within a function?

I've taken the liberty to condense my code for readability purposes. Everything you see is here is mirroring my issues with my Xbee code.

byte ledOn[] = { 0x7E, 0xAA, 0x1C, 0x3B }; // turn led on
byte ledOff[] = { 0x7E, 0xAB, 0x1D }; // turn led off

int ledOn_len; // to be used for the array size of ledOn[] NOT a constant
int ledOff_len; // to be used for the array size of ledOff[] NOT a constant

// this doesn't work, because the array length must be a constant
//int arrayLength = 4;
//byte rando[arrayLength];

// this works just fine, since arrayLength is now a constant
//const int arrayLength = 4;
//byte rando[arrayLength];

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

  // since using sizeof(data) inside of changeLed() doesn't
  // work (it returns with 2, which is the size of a pointer)
  // set them here, and pass them along.
  ledOn_len = sizeof(ledOn);
  ledOff_len = sizeof(ledOff); 
}

void loop()
{
  changeLed(ledOn, ledOn_len);
  delay(1000);
  changeLed(ledOff, ledOff_len);
  delay(1000);
}

void sendToXbee()
{
  //Xbee.write(packet); // packet isn't global, this doesn't work
  // yes I know I didn't include any software serial in this demonstration
}

void changeLed(byte data[], int arraySize)
{
  byte packet[arraySize]; // how does this work???? Pass by value
  memcpy(packet, data, arraySize); // clone data to packet

  // this is all the Serial print code 

}

1) I created the function void changeLed() to calculate the checksum (xbee crap). I need to pass it the array ledOn[] or ledOff[] for the checksum calculations to occur. So I simply added void changeLed(byte data[]) (and then void loop calls changeLed(ledOn) ) which works, but why?! Anywhere else if you declare an array without an array size it says you must provide one. Confusing to me, but it still works. . . kinda. When I serial print data[i] byte by byte, it perfectly displays each byte in ledOn[], which is what I'm passing it. But when I call sizeof(data) it says it's only 2 bytes. How can it contain the exact data of 4 bytes I give it while only being 2 bytes? Here is the serial monitor of it. The first line is the printout of data[], following the 4 bytes is the length which it claims is 2. The second line is the array of bytes packet[]. I set packet[] equal to data[] with this: memcpy(packet, data, arraySize);. Now you'll notice that the array size is correct. Interesting.

Update #1: It seems like this all has to do with pointers somehow. I guess data[] is just pointing to or referencing the array ledOn[], and using memcpy(packet, data, arraySize); is actually copying the array ledOn[] to the array packet[]?

enter image description here


2) I try to declare a random array (byte rando[]) using int arrayLength as the size. This does not work because the array size must be a constant. So I simply add const int arrayLength and that works. Great. So how am I able to do byte packet[arraySize];!? ArraySize is being passed ledOn_len which is not a constant, so how come that works?


3) I need to be able to use the array packet[] inside of another function void sendToXbee(). Packet[] will be my final array of bytes to send to the Xbee containing either the array ledOn[] or ledOff[] which are different array lengths. As is, I cannot use packet[] in void sendToXbee() because packet[] is local to void changeLed(). But here's the catch 22: I cannot declare packet[] in the setup making it global, because I don't have a size for that array yet!! The size of that array must be calculated within void changeLed(), depending on which array I need to send to the Xbee, ledOn[] or ledOff[].

I know this is a lot but I'd appreciate the help! Thanks guys.

1 Answer 1

0

Arduino requires arrays contain to a size. How can I declare byte data[] without a size?

You can't. Pure and simple. The compiler can infer a size from a declaration time assignment.

Arrays sizes must be declared with a number, or a const integer. So how come I can declare byte packet[arraySize]; when arraySize is being passed the integer ledOn_len that isn't constant?

It is a feature of C99 which GCC have also included in C++.

How can I create a global array within a function?

Simple: you can't.*

Ok. Now we've to that out of the way, let's look at what you want to do and how you should do it.

First off, you have a number of arrays of data:

uint8_t ledOn[] = { 0x7E, 0xAA, 0x1C, 0x3B }; // turn led on
uint8_t ledOff[] = { 0x7E, 0xAB, 0x1D }; // turn led off

Those arrays have an associated length, which is the size of the array divided by the size of one array entry:

const int ledOn_len = sizeof(ledOn) / sizeof(ledOn[0]);
const int ledOff_len = sizeof(ledOff) / sizeof(ledOff[0]);

At the moment you're getting away with just using sizeof(ledOn) because sizeof(ledOn[0]) is 1. But if you ever do this with arrays of anything other than byte you will come unstuck unless you get into this habit.

Now, those arrays are just blocks of memory with a fixed size, in a fixed location. That means that the arrays, as well as being arrays, are also pointers. That is, they are in effect just variables that contain the address where the data for the array is stored.

So you pass the pointer to the array, along with the length of the array, to your function:

void changeLed(const uint8_t *data, int len) {
}

changeLed(ledOn, ledOn_len);

When you have done is pass the address of the array to the function, not the array.

Now you can do things with that array data using that address. What you can't do is change the data (because I've marked it const).

One of the things you can do, though, is just send it. You know where the data is. You know how long it is. You know where you want to send it to.

So you just do:

void changeLed(const uint8_t *data, int len) {
    Xbee.write(data, len);
}

That is, write len bytes starting at the address pointed to by data.

There is no need to have variable size arrays. There is no need to have global packet buffers. There is no need to copy any data anywhere with memcpy(). Just pass the data and send it.

If you need to send more data than just the content of your array, then just send it. For example you may have to (I don't know the XBee protocol) add a header and footer to the packet.

void changeLed(const uint8_t *data, int len) {
    Xbee.write(0x01);
    Xbee.write(0x02);
    Xbee.write(len & 0xFF);
    Xbee.write(data, len);
    uint8_t cs = 0;
    for (int i = 0; i < len; i++) {
        cs += data[i];
    }
    Xbee.write(cs);
}

Just because they're not together in the same write function doesn't mean that they aren't "together". With serial communication, there is no such thing as "together". It's just one byte after another.

I need to be able to use the array packet[] inside of another function void sendToXbee(). Packet[] will be my final array of bytes to send to the Xbee containing either the array ledOn[] or ledOff[] which are different array lengths. As is, I cannot use packet[] in void sendToXbee() because packet[] is local to void changeLed(). But here's the catch 22: I cannot declare packet[] in the setup making it global, because I don't have a size for that array yet!! The size of that array must be calculated within void changeLed(), depending on which array I need to send to the Xbee, ledOn[] or ledOff[].

Why do you think you need to copy the data into a separate packet variable? You already have the data in a variable.

If you really want to do that, then just pass packet to the next function in the same way as you already pass your other arrays.


* Actually there are ways, using dynamic memory allocation, but I won't condone using that

4
  • Very helpful! There are a few things that come to mind however. Your second quote of mine says you can't, but the what I meant by I can is that the compiler accepts it, and it works. I was curious as to why the compiler says it's okay to do it like that and why it does work, even though it seems it shouldn't. Commented Mar 21, 2019 at 23:20
  • Ah, I misread that bit. If it's local then yes, it can be allocated by a variable (since C99 IIRC)
    – Majenko
    Commented Mar 21, 2019 at 23:26
  • @HavocRC stackoverflow.com/questions/30694199/…
    – Majenko
    Commented Mar 21, 2019 at 23:26
  • When I say 'together' I'm referring to keeping all of the variables within an array. The reason being is I have to calculate the checksum (addition of all bytes, & it with 0xFF, then subtract that value from 0xFF). I wrote a function that calculates the checksum, based on the bytes within the array. If tried to do the checksum calculation outside of an array, it would be a nightmare. Based on the information you provided I think I have a great path forward to fixing this. Thank you very much! Commented Mar 21, 2019 at 23:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.