diff --git a/platformio.ini b/platformio.ini
index 478c23fb2a444a536e08088e10d96749bc6cda92..925298cc7ee1f9dcfc10d27ebd6fb480b55511eb 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -26,3 +26,5 @@ lib_deps =
   TinyGPSPlus@1.0.2
   MCCI Arduino LoRaWAN Library@0.8.0
   OneButton@0.0.0-alpha+sha.eb583d713a
+  CayenneLPP@1.1.0
+  
diff --git a/src/main.cpp b/src/main.cpp
index 3f531ce11fd5f351290bec442f956c27e313d290..11dce95ebfa2564858794b8eb7f15fe5f16af43b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,44 +5,43 @@
 #include <hal/hal.h>
 #include <SPI.h>
 #include <TinyGPS++.h>
+#include <CayenneLPP.h>
 #include <OneButton.h>
 
 class cLoRaWAN : public Arduino_LoRaWAN_ttn
 {
 public:
-  cLoRaWAN(){};
+    cLoRaWAN(){};
 
 protected:
-  virtual bool GetOtaaProvisioningInfo(Arduino_LoRaWAN::OtaaProvisioningInfo *) override;
-  virtual void NetSaveFCntUp(uint32_t uFCntUp) override;
-  virtual void NetSaveFCntDown(uint32_t uFCntDown) override;
-  virtual void NetSaveSessionInfo(const SessionInfo &Info, const uint8_t *pExtraInfo, size_t nExtraInfo) override;
+    virtual bool GetOtaaProvisioningInfo(Arduino_LoRaWAN::OtaaProvisioningInfo *) override;
+    virtual void NetSaveFCntUp(uint32_t uFCntUp) override;
+    virtual void NetSaveFCntDown(uint32_t uFCntDown) override;
+    virtual void NetSaveSessionInfo(const SessionInfo &Info, const uint8_t *pExtraInfo, size_t nExtraInfo) override;
 };
 
 bool cLoRaWAN::GetOtaaProvisioningInfo(
     OtaaProvisioningInfo *pInfo)
 {
-  static const uint8_t DEVEUI[8] = TTN_DEVEUI;
-  static const uint8_t APPEUI[8] = TTN_APPEUI;
-  static const uint8_t APPKEY[16] = TTN_APPKEY;
+    static const uint8_t DEVEUI[8] = TTN_DEVEUI;
+    static const uint8_t APPEUI[8] = TTN_APPEUI;
+    static const uint8_t APPKEY[16] = TTN_APPKEY;
 
-  if (pInfo)
-  {
-    memcpy(pInfo->AppKey, APPKEY, sizeof(APPKEY));
-    memcpy(pInfo->DevEUI, DEVEUI, sizeof(DEVEUI));
-    memcpy(pInfo->AppEUI, APPEUI, sizeof(APPEUI));
-  }
-  return true;
+    if (pInfo)
+    {
+        memcpy(pInfo->AppKey, APPKEY, sizeof(APPKEY));
+        memcpy(pInfo->DevEUI, DEVEUI, sizeof(DEVEUI));
+        memcpy(pInfo->AppEUI, APPEUI, sizeof(APPEUI));
+    }
+    return true;
 }
 
 void cLoRaWAN::NetSaveFCntDown(uint32_t uFCntDown)
 {
-  // save uFcntDown somwwhere
 }
 
 void cLoRaWAN::NetSaveFCntUp(uint32_t uFCntUp)
 {
-  // save uFCntUp somewhere
 }
 
 void cLoRaWAN::NetSaveSessionInfo(
@@ -50,7 +49,6 @@ void cLoRaWAN::NetSaveSessionInfo(
     const uint8_t *pExtraInfo,
     size_t nExtraInfo)
 {
-  // save Info somewhere.
 }
 
 const cLoRaWAN::lmic_pinmap pinMap = {
@@ -64,233 +62,223 @@ AXP20X_Class axp;
 HardwareSerial GPS(1);
 TinyGPSPlus gps;
 cLoRaWAN LoRaWAN{};
+CayenneLPP lpp(55);
+DynamicJsonDocument jsonBuffer(1024);
 OneButton btn = OneButton(BUTTON_PIN, true, true);
+unsigned long statusTimer;
 
-static uint8_t mydata[] = "Hello, world!";
-static osjob_t sendjob;
-const unsigned TX_INTERVAL = 60;
+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;
+        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"));
+        }
+        break;
+    case EV_LOST_TSYNC:
+        Serial.println(F("EV_LOST_TSYNC"));
+        break;
+    case EV_RESET:
+        Serial.println(F("EV_RESET"));
+        break;
+    case EV_RXCOMPLETE:
+        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;
+    case EV_SCAN_FOUND:
+        Serial.println(F("EV_SCAN_FOUND"));
+        break;
+    case EV_TXSTART:
+        Serial.println(F("EV_TXSTART"));
+        break;
+    case EV_TXCANCELED:
+        Serial.println(F("EV_TXCANCELLED"));
+        break;
+    case EV_JOIN_TXCOMPLETE:
+        Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept"));
+        break;
+    default:
+        Serial.println(F("Unknown event " + (unsigned)ev));
+        break;
+    }
+}
 
 void setupPower()
 {
-  if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS))
-  {
-    Serial.println("AXP192 Begin PASS");
-  }
-  else
-  {
-    Serial.println("AXP192 Begin FAIL");
-  }
-  axp.setPowerOutPut(AXP192_DCDC1, AXP202_OFF); // OLED  : off
-  axp.setPowerOutPut(AXP192_DCDC2, AXP202_OFF); // N/C   : off
-  axp.setPowerOutPut(AXP192_DCDC3, AXP202_ON);  // ESP32 : on
-  axp.setPowerOutPut(AXP192_LDO2,  AXP202_ON);  // LORA  : on
-  axp.setPowerOutPut(AXP192_LDO3,  AXP202_ON);  // GPS   : on
-  axp.setDCDC3Voltage(3300);                    // ESP32 : 3.3V
-  axp.setLDO2Voltage(3300);                     // LORA  : 3.3V
-  axp.setLDO3Voltage(3300);                     // GPS   : 3.3V
-  axp.setChgLEDMode(AXP20X_LED_OFF);
+    if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS))
+    {
+        Serial.println("AXP192 Begin PASS");
+    }
+    else
+    {
+        Serial.println("AXP192 Begin FAIL");
+    }
+    axp.setPowerOutPut(AXP192_DCDC1, AXP202_OFF); // OLED  : off
+    axp.setPowerOutPut(AXP192_DCDC2, AXP202_OFF); // N/C   : off
+    axp.setPowerOutPut(AXP192_DCDC3, AXP202_ON);  // ESP32 : on
+    axp.setPowerOutPut(AXP192_LDO2, AXP202_ON);   // LORA  : on
+    axp.setPowerOutPut(AXP192_LDO3, AXP202_ON);   // GPS   : on
+    axp.setDCDC3Voltage(3300);                    // ESP32 : 3.3V
+    axp.setLDO2Voltage(3300);                     // LORA  : 3.3V
+    axp.setLDO3Voltage(3300);                     // GPS   : 3.3V
+    axp.setChgLEDMode(AXP20X_LED_OFF);
 }
 
 void setupGps()
 {
-  GPS.begin(9600, SERIAL_8N1, 34, 12); // IO34: RX, IO12: TX
+    GPS.begin(9600, SERIAL_8N1, 34, 12); // IO34: RX, IO12: TX
 }
 
 void printStatus()
 {
-  axp.setChgLEDMode(AXP20X_LED_LOW_LEVEL);
-  while (GPS.available())
-  {
-    gps.encode(GPS.read());
-  }
-  Serial.println("##############################");
-  Serial.println("--- Power ---");
-  Serial.printf("DCDC1/OLED Status   : %s\n", axp.isDCDC1Enable() ? "enabled" : "disabled");
-  Serial.printf("DCDC1/OLED Voltage  : %g V\n", (float)axp.getDCDC1Voltage() / 1000);
-  Serial.printf("DCDC2/N/C Status    : %s\n", axp.isDCDC2Enable() ? "enabled" : "disabled");
-  Serial.printf("DCDC2/N/C Voltage   : %g V\n", (float)axp.getDCDC2Voltage() / 1000);
-  Serial.printf("DCDC3/ESP32 Status  : %s\n", axp.isDCDC3Enable() ? "enabled" : "disabled");
-  Serial.printf("DCDC3/ESP32 Voltage : %g V\n", (float)axp.getDCDC3Voltage() / 1000);
-  Serial.printf("LDO2/LoRa Status    : %s\n", axp.isLDO2Enable() ? "enabled" : "disabled");
-  Serial.printf("LDO2/LoRa Voltage   : %g V\n", (float)axp.getLDO2Voltage() / 1000);
-  Serial.printf("LDO3/GPS Status     : %s\n", axp.isLDO3Enable() ? "enabled" : "disabled");
-  Serial.printf("LDO3/GPS Voltage    : %g V\n", (float)axp.getLDO3Voltage() / 1000);
-  Serial.println("--- Battery ---");
-  Serial.printf("Battery Connected: %s\n", axp.isBatteryConnect() ? "true" : "false");
-  if (axp.isBatteryConnect())
-  {
-    Serial.printf("Battery Percentage : %d %%\n", axp.getBattPercentage());
-    Serial.printf("Battery Voltage    : %g V\n", axp.getBattVoltage() / 1000);
-    Serial.printf("Charging Enabled   : %s\n", axp.isChargeingEnable() ? "true" : "false");
-    Serial.printf("Battery Charging   : %s\n", axp.isChargeing() ? "true" : "false");
-    Serial.printf("Set Charge Current : %g mA\n", axp.getSettingChargeCurrent());
-  }
-  Serial.println("--- Chip ---");
-  Serial.printf("Chip Temp : %.1f C°\n", axp.getTemp() / 10);
-  Serial.println("--- GPS ---");
-  Serial.print("Latitude   : ");
-  Serial.println(gps.location.lat(), 5);
-  Serial.print("Longitude  : ");
-  Serial.println(gps.location.lng(), 4);
-  Serial.print("Satellites : ");
-  Serial.println(gps.satellites.value());
-  Serial.print("Altitude   : ");
-  Serial.print(gps.altitude.feet() / 3.2808);
-  Serial.println("M");
-  Serial.print("Time       : ");
-  Serial.print(gps.time.hour());
-  Serial.print(":");
-  Serial.print(gps.time.minute());
-  Serial.print(":");
-  Serial.println(gps.time.second());
-  Serial.print("Speed      : ");
-  Serial.println(gps.speed.kmph());
-  Serial.println("##############################");
-  axp.setChgLEDMode(AXP20X_LED_OFF);
-}
-
-static void handleClick() {
-  printStatus();
-}
-
-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
-  {
-    // Prepare upstream data transmission at the next possible time.
-    LMIC_setTxData2(1, mydata, sizeof(mydata) - 1, 0);
-    Serial.println(F("Packet queued"));
-  }
-  // Next TX is scheduled after TX_COMPLETE event.
+    axp.setChgLEDMode(AXP20X_LED_LOW_LEVEL);
+    while (GPS.available())
+    {
+        gps.encode(GPS.read());
+    }
+    Serial.println("##############################");
+    Serial.println("--- Power ---");
+    Serial.printf("DCDC1/OLED Status   : %s\n", axp.isDCDC1Enable() ? "enabled" : "disabled");
+    Serial.printf("DCDC1/OLED Voltage  : %g V\n", (float)axp.getDCDC1Voltage() / 1000);
+    Serial.printf("DCDC2/N/C Status    : %s\n", axp.isDCDC2Enable() ? "enabled" : "disabled");
+    Serial.printf("DCDC2/N/C Voltage   : %g V\n", (float)axp.getDCDC2Voltage() / 1000);
+    Serial.printf("DCDC3/ESP32 Status  : %s\n", axp.isDCDC3Enable() ? "enabled" : "disabled");
+    Serial.printf("DCDC3/ESP32 Voltage : %g V\n", (float)axp.getDCDC3Voltage() / 1000);
+    Serial.printf("LDO2/LoRa Status    : %s\n", axp.isLDO2Enable() ? "enabled" : "disabled");
+    Serial.printf("LDO2/LoRa Voltage   : %g V\n", (float)axp.getLDO2Voltage() / 1000);
+    Serial.printf("LDO3/GPS Status     : %s\n", axp.isLDO3Enable() ? "enabled" : "disabled");
+    Serial.printf("LDO3/GPS Voltage    : %g V\n", (float)axp.getLDO3Voltage() / 1000);
+    Serial.println("--- Battery ---");
+    Serial.printf("Battery Connected: %s\n", axp.isBatteryConnect() ? "true" : "false");
+    if (axp.isBatteryConnect())
+    {
+        Serial.printf("Battery Percentage : %d %%\n", axp.getBattPercentage());
+        Serial.printf("Battery Percentage : %f %%\n", axp.getBattPercentage());
+        Serial.printf("Battery Percentage : %g %%\n", axp.getBattPercentage());
+        Serial.printf("Battery Voltage    : %g V\n", axp.getBattVoltage() / 1000);
+        Serial.printf("Battery Current    : %g mA\n", axp.getBattDischargeCurrent());
+        Serial.println("--- Charging ---");
+        Serial.printf("Charging Enabled   : %s\n", axp.isChargeingEnable() ? "true" : "false");
+        Serial.printf("Battery Charging   : %s\n", axp.isChargeing() ? "true" : "false");
+        Serial.printf("Set Charge Current : %g mA\n", axp.getSettingChargeCurrent());
+    }
+    Serial.println("--- Chip ---");
+    Serial.printf("Chip Temp : %.1f C°\n", axp.getTemp() / 10);
+    Serial.println("--- GPS ---");
+    Serial.print("Latitude   : ");
+    Serial.println(gps.location.lat(), 5);
+    Serial.print("Longitude  : ");
+    Serial.println(gps.location.lng(), 4);
+    Serial.print("Satellites : ");
+    Serial.println(gps.satellites.value());
+    Serial.print("Altitude   : ");
+    Serial.print(gps.altitude.meters());
+    Serial.println("M");
+    Serial.print("Time       : ");
+    Serial.print(gps.time.hour());
+    Serial.print(":");
+    Serial.print(gps.time.minute());
+    Serial.print(":");
+    Serial.println(gps.time.second());
+    Serial.print("Speed      : ");
+    Serial.println(gps.speed.kmph());
+    Serial.println("##############################");
+    axp.setChgLEDMode(AXP20X_LED_OFF);
 }
 
