Payload issue for my Beehive project

Hello every one,

I start in the world of programming with TTN.
I am developing a hive project connected with an esp32 and RFM95 module through my Raspberry Gateway sending data to node red and blynk application. I block for several days on sending data. I use a BME280 sensor with which I send temperature and humidity. Everything works correctly and I receive it to blynk but when I want to add a data like altitude I do not receive anything. I can’t understand why and especially how sflt16 datum is working. I read a lot of thinks but it’s too complicated for me to understand…

I try to understand this : https://www.thethingsnetwork.org/docs/devices/bytes.html#how-to-send-big-numbers

but the code I have is different…

My code is:

 #include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>


//BME sensor
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; // I2C

//
// For normal use, we require that you edit the sketch to replace FILLMEIN
// with values assigned by the TTN console. However, for regression tests,
// we want to be able to compile these scripts. The regression tests define
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
// working but innocuous value.
//
#ifdef COMPILE_REGRESSION_TEST
# define FILLMEIN 0
#else
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
#endif


// LoRaWAN NwkSKey, network session key
static const PROGMEM u1_t NWKSKEY[16] = { ##, ## };

// LoRaWAN AppSKey, application session key
static const u1_t PROGMEM APPSKEY[16] = { ##, ## };

// LoRaWAN end-device address (DevAddr)
// See http://thethingsnetwork.org/wiki/AddressSpace
// The library converts the address to network byte order as needed.
#ifndef COMPILE_REGRESSION_TEST
static const u4_t DEVADDR = #######;
#else
static const u4_t DEVADDR = 0;
#endif

// 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 arduino-lmic/project_config/lmic_project_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) { }

// payload to send to TTN gateway
static uint8_t payload[5];
static osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 30;

// Pin mapping for Adafruit Feather M0 LoRa
const lmic_pinmap lmic_pins = {
  .nss = 5,
  .rxtx = LMIC_UNUSED_PIN,
  .rst = 13,
           // LBT cal for the Adafruit Feather M0 LoRa, in dB
   .dio = {12, 14, LMIC_UNUSED_PIN}
};


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;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || 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;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || case EV_SCAN_FOUND:
        ||    Serial.println(F("EV_SCAN_FOUND"));
        ||    break;
        */
        case EV_TXSTART:
            Serial.println(F("EV_TXSTART"));
            break;
        default:
            Serial.print(F("Unknown event: "));
            Serial.println((unsigned) ev);
            break;
    }
}

