How to send more than 60bytes

Started by javiercuellar, April 05, 2016, 05:32:58 PM

javiercuellar

Hi.

Please if anyone can help with this code.... It complie properly but nothing is sent:

    unsigned int data[] = {8950,4450,550,550,550,550,550,1650,600,550,550,550,550,550,550,550,550,600,550,1650,550,1650,600};
    byte dataLen= sizeof(data) / sizeof(data[0]) ;
  
   Serial.println("Start...");           
  
   for (int x=0;x<dataLen-1; x++){
       if (radio.sendWithRetry(RECEIVER, data, dataLen)) {
           Serial.println(data[x]);           
           delay(10);
       }    
    }
   Serial.println("End...");           



The output is:
Start...
End...


I know there is a problem sending more than 60bytes. Any recomendation on how to implement it?
Is there any problem sending int array?


Thanks

TomWS

It would be helpful if you included the rest of your sketch, however, in this case, the immediate problem is (see comments in code):

   for (int x=0;x<dataLen-1; x++){   // you have a loop around radio.sendWithRetry() so it will send the same packet about 46 times (the size of your data).
       if (radio.sendWithRetry(RECEIVER, data, dataLen)) {
           Serial.println(data[x]);           // This will ONLY print if you receive an ACK from the node on the far end.  Otherwise sendWithRetry returns false.
           delay(10);
       }   
    }


Perhaps you want:
    if (radio.sendWithRetry(RECEIVER, data, dataLen)) {  // This will send once and if you get a good result then....
      for (int x=0;x<dataLen-1; x++){   // this will print all the data that you sent... (not sure why you want to do that, but, hey, have fun!)
           Serial.println(data[x]);           
           delay(10);
       }   
    }


Tom

javiercuellar

Thanks Tom for your answer.

My problem is that the data I want to send will probably be more than 62 byte, so I can't send them in 1 package.
My testing idea was to send each int from the array, 1 by 1. I realize my mistake in the code. Now I show the idea, but is not working because datatype conversion.
Also changed radio.sendWithRetry to only radio.send for testing.

Is there a better way (of course yes  ;)) to send int array bigger than 62bytes?

    
    unsigned int data[] = {8950,4450,550,550,550,550,550,1650,600,550,550,550,550,550,550,550,550,600,550,1650,550,1650,600}; // This will be bigger array
    byte dataLen= sizeof(data) / sizeof(data[0]) ;
  
   Serial.println("Start...");           
  
   for (int x=0;x<dataLen-1; x++){
       radio.send(RECEIVER, data[x], sizeof(data[0]))) {   [color=red] // here shows error invalid conversion from 'unsigned int' to 'const void*' [-fpermissive][/color]
       Serial.println(data[x]);           
       delay(10);           
    }
   Serial.println("End...");



Regards

Javier

TomWS

Quote from: javiercuellar on April 06, 2016, 12:04:12 AM
Thanks Tom for your answer.

My problem is that the data I want to send will probably be more than 62 byte, so I can't send them in 1 package.
My testing idea was to send each int from the array, 1 by 1. I realize my mistake in the code. Now I show the idea, but is not working because datatype conversion.
Also changed radio.sendWithRetry to only radio.send for testing.

Is there a better way (of course yes  ;)) to send int array bigger than 62bytes?

    
    unsigned int data[] = {8950,4450,550,550,550,550,550,1650,600,550,550,550,550,550,550,550,550,600,550,1650,550,1650,600}; // This will be bigger array
    byte dataLen= sizeof(data) / sizeof(data[0]) ;
  
   Serial.println("Start...");           
  
   for (int x=0;x<dataLen-1; x++){
       radio.send(RECEIVER, data[x], sizeof(data[0]))) {   [color=red] // here shows error invalid conversion from 'unsigned int' to 'const void*' [-fpermissive][/color]
       Serial.println(data[x]);           
       delay(10);           
    }
   Serial.println("End...");



Regards

