diff --git a/iot/TTNTest/TTNTest.ino b/iot/TTNTest/TTNTest.ino
index d3fbc6f..408a713 100644
--- a/iot/TTNTest/TTNTest.ino
+++ b/iot/TTNTest/TTNTest.ino
@@ -1,5 +1,6 @@
 /*******************************************************************************
  * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
+ * Copyright (c) 2018 Terry Moore, MCCI
  *
  * Permission is hereby granted, free of charge, to anyone
  * obtaining a copy of this document and accompanying files,
@@ -9,18 +10,30 @@
  *
  * This example sends a valid LoRaWAN packet with payload "Hello,
  * world!", using frequency and encryption settings matching those of
- * the (early prototype version of) The Things Network.
+ * the The Things Network.
  *
- * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in g1,
- *  0.1% in g2).
+ * This uses ABP (Activation-by-personalisation), where a DevAddr and
+ * Session keys are preconfigured (unlike OTAA, where a DevEUI and
+ * application key is configured, while the DevAddr and session keys are
+ * assigned/generated in the over-the-air-activation procedure).
  *
- * Change DEVADDR to a unique address!
- * See http://thethingsnetwork.org/wiki/AddressSpace
+ * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
+ * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
+ * violated by this sketch when left running for longer)!
  *
- * Do not forget to define the radio type correctly in config.h.
+ * To use this sketch, first register your application and device with
+ * the things network, to set or generate a DevAddr, NwkSKey and
+ * AppSKey. Each device should have their own unique values for these
+ * fields.
+ *
+ * Do not forget to define the radio type correctly in
+ * arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
  *
  *******************************************************************************/
 
+ // References:
+ // [feather] adafruit-feather-m0-radio-with-lora-module.pdf
+
 #include <lmic.h>
 #include <hal/hal.h>
 #include <SPI.h>
@@ -29,7 +42,8 @@
 
 // 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).
