diff --git a/ESP8266_Transmitter/ESP8266_Transmitter.ino b/ESP8266_Transmitter/ESP8266_Transmitter.ino
index b3fa9a4dbdd36a4d1a67c4d76497c17c4efd019a..a3ac0ab844625ac825ccf40339ac5b127c288530 100644
--- a/ESP8266_Transmitter/ESP8266_Transmitter.ino
+++ b/ESP8266_Transmitter/ESP8266_Transmitter.ino
@@ -29,11 +29,13 @@
  *  =========================================================================================
  */
 
+#include <TimeLib.h>
 #include <SPI.h>
 #include <Wire.h>
 #include <LoRa.h>
 #include <SoftwareSerial.h>
 #include <ArduinoJson.h>
+#include <base64.h>
 #include "SHTSensor.h"
 
 // Lora Config
@@ -90,9 +92,10 @@ struct pms5003data {
 struct pms5003data data;
 
 // Polling / Transmitting Config
-int pollingFrequency = 1000; // Polling Frequency, ms
+int pollingFrequency = 2500; // Polling Frequency, ms
 int pollEventCount = 0; // Number of times data has been sampled in this recording period
-int sendAfterPolls = 5; // Sample Period, after which Transmit Reading 
+int sendAfterPolls = 10; // Sample Period, after which Transmit Reading 
+int reservationTime = 1; // How long do we require use of TX / RX airways for sending packets?
 
 /**
  * Setup Humidity / Temperature Sensor
@@ -187,7 +190,7 @@ boolean readPMSdata(Stream *s) {
   }
     
   uint8_t buffer[32];    
-  uint16_t sum = 0;
+  uint32_t sum = 0;
   s->readBytes(buffer, 32);
  
   // get checksum ready
@@ -207,29 +210,148 @@ boolean readPMSdata(Stream *s) {
  
   if (sum != data.checksum) {
     Serial.println("Checksum failure");
+    Serial.print("Expected: "); Serial.print(sum);
+    Serial.print(", Got: "); Serial.print(data.checksum);
     return false;
   }
 
   return true;
 }
 
-boolean transmitData(DynamicJsonDocument doc) {
-  // TODO Include methods for error detection here
-  
-    LoRa.beginPacket();
-    //LoRa.print(avgPpm10); LoRa.print(',');
-    //LoRa.print(avgPpm25); LoRa.print(',');
-    //LoRa.print(avgPpm100); LoRa.print(',');
-    //LoRa.print(avgTemperature); LoRa.print(',');
-    //LoRa.print(avgHumidity);
+/* Turn on Reciever, 
+ * listen for messages for [listenDuration] seconds,
+ * Return true if no messages recieved
+ */
+boolean clearToSend(int listenDuration) {  
+  int listenUntil = now() + listenDuration;
+
+  // Listen until timeout expires
+  while (listenUntil >= now()) {
+    int packetSize = LoRa.parsePacket();
+    
+    if (packetSize) {
+      return false; // Other message heard on Rx, we can not transmit just now. 
+    }
+
+    delay(5);
+  }
+
+  return true; // We didn't hear anything, so continue
+}
+
+// Listen of listenDuration seconds
+// If we see a response to our txHello packet, and txHello.okToTransmit is True, we can send our packet
+boolean listenTxHelloAccept(int listenDuration) {
+  int time_now = now() + listenDuration;
+
+  // Listen until timeout expires
+  while (time_now >= now()) {
+    Serial.println("Listening for auth");
+    int packetSize = LoRa.parsePacket();
+    
+    if (packetSize) {
+      String incoming = "";
+      char temp;
+      
+      while (LoRa.available()) {
+        // TODO - Tidy this  up, ensure we only read in valid JSON
+        temp = (char)LoRa.read();
+
+        // Opening {
+        if (incoming.length() == 0 && temp == '{') {
+          incoming = "{";
+          
+        // Closing }  
+        } else if (temp == '}') {
+          incoming.concat("}");
+          break;
+
+        // Anything else that's valid
+        } else if (incoming.length() > 0) {
+          incoming.concat(temp);
+        }
+      }
+
+      // DEBUG
+//      Serial.print("in listenTxHelloAccept() Recieved: \n"); 
+//      Serial.println(incoming);
+
+      // Verify its OK to transmit
+      StaticJsonDocument<200> doc;
+      deserializeJson(doc, incoming);
+
+      const bool okToCopy = doc["okToTransmit"];
+      const String authIsForUid = doc["uid"];
+
+      // Verify txHello.okToTransmit is True & UID Match
+      return (okToCopy && authIsForUid == getSensorUID());
+    }
+
+    delay(5);
+  }
+
+  return false; // We didn't hear anything, conside this as meaning "Can't Send"
+}
+
+void sendJsonPayloadWithLoRa(DynamicJsonDocument payload) {
+  LoRa.beginPacket();
+  serializeJson(payload, LoRa);
+  LoRa.endPacket();
+}
+
+// TODO - Finish Acks & Naks
+boolean transmitData(DynamicJsonDocument payload) {
+
+  // Listen for other communication
+  //    Rx - Listen for other messages, if no messages heard then continue
+//  if (!clearToSend(0.5)) {
+//    Serial.println("[-] Airways busy, could not send");
+//    delay(random(500, 1250)); // Introduce random delay to avoid another collision
+//    return false;
+//  }
+
+
+  // Send short "clear to send?" packet
+  //    Tx - "I've got data to send!" + UID
+  //    RX - Continue upon recieving "OK" + UID + TX_Auth_ID
+  DynamicJsonDocument txHello(2048);
+  txHello["uid"] = sensorID;
+  txHello["reservationTime"] = reservationTime; // How long do we require reservation of radio?
+  sendJsonPayloadWithLoRa(txHello);
+
+  if (!listenTxHelloAccept(reservationTime * 1.1)) { // Else we have clear to send
+    return false; // Can't transmit just now
+    
+  } else {
+    Serial.println("OK To Transmit");
+  }
+
+
+  // Transmit Payload
+  //    Tx - Send payload + UID + TX_Auth_ID
+  //    Rx - Listen for Ack/Nack
+  Serial.println("Sending Payload Now");
+  sendJsonPayloadWithLoRa(payload);
+
+
+  // TODO Await Response Ack/Nak
+
+
+  // TODO Listen for ack/nak
+ 
+  // T+2  Rx OFF
+  //      Handle Ack + UID + Next_Send_Interval
+  //      Handle Nak + UID: GoTo T-3
+  //      Handle No Response: GoTo T-3
+          
+  // T+3  Tx/Rx Window Expires
+
     
     //LoRa.write(localAddress);
 //      LoRa.write(msgCount);
     //LoRa.write(outgoing.length());
     //LoRa.print(outgoing);
-    serializeJson(doc, LoRa);
-    LoRa.endPacket();
-
+    
     return true;
 }
 
@@ -252,111 +374,119 @@ void setup() {
 
 void loop() {
   // TODO Gather Sensor Data
-  if (readPMSdata(&pmsSerial)) {
-    pollEventCount++;
-    
-    Serial.println();
-    Serial.println("---------------------------------------");
-    Serial.println("Concentration Units (standard)");
-    Serial.print("PM 1.0: "); Serial.print(data.pm10_standard);
-    Serial.print("\t\tPM 2.5: "); Serial.print(data.pm25_standard);
-    Serial.print("\t\tPM 10: "); Serial.println(data.pm100_standard);
-    
-    Serial.print("Temp: "); Serial.print(getTemperature());
-    Serial.print("\t\tHumidity: "); Serial.println(getHumidity());
-    Serial.println("-------------------" + String(pollEventCount) + "--------------------");
-
-    // Add to Average
-    // Standard (_standard) or Environmental (_env)
-    ppm10 = ppm10 + data.pm10_standard;
-    ppm25 = ppm25 + data.pm25_standard;
-    ppm100 = ppm100 + data.pm100_standard;
-
-    if (pollEventCount == sendAfterPolls) {
-      // Average Values over recording period
-      avgPpm10 = ppm10 / sendAfterPolls;
-      avgPpm25 = ppm25 / sendAfterPolls;
-      avgPpm100 = ppm100 / sendAfterPolls;
-
-      avgTemperature = temperature / sendAfterPolls;
-      avgHumidity = humidity / sendAfterPolls;
-
-      // TODO Transmit Sensor Data
-      Serial.print("Avg ppm10: "); Serial.println(avgPpm10);
-      Serial.print("Avg ppm25: "); Serial.println(avgPpm25);
-      Serial.print("Avg ppm100: "); Serial.println(avgPpm100);
-
-      Serial.print("Avg Temperature: "); Serial.println(avgTemperature);
-      Serial.print("Avg Humidity: "); Serial.println(avgHumidity);
-
-      Serial.print("Chip ID: "); Serial.println(sensorID);
-
-
-      // LORA SEND
-      DynamicJsonDocument doc(2048);
-
-      JsonObject metadata = doc.createNestedObject("sensorMetadata");
-      metadata["uid"] = sensorID;
-      metadata["messageID"] = msgCount;
-      metadata["samplePeriod"] = sendAfterPolls; // TODO: Multiply by poll duration!
-
-      JsonObject data = doc.createNestedObject("data");
 
-      JsonObject ppm = data.createNestedObject("ppm"); // Particulates
-      ppm["p10"] = avgPpm10;
-      ppm["p25"] = avgPpm25;
-      ppm["p100"] = avgPpm100;
-
-      JsonObject sht = data.createNestedObject("sht");  // Temp, Humidity
-      sht["temperature"] = avgTemperature;
-      sht["humidity"] = avgHumidity;
-
-      JsonObject co2 = data.createNestedObject("co2"); // Co2
-      co2["tmp"] = 0;
-
-      if (transmitData(doc)) {
-        Serial.println("Packet Sent\n");
-      } else {
-        Serial.println("[-] Failed to send packet\n");
-      }
-
-      // TODO Wait for reply
-      int i = 3000;
-      int j = 0;
-      String incoming = "";
+  // Error reading PMS Data, ignore on this round
+  if (!readPMSdata(&pmsSerial)) {
+    data.pm10_standard = 0;
+    data.pm25_standard = 0;
+    data.pm100_standard = 0; 
+  }
+  
+  pollEventCount++;
+  
+  Serial.println();
+  Serial.print(String(pollEventCount) + ") Std Units: ");
+  Serial.print("PM 1.0: "); Serial.print(data.pm10_standard);
+  Serial.print("\t\tPM 2.5: "); Serial.print(data.pm25_standard);
+  Serial.print("\t\tPM 10: "); Serial.print(data.pm100_standard);
+  Serial.print("\t\tTemp: "); Serial.print(getTemperature());
+  Serial.print("\t\tHumidity: "); Serial.print(getHumidity());
+
+  // Add to Average
+  // Standard (_standard) or Environmental (_env)
+  ppm10 = ppm10 + data.pm10_standard;
+  ppm25 = ppm25 + data.pm25_standard;
+  ppm100 = ppm100 + data.pm100_standard;
+
+  if (pollEventCount == sendAfterPolls) {
+    // Average Values over recording period
+    avgPpm10 = ppm10 / sendAfterPolls;
+    avgPpm25 = ppm25 / sendAfterPolls;
+    avgPpm100 = ppm100 / sendAfterPolls;
+
+    avgTemperature = temperature / sendAfterPolls;
+    avgHumidity = humidity / sendAfterPolls;
+
+    // TODO Transmit Sensor Data
+    Serial.println("---------------------------------------");
+    Serial.print("Avg ppm10: "); Serial.print(avgPpm10);
+    Serial.print("\t\tAvg ppm25: "); Serial.print(avgPpm25);
+    Serial.print("\t\tAvg ppm100: "); Serial.print(avgPpm100);
+    Serial.print("\t\tAvg Temp: "); Serial.print(avgTemperature);
+    Serial.print("\t\tAvg Humidity: "); Serial.println(avgHumidity);
+    Serial.print("Chip ID: "); Serial.println(sensorID);
+    Serial.println("");
+
+    // LORA SEND
+    DynamicJsonDocument doc(2048);
+
+    JsonObject metadata = doc.createNestedObject("sensorMetadata");
+    metadata["uid"] = sensorID;
+    metadata["messageID"] = msgCount;
+    metadata["samplePeriod"] = sendAfterPolls; // TODO: Multiply by poll duration!
+
+    JsonObject data = doc.createNestedObject("data");
+
+    JsonObject ppm = data.createNestedObject("ppm"); // Particulates
+    ppm["p10"] = avgPpm10;
+    ppm["p25"] = avgPpm25;
+    ppm["p100"] = avgPpm100;
+
+    JsonObject sht = data.createNestedObject("sht");  // Temp, Humidity
+    sht["temperature"] = avgTemperature;
+    sht["humidity"] = avgHumidity;
+
+    JsonObject co2 = data.createNestedObject("co2"); // Co2
+    co2["tmp"] = 0;
+
+    if (transmitData(doc)) {
+      Serial.println("Packet Sent\n");
       
-      while (j < i) {
-        
-        int packetSize = LoRa.parsePacket();
-        if (packetSize) {
-          while (LoRa.available()) {
-            incoming.concat((char)LoRa.read());
-          }
-
-          // TODO Parse response to JSON, check if matches device & message ID
-          Serial.print("Inbound!: \n");
-          Serial.print(incoming);
-          break;
-        }
-
-        j+=10;
-        delay(10);
-
-        // TODO If no response then consider not delivered
+    } else {
+      while (!transmitData(doc)){
+        Serial.println("[-] Failed to send packet, retrying\n");
+        delay(reservationTime + random(250, 1250)); // Introduce random delay to avoid another collision
       }
-      Serial.print("\n");
-
-      // Reset Loop Values
-      ppm10 = 0;
-      ppm25 = 0;
-      ppm100 = 0;
-      temperature = 0;
-      humidity = 0;
-      
-      pollEventCount = 0;
-      Serial.println("---------------+++++-------------------");
     }
 
+    Serial.println("---------------------------------------");
+
+    // TODO Wait for reply
+//      int i = 3000;
+//      int j = 0;
+//      String incoming = "";
+//      
+//      while (j < i) {
+//        
+//        int packetSize = LoRa.parsePacket();
+//        if (packetSize) {
+//          while (LoRa.available()) {
+//            incoming.concat((char)LoRa.read());
+//          }
+//
+//          // TODO Parse response to JSON, check if matches device & message ID
+//          Serial.print("Inbound!: \n");
+//          Serial.print(incoming);
+//          break;
+//        }
+//
+//        j+=10;
+//        delay(10);
+//
+//        // TODO If no response then consider not delivered
+//      }
+//      Serial.print("\n");
+
+    // Reset Loop Values
+    ppm10 = 0;
+    ppm25 = 0;
+    ppm100 = 0;
+    temperature = 0;
+    humidity = 0;
+    
+    pollEventCount = 0;
+    Serial.println("---------------+++++-------------------");
+
     msgCount++;
   }