Javier
Javier,
if you break the problem down into a series of separate issues it is easier to deal with.  What I see is:
1. You can't really test basic send because, presumably, you don't have another end point to receive and display the data.
2. When you do send, the packet size limitation prevents you from sending in a single packet.
3. You would like to print the content of each radio transmission.

Issue one can be addressed in one of two ways.  The method you used, converting to radio.send(), avoids the problem because you can't tell if an endpoint receives it or not.  This can also be addressed by temporarily ignoring (or simply reporting) the return code from radio.sendWithRetry().  Both will send, but only the second approach will handle the case where you DO have an endpoint receiving the data and acknowledges it.

Issue two obviously requires that you break your overall packet into 'send' sized chunks that can be reassembled at the far end.  This is addressed simply by adding a couple of bytes to the beginning of each packet so that you can reassemble the overall packet and also check that you've received all packets in order.   Since datalen is part of the received data, you could get a away with simply sending a single additional byte that has a sequence number: 0, 1, 2, etc.  Then on receive you can reconstruct the packet.  There are lots of ways to address this, but, in this case I'll show the simplest case and you can build on it as you wish.  If you could destructively modify the data array after send, then you COULD incorporate the sequence number as the first by in the array (and then overwrite the data elements with the next sequence number for each subsequent send) - I'll assume that you can't do this so we'll use a separate send buffer...

Finally, I recommend separating the 'print' of sent data from the 'send of sent' data.  It's not really that inefficient, especially considering that the 'print' part is temporary.

So, given this:
    
