diff --git a/.gitignore b/.gitignore
index a20dc5c0d8f54ffa862078d9182acdd6bc6b4873..55f017b647d2f2124c1ad1a9cacde7719ab6847a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
 .travis.yml
 platformio.ini
 BikeBeam.code-workspace
+src/config.h
diff --git a/src/main.cpp b/src/main.cpp
index 99f8578959165f8652db4cf1e722d9d70cabd717..9e5569fa81166d686263e0d4e0681d5cf6808bac 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,14 +1,83 @@
-#include <TinyGPS++.h>
+#include <config.h>
 #include <axp20x.h>
+#include <Arduino_LoRaWAN_ttn.h>
+#include <lmic.h>
+#include <hal/hal.h>
+#include <SPI.h>
+#include <TinyGPS++.h>
+
+class cLoRaWAN : public Arduino_LoRaWAN_ttn
+{
+public:
+  cLoRaWAN(){};
+
+protected:
+  // you'll need to provide implementations for each of the following.
+  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;
+
+  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(
+    const SessionInfo &Info,
+    const uint8_t *pExtraInfo,
+    size_t nExtraInfo)
+{
+  // save Info somewhere.
+}
+
+const cLoRaWAN::lmic_pinmap pinMap = {
+     .nss = 18,
+     .rxtx = cLoRaWAN::lmic_pinmap::LMIC_UNUSED_PIN,
+     .rst = 23,
+     .dio = { 26, 33, 32 },
+};
+
 
-TinyGPSPlus gps;
-HardwareSerial GPS(1);
 AXP20X_Class axp;
+HardwareSerial GPS(1);
+TinyGPSPlus gps;
+cLoRaWAN LoRaWAN{};
 
-void setupPower() {
-  if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) {
+static uint8_t mydata[] = "Hello, world!";
+static osjob_t sendjob;
+const unsigned TX_INTERVAL = 60;
+
+void setupPower()
+{
+  if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS))
+  {
     Serial.println("AXP192 Begin PASS");
-  } else {
+  }
+  else
+  {
     Serial.println("AXP192 Begin FAIL");
   }
   axp.setPowerOutPut(AXP192_LDO2, AXP202_ON);
@@ -19,22 +88,29 @@ void setupPower() {
   axp.setChgLEDMode(AXP20X_LED_OFF);
 }
 
-void setupGps() {
-  GPS.begin(9600, SERIAL_8N1, 34, 12);   //17-TX 18-RX
-}
-
-static void smartDelay(unsigned long ms)
+void setupGps()
 {
-  unsigned long start = millis();
-  do
-  {
-    while (GPS.available())
-      gps.encode(GPS.read());
-  } while (millis() - start < ms);
+  GPS.begin(9600, SERIAL_8N1, 34, 12); //17-TX 18-RX
 }
 
-void printGpsStatus() {
+void printStatus()
+{
   axp.setChgLEDMode(AXP20X_LED_LOW_LEVEL);
+  while (GPS.available())
+    gps.encode(GPS.read());
+  Serial.println("**********************");
+  Serial.println("--- Power ---");
+  Serial.printf("Battery Connected: %s\n", axp.isBatteryConnect() ? "true" : "false");
+  if (axp.isBatteryConnect())
+  {
+    Serial.printf("Battery Charging Enabled: %s\n", axp.isChargeingEnable() ? "true" : "false");
+    Serial.printf("Battery Charging: %s\n", axp.isChargeing() ? "true" : "false");
+    Serial.printf("Battery Voltage: %.2f V\n", axp.getBattVoltage() / 1000);
+    Serial.printf("Battery Percentage: %d %%\n", axp.getBattPercentage());
+    Serial.printf("Battery Charge Current: %g mA\n", axp.getSettingChargeCurrent());
+  }
+  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 : ");
@@ -51,12 +127,133 @@ void printGpsStatus() {
   Serial.print(":");
   Serial.println(gps.time.second());
   Serial.print("Speed     : ");
-  Serial.println(gps.speed.kmph()); 
+  Serial.println(gps.speed.kmph());
   Serial.println("**********************");
   axp.setChgLEDMode(AXP20X_LED_OFF);
-  smartDelay(1000);
-  if (millis() > 5000 && gps.charsProcessed() < 10)
-    Serial.println(F("No GPS data received: check wiring"));
+}
+
+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.
+}
+
+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"));
+    // TTN uses SF9 for its RX2 window.
+    LMIC.dn2Dr = EU868_DR_SF9;
+    break;
+  case EV_JOINED:
+    Serial.println(F("EV_JOINED"));
+    {
+      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);
+    }
+    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"));
+    }
+    // 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;
+  }
 }
 
 void setup()
@@ -65,9 +262,14 @@ void setup()
   Wire.begin(21, 22);
   setupPower();
   setupGps();
+  LoRaWAN.begin(pinMap);
+  printStatus();
+  do_send(&sendjob);
 }
 
 void loop()
 {
-  printGpsStatus();
+  LoRaWAN.loop();
+  //printStatus();
+  //delay(1000);
 }