+// 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) { }
@@ -71,23 +85,28 @@ void onEvent (ev_t ev) {
 		case EV_JOINED:
 			Serial.println(F("EV_JOINED"));
 			break;
-		case EV_RFU1:
-			Serial.println(F("EV_RFU1"));
-			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;
-			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();
+			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);
@@ -108,8 +127,20 @@ void onEvent (ev_t ev) {
 		case EV_LINK_ALIVE:
 			Serial.println(F("EV_LINK_ALIVE"));
 			break;
-		 default:
-			Serial.println(F("Unknown event"));
+		/*
+		|| 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;
 	}
 }
@@ -127,15 +158,17 @@ void do_send(osjob_t* j){
 }
 
 void setup() {
+//    pinMode(13, OUTPUT); 
+	while (!Serial); // wait for Serial to be initialized
 	Serial.begin(115200);
+	delay(100);     // per sample code on RF_95 test
 	Serial.println(F("Starting"));
 	
-	#define PIN_CS  	10
-	#define PIN_CS_2	3
-	pinMode(PIN_CS, OUTPUT);
-	pinMode(PIN_CS_2, OUTPUT);
-	digitalWrite(PIN_CS, LOW); // We want to talk to the RFM 95
-	digitalWrite(PIN_CS_2, HIGH);
+	// Activate the right SPI device
+	pinMode(10, OUTPUT);
+	pinMode(3, OUTPUT);
+	digitalWrite(10, LOW);
+	digitalWrite(3, HIGH);
 
 	#ifdef VCC_ENABLE
 	// For Pinoccio Scout boards
@@ -159,12 +192,13 @@ void setup() {
 	uint8_t nwkskey[sizeof(NWKSKEY)];
 	memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
 	memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
-	LMIC_setSession (0x1, DEVADDR, nwkskey, appskey);
+	LMIC_setSession (0x13, DEVADDR, nwkskey, appskey);
 	#else
-	// If not running an AVR with PROGMEM, just use the arrays directly 
-	LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
+	// If not running an AVR with PROGMEM, just use the arrays directly
+	LMIC_setSession (0x13, DEVADDR, NWKSKEY, APPSKEY);
 	#endif
 
+	#if defined(CFG_eu868)
 	// Set up the channels used by the Things Network, which corresponds
 	// to the defaults of most gateways. Without this, only three base
 	// channels from the LoRaWAN specification are used, which certainly
@@ -186,11 +220,21 @@ void setup() {
 	// devices' ping slots. LMIC does not have an easy way to define set this
 	// frequency and support for class B is spotty and untested, so this
 	// frequency is not configured here.
+	#elif defined(CFG_us915)
+	// NA-US channels 0-71 are configured automatically
+	// but only one group of 8 should (a subband) should be active
+	// TTN recommends the second sub band, 1 in a zero based count.
+	// https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json
+	LMIC_selectSubBand(1);
+	#endif
 
 	// Disable link check validation
 	LMIC_setLinkCheckMode(0);
 
-	// Set data rate and transmit power (note: txpow seems to be ignored by the library)
+	// 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
@@ -198,5 +242,15 @@ void setup() {
 }
 
 void loop() {
+	unsigned long now;
+	now = millis();
+	if ((now & 512) != 0) {
+	  digitalWrite(13, HIGH);
+	}
+	else {
+	  digitalWrite(13, LOW);
+	}
+	  
 	os_runloop_once();
+	
 }
diff --git a/iot/TTNTestOTAA/TTNTestOTAA.ino b/iot/TTNTestOTAA/TTNTestOTAA.ino
new file mode 100644
index 0000000..db59ca6
--- /dev/null
+++ b/iot/TTNTestOTAA/TTNTestOTAA.ino
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
+ *
+ * Permission is hereby granted, free of charge, to anyone
+ * obtaining a copy of this document and accompanying files,
+ * to do whatever they want with them without any restriction,
+ * including, but not limited to, copying, modification and redistribution.
+ * NO WARRANTY OF ANY KIND IS PROVIDED.
+ *
+ * This example sends a valid LoRaWAN packet with payload "Hello,
+ * world!", using frequency and encryption settings matching those of
+ * the The Things Network.
+ *
+ * This uses OTAA (Over-the-air activation), where where a DevEUI and
+ * application key is configured, which are used in an over-the-air
+ * activation procedure where a DevAddr and session keys are
+ * assigned/generated for use with all further communication.
+ *
+ * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
+ * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
+ * violated by this sketch when left running for longer)!
+
+ * To use this sketch, first register your application and device with
+ * the things network, to set or generate an AppEUI, DevEUI and AppKey.
+ * Multiple devices can use the same AppEUI, but each device has its own
+ * DevEUI and AppKey.
+ *
+ * Do not forget to define the radio type correctly in config.h.
+ *
+ *******************************************************************************/
+#define DISABLE_PING
+#define DISABLE_BEACONS
+
+#include <lmic.h>
+#include <hal/hal.h>
+#include <SPI.h>
+
+#include "config.custom.h"
+
+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;
+
+
+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"));
+
+            // Disable link check validation (automatically enabled
+            // during join, but not supported by TTN at this time).
+            LMIC_setLinkCheckMode(0);
+            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;
+         default:
+            Serial.println(F("Unknown event"));
+            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 {
+        // 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 setup() {
+    Serial.begin(115200);
+    Serial.println(F("Starting"));
+	
+	// Activate the right SPI device
+	pinMode(10, OUTPUT);
+	pinMode(3, OUTPUT);
+	digitalWrite(10, LOW);
+	digitalWrite(3, HIGH);
+
+    #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(5 * MAX_CLOCK_ERROR / 100);
+
+    // Start job (sending automatically starts OTAA too)
+    do_send(&sendjob);
+}
+
+void loop() {
+    os_runloop_once();
+}
diff --git a/iot/TTNTestOTAA/config.custom.h.example b/iot/TTNTestOTAA/config.custom.h.example
new file mode 100644
index 0000000..43e3409
--- /dev/null
+++ b/iot/TTNTestOTAA/config.custom.h.example
@@ -0,0 +1,26 @@
+
+// This EUI must be in little-endian format, so least-significant-byte
+// first. When copying an EUI from ttnctl output, this means to reverse
+// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
+// 0x70.
+static const u1_t PROGMEM APPEUI[8]={ ..... };
+void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
+
+// This should also be in little endian format, see above.
+static const u1_t PROGMEM DEVEUI[8]={ ...... };
+void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
+
+// This key should be in big endian format (or, since it is not really a
+// number but a block of memory, endianness does not really apply). In
+// practice, a key taken from ttnctl can be copied as-is.
+// The key shown here is the semtech default key.
+static const u1_t PROGMEM APPKEY[16] = { ...... };
+void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}
+
+
+const lmic_pinmap lmic_pins = {
+	.nss = 10,
+	.rxtx = LMIC_UNUSED_PIN,
+	.rst = 9,
+	.dio = {2, 6, 7},
+};
diff --git a/iot/main/main.ino b/iot/main/main.ino
index 73b100b..0d0698f 100644
--- a/iot/main/main.ino
+++ b/iot/main/main.ino
@@ -7,6 +7,7 @@
 // BAD PRACTICE: For some extremely strange reason, the Arduino IDE doesn't pick up random.cpp like it does our other source files - so we've got to explicitly include it here. If we had control over the build process (which we don't), we've use a Makefile here that handled this better.
 #include "random.cpp"
 #include "gps.h"
