Sending Multiple Strings

Started by dale.s, June 11, 2015, 05:52:17 PM

dale.s

In my project i need to send variables for several sensors, sadly i end up way over the 69 char limit.

If i try to do 2 send with no delay, only one is received.  If i add a 5 second delay then they are both received, however when i try to lower this value i get varied results, sometimes first one is received, sometimes the second one.

Im guessing the second send is happening while the radio is transmitting the first send, is there a function to call to see if the radio is busy?

example of what im trying to do
sprintf(sendBuf, "%s servo:%d set:%s pit:%s p1:%s p2:%s p3:%s", UPstr, servoPercent, str_setpoint, str_pit_temp, str_temp1, str_temp2, str_temp3);
sendLen = strlen(sendBuf);
radio.sendWithRetry(GATEWAYID, sendBuf, sendLen);

//delay(1000);

sprintf(sendBuf2, "pit:%s p1:%s p2:%s p3:%s", str_pit_temp, str_temp1, str_temp2, str_temp3);
DEBUGln(sendBuf2);
sendLen2 = strlen(sendBuf2);
radio.sendWithRetry(GATEWAYID, sendBuf2, sendLen2);


(yes i know im sending the same data twice, i had to cut back to get into one send for testing).

id like to be able to do something like
while(radio.busy()) {
delay 10;
}
instead of a fixed delay.

Or am i way off base and something else is happening?

TomWS

Quote from: dale.s on June 11, 2015, 05:52:17 PM
In my project i need to send variables for several sensors, sadly i end up way over the 69 char limit.

If i try to do 2 send with no delay, only one is received.  If i add a 5 second delay then they are both received, however when i try to lower this value i get varied results, sometimes first one is received, sometimes the second one.

Im guessing the second send is happening while the radio is transmitting the first send, is there a function to call to see if the radio is busy?

example of what im trying to do
sprintf(sendBuf, "%s servo:%d set:%s pit:%s p1:%s p2:%s p3:%s", UPstr, servoPercent, str_setpoint, str_pit_temp, str_temp1, str_temp2, str_temp3);
sendLen = strlen(sendBuf);
radio.sendWithRetry(GATEWAYID, sendBuf, sendLen);

//delay(1000);

sprintf(sendBuf2, "pit:%s p1:%s p2:%s p3:%s", str_pit_temp, str_temp1, str_temp2, str_temp3);
DEBUGln(sendBuf2);
sendLen2 = strlen(sendBuf2);
radio.sendWithRetry(GATEWAYID, sendBuf2, sendLen2);


(yes i know im sending the same data twice, i had to cut back to get into one send for testing).

id like to be able to do something like
while(radio.busy()) {
delay 10;
}
instead of a fixed delay.

Or am i way off base and something else is happening?
Is there some reason that you need to send formatted data?  If you send as a single packed structure all the data should fit into a single transmission.  I suspect that most of your data is natively two or four bytes each so you can pack a lot of data into a 64 byte packet.

I'd be happy to suggest a packet if you let us know what the native data type is (before converting to character strings).

dale.s

Id love a example of what your talking about.  I was going for this format as im going to be using a PiGateway style hub, but i know i can change things around on that end once i know what direction to go.
I have one string with 9 chars, then 8 floats and 1 int.

ssmall

I think Dale raises an interesting issue.  If one chooses to use Felix's framework strings are required. Structs are not supported.  If one needs to send a string longer than 64 characters the logical thing to do would be to break up the string into two transmissions.  Would anyone be able to shed some light on why it seems you need to put a delay between the transmissions?

TomWS

Quote from: ssmall on June 11, 2015, 08:32:10 PM
I think Dale raises an interesting issue.  If one chooses to use Felix's framework strings are required. Structs are not supported.  If one needs to send a string longer than 64 characters the logical thing to do would be to break up the string into two transmissions. 
Good point.  It's one of those double edged swords of 'frameworks', the framework has some overhead and, with such a constrained packet size, there needs to be a construct that allows spanning multiple packets.  Sounds like an interesting project.  XML anyone?  :D
Quote from: ssmall on June 11, 2015, 08:32:10 PMWould anyone be able to shed some light on why it seems you need to put a delay between the transmissions?
I think that depends on the implementation of the gateway.  Once it gets a single packet, it needs to do 'something' with it.  If that 'something' takes some time, then don't plan on sending anything sooner than the recovery time for the first 'something'.  If the gateway quickly queues the data to another process then 'somethings' are quick.  If not, well, then...

Tom


Felix

Guys, remember - the gateway software stack implementation is completely separated from the hardware implementation.
How the RF is handled is abstracted in the gateway Moteino node itself, nobody else cares (well except the end nodes who want to talk to it).
As proven by the Gateway and Node examples in the RFM69 lib, sending large amounts of data very rapidly is possible (two ways with ACK each way). So when dale.s said that 5sec works but less doesn't work reliably I think there's something else going on.