#define MAX_PACKET_SIZE  56    // let's be conservative and leave room for a more sophisticated header

    unsigned int data[] = {8950,4450,550,550,550,550,550,1650,600,550,550,550,550,550,550,550,550,600,550,1650,550,1650,600}; // This will be bigger array AND can be as big as will fit in memory...
    byte dataLen= sizeof(data) / sizeof(data[0]) ;     

    struct DATABUFFER_WITH_HEADER    // use this so that you can precede the data with a separate sequence number field
    {
       uint8_t 
          sequence,
          pkt_data[MAX_PACKET_SIZE];   // BYTE buffer to receive the subset of data to transmit.  You COULD have this be the same data type as your data array (if you change the code below), however it begs for data alignment issues that are beyond the scope of this example.
    } buf;
  
   Serial.println("Start...");           
   buf.sequence = 0;

   for (int x=0;x<dataLen; ){  // NOTE: I eliminated the post increment of x in the 'for' statement.  x is adjusted below

       // this next statement gets the size (in bytes) of THIS packet
       int packet_size = (dataLen <= (MAX_PACKET_SIZE/sizeof(data[0]))? ((dataLen-x)*sizeof(data[0])) : MAX_PACKET_SIZE;

       // copy the limited amount of data to our send buffer
       memcpy(buf.pkt_data, &data[x], packet_size);    

       radio.send(RECEIVER, &buf, packet_size+1);   // send the limited block of data plus the sequence number   

       // NOW you can print what you sent starting at the x'th element, 
       Serial.print("Sequence:"); Serial.print(buf.sequence); 
       for (int i = x; i < (x+(packet_size/sizeof(data[0]))); i++)
       {
         Serial.print(" ");          // print separator
         Serial.print(data[i]);  // print each integer sent      
         delay(10);           
       }
      Serial.println();           // terminate the line

      // FINALLY, go to the next data in this overall packet, update sequence number, index to data
      buf.sequence++;  // go to next sequence number
      x += packet_size/sizeof(data[0]);  // and index to next data element (if there is one)
    }
   Serial.println("End...");


Note that this is somewhat complicated by having mixed data types.  It would be easier if you declare a union of datatypes so that you can index them with a simple variable (rather than compensating with sizeof()...

The receive side of this code is left as an exercise to the student...  :D

Tom

javiercuellar

Hi Tom.

Thanks again for your time.

I saw your solution, great!!!!!
I fix a missing )  and changed the datatype of dataLen from byte into int, just in case the size exceeds 256b.


I have 2 questions:

1.  int packet_size = (dataLen <= (MAX_PACKET_SIZE/sizeof(data[0]))? ((dataLen-x)*sizeof(data[0])) : MAX_PACKET_SIZE);
Could you explaine me  the way that works? Basically a C++  tutorial about that instruction :D

Why not only  int packet_size = MAX_PACKET_SIZE / sizeof(data[0];  ???


2. On the other side (receiver) when sending

unsigned int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,57};


I get:
Sequence,=0 pkt_data =1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 0 10 0 11 0 12 0 13 0 14 0 15 0 16 0 17 0 18 0 19 0 20 0 21 0 22 0 23 0 24 0 25 0 26 0 27 0 28
Sequence,=1 pkt_data =29 0 30 0 31 0 32 0 33 0 34 0 35 0 36 0 37 0 38 0 39 0 40 0 41 0 42 0 43 0 44 0 45 0 46 0 47 0 48 0 49 0 50 0 51 0 52 0 53 0 54 0 55 0 56
Sequence,=2 pkt_data =57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57


The code is:

if (radio.receiveDone())
  {

    #define MAX_PACKET_SIZE  56    // let's be conservative and leave room for a more sophisticated header
    
    typedef struct     // use this so that you can precede the data with a separate sequence number field
    {
       uint8_t 
          sequence,
          pkt_data[MAX_PACKET_SIZE];   // BYTE buffer to receive the subset of data to transmit.  You COULD have this be the same data type as your data array (if you change the code below), however it begs for data alignment issues that are beyond the scope of this example.
    } buf;

    buf theData;
       
      theData = *(buf*)radio.DATA; //assume radio.DATA actually contains our struct and not something else
      Serial.print("Sequence,=");     Serial.print(theData.sequence);
      Serial.print(" pkt_data =");  
      for (int i = 0 ; i< sizeof(theData.pkt_data)-1;i++)  
      { Serial.print(theData.pkt_data[i]); Serial.print(" ");}
      Serial.println();


Why the ceros between each number?  (now I'm not worried about the last package  that repeats last value)

Really thanks

Regards

javier

TomWS

Quote from: javiercuellar on April 06, 2016, 04:56:02 PM
1.  int packet_size = (dataLen <= (MAX_PACKET_SIZE/sizeof(data[0]))? ((dataLen-x)*sizeof(data[0])) : MAX_PACKET_SIZE);
Could you explaine me  the way that works? Basically a C++  tutorial about that instruction :D
Look up under 'C' programming, "conditional operator".
Breaking this statement into its parts, we have:
     int packet_size = // variable being assigned a value
                             (dataLen <= (MAX_PACKET_SIZE/sizeof(data[0]))  // logical expression testing if dataLen is less than or equal to MAX_PACKET_SIZE divided by the size of your variable.
                             ?   // CONDITIONAL OPERATOR!  Tests the preceding logical expression and...
                                 ((dataLen-x)*sizeof(data[0]))  // chooses THIS if the expression is true, OR
                                 :                                           // chooses the next thing if the expression is false
                                 MAX_PACKET_SIZE);                // this is what would be chosen


Putting it all together, this statement will set packet_size to dataLen * 2 IF there are fewer than MAX_PACKET_SIZE bytes left to send, OR
will set packet_size to MAX_PACKET_SIZE if there is MORE than a single packet's worth of data to send.
Quote
Why not only  int packet_size = MAX_PACKET_SIZE / sizeof(data[0];  ???
Because this would be wrong.  You want to set packet_size to the amount of data you are actually sending and this could be less than MAX_PACKET_SIZE.  Also, you wouldn't divide by sizeof(data[0]) anyway since MAX_PACKET_SIZE is already in the number of BYTES to send.

Quote

2. On the other side (receiver) when sending

unsigned int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,57};


I get:
Sequence,=0 pkt_data =1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 0 10 0 11 0 12 0 13 0 14 0 15 0 16 0 17 0 18 0 19 0 20 0 21 0 22 0 23 0 24 0 25 0 26 0 27 0 28
Sequence,=1 pkt_data =29 0 30 0 31 0 32 0 33 0 34 0 35 0 36 0 37 0 38 0 39 0 40 0 41 0 42 0 43 0 44 0 45 0 46 0 47 0 48 0 49 0 50 0 51 0 52 0 53 0 54 0 55 0 56
Sequence,=2 pkt_data =57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57 0 2 57


The code is:

if (radio.receiveDone())
  {

    #define MAX_PACKET_SIZE  56    // let's be conservative and leave room for a more sophisticated header
    
    typedef struct     // use this so that you can precede the data with a separate sequence number field
    {
       uint8_t 
          sequence,
          pkt_data[MAX_PACKET_SIZE];   // BYTE buffer to receive the subset of data to transmit.  You COULD have this be the same data type as your data array (if you change the code below), however it begs for data alignment issues that are beyond the scope of this example.
    } buf;

    buf theData;
       
      theData = *(buf*)radio.DATA; //assume radio.DATA actually contains our struct and not something else
      Serial.print("Sequence,=");     Serial.print(theData.sequence);
      Serial.print(" pkt_data =");  
      for (int i = 0 ; i< sizeof(theData.pkt_data)-1;i++)  
      { Serial.print(theData.pkt_data[i]); Serial.print(" ");}
      Serial.println();


Why the ceros between each number?  (now I'm not worried about the last package  that repeats last value)

Really thanks

Regards

javier
Without specifically answering the second question, show me WHERE you are determining how much data was sent in the packet???

And answering the third question, you are sending 2 byte integers but printing single byte values.  The upper (ie, second) byte is cero... 

Tom

javiercuellar

Thanks Tom for your great help.

Attached are the send.ino and receive.ino  so this can be use as an example of anyone learning.

The send.ini will send an   int array of any length, in packages of 56bytes.
It sends a structure having:
   sequence --> the package number, so they can be sorted properly on destination
   pkt_size  --> indicating the size of the package
   pkt_data  --> the data.



The receive will just receive the packages and print them to serial.  Extra programming is needed to sort and print the whole package.


Again, really thanks for you help and time.

Regards

Javier

TomWS

Quote from: javiercuellar on April 07, 2016, 03:26:39 PM
Thanks Tom for your great help.

Attached are the send.ino and receive.ino  so this can be use as an example of anyone learning.

The send.ini will send an   int array of any length, in packages of 56bytes.
It sends a structure having:
   sequence --> the package number, so they can be sorted properly on destination
   pkt_size  --> indicating the size of the package
   pkt_data  --> the data.



The receive will just receive the packages and print them to serial.  Extra programming is needed to sort and print the whole package.


Again, really thanks for you help and time.

Regards

Javier
I recommend two changes to receive.ino:
      // AFTER THE NEXT LINE...
      theData = *(buf*)radio.DATA; //assume radio.DATA actually contains our struct and not something else

      // ADD ERROR CHECK TO MAKE SURE WE RECEIVE THE CORRECT AMOUNT OF DATA
      if (theData.pkt_size != ((radio.DATALEN-2)/sizeof(int)))
      {
         Serial.println("ERROR! received data amount is incorrect!");
         theData.pkt_size = ((radio.DATALEN-2)/sizeof(int));  // fix it!
      }


and, going to powerDown without putting the radio to sleep will not save much power.
You 'could' put the radio to sleep with the following code:
  
  radio.sleep();  //put radio in SLEEP mode
  Serial.flush(); //make sure all serial data is clocked out before sleeping the MCU
  LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_ON); //sleep Moteino in low power mode (to save battery)

But then you won't be able to receive for 8 seconds while everything sleeps (other than WDT).  So that may not work for you...

Tom

javiercuellar

Thanks Tom.

I will  put the error check.

Regarding the sleep, that pieze of program is from the  TxRxBlinky example: there is no sleep in the example, so maybe the owner of the example could correct it   ;)

Regards

Javier