+#include "peripheral.h"
 
 
 void setup() {
@@ -15,6 +16,9 @@ void setup() {
 	
 	random_begin();
 	
+	peripheral_register(PIN_SPI_CS_RFM95);
+	peripheral_register(PIN_SPI_CS_SD);
+	
 	
 	gps_begin();
 	TinyGPSPlus gps_data = gps_location();
@@ -27,7 +31,10 @@ void setup() {
 	Serial.print("[main] id: ");
 	Serial.println(id);
 	
-	store_init(); 
+	// Activate microSD card breakout board on the SPI bus
+	peripheral_unsilence(PIN_SPI_CS_SD);
+	
+	store_init();
 	store_reading(id, gps_data.location);
 	char debug_message[64];
 	int chars = snprintf(debug_message, 64, "%d-%d-%d %d:%d:%d | %f %f",
@@ -43,6 +50,13 @@ void setup() {
 	store_debug(debug_message, chars);
 	store_close();
 	
+	// ------------------------------------------------------------------------
+	
+	// Activate the RFM95
+	peripheral_unsilence(PIN_SPI_CS_RFM95);
+	
+	
+	
 	power_off(); // Doesn't return
 }
 
diff --git a/iot/main/peripheral.cpp b/iot/main/peripheral.cpp
new file mode 100644
index 0000000..f4ecfa2
--- /dev/null
+++ b/iot/main/peripheral.cpp
@@ -0,0 +1,20 @@
+#include <Arduino.h>
+
+void peripheral_register(int pin_number) {
+	pinMode(OUTPUT, pin_number);
+	// Disable the device by default to avoid issues
+	digitalWrite(pin_number, HIGH);
+}
+
+void peripheral_unsilence(int pin_number) {
+	digitalWrite(pin_number, LOW);
+}
+
+void peripheral_silence(int pin_number) {
+	digitalWrite(pin_number, HIGH);
+}
+
+void peripheral_switch(int pin_number_old, int pin_number_new) {
+	digitalWrite(pin_number_old, HIGH);
+	digitalWrite(pin_number_new, LOW);
+}
diff --git a/iot/main/peripheral.h b/iot/main/peripheral.h
new file mode 100644
index 0000000..d186a72
--- /dev/null
+++ b/iot/main/peripheral.h
@@ -0,0 +1,24 @@
+#pragma once
+
+/**
+ * Register a new SPI peripheral.
+ * @param	pin_number	The pin number of the device's chip select pin.
+ */
+void peripheral_register(int pin_number);
+
+/**
+ * Allows the device with the given chip select pin to talk on the SPI bus.
+ * @param pin_number The pin number of the chip select pin of the device to allow to talk.
+ */
+void peripheral_unsilence(int pin_number);
+/**
+ * Stops the device with the given chip select pin from talking on the SPI bus.
+ * @param pin_number The chip-select pin number of the device to stop.
+ */
+void peripheral_silence(int pin_number);
+/**
+ * Switches the active device from one to another on the SPI bus.
+ * @param pin_number_old The chip-select pin number of the old device to switch out from.
+ * @param pin_number_new The chip-select pin number of the new device to switch in to.
+ */
+void peripheral_switch(int pin_number_old, int pin_number_new);
diff --git a/iot/main/radio.cpp b/iot/main/radio.cpp
new file mode 100644
index 0000000..a83ca9e
--- /dev/null
+++ b/iot/main/radio.cpp
@@ -0,0 +1,160 @@
+#pragma once
+
+#include <lmic.h>
+#include <hal/hal.h>
+#include <SPI.h>
+
+// Global static variable that's used to detect when LMIC has finished doing it's thing
+static bool is_sending_complete = false;
+
+
+static osjob_t sendjob;
+
+void radio_init() {
+	// 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.
+	#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
+	
+	// Set up the channels used by the Things Network, which corresponds
+	// to the defaults of most gateways. Without this, only three base
+	// channels from the LoRaWAN specification are used, which certainly
+	// works, so it is good for debugging, but can overload those
+	// frequencies, so be sure to configure the full frequency range of
+	// your network here (unless your network autoconfigures them).
+	// Setting up channels should happen after LMIC_setSession, as that
+	// configures the minimal channel set.
+	LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
+	LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI);      // g-band
+	LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
+	LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
+	LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
+	LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
+	LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
+	LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
+	LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK,  DR_FSK),  BAND_MILLI);      // g2-band
+	// TTN defines an additional channel at 869.525Mhz using SF9 for class B
+	// devices' ping slots. LMIC does not have an easy way to define set this
+	// frequency and support for class B is spotty and untested, so this
+	// frequency is not configured here.
+
+	// Disable link check validation
+	LMIC_setLinkCheckMode(0);
+
+	// Set data rate and transmit power (note: txpow seems to be ignored by the library)
+	LMIC_setDrTxpow(DR_SF7,14);
+}
+
+/**
+ * Sends a specified message via LoRaWAN.
+ * @param data   The message to send.
+ * @param length The length of the given message.
+ */
+bool radio_send(uint8_t* data, int length) {
+	// Check if there is not a current TX/RX job running
+	if (LMIC.opmode & OP_TXRXPEND) {
+		Serial.println(F("OP_TXRXPEND: There's already a job running, not sending"));
+		return false;
+	}
+	// Prepare upstream data transmission at the next possible time.
+	LMIC_setTxData2(1, data, length, 0);
+	Serial.println(F("Packet queued"));
+	
+	// Run the LMIC loop, but only until it's finished sending the packet
+	while (!is_sending_complete) {
+		os_runloop_once();
+	}
+	
+	// Reset it for next time (just in case)
+	is_sending_complete = false;
+	
+	return true;
+}
+
+// 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) { }
+
+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.dataLen) {
+				// data received in rx slot after tx
+				Serial.print(F("Data Received: "));
+				Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen);
+				Serial.println();
+			}
+			// We're done!
+			is_sending_complete = true;
+			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;
+	}
+}
diff --git a/iot/main/radio.h b/iot/main/radio.h
new file mode 100644
index 0000000..d572f63
--- /dev/null
+++ b/iot/main/radio.h
@@ -0,0 +1,13 @@
+#pragma once
+
+/**
+ * Initialises the RFM95 LoRa radio.
+ */
+void radio_init();
+
+/**
+ * Sends a specified message via LoRaWAN.
+ * @param data   The message to send.
+ * @param length The length of the given message.
+ */
+void radio_send(byte* data, int length);
diff --git a/iot/main/settings.h b/iot/main/settings.h
index df2f353..d49ac0e 100644
--- a/iot/main/settings.h
+++ b/iot/main/settings.h
@@ -1,5 +1,7 @@
 #pragma once
 