void do_send(osjob_t* j){
    // Check if there is not a current TX/RX job running
    
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
    } else {
        // read the temperature from the DHT22
        float Temperature = bme.readTemperature();
        Serial.print("Temperature: "); Serial.print(Temperature);
        Serial.println(" *C");
        // adjust for the f2sflt16 range (-1 to 1)
        Temperature = Temperature / 100; 

        // read the humidity from the DHT22
        float Humidity = bme.readHumidity();
        Serial.print("humidité: ");Serial.print(Humidity);
        Serial.println("% ");
        // adjust for the f2sflt16 range (-1 to 1)
        Humidity = Humidity / 100;

      //  float Altitude = bme.readAltitude(SEALEVELPRESSURE_HPA);
         float Altitude = 100;
        Serial.print("Approx. Altitude = ");
        Serial.print(Altitude);
        Serial.println(" m");
        Altitude = Altitude / 100;
        
        // float -> int
        // note: this uses the sflt16 datum (https://github.com/mcci-catena/arduino-lmic#sflt16)
        uint16_t payloadTemp = LMIC_f2sflt16(Temperature);
        // int -> bytes
        byte tempLow = lowByte(payloadTemp);
        byte tempHigh = highByte(payloadTemp);
        Serial.print("payloadTemp= ");
        Serial.println(payloadTemp);
        Serial.print("tempLow= ");
        Serial.println(tempLow);
        Serial.print("tempHigh= ");
        Serial.println(tempHigh);
        // place the bytes into the payload
        payload[0] = tempLow;
        payload[1] = tempHigh;

        // float -> int
        uint16_t payloadHumid = LMIC_f2sflt16(Humidity);
        // int -> bytes
        byte humidLow = lowByte(payloadHumid);
        byte humidHigh = highByte(payloadHumid);
        payload[2] = humidLow;
        payload[3] = humidHigh;

       // float -> int
        uint16_t payloadAlt = LMIC_f2sflt16(Altitude);
        // int -> bytes
        byte altLow = lowByte(payloadAlt);
        byte altHigh = highByte(payloadAlt);
        payload[4] = altLow;
        payload[5] = altHigh;

        // prepare upstream data transmission at the next possible time.
        // transmit on port 1 (the first parameter); you can use any value from 1 to 223 (others are reserved).
        // don't request an ack (the last parameter, if not zero, requests an ack from the network).
        // Remember, acks consume a lot of network resources; don't ask for an ack unless you really need it.
        LMIC_setTxData2(1, payload, sizeof(payload)-1, 0);
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

void setup() {
    delay(4000);
    while (!Serial);
    Serial.begin(115200);
    delay(100);
    Serial.println(F("Starting"));
    bme.begin(0x76);  
    // LMIC init
    os_init();
    // Reset the MAC state. Session and pending data transfers will be discarded.
    LMIC_reset();

    // Set static session parameters. Instead of dynamically establishing a session
    // by joining the network, precomputed session parameters are be provided.
    // 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 (0x13, DEVADDR, nwkskey, appskey);
    
   #if defined(CFG_eu868) // EU channel setup
  int channel = 0;
   int dr = DR_SF7;
   for(int i=0; i<9; i++) { 
     if(i != channel) {
       LMIC_disableChannel(i);
     }
   }
   // Set data rate (SF) and transmit power for uplink
   LMIC_setDrTxpow(dr, 14);    // g-band
#elif defined(CFG_us915) // US channel setup
  // Instead of using selectSubBand, which will cycle through a sub-band of 8
  // channels. We'll configure the device to only use one frequency.
  // First disable all sub-bands
  for (int b = 0; b < 8; ++b) {
    LMIC_disableSubBand(b);
  }
  // Then enable the channel(s) you want to use
  //LMIC_enableChannel(8); // 903.9 MHz
  LMIC_enableChannel(17);
#endif
    // Disable link check validation
    LMIC_setLinkCheckMode(0);

    // TTN uses SF9 for its RX2 window.
    LMIC.dn2Dr = DR_SF9;

    // Set data rate and transmit power for uplink
    LMIC_setDrTxpow(DR_SF7,14);

    // Start job
    do_send(&sendjob);
}

void loop() {
  os_runloop_once();  
}

My payload format is:

function Decoder(bytes, port) {
// Decode an uplink message from a buffer
// (array) of bytes to an object of fields.
var decoded = {};
// temperature
rawTemp = bytes[0] + bytes[1] * 256;
decoded.degreesC = sflt162f(rawTemp) * 100;
// humidity 
rawHumid = bytes[2] + bytes[3] * 256;
decoded.humidity = sflt162f(rawHumid) * 100;
//Altitude
rawAlt = bytes[4] + bytes[5] * 256;
decoded.altitude = sflt162f(rawAlt) * 100;
return decoded;
}
 
function sflt162f(rawSflt16)
{
// rawSflt16 is the 2-byte number decoded from wherever;
// it's in range 0..0xFFFF
// bit 15 is the sign bit
// bits 14..11 are the exponent
// bits 10..0 are the the mantissa. Unlike IEEE format, 
// the msb is transmitted; this means that numbers
// might not be normalized, but makes coding for
// underflow easier.
// As with IEEE format, negative zero is possible, so
// we special-case that in hopes that JavaScript will
// also cooperate.
//
// The result is a number in the open interval (-1.0, 1.0);
//
// throw away high bits for repeatability.
rawSflt16 &= 0xFFFF;
 
// special case minus zero:
if (rawSflt16 == 0x8000)
    return -0.0;
 
// extract the sign.
var sSign = ((rawSflt16 & 0x8000) !== 0) ? -1 : 1;
 
// extract the exponent
var exp1 = (rawSflt16 >> 11) & 0xF;
 
// extract the "mantissa" (the fractional part)
var mant1 = (rawSflt16 & 0x7FF) / 2048.0;
 
// convert back to a floating point number. We hope 
// that Math.pow(2, k) is handled efficiently by
// the JS interpreter! If this is time critical code,
// you can replace by a suitable shift and divide.
var f_unscaled = sSign * mant1 * Math.pow(2, exp1 - 15);
 
return f_unscaled;
}

I get good temperature and humidity but not altitude and I want to add more sensor like weight sensor.

ttn

Many thanks for your help

christophe

//  float Altitude = bme.readAltitude(SEALEVELPRESSURE_HPA);
         float Altitude = bme.xxxxx ?
        Serial.print("Approx. Altitude = ");
        Serial.print(Altitude);
        Serial.println(" m");
        Altitude = Altitude / 100;

float readAltitude(float seaLevel);
float seaLevelForAltitude(float altitude, float pressure);

In my example I fixed the altitude to 100 and I get 0 normaly I have in my code the correct Line:
float Altitude = bme.readAltitude(SEALEVELPRESSURE_HPA);

Pass in the current sea level pressure in hPa - so the value will be somewhere around ~1000.
You can also test with the generic 1013.25 value.


page 15

Many thanks for your answer but I don’t have issue with the reeding of the pressure all is good from my sensor, it’s only the way how my payload is made.

ok sorry I see now… how you decoder works (or not works)
that’s not my speciality … pass :sunglasses:

just curiosity …why do you need Altitude for a beehive ?

Just because the sensor give me thé pressure. But next I have some others sensors to connect and I need to understand how I can send more data to ttn

1 Like

I finally found the solution :). When my project is finished, I will present it.

1 Like