With the recent surge in Omicron cases, shipping may be slower than stated times. We are working to build, ship and respond to everything as quickly as possible. Please see all COVID-19 updates here. Thank you for your continued support.


Member Since: August 26, 2009

Country: United States

  • in ttn, added a Javascript decoder :

    function decodeUplink(input) {
      var data = {};
      data.b0 = input.bytes[0];
      data.b1 = input.bytes[1];
      data.pressure = ((input.bytes[0] << 8) + input.bytes[1])*20 + 95000;
      var warnings = [];
      if (data.pressure < 990) {
        warnings.push("it's low pressure");
      if (data.pressure > 1030) {
        warnings.push("it's high pressure");
      return {
        data: data,
        warnings: warnings

    ... and then watch as the messages come through

  • #include <lmic.h>
    #include <hal/hal.h>
    #include <SPI.h>
    // BME280 stuff ------------------------------
    #include <Wire.h>
    #include "SparkFunBME280.h"
    BME280 mySensor;
    // BME280 stuff ------------------------------
    # define FILLMEIN 0
    # 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)
    // fetch yr key on TTN, 00 is fine here
    static const u1_t PROGMEM APPEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
    // fetch yr key on TTN, this one always ends D5-B3-70
    static const u1_t PROGMEM DEVEUI[8]={ 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xD5, 0xB3, 0x70 };
    void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
    // fetch yr key on TTN
    static const u1_t PROGMEM APPKEY[16] = { 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ };
    void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}
    //the hello world is from the tutorial, I commented this out and instead push a sensor value down below
    //static uint8_t mydata[] = "Hello, world!";
    static osjob_t sendjob;
    // Schedule TX every this many seconds (might become longer due to duty
    // cycle limitations).
    const unsigned TX_INTERVAL = 60;
    // Pin mapping for SparkFun SAMD21 Pro RF
    const lmic_pinmap lmic_pins = {
        .nss = 12,
        .rxtx = LMIC_UNUSED_PIN,
        .rst = 7,
        .dio = {6, 10, 11},
    void printHex2(unsigned v) {
        v &= 0xff;
        if (v < 16)
        SerialUSB.print(v, HEX);
    void onEvent (ev_t ev) {
        SerialUSB.print(": ");
        switch(ev) {
            case EV_SCAN_TIMEOUT:
            case EV_BEACON_FOUND:
            case EV_BEACON_MISSED:
            case EV_BEACON_TRACKED:
            case EV_JOINING:
            case EV_JOINED:
                  u4_t netid = 0;
                  devaddr_t devaddr = 0;
                  u1_t nwkKey[16];
                  u1_t artKey[16];
                  LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
                  SerialUSB.print("netid: ");
                  SerialUSB.println(netid, DEC);
                  SerialUSB.print("devaddr: ");
                  SerialUSB.println(devaddr, HEX);
                  SerialUSB.print("AppSKey: ");
                  for (size_t i=0; i<sizeof(artKey); ++i) {
                    if (i != 0)
                  SerialUSB.print("NwkSKey: ");
                  for (size_t i=0; i<sizeof(nwkKey); ++i) {
                          if (i != 0)
                // Disable link check validation (automatically enabled
                // during join, but because slow data rates change max TX
          // size, we don't use it in this example.
            || This event is defined but not used in the code. No
            || point in wasting codespace on it.
            || case EV_RFU1:
            ||     SerialUSB.println(F("EV_RFU1"));
            ||     break;
            case EV_JOIN_FAILED:
            case EV_REJOIN_FAILED:
            case EV_TXCOMPLETE:
                SerialUSB.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
                if (LMIC.txrxFlags & TXRX_ACK)
                  SerialUSB.println(F("Received ack"));
                if (LMIC.dataLen) {
                  SerialUSB.print(F("Received "));
                  SerialUSB.println(F(" bytes of payload"));
                // Schedule next transmission
                os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
            case EV_LOST_TSYNC:
            case EV_RESET:
            case EV_RXCOMPLETE:
                // data received in ping slot
            case EV_LINK_DEAD:
            case EV_LINK_ALIVE:
            || This event is defined but not used in the code. No
            || point in wasting codespace on it.
            || case EV_SCAN_FOUND:
            ||    SerialUSB.println(F("EV_SCAN_FOUND"));
            ||    break;
            case EV_TXSTART:
            case EV_TXCANCELED:
            case EV_RXSTART:
                /* do not print anything -- it wrecks timing */
            case EV_JOIN_TXCOMPLETE:
                SerialUSB.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept"));
                SerialUSB.print(F("Unknown event: "));
                SerialUSB.println((unsigned) ev);
    void do_send(osjob_t* j){
    //reading the BME280 sensor here, pushing to serial for bugtracking
        float val = mySensor.readFloatPressure() ;
        SerialUSB.print("pressure : ");
    // convert pressure to just 2 bytes. this works but can be improved upon
        int valbyte = (val - 95000)/20;
        SerialUSB.print("press adj : ");
    // create array and store the pressure in 2 bytes
        byte mydata[2];
        mydata[0] = highByte(valbyte);
        mydata[1] = lowByte(valbyte);
    //decode the value here, same decoder on the other end in TTN
        int decoded = ((mydata[0] << 8) + mydata[1])*20 + 95000;
        SerialUSB.print("bytes decoded : ");
        // Check if there is not a current TX/RX job running
        if (LMIC.opmode & OP_TXRXPEND) {
            SerialUSB.println(F("OP_TXRXPEND, not sending"));
        } else {
            // Prepare upstream data transmission at the next possible time. -- here is where the 2 bytes get sent over LoRa
            LMIC_setTxData2(1, mydata, sizeof(mydata), 0);
            SerialUSB.println(F("Packet queued"));
        // Next TX is scheduled after TX_COMPLETE event.
    void setup() {
    //BME280 stuff-------------------
       if (mySensor.beginI2C() == false) //Begin communication over I2C
        SerialUSB.println("The sensor did not respond. Please check wiring.");
        delay(1000); //Freeze
    //BME280 stuff-------------------
      // line added to halt execution until serial port is online
        #ifdef VCC_ENABLE
        // For Pinoccio Scout boards
        pinMode(VCC_ENABLE, OUTPUT);
        digitalWrite(VCC_ENABLE, HIGH);
        // LMIC init
        // Reset the MAC state. Session and pending data transfers will be discarded.
        // Start job (sending automatically starts OTAA too)
    void loop() {
  • Not sure where you are getting stuck so hard to address the relevant point. You can still pretty much follow the guide, (add Arduino SAMD board definitions, add Sparkfun Board Definitions) except when it comes to installing the LMIC you go for "MCCI LoraWAN LMIC library" by Terry Moore. This will create some folders in yr Arduino directory, find this file "Arduino\libraries\MCCI_LoRaWAN_LMIC_library\project_config\lmic_project_config.h and select the appropriate frequency for your region, comment out the others (Its EU868 for me). Then back to the guide. static const u1_t PROGMEM APPEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; //For TTN issued EUIs the last bytes should be 0xD5, 0xB3,0x70. static const u1_t PROGMEM DEVEUI[8]={ 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xZZ, 0xD5, 0xB3, 0x70 }; the "ZZ" you will need to replace with whatever key your device has in TTN, you can toggle the key format until it shows the D5-B3-70 at the end. I played with the SparkFun BME sensor and the below worked fine for me. Hope this helps ! ps Will post code in a separate reply, struggling with formatting

  • There is a new LMIC library (MCCI), one has to edit a config file for the frequency plan selection "Arduino\libraries\MCCI_LoRaWAN_LMIC_library\project_config\lmic_project_config.h". I struggled until I located that...

  • Can you provide updated example for TTN please now that they are closing down TTN v2 and imposing TTN v3 ? The existing hookup guide is no longer applicable and I am struggling badly. Any chance on an example of both ABR and OTAA ? Thanks guys !

  • Splendid, thanks Mike. Dear Sparfun : have a look at the OpenROV pricepoint. Any chance you could throw in a few pieces of acrylic (or any other epoxy-container) for the money ? Or publish a 3d-printer file that I could just take to my local printers ? Or just watertight the whole thing already (..hey, Lennon warned you : he was not the only dreamer...) ?

  • I am by no means an expert, YMMV. The fluid pressure will push back on the sensor and thus on the PCB, while the screws are supposed to hold it in place. Perhaps backing up the PCB will keep the sensor from getting pushed beyond the o-ring ? I am very interested in learning an easy way to make this sensor watertight (5m would do for me). At $60, it's too expensive to just fool around with, so calling all experts !

  • In my project (a very simple case of transfering 1 remote digital input (switch) to a base digital output (ssrelay), as in the digi sample. Using the 2 AAA battery case with switch and 2 basic series 1 xbees, I wanted to power the remote unit with 3 Volts. Can/should I use this breakout board or solder the whole setup straight to the XBEE ? In testing, using this breakout works, but fails after 4 meters in direct clear line. Before I solder to the xbee, any advice/reasoning/warnings ?

  • Frequent contributor hugof is actually/supposedly the CEO and founder of ElectricImp. We/I may or may not always be completely in tune with his frequency, but I admire his contributions and answers here on this forum. I shows how big a driver Sparkfun is and how important the Sparkfun community is, but it also show how seriously Hugo/ElectricImp take us, the Sparkfun clients. That I can only applaud : kind thanks Hugo !

  • Dear Clint, I second the idea of a bidirectional current sensor similar to an aircraft ammeter. As in www.aircraftspruce.com/menus/in/ammeters.html My application would be a motorcycle monitor, where the bettery will at times be discharged (idle) and then again be charged (higher revs with little load). I am not nearly good enough to develop one myself, so just want to convey my support to either you or Dean to implement the circuit.

No public wish lists :(