a 16bit integer is 2 bytes and a 2 byte array is 16bits. Ie you don’t need to create the 2 byte array. Just send the 16bit int.
You might also want to have a look at https://github.com/thesolarnomad/lora-serialization (as mentioned before in Approaches for efficient payload encoding). It should take care of any endiannes issues.
var msg2 = { payload: msg.payload.length };
msg2.payload = JSON.parse(msg.payload);
msg2.payload = new Buffer(msg2.payload.payload, ‘base64’).toString(‘hex’);
var lat = Buffer(msg2.payload, ‘hex’).readFloatLE(0);
var lon = Buffer(msg2.payload, ‘hex’).readFloatLE(4);
msg2.payload= “[{“lat”:” + lat + “,“lng”:” + lon + “}]”;//"{“lat”:lat,“lng”:lon};
return msg2;
This is how I am decoding the ttn payload via node-red. This assumes that the payload I am getting from ttn via mqtt is an 8 byte payload, coming out of the node-red mqtt node in base64, with the first four bytes being one float, and the second four bytes being the second float.
If you are trying to decode a raw 8 byte value, it’s even easier:
var lat = new Buffer(msg.payload).readFloatLE(0);
var lon = new Buffer(msg.payload).readFloatLE(4);
msg.payload= “[{“lat”:” + lat + “,“lng”:” + lon + “}]”;//"{“lat”:lat,“lng”:lon};
return msg;
I hate javascript and am terrible at doing anything with it, so there is probably a more efficient way to do this.
@erikHouten
On staging.thethingsnetwork.org/applications I use this function:
function(bytes) {
var Bytes2Float32 = {
decodeFloat: function(bytes, signBits, exponentBits, fractionBits, eMin, eMax, littleEndian) {
var totalBits = (signBits + exponentBits + fractionBits);
var binary = "";
for (var i = 0, l = bytes.length; i < l; i++) {
var bits = bytes[i].toString(2);
while (bits.length < 8)
bits = "0" + bits;
if (littleEndian)
binary = bits + binary;
else
binary += bits;
}
var sign = (binary.charAt(0) == '1') ? -1 : 1;
var exponent = parseInt(binary.substr(signBits, exponentBits), 2) - eMax;
var significandBase = binary.substr(signBits + exponentBits, fractionBits);
var significandBin = '1' + significandBase;
var i = 0;
var val = 1;
var significand = 0;
if (exponent == -eMax) {
if (significandBase.indexOf('1') == -1)
return 0;
else {
exponent = eMin;
significandBin = '0' + significandBase;
}
}
while (i < significandBin.length) {
significand += val * parseInt(significandBin.charAt(i));
val = val / 2;
i++;
}
return sign * significand * Math.pow(2, exponent);
}
}
var value1 = [bytes[4], bytes[5], bytes[6], bytes[7]];
var value2 = [bytes[10], bytes[11], bytes[12], bytes[13]];
var value3 = [bytes[14], bytes[15], bytes[16], bytes[17]];
var value1float = Bytes2Float32.decodeFloat(value1, 1, 8, 23, -126, 127, false);
var value2float = Bytes2Float32.decodeFloat(value2, 1, 8, 23, -126, 127, false);
var value3float = Bytes2Float32.decodeFloat(value3, 1, 8, 23, -126, 127, false);
return {
payload : bytes,
batteryVoltage: value1float,
pressure: value2float,
temperature: value3float
};
}
It works for me. The decoded json is sent to the Azure IoTHub (with webjobs), there it goes to a SQL-server and a demo-web app shows the data (http://cloud.keller-druck.com).
Be aware that a Test doesn’t work (
TypeError(“Cannot access member ‘toString’ of undefined”) )
On console.thethingsnetwork.org/applications on the other hand, you must have a successful Test before saving the payload function. Therefore, I couldn’t get it working there.
Hi,
Can someone send me a code for HX711 loadcell amplifier and DHT22 library?
I have tried this:
uint16_t weight = scale.get_units() * 100;
// Read sensor values and multiply by 100 to effictively have 2 decimals
uint16_t humidity = dht.readHumidity(false) * 100;
// false: Celsius (default)
// true: Farenheit
uint16_t temperature = dht.readTemperature(false) * 100;
// Split both words (16 bits) into 6 bytes of 8
byte message[6];
message[0] = highByte(temperature);
message[1] = lowByte(temperature);
message[2] = highByte(humidity);
message[3] = lowByte(humidity);
message[4] = highByte(weight);
message[5] = lowByte(weight);
LMIC_setTxData2(1, message, sizeof(message), 0);
}
On TTN side this is the payload function:
function (bytes) {
var decodedValue1 = (bytes[4] << 8) | bytes[5];
var decodedValue2 = (bytes[2] << 8) | bytes[3];
var decodedValue3 = (bytes[0] << 8) | bytes[1];
return { Weight: decodedValue1/100, Temp: decodedValue2/100, Hum: decodedValue3/100};
}
The result is this. It should be 4 decimal each:
Hum 20.3
Temp 1
Weight 654.36
You’re multiplying by 100, and storing that in memory as an “unsigned 16 bits integer” (uint16
), hence losing all remaining decimals. For example: 12.34567 is stored and sent as 1234, which is probably just fine as most sensors really don’t have an accuracy of 4 decimals (or even 2 decimals). It even says this in your code:
// Read sensor values and multiply by 100 to effictively have 2 decimals
uint16_t humidity = dht.readHumidity(false) * 100;
As 2 bytes unsigned integers can only hold values 0 to 65535, you don’t really have room to multiply by, say, 1000 to get 3 decimals as then any value larger than 65.5 would cause an overflow with unexpected results.
Things are even more complicated if the values might be negative, in which case the JavaScript in the payload functions expects 4 bytes to do proper calculations for negative values:
// Signed 16 bits integer, -32767 up to +32767
int16_t temperature = dht.readTemperature(false) * 100;
…with in the payload function:
decodedValue3 = (bytes[0] & 0x80 ? 0xFFFF<<16 : 0) | bytes[0]<<8 | bytes[1];
More details on the above “sign-extension” in Decrypting messages for dummies (and more links to examples in Is there any documentation on payload functions?).
You’re also mixing the values. You’re sending 2 bytes for temperature, humidity and weight. But you’re are showing that as humidity, temperate and weight. So, 20.3
is your temperature. I’d clean up and use the same order in your code:
// For staging-v1
function(bytes) {
// temperature might be negative; sign-extend to get 4 bytes:
var t = (bytes[0] & 0x80 ? 0xFFFF<<16 : 0) | bytes[0]<<8 | bytes[1];
var h = bytes[2]<<8 | bytes[3];
var w = bytes[4]<<8 | bytes[5];
return {
temperature: t / 100,
humidity: h / 100,
weight: w / 100
}
}
I don’t understand why you’re getting a humidity of exactly 1. What does the false
in the formula do? I guess the result of dht.readHumidity(false)
is 1
or 1.00
, which multiplied by 100 is sent as 100
and then decoded back to 1
.
By the way, on the production environment the first line of a Decoder payload function needs to read:
function Decoder(bytes, port) {
And as Staging will be taken offline at the end of March, I strongly advise to use production.
Hi !
I have some difficult with the payload function.
Here is my Payload: 32352E3736302E32
This is for a DHT22 sensor. When i use python I can read values:
>>> binascii.unhexlify('32352E37')
b'25.7'
>>> binascii.unhexlify('36302E32')
b'60.2'
But I can’t make readable on the decoder. What can I do I tried … a lot !
thx (and sorry for my english)
That’s decoding text (where the hexadecimal 0x32
is the character 2
, 0x35
is 5
, 0x2E
is the dot, and 0x37
is 7
). But your node should not send text as that needs 8 bytes for just two values with a very limited range.
Still then, while you’ve not changed your node’s code to use a better encoding, and assuming that always 4 characters per value are used, so if:
- no negative values are sent
- zero is sent as
00.0
, or as0.0
with a leading space - values below 10 are sent with a leading space or zero, like
08.1
- integer values are sent with a fractional part, like
12.0
…then you could decode the text using:
function Decoder(bytes, port) {
// test with 32352E3736302E32
var text1 = String.fromCharCode.apply(null, bytes.slice(0, 4));
var text2 = String.fromCharCode.apply(null, bytes.slice(4, 8));
return {
text1: text1,
float1: parseFloat(text1),
text2: text2,
float2: parseFloat(text2)
}
}
…which will get you:
{
"float1": 25.7,
"float2": 60.2,
"text1": "25.7",
"text2": "60.2"
}
Oh I understand, so I need to optimise the code. I’m totally “noob” I need to learn. Thanks for your explanation.
BTW, here is the code
void onEvent (ev_t ev) {
Serial.print(os_getTime());
Serial.print(": ");
switch(ev) {
case EV_SCAN_TIMEOUT:
Serial.println(F("EV_SCAN_TIMEOUT"));
break;
case EV_BEACON_FOUND:
Serial.println(F("EV_BEACON_FOUND"));
break;
case EV_BEACON_MISSED:
Serial.println(F("EV_BEACON_MISSED"));
break;
case EV_BEACON_TRACKED:
Serial.println(F("EV_BEACON_TRACKED"));
break;
case EV_JOINING:
Serial.println(F("EV_JOINING"));
break;
case EV_JOINED:
Serial.println(F("EV_JOINED"));
break;
case EV_RFU1:
Serial.println(F("EV_RFU1"));
break;
case EV_JOIN_FAILED:
Serial.println(F("EV_JOIN_FAILED"));
break;
case EV_REJOIN_FAILED:
Serial.println(F("EV_REJOIN_FAILED"));
break;
case EV_TXCOMPLETE:
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
if (LMIC.txrxFlags & TXRX_ACK)
Serial.println(F("Received ack"));
if (LMIC.dataLen) {
Serial.println(F("Received "));
Serial.println(LMIC.dataLen);
Serial.println(F(" bytes of payload"));
}
// Schedule next transmission
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
break;
case EV_LOST_TSYNC:
Serial.println(F("EV_LOST_TSYNC"));
break;
case EV_RESET:
Serial.println(F("EV_RESET"));
break;
case EV_RXCOMPLETE:
// data received in ping slot
Serial.println(F("EV_RXCOMPLETE"));
break;
case EV_LINK_DEAD:
Serial.println(F("EV_LINK_DEAD"));
break;
case EV_LINK_ALIVE:
Serial.println(F("EV_LINK_ALIVE"));
break;
default:
Serial.println(F("Unknown event"));
break;
}
}
void do_send(osjob_t* j){
byte buffer[8];
float t = dht.readTemperature();
float h = dht.readHumidity();
dtostrf(t, 2, 1, buffer);
dtostrf(h, 2, 1, &buffer[4]);
String res = buffer;
res.getBytes(buffer, res.length() + 1);
Serial.println("");
Serial.print("Sending - temperature: ");
Serial.print(t);
Serial.print(", humidity: ");
Serial.print(h);
Serial.println("");
// Check if there is not a current TX/RX job running
if (LMIC.opmode & OP_TXRXPEND) {
Serial.println(F("OP_TXRXPEND, not sending"));
} else {
// Prepare upstream data transmission at the next possible time.
LMIC_setTxData2(1, (uint8_t*) buffer, res.length(), 0);
Serial.println(F("Packet queued"));
}
// Next TX is scheduled after TX_COMPLETE event.
}
This goes horribly wrong for any value that does not convert to exactly four characters. (If you found that code elsewhere, please refer the author to this answer…)
Instead, use something like explained above:
// Read sensor values and multiply by 100 to effectively keep 2 decimals
// Signed 16 bits integer, -32767 up to +32767
int16_t t = dht.readTemperature() * 100;
// Unsigned 16 bits integer, 0 up to 65535
uint16_t h = dht.readHumidity() * 100;
This can be encoded into just 4 bytes for the 2 values, using some “shifting” as explained in more detail in Decrypting messages for dummies:
byte buffer[4];
buffer[0] = t >> 8;
buffer[1] = t;
buffer[2] = h >> 8;
buffer[3] = h;
LMIC_setTxData2(1, buffer, sizeof(buffer), 0);
And decoded like also explained in more detail in the link above:
function Decoder(bytes, port) {
var t = (bytes[0] & 0x80 ? 0xFFFF<<16 : 0) | bytes[0]<<8 | bytes[1];
var h = bytes[2]<<8 | bytes[3];
return {
temperature: t / 100,
humidity: h / 100
}
}
You could probably even send humidity without any decimals, saving yet another byte:
// Unsigned 8 bits integer, 0 up to 255
uint8_t h = dht.readHumidity();
…with:
byte buffer[3];
buffer[0] = t >> 8;
buffer[1] = t;
buffer[2] = h;
LMIC_setTxData2(1, buffer, sizeof(buffer), 0);
…and:
function Decoder(bytes, port) {
var t = (bytes[0] & 0x80 ? 0xFFFF<<16 : 0) | bytes[0]<<8 | bytes[1];
var h = bytes[2];
return {
temperature: t / 100,
humidity: h
}
}
Work Perfectly (with the optimized version for humidity) ! Big thank for all your explanations ! Clear and working. I’m getting smarter today
Any ideas i could do what you are describing?
Let me first tell you that i use loriot server instead of ttn.
I tried to connect loriot backend with node red and ibm bluemix but i am struggling with javascript.
So any ideas about decoding with python and then connecting to an application server?
and you asked questions on the TTN forum ? … strange
your welcome
Just for future reference, another option to decode an IEEE-754 floating point in Decode float sent by Lopy as Node - #2 by arjanvanb.
I pulled my answer from here:
So my payload function, making up for the ATSAMD21G18’s opposite endianness looks like this:
This was very helpful! Thanks.
Thanks to @arjanvanb we updated our payload decoder code:
https://github.com/KELLERAGfuerDruckmesstechnik/KellerAgTheThingsNetworkPayloadDecoder
Hi there,
I am trying to decode the DHT11 data on the things network and integrate it to thingspeak. i am having an issue with decoding the payload.
function Decoder(bytes, port)
{
var tem = (bytes[2]<<8) | bytes[3];
var hum = (bytes[6])*2;
return {
field3: tem,
field4: hum
}
}
Following is the sketch:
#include <lmic.h>
#include <dht.h>
#include <hal/hal.h>
#include <SPI.h>
dht DHT;
#define DHT11_PIN 5
#define PIN_A A0
float temperature,humidity;
float tem,hum;
unsigned int count = 1; //For times count
String datastring1="";
String datastring2="";
String datastring3="";
static uint8_t mydata[11] = {0x01,0x67,0x00,0x00,0x02,0x68,0x00,0x03,0x65,0x00,0x00};
/* LoRaWAN NwkSKey, network session key
This is the default Semtech key, which is used by the prototype TTN
network initially.
ttn*/
static const PROGMEM u1_t NWKSKEY[16] = { 0x72, 0x13, 0xF8, 0xF0, 0x9A, 0x3C, 0xF9, 0xE5, 0xE6, 0x01, 0xDA,0xAC, 0x32, 0xBA, 0x37 };
/* LoRaWAN AppSKey, application session key
This is the default Semtech key, which is used by the prototype TTN
network initially.
ttn*/
static const u1_t PROGMEM APPSKEY[16] = { 0x2F, 0xF5, 0xF4, 0x08, 0x4E, 0x37, 0x20, 0x02, 0xBA, 0x2B, 0xED, 0x40, 0x24, 0xA2, 0x945 };
/*
LoRaWAN end-device address (DevAddr)
See http://thethingsnetwork.org/wiki/AddressSpace
ttn*/
static const u4_t DEVADDR = 0x260D0CA5;
/* These callbacks are only used in over-the-air activation, so they are
left empty here (we cannot leave them out completely unless
DISABLE_JOIN is set in config.h, otherwise the linker will complain).*/
void os_getArtEui (u1_t* buf) { }
void os_getDevEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }
static osjob_t initjob,sendjob,blinkjob;
/* Schedule TX every this many seconds (might become longer due to duty
cycle limitations).*/
const unsigned TX_INTERVAL = 10;
// Pin mapping
const lmic_pinmap lmic_pins = {
.nss = 10,
.rxtx = LMIC_UNUSED_PIN,
.rst = 9,
.dio = {2, 6, 7},
};
void do_send(osjob_t* j){
// Check if there is not a current TX/RX job running
if (LMIC.opmode & OP_TXRXPEND) {
Serial.println("OP_TXRXPEND, not sending");
} else {
dhtTem();
light();
// Prepare upstream data transmission at the next possible time.
// LMIC_setTxData2(1,datasend,sizeof(datasend)-1,0);
LMIC_setTxData2(1, mydata, sizeof(mydata), 0);
Serial.println("Packet queued");
Serial.print("LMIC.freq:");
Serial.println(LMIC.freq);
Serial.println("Receive data:");
}
// Next TX is scheduled after TX_COMPLETE event.
}
void onEvent (ev_t ev) {
Serial.print(os_getTime());
Serial.print(": ");
Serial.println(ev);
switch(ev) {
case EV_SCAN_TIMEOUT:
Serial.println(F("EV_SCAN_TIMEOUT"));
break;
case EV_BEACON_FOUND:
Serial.println(F("EV_BEACON_FOUND"));
break;
case EV_BEACON_MISSED:
Serial.println(F("EV_BEACON_MISSED"));
break;
case EV_BEACON_TRACKED:
Serial.println(F("EV_BEACON_TRACKED"));
break;
case EV_JOINING:
Serial.println(F("EV_JOINING"));
break;
case EV_JOINED:
Serial.println(F("EV_JOINED"));
break;
case EV_RFU1:
Serial.println(F("EV_RFU1"));
break;
case EV_JOIN_FAILED:
Serial.println(F("EV_JOIN_FAILED"));
break;
case EV_REJOIN_FAILED:
Serial.println(F("EV_REJOIN_FAILED"));
break;
case EV_TXCOMPLETE:
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
if(LMIC.dataLen) {
// data received in rx slot after tx
Serial.print(F("Data Received: "));
Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen);
Serial.println();
}
// Schedule next transmission
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
break;
case EV_LOST_TSYNC:
Serial.println(F("EV_LOST_TSYNC"));
break;
case EV_RESET:
Serial.println(F("EV_RESET"));
break;
case EV_RXCOMPLETE:
// data received in ping slot
Serial.println(F("EV_RXCOMPLETE"));
break;
case EV_LINK_DEAD:
Serial.println(F("EV_LINK_DEAD"));
break;
case EV_LINK_ALIVE:
Serial.println(F("EV_LINK_ALIVE"));
break;
default:
Serial.println(F("Unknown event"));
break;
}
}
void setup() {
// initialize digital pin as an output.
Serial.begin(9600);
while(!Serial);
Serial.println("Connect to TTN and Send data to mydevice(Use DHT11 Sensor):");
#ifdef VCC_ENABLE
// For Pinoccio Scout boards
pinMode(VCC_ENABLE, OUTPUT);
digitalWrite(VCC_ENABLE, HIGH);
delay(1000);
#endif
// LMIC init
os_init();
// Reset the MAC state. Session and pending data transfers will be discarded.
LMIC_reset();
/*LMIC_setClockError(MAX_CLOCK_ERROR * 1/100);
Set static session parameters. Instead of dynamically establishing a session
by joining the network, precomputed session parameters are be provided.*/
#ifdef PROGMEM
/* On AVR, these values are stored in flash and only copied to RAM
once. Copy them to a temporary buffer here, LMIC_setSession will
copy them into a buffer of its own again.*/
uint8_t appskey[sizeof(APPSKEY)];
uint8_t nwkskey[sizeof(NWKSKEY)];
memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
LMIC_setSession (0x1, DEVADDR, nwkskey, appskey);
#else
// If not running an AVR with PROGMEM, just use the arrays directly
LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
#endif
for (int channel=0; channel<8; ++channel) {
LMIC_disableChannel(channel);
}
for (int channel=16; channel<72; ++channel) {
LMIC_disableChannel(channel);
}
// Disable link check validation
LMIC_setLinkCheckMode(0);
// TTN uses SF9 for its RX2 window.
LMIC.dn2Dr = DR_SF9;
// Set data rate and transmit power (note: txpow seems to be ignored by the library)
LMIC_setDrTxpow(DR_SF7,14);
// Start job
do_send(&sendjob);
}
void dhtTem()
{
int16_t tem1;
temperature = DHT.read11(DHT11_PIN); //Temperature detection
tem = DHT.temperature*1.0;
float humidity = DHT.read11(DHT11_PIN);
float hum = DHT.humidity* 1.0;
Serial.print(F("########### "));
Serial.print(F("NO."));
Serial.print(count);
Serial.println(F(" ###########"));
Serial.println(F("The temperautre and humidity :"));
Serial.print(F("["));
Serial.print(tem);
Serial.print(F("℃"));
Serial.print(F(","));
Serial.print(hum);
Serial.print(F("%"));
Serial.print(F("]"));
Serial.println("");
count++;
tem1=(tem*10);
mydata[2] = tem1>>8;
mydata[3]= tem1;
mydata[6] = hum * 2;
}
void light(){
int16_t lux;
int val,val1;
val=analogRead(PIN_A);
// Serial.print(F("a:"));
//Serial.println(val);
delay(500);
val1=val*1.0;
lux=val1;
mydata[9]=lux>>8;
mydata[10]=lux;
//Serial.print(lux);
}
void loop() {
os_runloop_once();
}
below is the result on the serial monitor.