You can certainly use a struct approach instead of strings. Look at the struct send/receive examples. Pack your data as structs instead of plain strings.
The default example stack uses strings because it makes it easier to graps for the wide audience. And it's used as a learning tool, so hopefully the curve to learn it is not that steep.
The radio itself can do 61 bytes (+5 header = 66 total) of RF data in 1 packet. If you drop the packet approach it can do unlimited. But that also drops other features like CRC/encryption (which are really nice hardware abstracted features we don't have to worry in the lib). So again ... the default packet approach given in the library is to make it as easy as possible for the beginners to understand and start using with only a few function calls to the lib. We could really make the radio a lot more flexible and add many complex features supported by the RF chip, but it would also mean the lib would be far harder to use and understand. I think what is given as default, is really a great starting point for everyone. If you need to send data in a different way, just use structs, the lib doesn't have to change. If you want to set different settings, you can use the predefined functions like setFrequency() or access the registers directly and do your own magic. The possibilities are there.

I hope this wall of text makes sense.

syrinxtech

Dale, if it helps any I've attached a snippet of code from my project.  I'm sending three different structures of data over the Moteino wireless.  I'm using a C union to allow me to refer to all three as one variable and it makes the coding easier (not necessarily to understand but from an efficiency standpoint).

enum PKTTYPE {
  THP=0,
  WIND,
  ELEC
}; 

struct THPStruct {
  float temperature;
  float humidity;
  float pressure;
};

struct WindSpeedStruct {
  float windspeed;
};

struct VoltageCurrentStruct {
  float LipoVoltage;
  float LipoCurrent;
  float SolarVoltage;
  float SolarCurrent;
  float LoadVoltage;
  float LoadCurrent;
};

typedef struct WirelessData {
  PKTTYPE type;
  union {
    struct THPStruct env;
    struct WindSpeedStruct wind;
    struct VoltageCurrentStruct elec;
  } data;
} WData;


You could easily add strings into any of the structures that you need.

abraxas1

this is a great discussion, thanks. i need to switch to structured packets, but i love the easy readability of ascii messages while developing...
i assume the 61 byte limit still applies to structures, so just design each structure to be less and send them separately?

is the size of a structure always the same even if all the elements haven't been filled?

thanks


Felix

Quote from: abraxas1 on June 18, 2015, 01:47:16 AM
this is a great discussion, thanks. i need to switch to structured packets, but i love the easy readability of ascii messages while developing...
i assume the 61 byte limit still applies to structures, so just design each structure to be less and send them separately?

is the size of a structure always the same even if all the elements haven't been filled?

thanks
Yes and yes

TomWS

Quote from: abraxas1 on June 18, 2015, 01:47:16 AM
this is a great discussion, thanks. i need to switch to structured packets, but i love the easy readability of ascii messages while developing...
i assume the 61 byte limit still applies to structures, so just design each structure to be less and send them separately?

is the size of a structure always the same even if all the elements haven't been filled?

thanks
The construction of syrinxtech's packet is a structure that has its first element identifying what type of structure the packet contains, ie THP, WIND, or ELEC, and only one type can be sent per structure.
Since the structure being sent is a union, the size is the maximum of all of the elements in the structure and it is fixed.  When receiving, you would look at the type element to determine which type of packet it is.  As in:
  switch (pkt.type) {
    case THP:
        ...
        break;
    case WIND:
       ...
       break;
    case ELEC:
       ...
       break;
    default:    // unknown packet!!
      break;
   }


If you want to send variable length packets then you could modify the original definition of each structure to include the 'type' element in each one, then send the individual structure using it's sizeof() in the len field, and then looking at the first byte will again let you determine which type of packet it is.

As in:
enum PKTTYPE {
  THP=0,
  WIND,
  ELEC
}; 

struct THPStruct {
 PKTTYPE type;
  float temperature;
  float humidity;
  float pressure;
} thp;

struct WindSpeedStruct {
  PKTTYPE type;
 float windspeed;
} wind;

struct VoltageCurrentStruct {
 PKTTYPE type;
  float LipoVoltage;
  float LipoCurrent;
  float SolarVoltage;
  float SolarCurrent;
  float LoadVoltage;
  float LoadCurrent;
} elec;

  // example sending THP data:
  thp.type=THP;    // you only need to do this once since it never changes for this variable
  thp.temperature=t;
  thp.humidity=rh;
  thp.pressure=p;
  radio.sendWithRetry(GATEWAY, &thp, sizeof(thp));

  The downside of this approach is that you have to recast the packet on the receiving side since the type will change based on the 'type' element's value but it is more efficient from a code (recasting doesn't add executable code) and transmission size perspective.

Tom

Felix

#10
Tom is of course correct and comprehensive as usual ;)
Bytes in C++ are just a bunch of 1s and 0s (bits) grouped in 8 bits for each byte. How you interpret that information it's up to you. This is what is hard to get in programming. People think strings are different than integers. Not really, at a memory level they are all bytes. You can look at the bytes in the string and chop them up as integers, or bytes, or chars, or unsigned longs, or STRUCTS!
What is my point? That a struct is just a bunch of bytes. If you expect a struct then it's again up to you how you interpret that struct, and here's where Tom's answer explained it plainly. Make your structs smart and dynamic. Read the first byte that tells you the type of struct. That also gives the length (calculate the length of the struct type after identification). Then you read the next N bytes for that structure, and if you expect more in the same 61 bytes, go on reading more the same way.

abraxas1

great, thanks for the help. i'll move to structured packets soon because it's the slick thing to do. and now i know how they can be more dynamically sized for delivery, i'm good.
such great tips laying all around this forum, i've got to form a cue to tackle them all.

now to tackle ultra low power listening mode....