LowPowerLab Forum

Hardware support => Moteino => Topic started by: LesB on December 09, 2014, 11:01:59 PM

Title: Getting started with sending radio/wireless payloads
Post by: LesB on December 09, 2014, 11:01:59 PM
I'm a noob at coding, have completed one Arduino project so far.

Now, I'm building a remote temperature logging system.  I have my Modeino successfully working with a sample sketch for the MLX90614 IR temperature detector over an I2C interface.  I can get the same Modeino to communicate with a partner using the Node and Gateway sample sketches on this site.  So far so good.

Next step is to get my Modeino at the node end to perform the node and I2C functions simultaneously.  And that will involve some coding on my part.  I tried to backwards-engineer the Node sketch to figure out the process for stuffing data over the radio, but I'm baffled.  Looks like the data to be transmitted needs to be put into a "payload" variable.  But I'm not getting the whole process. 

Can you name a source for me to read up on this process?  I'm assuming that it's a little involved to explain here on the forum.  Maybe not.

Thanks,
LesB
Title: Re: Radio coding
Post by: Reck_law on December 10, 2014, 11:31:01 AM
I am not sure if this will help.  I think what you need to do is modify the payload structure to me the same on both the node and gateway.  Then you put the data to be sent in the payload.  The gateway will recieve the data and you can use it as needed.  Look at the mail box code.
Title: Re: Radio coding
Post by: Felix on December 10, 2014, 01:47:01 PM
I think you're looking for something like this, (taken from the node  (https://github.com/LowPowerLab/RFM69/blob/master/Examples/Node/Node.ino)example sketch):

sprintf(buff, "FLASH_MEM_ID:0x%X", flash.readDeviceId());
byte buffLen=strlen(buff);
if (radio.sendWithRetry(GATEWAYID, buff, buffLen))
   Serial.print(" ok!");


First line will generate the payload - it writes a message with a variable in the middle of the message. For more such formatting and data types check the printf format definition (http://www.cplusplus.com/reference/cstdio/printf/).
Second line gets the payload length, as it was generated. This is needed to pass on to the radio so it knows how many bytes to read from the buffer.
Third line is sends the message to the GATEWAYID. The 2nd and 3rd parameters are the payload and the payload length.
The sendWithRetry function will retry up to a default of 3 times to send the message, each time waiting for a ACKnowledgement from the receiver. If the ACK is received, it will return TRUE and the "ok" message is printed.

You can generate your own payloads like this, with as many variables as you need. You can use integers, decimals, etc. Just get familiar with the printf syntax. For instance to put an unsigned integer in your message you would use a %u. Use the strlen(buff) function to get the length of your generated buffer, then pass them to the radio send/sendWithRetry() function. I don't think it's that hard, except the first time ;)
Title: Re: Getting started with sending radio/wireless payloads
Post by: ggallant on December 10, 2014, 07:10:55 PM
You control the data format at both ends. The radio transmits/receives raw bytes. There is nothing stopping you from transmitting binary data. There are some practical limitations on message length, especially if you enable encryption.
Title: Re: Getting started with sending radio/wireless payloads
Post by: TomWS on December 11, 2014, 12:10:14 AM
In Felix's example, you can save a line (and a bit of processor time) if you take advantage of the fact that sprintf returns the string length already:

byte buffLen=sprintf(buff, "FLASH_MEM_ID:0x%X", flash.readDeviceId());
if (radio.sendWithRetry(GATEWAYID, buff, buffLen))
   Serial.print(" ok!");


And, in ggallant's case, an example would be:

struct PAYLOAD {
   byte len;
   byte type;
   struct SOME_KIND_OF DATA_PACKAGE {
    ...
   } data;
} payload;

payload.len = sizeof(payload);
payload.type = SOME_KIND_OF_DATA_PACKAGE_TYPE;
payload.data = { ... };
if (radio.sendWithRetry(GATEWAYID,(byte *)&payload, payload.len))
   Serial.print(" ok!");


On the receiving end you would have the same struct declaration and simply cast the radio.DATA as a struct of that type (probably using pointers to the data is less cumbersome from a coding perspective). You can check the integrity of the message by comparing radio.DATALEN with payload.len or, alternatively, send multiple different structures if you are consistent in keeping .len and .type members at the beginning of every struct.

Tom

Title: Re: Getting started with sending radio/wireless payloads
Post by: LesB on December 12, 2014, 10:04:12 PM
Thanks for the helpful hints everyone.
I'm having to do other stuff right now, holidays & stuff, you know.  When I get back on to this I'll report in.
LesB
Title: Re: Getting started with sending radio/wireless payloads
Post by: LesB on December 30, 2014, 08:10:52 PM
Farther along on my project, having solved the problem above, and posting a problem farther down the line.