+#include <arduino-lmic/src/hal/hal.h>
+
 //////////////////////////////////
 ////////////// Main //////////////
 //////////////////////////////////
@@ -7,23 +9,44 @@
 // The speed at which we should talk over our main hardware serial connection.
 #define BAUD_PC 		115200
 
-// Multiple devices can use the same SPI data pin AFAIKT, but some libraries *cough* SD *cough* are too stupid to figure out which pin it is on their own.
+// Multiple devices can use the same SPI data pin AFAIKT, but some libraries 
+// *cough* SD *cough* are too stupid to figure out which pin it is on their own.
 #define PIN_SPI_DATA	9
 
+// The 'done' pin to pulse to signal to the TPL5111
 #define PIN_TPL_DONE	8
 
+/////////////
+/// RFM95 ///
+/////////////
+
+// Pin mapping
+const lmic_pinmap lmic_pins = {
+	.nss = 10,
+	.rxtx = LMIC_UNUSED_PIN,
+	.rst = 9,
+	.dio = {2, 6, 7},
+};
+
+// The SPI chip-select pin for the RFM 95
+#define PIN_SPI_CS_RFM95	10
+
 /////////////////////////////////
 ////////////// GPS //////////////
 /////////////////////////////////
 
 // The *TX* gin of the GPS device.
-// This is swapped because we receive the GPS device's message on our side on the RX pin, and the GPS device transmits messages on the TX.
+// This is swapped because we receive the GPS device's message on our side on 
+// the RX pin, and the GPS device transmits messages on the TX.
 #define PIN_GPS_RX	5
 // The *RX* pin on the GPS device.
-// This is swapped because where the GPs device is receiving, we aresending and vice versa.
+// This is swapped because where the GPs device is receiving, we aresending and 
+// vice versa.
 // The TX / RX here are according to *our* side, not the GPS device's side.
 #define PIN_GPS_TX	4
-// The speed at which we should talk to the GPS device. Some GPS devices require a certain speed in order to use certain commands, so it's important that you check the datasheets for the device you're using.
+// The speed at which we should talk to the GPS device. Some GPS devices 
+// require a certain speed in order to use certain commands, so it's important 
+// that you check the datasheets for the device you're using.
 // 9600 is the correct speed for a NEO-6M.
 #define BAUD_GPS	9600
 
@@ -32,7 +55,7 @@
 //////////////////////////////////
 
 // The chip select pin that activates the connection to the microSD card over SPI.
-#define	PIN_SD_SPI_CHIP_SELECT	3
+#define PIN_SPI_CS_SD		3
 
 // The filename on the microSD card to store data in.
 #define SD_FILENAME	"data.tsv"
