I'm sending values from a RFM69 sender to a receiver.
Ad the moment it is done with "struct":
typedef struct __attribute__((__packed__)) {
int16_t nodeId; // Store this nodeId
float temp; // Temperature reading
int16_t vcc; // Battery voltage
byte tx; // Transmit level
float lx; // Light level
int16_t values[POINTS];
} Payload;
Payload theData;
The problem here is the struct needs to look exactly the same on the other side.
Thats not practical because every sender has other sensors/data.
I'm looking for a way to send that struct as a string:
val1,val2,val3,val4...
And on the other side it should not matter how many values.
That means splitting that string up on the other side.
If the first value is always the sender ID and i know in what
order the values are, i can work with them on the receiver.
I also tried it with "dtostrf" and "sprintf" but can't get it to work.
Sender:
float temp = 5;
char str_temp[6];
char sendBuf[32];
byte sendLen;
void loop() {
sensors.begin(); // Start Dallas Temperature library
sensors.requestTemperatures(); // Get the temperature
theData.temp = (sensors.getTempCByIndex(0)); // Read first sensor
temp = (sensors.getTempCByIndex(0));
dtostrf((sensors.getTempCByIndex(0)), 4, 2, str_temp);
sprintf(sendBuf, "T: %s", str_temp);
sendLen = strlen(sendBuf);
radio.sendWithRetry(GATEWAYID, sendBuf, sendLen);
Sleepy::loseSomeTime(5000);
}
Receiver:
void loop() {
if (radio.receiveDone())
{
for (byte i = 0; i < radio.DATALEN; i++)
Serial.print((char)radio.DATA[i]);
}
Serial.println();
}
}
But that doesn't work. I only receive -127.
Maybe because the receiver is 32 bit and i send from a 8 bit Arduino?
I know for int i have to use "int16_t" but float just works in the struct 8<>32bit.
If you need to transfer arbitrary data structures, there are various methods for doing that. ASN/BER is very compact, but JSON is better supported.
In any case, once a C++ program is compiled, all the names of structure elements disappear - they are just compiled in as numerical offsets into the structure. There's no magic way of sending a stucture - any structure - of unknown format over a stream and having the receiver reconstitute it.
How about defining a structure that contains a union of all the different sender configurations? The first element of the structure would be the Sender ID followed by the union. The structure would always be the same size, determined by the largest element of the union. But, that’s still probably more efficient than sending strings because numerical values would go as binary.
I wish i would understand that stuff like others. I'm lost
As i said it works this way:
Sender:
/**************************************************************
* DATA STRUCTURE TO BE SENDE *
**************************************************************/
typedef struct __attribute__((__packed__)) {
uint16_t nodeId; // Store this nodeId
float temp; // Temperature reading
uint16_t vcc; // Battery voltage
byte tx; // Transmit level
float lx; // Light level
uint16_t values[POINTS];
} Payload;
Payload theData;
/**************************************************************
* SEND PAYLOAD DATA *
**************************************************************/
static void rfwrite() {
radio.receiveDone();
if (radio.sendWithRetry(GATEWAYID, (const void*)(&theData), sizeof(theData), RETRY_LIMIT, ACK_TIME)) {
if (DEBUG) {
Serial.print("ACK received! ");
Serial.print("Sending ");
Serial.print(sizeof(theData));
Serial.print(" bytes...");
Serial.print(", TX level: ");
Serial.print(radio._transmitLevel,DEC); // current transmit level used by this sender
Serial.print(", RSSI: ");
Serial.println(radio.getAckRSSI(),DEC); // this sender's RSSI acked back from the receiving node (Gateway)
Serial.flush();
}
radio.sleep(); // Put radio to sleep
} else {
if (DEBUG) {
Serial.println("No ACK response!");
Serial.flush();
}
Sleepy::loseSomeTime(RETRY_PERIOD * 1000); // If no ack received wait and try again
}
}
void loop() {
digitalWrite(ONE_WIRE_POWER, HIGH); // Turn DS18B20 on
delay(5); // Allow 5ms for the sensor to be ready
sensors.begin(); // Start Dallas Temperature library
sensors.requestTemperatures(); // Get the temperature
theData.temp = (sensors.getTempCByIndex(0)); // Read first sensor
digitalWrite(ONE_WIRE_POWER, LOW); // Turn DS18B20 off
theData.nodeId = NODEID;
theData.tx = radio._transmitLevel; // current transmit level used by this sender
theData.vcc = readVcc(); // Get battery voltage
rfwrite(); // Send data via RF
Sleepy::loseSomeTime(90000);
}
Receiver:
typedef struct __attribute__((__packed__)) {
uint16_t nodeId; // Store this nodeId
float temp; // Temperature reading
uint16_t vcc; // Battery voltage
byte tx; // Transmit level
float lx; // Light level
uint16_t values[POINTS];
} Payload;
Payload theData;
void loop() {
if (radio.receiveDone()) {
if (radio.DATALEN != sizeof(Payload)) {
Serial.print("Invalid Payload received!");
} else {
theData = *(Payload*)radio.DATA; //assume radio.DATA actually contains our struct and not something else
if (DEBUG) {
Serial.print("Node:");
Serial.print(theData.nodeId);
Serial.print(", Temp:");
Serial.print(theData.temp);
Serial.print(", Lux:");
Serial.print(theData.lx);
Serial.print(", Vcc:");
Serial.print(theData.vcc);
Serial.print(", TX Level:");
Serial.print(theData.tx);
Serial.print(", RSSI:");
Serial.print(radio.RSSI);
}
}
if (radio.ACKRequested()) { // When a node requests an ACK, respond to the ACK
radio.sendACK();
Serial.print(" - ACK sent.");
}
Serial.println();
}
}
sprintf() and dtostrf() work exactly as the documentation states, and make it quite easy to format numbers into character arrays (C-strings).
What sort of problems have you encountered with them?
PS: there is really no reason to send a float value from a sensor. Ints have all the range you normally need (4 orders of magnitude). Instead of a float, send an integer that is the float value*100, if you think you need to preserve a couple of decimal places.
I still don’t understand why you insist on sending strings. It’s inefficient. Remember there is a limit on RFM69 packet size. And, you say the technique using structures is working. Why not expand it to meet your needs rather than inventing a new way?
Suggestions:
* If your sender and receive units are different processor types (ESPECIALLY if one is 8-bit and the other is 32-bit), then make each element in the structure a multiple of 4 bytes long. That will assure that the elements are aligned along word boundaries for the 32-bit device. Also, get rid of the “packed” attribute in the structure definition. It may provide unpredictable results when TX and RX units are different processor architectures.
-- So, all integers should be type int32_t or uint32_t.
-- Type float is 4 bytes, so you’re probably OK. But, as has been mentioned already, you’re better off not using floats. For temperature, just send (int32_t) (temp* 10.0 + 0.5). The Dallas temp sensor won’t give you better than 0.1 accuracy / precision anyway.
-- Scale the light level into a uint32_t in a similar manner. In this case I’m assuming light levels are only positive, otherwise use an int32_t.
* If the different sensors provide different amounts/types of data, then use a union of structures inside the main structure as I suggested. Make the senderNumber a uint32_t (rather than a uint8_t as I originally suggested) to maintain word alignment. If you don’t understand structures and unions, buy a book on C programming or find an online tutorial.
* Why are you sending the TX power setting in the data packet? What good is that information to the RX?
* Why are you calling sensors.begin() every time through loop()? Shouldn’t that be done once in setup()?
Mr glasspoole I solved it by sending integers and using a switch statement on the other end to detect which device it is.
Also the receiver can read signal levels so no need to send them.
gfvalvo:
Also, get rid of the “packed” attribute in the structure definition.
I thought "Data structure alignment" is used if you have 8bit, 16bit and 32bit processors mixed?
Is there something wrong using "int32_t" on numbers from 0-255?
I was reading this: Data Types in Arduino
and thought always use the least possible data type.
gfvalvo:
sending strings. It's inefficient
Is that always the case?
I see a lot people using JSON.
Especial if they build a gateway and send the data to a server.
gfvalvo:
For temperature, just send (int32_t) (temp* 10.0 + 0.5).
Is this correct:
int t = (sensors.getTempCByIndex(0)*10);
if (t >= 0) {
theData.temp = (t + 0.5);
} else {
theData.temp = (t - 0.5);
}
EDIT
No because 26.38 prints 26.3 instead of 26.4 EDIT
gfvalvo:
Why are you sending the TX power setting in the data packet? What good is that information to the RX?
It is not for the receiver it is information for me.
gfvalvo:
Why are you calling sensors.begin() every time through loop()? Shouldn’t that be done once in setup()?
After sleeping the whole Arduino and powering down the DS18B20 you need to do that.
The loop is running just once before the machine sleeps for 90 seconds.
aot2002:
Also the receiver can read signal levels so no need to send them.
When mixing 8, 16, 32 and so on environments you need to take into account if processors are big endian or little endian. E.g. an int16 with the value 255 can either be stored as 0x00 followed by 0xFF or the other way around.
When sending it to an environment with another endianess, you need to swap the bytes.
You need to rephrase the question in reply #11. Show both versions of the struct as well so we can see what you mean.
Yes the size is what i already wrote sending 16 bytes (int32) vs 8 bytes (int16).
I still don't get the math.
If 4 times 2 bytes is a multiple
then
8 times 1 byte is also - isn't it?
gfvalvo wrote:
So, all integers should be type int32_t or uint32_t.
but it also works with in16 and that means sending less...
I did see somebody doing this:
#define POINTS 5 // number of values to send at each transmission
typedef struct __attribute__((__packed__)) {
long timestamp;
uint8_t period;
int16_t values[POINTS];
} Payload;
Payload theData;
It seems like then it also works with int8?
What does the POINTS do and where does the 5 come from?
I suggested using all int32_t to ensure proper alignment for more general struct definitions. I'm guessing the compiler placed your struct containing int16_t members starting on a word boundary in the 32 bit machine. So the alignment worked out. Try this:
typedef struct {
uint8_t nodeId; // Store this nodeId
int32_t temp; // Temperature reading
uint16_t vcc; // Battery voltage
uint32_t tx; // Transmit level
} Payload;
Payload theData;
Then do a sizeof() check on both the 8 and 32 bit processors.
Now you want to adjust the size of the array. With the above you have 3 places that you need to modify:
1)
the array declaration
2)
the for loop that reads the sensors
3)
the for loop that prints the values
There is always a risk that you forget one which will result in all kinds of issues.