In Gateway.ino I get the transmitted data in volatile byte radio.data.
To work with the data I need to pass it to a non-volatile byte.
I understand that C++ has no built-in workaround on this.

Suggestions on how to do this?

Thanks,
LesB
Title: Re: Getting started with sending radio/wireless payloads
Post by: TomWS on December 30, 2014, 08:38:29 PM
Quote from: LesB on December 30, 2014, 08:10:52 PM
Farther along on my project, having solved the problem above, and posting a problem farther down the line.

In Gateway.ino I get the transmitted data in volatile byte radio.data.
To work with the data I need to pass it to a non-volatile byte.
I understand that C++ has no built-in workaround on this.

Suggestions on how to do this?

Thanks,
LesB
I'm not sure what you're saying here.  Are you getting compiler errors/warnings because you're trying to pass this data somewhere else?

The KEY to 'volatile' keyword is that you're telling the compiler that this value could change at any time so don't assume that if the compiler moves it to a register, it hasn't changed before its referenced again.  However, IF YOU KNOW that the only time it changes is when you've called a 'radio' method, then the secret sauce is to 'consume' the data BEFORE you make the next call to a radio method.

For example:

    if (radio.receiveDone()) {
       // oh boy! We've got data!!! Let's save it to my own buffer!
       memcpy(myOwnBuffer, radio.DATA, radio.DATALEN);
       ...

seems like the right thing to do, and it is...  The only problem is that the compiler will tell you that it CAN'T do this!  Why? Because memcpy needs CONSTANT data to copy it and radio.DATA is 'volatile'!  But, guess what?  YOU KNOW that the data isn't gonna change at this point so you have to tell the compiler this.  Here's how:

    if (radio.receiveDone()) {
       // oh boy! We've got data!!! Let's save it to my own buffer!
       memcpy(myOwnBuffer,(const void *) radio.DATA, radio.DATALEN);
       ...

This tells the compiler that the data REALLY is constant and the compiler will do what you want...

Further, IF you will need to have anything else from this particular reception, then literally grab it while you can 'cause it ain't gonna last once you call another radio. method.  So, for example:

    if (radio.receiveDone()) {
       // oh boy! We've got data!!! Let's save it to my own buffer!
       memcpy(myOwnBuffer, (const void *)radio.DATA, radio.DATALEN);
       datalen = radio.DATALEN;
       sender = radio.SENDERID;
       target = radio.TARGETID

      if (radio.AckRequested()) {
         radio.sendAck();
      ...

where datalen, sender, target, are all variables that you've defined in your sketch and you're saving them even before you send the Ack.  Why?  Well, if the sender sends something immediately after Ack, the SENDERID, etc, MAY have changed before you even get to parse the original message...

Have fun.  This isn't too hard if you keep asking questions...

Tom





Title: Re: Getting started with sending radio/wireless payloads
Post by: LesB on January 19, 2015, 07:18:19 PM
OK, so in TomWS's line above:
memcpy(myOwnBuffer,(const void *) radio.DATA, radio.DATALEN);
what kind of variable is myOwnBuffer?  I'm having a time of it trying to use it.

What I'm looking to do with this data is:

1. Put it to Serial.print, which I can do already with:

for (byte i = 0; i < radio.DATALEN; i++)
Serial.print((char)radio.DATA);

2. Stuff the streaming data sequentially into the onboard 4G flash

3. Send the most recently received data to an LCD via an I2C interface

So what I'm ultimately trying to accomplish now is get data from radio.DATA to do 2 and 3 above.

Thanks. 
This is fun, but it would be more so if I had more coding experience.
LesB
Title: Re: Getting started with sending radio/wireless payloads
Post by: TomWS on January 19, 2015, 08:50:09 PM
Quote from: LesB on January 19, 2015, 07:18:19 PM
OK, so in TomWS's line above:
memcpy(myOwnBuffer,(const void *) radio.DATA, radio.DATALEN);
what kind of variable is myOwnBuffer?  I'm having a time of it trying to use it.

What I'm looking to do with this data is:

1. Put it to Serial.print, which I can do already with:

for (byte i = 0; i < radio.DATALEN; i++)
Serial.print((char)radio.DATA[i]);


2. Stuff the streaming data sequentially into the onboard 4G flash

3. Send the most recently received data to an LCD via an I2C interface

So what I'm ultimately trying to accomplish now is get data from radio.DATA to do 2 and 3 above.

Thanks. 
This is fun, but it would be more so if I had more coding experience.
LesB
Les,
I've modified your post slightly by putting the coding example inside a 'code' bracket (see '#' symbol on the second command line above post).  This will keep your code intact (so subscripted variables show up).

So, having done that, I'm not sure where your struggles are. You know how print each individual byte, what do you need to know to accomplish 2 & 3?  My suggestion is that you save the incoming data into your own buffer BEFORE you sendACK() and then you can use your own buffer to save to
"2. Stuff the streaming data sequentially into the onboard 4G flash

3. Send the most recently received data to an LCD via an I2C interface"

Re your question what type is 'myOwnBuffer', I'd say the easiest to work with is a byte array, so the declaration would be:
byte myOwnBuffer[MAX_NUM_BYTES_I_COULD_GET];

Note, with Moteino RFM69 radio, you'll never get more than 64 bytes in a transmission, so that's a good place to start.  What else do you need to know?

Tom
PS: I'm glad you're having fun.  That's VERY important!  Seriously.
Title: Re: Getting started with sending radio/wireless payloads
Post by: LesB on January 21, 2015, 11:31:51 PM
Maybe instead of composing a question I really need to post what I'm doing along with the admonishment the compiler spits out at me and let you formulate the question and answer.
In this example I'm trying to utilize suggested code for converting volatile data:

byte myOwnBuffer;///
memcpy(myOwnBuffer,(const void *) radio.DATA, radio.DATALEN);///

error :
Gateway_4:140: error: invalid conversion from 'byte' to 'void*'
Gateway_4:140: error: initializing argument 1 of 'void* memcpy(void*, const void*, size_t)'


In this next example I'm trying to utilize the suggested code for the byte array:

byte myOwnBuffer[radio.DATALEN]; ///
for (byte i = 0; i < radio.DATALEN; i++)///
myOwnBuffer[0]=(const void*)radio.DATA;

error:
Gateway_4.ino: In function 'void loop()':
Gateway_4:140: error: invalid conversion from 'const void*' to 'byte'
Title: Re: Getting started with sending radio/wireless payloads
Post by: Felix on January 22, 2015, 09:01:59 AM
Maybe you wanted something like:

byte myOwnBuffer[radio.DATALEN];
for (byte i = 0; i < radio.DATALEN; i++)
myOwnBuffer[i]=radio.DATA[i];
Title: Re: Getting started with sending radio/wireless payloads
Post by: TomWS on January 22, 2015, 02:53:49 PM
Quote from: LesB on January 21, 2015, 11:31:51 PM
Maybe instead of composing a question I really need to post what I'm doing along with the admonishment the compiler spits out at me and let you formulate the question and answer.
In this example I'm trying to utilize suggested code for converting volatile data:

byte myOwnBuffer;///
memcpy(myOwnBuffer,(const void *) radio.DATA, radio.DATALEN);///

error :
Gateway_4:140: error: invalid conversion from 'byte' to 'void*'
Gateway_4:140: error: initializing argument 1 of 'void* memcpy(void*, const void*, size_t)'

In this case you are only allocating a single byte scalar storage location (myOwnBuffer is only 1 byte in size) and trying to reference it as an array (or pointer).  If the compiler didn't flag this as an error, you'd have a real MESS if you actually executed the code (all the storage after myOwnBuffer would get overwritten).
Quote from: LesB on January 21, 2015, 11:31:51 PM

In this next example I'm trying to utilize the suggested code for the byte array:

byte myOwnBuffer[radio.DATALEN]; ///
for (byte i = 0; i < radio.DATALEN; i++)///
myOwnBuffer[0]=(const void*)radio.DATA;

error:
Gateway_4.ino: In function 'void loop()':
Gateway_4:140: error: invalid conversion from 'const void*' to 'byte'

In this case, Felix's suggestion is correct.  You need to index into both arrays with 'i', copying one byte from one array, into one byte of the other.  In this case, memcpy would have worked too:

byte myOwnBuffer[radio.DATALEN];
memcpy(myOwnBuffer,(const void *)radio.DATA, radio.DATALEN);

Note that 'myOwnBuffer' can ONLY be a local array, dynamically allocated at the point that the value of radio.DATALEN is known.  Otherwise the compiler won't know how much storage to allocate for it.  I'm also not sure if you could get away with this if it was pure 'C' (and not C++ code).

Finally, don't be discouraged that you're struggling with this.  The interchangeability of pointers and arrays and the distinction between an array and scalar value in 'C' confuses practically everyone new to 'C'.

Tom
Title: Re: Getting started with sending radio/wireless payloads
Post by: LesB on January 23, 2015, 08:52:32 PM
Success!

Got both TomTS's method and Felix's method to work.  Just having to understand some basics...

BTW, I am working my way through "Beginning Programming With C++ for Dummies".  At some point I might catch up to where I need to be in this project.

In the mean time, I'm heading over to another part of the forum to get some basics on reading/writing to the onboard flash.

What I'm up to here is building a system to monitor wheel rim temperatures on the front and rear wheels of a bicycle once per second with an i2C  IR temperature sensor that connects to Moteino's right there at the wheels.  Then sending that data via RF from the wheels to a receiving Moteino on the handlebar with an LCD to display the temperature.  At the same time it will stuff the 1/sec. readings to the onboard FLASH.

Once completed I will open-source the project as a wireless temperature monitoring & logging system for general usage on Instructables.com

Thanks for the help!
Title: Re: Getting started with sending radio/wireless payloads
Post by: TomWS on January 23, 2015, 09:09:41 PM
Quote from: LesB on January 23, 2015, 08:52:32 PM
Success!

Got both TomTS's method and Felix's method to work.  Just having to understand some basics...

BTW, I am working my way through "Beginning Programming With C++ for Dummies".  At some point I might catch up to where I need to be in this project.

In the mean time, I'm heading over to another part of the forum to get some basics on reading/writing to the onboard flash.

What I'm up to here is building a system to monitor wheel rim temperatures on the front and rear wheels of a bicycle once per second with an i2C  IR temperature sensor that connects to Moteino's right there at the wheels.  Then sending that data via RF from the wheels to a receiving Moteino on the handlebar with an LCD to display the temperature.  At the same time it will stuff the 1/sec. readings to the onboard FLASH.

Once completed I will open-source the project as a wireless temperature monitoring & logging system for general usage on Instructables.com

Thanks for the help!
OOOOOOH! That sounds like a fun project!  Is temperature all you need to monitor to (presumably) monitor tire pressure?  That's really interesting!  I'd have thought that you'd have to actually measure pressure, and, given that, what 'use' is that info?  ie, what would you adjust knowing what you'd learn from this?

Tom
PS: I'm glad you're making progress. 
Title: Re: Getting started with sending radio/wireless payloads
Post by: LesB on February 05, 2015, 12:31:31 AM
First of all, the deal is, besides being an avid cyclist, I'm just a real geek.

With rim brakes on a bike, the bike is slowed by transferring the energy from forward motion into heat, which gets dumped into the aluminum rims.  On a technical descent like the one linked below, the rims can get in the vicinity of 200F.  Being the geek I am, I am interested in logging this heat and charting it in Excel. 

And now that you mention it, tire pressure is a definite point of interest.  Heat from the rim will transfer to the air inside the tire, and to the tire itself. The air will expand from the heat and the rubber in tire will be weakened.  This creates a situation where the tire can have a critical failure.  There have been serious crashes from cyclists having a tire failure during a fast descent.

So, yes now that you mention it, tire pressure will be a good addition, which I will add after I  have the system operating with temperature logging.

https://www.youtube.com/watch?v=MBYg36UtR3s&feature=youtu.be

Now for my next question regarding RF: 
Since I will have 2 nodes sending from separate locations, I plan on having two gateway Moteinos on the handlebar, one to receive from each of the nodes. 

Question is, is it possible to have just one gateway Moteino and have each of the nodes respond only when requested by the gateway?
Title: Re: Getting started with sending radio/wireless payloads
Post by: TomWS on February 05, 2015, 08:05:34 AM
Quote from: LesB on February 05, 2015, 12:31:31 AM
<...snip>
Now for my next question regarding RF: 
Since I will have 2 nodes sending from separate locations, I plan on having two gateway Moteinos on the handlebar, one to receive from each of the nodes. 

Question is, is it possible to have just one gateway Moteino and have each of the nodes respond only when requested by the gateway?
ABSOLUTELY!  One Moteino as a data collector and (presumably reporter to the rider) and a mote on each tire measuring and sending their data to the single collector is trivial.  The hard part is the interface design, the SW is a piece of cake. 

Basically, your bike (am I allowed to call your high tech machine that?) will have its own 'network' with three devices.  The central one on the handlebars isn't a 'gateway'.  It's simply another node in the network that the other two devices talk to.  As long as they 'know' the central mote's node id, they can each carry on their individual 'conversations' with it and the central mote 'knows' who it's talking to from the SENDERID...

Easy peasy   :D
Tom
AND I was right, a REALLLLLY cool project!