-void onEvent(ev_t ev)
+void do_send()
 {
-  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"));
-    // TTN uses SF9 for its RX2 window.
-    LMIC.dn2Dr = EU868_DR_SF9;
-    break;
-  case EV_JOINED:
-    Serial.println(F("EV_JOINED"));
+    // Check if there is not a current TX/RX job running
+    if (LMIC.opmode & OP_TXRXPEND)
     {
-      u4_t netid = 0;
-      devaddr_t devaddr = 0;
-      u1_t nwkKey[16];
-      u1_t artKey[16];
-      LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
-      Serial.print("netid: ");
-      Serial.println(netid, DEC);
-      Serial.print("devaddr: ");
-      Serial.println(devaddr, HEX);
-      Serial.print("artKey: ");
-      for (int i = 0; i < sizeof(artKey); ++i)
-      {
-        Serial.print(artKey[i], HEX);
-      }
-      Serial.println("");
-      Serial.print("nwkKey: ");
-      for (int i = 0; i < sizeof(nwkKey); ++i)
-      {
-        Serial.print(nwkKey[i], HEX);
-      }
-      Serial.println("");
-
-      LMIC_setSeqnoUp(140);
+        Serial.println(F("OP_TXRXPEND, not sending"));
     }
-    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;
-    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)
+    else
     {
-      Serial.println(F("Received "));
-      Serial.println(LMIC.dataLen);
-      Serial.println(F(" bytes of payload"));
+        while (GPS.available())
+        {
+            gps.encode(GPS.read());
+        }
+        lpp.reset();
+        lpp.addGPS(1, gps.location.lat(), gps.location.lng(), gps.altitude.meters());
+        lpp.addAnalogInput(2, axp.getBattVoltage()/1000);
+        LMIC_setTxData2(1, lpp.getBuffer(), lpp.getSize(), 0);
+        Serial.println(F("Packet queued"));
+        Serial.println();
+        JsonObject json = jsonBuffer.to<JsonObject>();
+        lpp.decodeTTN(lpp.getBuffer(), lpp.getSize(), json);
+        serializeJsonPretty(json, Serial);
+        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;
-  case EV_SCAN_FOUND:
-    Serial.println(F("EV_SCAN_FOUND"));
-    break;
-  case EV_TXSTART:
-    Serial.println(F("EV_TXSTART"));
-    break;
-  case EV_TXCANCELED:
-    Serial.println(F("EV_TXCANCELLED"));
-    break;
-  case EV_JOIN_TXCOMPLETE:
-    Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept"));
-    break;
-  default:
-    Serial.println(F("Unknown event " + (unsigned)ev));
-    break;
-  }
+}
+
+static void handleClick()
+{
+    printStatus();
+    do_send();
 }
 
 void setup()
 {
-  Serial.begin(115200);
-  Wire.begin(21, 22);
-  setupPower();
-  setupGps();
-  LoRaWAN.begin(pinMap);
-  btn.attachClick(handleClick);
-  printStatus();
-  do_send(&sendjob);
+    Serial.begin(115200);
+    Wire.begin(21, 22);
+    setupPower();
+    setupGps();
+    LoRaWAN.begin(pinMap);
+    btn.attachClick(handleClick);
+    printStatus();
+    statusTimer = millis();
 }
 
 void loop()
 {
-  LoRaWAN.loop();
-  btn.tick();
+    LoRaWAN.loop();
+    btn.tick();
+    if (millis() - statusTimer >= 1000 * 30) {
+        printStatus();
+        do_send();
+        statusTimer = millis();
+   }
 }