diff --git a/ESP8266_Transmitter/ESP8266_Transmitter.ino b/ESP8266_Transmitter/ESP8266_Transmitter.ino
index bddeadc164194b3b9277b39b3126739108ac4394..47229255dd7399f09248a61638175af0a8701ec9 100644
--- a/ESP8266_Transmitter/ESP8266_Transmitter.ino
+++ b/ESP8266_Transmitter/ESP8266_Transmitter.ino
@@ -38,6 +38,16 @@
 #include <base64.h>
 #include "SHTSensor.h"
 
+// Debug / Polling / Transmitting Config
+#define DEBUG 1
+#define MIN_RAND_DELAY 500 // ms
+#define MAX_RAND_DELAY 1250 // ms
+#define POLLING_FREQUENCY 2500 // ms Take value every POLLING_FREQUENCY ms
+#define MAX_TRANSMISSION_RETRIES 5 // No. Retries before recordings new values then retry
+#define TX_AFTER_N_READINGS 10 // No. of samples, after which Transmit Avg Readings
+#define TX_RESERVATION_TIME 1 // How long do we require use of TX for sending packets? (Seconds)
+int pollEventCount = 0; // Number of times data has been sampled in this recording period
+
 // Lora Config
 #define ss 16 // Physical Pin 16 = D0 (Default is Physical Pin 5)
 #define rst 0
@@ -61,23 +71,12 @@ byte localAddress = 0xBB;
 byte destination = 0xFF;
 uint32_t msgCount = 0;
 
-// Sensor Values - Temp / Humidity
-double temperature;
-double humidity;
-double avgTemperature = 0;
-double avgHumidity = 0;
-
-// Sensor Values - Co2
-byte co2;
-double avgCo2 = 0;
-
-// Sensor Values - Particles
-uint32_t ppm10;
-uint32_t ppm25;
-uint32_t ppm100;
-uint32_t avgPpm10 = 0;
-uint32_t avgPpm25 = 0;
-uint32_t avgPpm100 = 0;
+double temperature; // Sensor Values - Temp 
+double humidity;    // Sensor Values - Humidity
+double co2;         // Sensor Values - Co2
+uint32_t ppm10;     // Sensor Values - Particulate
+uint32_t ppm25;     // Sensor Values - Particulate
+uint32_t ppm100;    // Sensor Values - Particulate
 
 // Partial Sensor Data
 struct pms5003data {
@@ -91,12 +90,6 @@ struct pms5003data {
 
 struct pms5003data data;
 
-// Polling / Transmitting Config
-int pollingFrequency = 2500; // Polling Frequency, ms
-int pollEventCount = 0; // Number of times data has been sampled in this recording period
-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
  */
@@ -117,10 +110,9 @@ double getTemperature() {
 
   if (sht.readSample()) {
     t = sht.getTemperature();
-    temperature += t;
   }
 
-  return t; // -1 on error TODO Make this better!
+  return t; // -1 on error
 }
 
 double getHumidity() {
@@ -128,12 +120,29 @@ double getHumidity() {
 
   if (sht.readSample()) {
     h = sht.getHumidity();
-    humidity += h;
   }
 
   return h; // -1 on error
 }
 
+/**
+ * TODO getCo2()
+ */
+double getCo2() {
+  return 0;
+}
+
+
+// TODO DOC
+void resetCounters() {
+  ppm10 = 0;
+  ppm25 = 0;
+  ppm100 = 0;
+  temperature = 0;
+  humidity = 0;
+  pollEventCount = 0;
+}
+
 bool setupLoRa() {
   LoRa.setPins(ss, rst, dio0);  
   if (!LoRa.begin(loraFrequency)) {
@@ -155,15 +164,8 @@ bool setupLoRa() {
  * 2021-11-07 - This method proved unreliable, switched to casting ESP.getChipID() as a string instead
  */
 String getSensorUID() {
-//  uint64_t chipid = ESP.getChipId();
-//  uint16_t chip = (uint16_t)(chipid >> 32);
-//  
-//  char sensorID[23];
-//  snprintf(sensorID, 23, "EMIEI-%04X%08X", chip, (uint32_t)chipid);
-
-  String sensorID = "EMIEI-";
+  String sensorID = "EMIEI-"; // [E]nviornmental [M]onitoring [I]ndepenet of [E]xisting [I]nfrastructure
   sensorID.concat(String(ESP.getChipId()));
-  
   return sensorID;
 }
 
@@ -218,9 +220,51 @@ boolean readPMSdata(Stream *s) {
   return true;
 }
 
-/* Turn on Reciever, 
- * listen for messages for [listenDuration] seconds,
- * Return true if no messages recieved
+/**
+ * Provided sensor data, construct JSON object ready for transmission, Averaged over numSamples
+ * 
+ * @param messageID
+ * @param numSamples Numer of samples averages
+ * @param avgPpm10 Average Particuate readings
+ * @param avgPpm25 Average Particuate readings
+ * @param avgPpm100 Average Particuate readings
+ * @param avgTemperature Average Temperature
+ * @param avgHumidity Average Humidity
+ * @param avgCo2 Average Co2
+ * 
+ * @return DynamicJsonDocument sensorData
+ */
+DynamicJsonDocument prepareSensorData(int messageID, int numSamples, uint32_t avgPpm10, uint32_t avgPpm25, uint32_t avgPpm100, double avgTemperature, double avgHumidity, double avgCo2) {
+  DynamicJsonDocument doc(2048);
+
+  JsonObject metadata = doc.createNestedObject("sensorMetadata");
+  metadata["uid"] = getSensorUID();
+  metadata["messageID"] = messageID;
+  metadata["samplePeriod"] = numSamples;
+
+  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"); // TODO Co2
+  co2["tmp"] = avgCo2;
+
+  return doc;
+}
+
+/**
+ * Listen on Lora for other messages, returns true if no messages detected within [listenDuration] seconds
+ * Use prior to transmissions to avoid interruption of other messages
+ *
+ * @param int listenDuration How long to listen on Lora RX in Seconds
+ * @returns boolean If messages detected within listenDuration  
  */
 boolean clearToSend(int listenDuration) {  
   int listenUntil = now() + listenDuration;
@@ -230,7 +274,8 @@ boolean clearToSend(int listenDuration) {
     int packetSize = LoRa.parsePacket();
     
     if (packetSize) {
-      return false; // Other message heard on Rx, we can not transmit just now. 
+      Serial.println("[-] TX Busy - Not Clear To Send");
+      return false; // Other message heard on Rx, infer that we can not transmit just now. 
     }
 
     delay(5);
@@ -239,193 +284,223 @@ boolean clearToSend(int listenDuration) {
   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 messageID) {
-  int time_now = now() + listenDuration;
+// Introduce random delay to avoid another collision
+void waitRandomDelay(int minSeconds = 0) {
+  int randDelay = (minSeconds * 1000) + random(MIN_RAND_DELAY, MAX_RAND_DELAY);
+  if (DEBUG){
+    Serial.println("[i] Delay for " + String(randDelay) + "ms");
+  }
+  delay(randDelay);
+}
+
+/**
+ * Send short "clear to send?" packet
+ * Tx - "I've got data to send!" + UID
+ * @param messageID ID of this message / sample period
+ */
+void sendTxHello(int messageID) {
+  Serial.println("[+] Transmit - \"Hello\"");
+  
+  DynamicJsonDocument txHello(2048);
+  txHello["uid"] = getSensorUID();
+  txHello["reservationTime"] = TX_RESERVATION_TIME;
+  txHello["messageID"] = messageID;
+
+  sendJsonPayloadWithLoRa(txHello);
+}
+
+/**
+ * Send Payload
+ * Tx - Sensor Payload
+ * @param payload JSON Payload to be sent
+ */
+void sendTxPayload(DynamicJsonDocument payload) {
+  Serial.println("[+] Transmit - Payload");
+  sendJsonPayloadWithLoRa(payload);
+}
+
+/**
+ * Listen for messages on TX, expecting JSON
+ * 
+ * @param listenDuration How long to listen on Lora RX in Seconds
+ * @return StaticJsonDocument|null RX Payload in JSON, null on timeout reached or error
+ */
+StaticJsonDocument<1024> listenForAndConsumeMessage(int listenDuration) {
+  int listenUntil = now() + listenDuration;
+  StaticJsonDocument<1024> json;
 
   // Listen until timeout expires
-  Serial.println("[+] Transmit - \"Hello\" - Listening for ack & clear to send");
-  while (time_now >= now()) {
+  while (listenUntil >= now()) {
     int packetSize = LoRa.parsePacket();
+
+    if (!packetSize) {
+      delay(3);
+      continue;
+    }
+
+    // Read in packet, ensure we only bring in anything after { and before } inclusive
+    String incoming = "";
+    char temp;
     
-    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);
-        }
-      }
+    while (LoRa.available()) {
+      temp = (char)LoRa.read();
 
-      // DEBUG
-//      Serial.print("in listenTxHelloAccept() Recieved: \n"); 
-//      Serial.println(incoming);
-
-      // Verify its OK to transmit
-      StaticJsonDocument<200> doc;
-      deserializeJson(doc, incoming);
-
-      const bool okToTransmit = doc["okTransmit"];
-      const String authIsForUid = doc["uid"];
-      const int authIsForMessageID = doc["messageID"];
-      const String gatewayUid = doc["gatewayUid"];
-
-      // Verify txHello.okToTransmit is True & UID Match & Message IDs Match
-      if (authIsForUid == getSensorUID()) { Serial.println("[+] Transmit - \"Hello\" - Sensor UID Match!"); } else { Serial.println("[-] Transmit - \"Hello\" - Sensor UID Mis-Match! " + String(authIsForUid) + " vs " + String(getSensorUID())); }
-      if (authIsForMessageID == messageID) { Serial.println("[+] Transmit - \"Hello\" - Message ID Match!"); } else { Serial.println("[-] Transmit - \"Hello\" - MessageID Mis-Match!"); }
-      
-      return (okToTransmit 
-        && authIsForUid == getSensorUID() 
-        && authIsForMessageID == messageID);
+      if (incoming.length() == 0 && temp == '{') { // Opening {
+        incoming = "{";
+
+      } else if (temp == '}') { // Closing } 
+        incoming.concat("}");
+        break;
+
+      } else if (incoming.length() > 0) { // Anything else that's valid
+        incoming.concat(temp);
+      }
     }
 
-    delay(3);
+    // Deserialize - TODO Error Handling https://arduinojson.org/v6/api/json/deserializejson/
+    deserializeJson(json, incoming);
+    break;
   }
 
-  Serial.println("[-] Transmit - \"Hello\" - Timeout while waiting for Clear to Send\n\n");
-  return false; // We didn't hear anything, conside this as meaning "Can't Send"
+  return json;
 }
 
-void sendJsonPayloadWithLoRa(DynamicJsonDocument payload) {
-  LoRa.beginPacket();
-  serializeJson(payload, LoRa);
-  LoRa.endPacket();
-}
+/**
+ * Listen for response to TxHello for [listenDuration] seconds, check Sensor UID & Message ID match and return true if we have clear to transmit payload.
+ * @param listenDuration How long to listen on Lora RX in Seconds
+ * @param messageID ID of message we are expecting to receive
+ * @return boolean Clear to Transmit 
+ */
+boolean listenForTxHelloAccept(int listenDuration, int messageID) {
+  Serial.println("[+] Transmit - \"Hello\" - Listening for \"Hello\" Ack & Clear to Send");
 
-// TODO - Finish Acks & Naks
-boolean transmitData(DynamicJsonDocument payload) {
+  StaticJsonDocument<1024> json = listenForAndConsumeMessage(listenDuration);
 
-  // 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
+  // Timeout, likely TX Hello was not recieved or ack got lost
+  if (json.isNull()) {
+    Serial.println("[-] Transmit - \"Hello\" - Timeout while waiting for Clear to Send\n\n");
+    return false;
+  }
 
-    // TODO Refactor
-    while (!clearToSend(0.5)) {
-      Serial.println("[-] Airways busy, could not send");
-      delay(random(500, 1250)); // Introduce random delay to avoid another collision
+  const bool okToTransmit = json["okTransmit"];
+  const String authIsForUid = json["uid"];
+  const int authIsForMessageID = json["messageID"];
+  const String gatewayUid = json["gatewayUid"];
+
+  // Verify txHello.okTransmit is True and Sensor UID & Message IDs Match
+  if (DEBUG) {
+    if (authIsForUid == getSensorUID()) { 
+      Serial.println("[+] Transmit - \"Hello\" - Sensor UID Match!"); 
+    } else { 
+      Serial.println("[-] Transmit - \"Hello\" - Sensor UID Mis-Match! " + String(authIsForUid) + " vs " + String(getSensorUID())); 
+    }
+
+    if (authIsForMessageID == messageID) {
+      Serial.println("[+] Transmit - \"Hello\" - Message ID Match!"); 
+    } else { 
+      Serial.println("[-] Transmit - \"Hello\" - MessageID Mis-Match!"); 
     }
-    //return false;
   }
 
-  Serial.println("[+] Transmit - \"Hello\"");
-  // 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?
-  txHello["messageID"] = msgCount;
-  sendJsonPayloadWithLoRa(txHello);
+  // Ok To Trasmit, Sensor UID Match & Message ID Match
+  bool clearToSend = (okToTransmit 
+                      && authIsForUid == getSensorUID() 
+                      && authIsForMessageID == messageID);
 
-  if (!listenTxHelloAccept(reservationTime * 1.5, msgCount)) { // Can't transmit just now
-    Serial.println("[-] Transmit - \"Hello\" - Can Not Transmit At This Time\n");
-    return false; 
+  if (DEBUG) {
+    if (clearToSend) {
+      Serial.println("[+] Transmit - \"Hello\" - Recieved \"Clear to Transmit\" Payload");
+    } else {
+      Serial.println("[-] Transmit - \"Hello\" - Can Not Transmit At This Time");
+    }
   }
 
-  Serial.println("[+] Recieved - Clear to Transmit Payload"); // Else we have clear to send
+  return clearToSend;
+}
+
+// TODO DOC
+boolean listenForTxPayloadAccept(int listenDuration, int messageID) {
+  Serial.println("[.] Transmit - Payload - Listening for Ack");
 
-  // Transmit Payload
-  //    Tx - Send payload + UID + TX_Auth_ID
-  //    Rx - Listen for Ack/Nack
-  Serial.println("[+] Transmit - Payload");
-  sendJsonPayloadWithLoRa(payload);
+  StaticJsonDocument<1024> json = listenForAndConsumeMessage(listenDuration);
 
+  // Timeout, likely TX Payload was not recieved or ack got lost.
+  if (json.isNull()) {
+    Serial.println("[-] Transmit - Payload - Ack Timeout Reached - Assuming Message Was Not Delivered\n\n");
+    return false;
+  }
 
-  // TODO Await Response Ack/Nak
-  int ackTimeout = 2; // Seconds
-  int time_now = now() + ackTimeout;
+  const bool ackStatus = json["ackStatus"];
+  const String authIsForUid = json["uid"];
+  const int authIsForMessageID = json["replyMsgID"];
+  const String gatewayUid = json["gatewayUid"];
+  
 
-  // Listen until timeout expires
-  Serial.println("[.] Transmit - Payload - Listening for Ack");
-  while (time_now >= now()) {
-    int packetSize = LoRa.parsePacket();
+  // Verify Sensor UID Match
+  if (authIsForUid == getSensorUID()
+      && authIsForMessageID == messageID) {
+
+    Serial.println("[+] Transmit - Payload - Ack Recieved: " + String(ackStatus) + "\n");
     
-    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);
-        }
-      }
+    if (ackStatus) { return true; } // It all worked :)
+
+    // TODO Retransmit, recover, etc
+    Serial.println("[-] Transmit - Payload - Ack Failed - TODO Setup Retransmission");
+    return false;
+  }
+
+  // TODO Else UID Mis-Match so we wait for next message
+  Serial.println("[-] Transmit - Payload - Ack Message ID or Sensor ID Mis-Match");
+  return false;
+}
+
+/**
+ * Send JSON Payload over LoRa
+ * @param payload JSON Payload to be send
+ */
+void sendJsonPayloadWithLoRa(DynamicJsonDocument payload) {
+  LoRa.beginPacket();
+  serializeJson(payload, LoRa);
+  LoRa.endPacket();
+}
 
-      // DEBUG
-//      Serial.print("\nin listedForAck() Recieved: \n"); 
-//      Serial.println(incoming);
+// TODO - Finish Acks & Naks
+boolean transmitData(DynamicJsonDocument payload) {
 
-      StaticJsonDocument<200> doc;
-      deserializeJson(doc, incoming);
+  // TODO MAX_RETRIES
 
-      const bool ackStatus = doc["ackStatus"];
-      const String authIsForUid = doc["uid"];
-      const String gatewayUid = doc["gatewayUid"];
+  // Listen for other communication
+  //    Rx - Listen for other messages, if no messages heard then continue
+  if (!clearToSend(0.5)) {
+    waitRandomDelay(); // Wait for short time before retrying
+    while (!clearToSend(0.5)) { waitRandomDelay(); } // TODO MAX_TIMEOUT
+  }
 
-      // Verify txHello.okToTransmit is True & UID Match
-      if (authIsForUid == getSensorUID()) {
-        Serial.println("[+] Transmit - Payload - Ack Recieved: " + String(ackStatus) + "\n");
-        
-        if (ackStatus) { return true; } // It all worked :)
+  // Send TX Hello
+  sendTxHello(msgCount);
 
-        // TODO Retransmit, recover, etc
-        return false;
-      }
+  // Await TX Hello Auth - Expect: Timeout | Not Auth | Accept + Clear To Transmit
+  if (!listenForTxHelloAccept(TX_RESERVATION_TIME * 1.5, msgCount)) { 
+    return false; // Can't transmit just now, we will retry
+  }
 
-      // Else UID Mis-Match so we wait for next message
-    }
+  // Send TX Payload
+  sendTxPayload(payload);
 
-    delay(5);
+  // Await TX Payload Ack - Expect: Timeout | Nack | Accept + Data Match
+  if (!listenForTxPayloadAccept(2, msgCount)) {
+    return false; // TODO Ack Failed Setup a retry here!
   }
 
-  // TODO After Timeout we need to deal with!
-  Serial.println("[-] Transmit - Payload - Ack Timeout Reached - Assuming Message Was Not Delivered");
+  // TODO Update Sensor Config
 
-  // 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
+  // TODO Clear values & Continue
+  resetCounters();
+  Serial.println("Packet Sent Succesfully\n");
+  Serial.println("---------------+++++-------------------");
+  msgCount++;
 
-    
-    //LoRa.write(localAddress);
-//      LoRa.write(msgCount);
-    //LoRa.write(outgoing.length());
-    //LoRa.print(outgoing);
-    
-    return true;
+  return true;
 }
 
 void setup() {
@@ -434,17 +509,14 @@ void setup() {
   Serial.begin(115200); // Console Debug
   Serial.println("\n\n[+] Transmitter Node");
 
-  sensorID = getSensorUID();
-
   pmsSerial.begin(9600); // Partical Sensor
 
-  // Setup Sensors
+  // Setup Hardware
   if (!setupSHT()) { while(1); } // Temp/Humidity - Die on Error
-
-  // Setup LoRa
   if (!setupLoRa()) { while(1); } // Die on error
 }
 
+// Main
 void loop() {
   // TODO Gather Sensor Data
 
@@ -457,93 +529,74 @@ void loop() {
   
   pollEventCount++;
   
-  Serial.println();
-  Serial.print(String(pollEventCount) + ") ");
-  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());
+  if (DEBUG) {
+    Serial.println();
+    Serial.print(String(pollEventCount) + ") ");
+    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());
+    Serial.print("\t\tCo2: "); Serial.print(getCo2());
+  }
 
   // Add to Average
-  // Standard (_standard) or Environmental (_env)
-  ppm10 = ppm10 + data.pm10_standard;
+  temperature += getTemperature();
+  humidity += getHumidity();
+  co2 += getCo2();
+  ppm10 = ppm10 + data.pm10_standard; // Standard (_standard) or Environmental (_env) readings for Particulate Data
   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.print(avgHumidity);
-    Serial.print("\t\tChip 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");
-      
-    } else {
-      int maxRetries = 10; // TODO Move to Config
-      int numRetries = 1;
-      
-      while (!transmitData(doc) && numRetries < maxRetries){
-        numRetries++;
-        Serial.println("[-] Failed to send packet, retrying. Attempt " + String(numRetries) + " of " + String(maxRetries) + "\n");
-        delay(reservationTime + random(1250, 5250)); // Introduce random delay to avoid another collision
-      }
-
-      if (numRetries >= maxRetries) {
-        Serial.println("[-] Failed to send packet, max retries reached. Aborting");
+  // If we should now transmit
+  if (pollEventCount >= TX_AFTER_N_READINGS) {
 
-        // TODO Don't Clear Counters, record more values then try to retransmit
-      }
+    // Average Values over recording period
+    double avgTemperature = temperature / pollEventCount;
+    double avgHumidity = humidity / pollEventCount;
+    double avgCo2 = co2 / pollEventCount;
+    uint32_t avgPpm10 = ppm10 / pollEventCount;
+    uint32_t avgPpm25 = ppm25 / pollEventCount;
+    uint32_t avgPpm100 = ppm100 / pollEventCount;
+
+    if (DEBUG) {
+      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.print(avgHumidity);
+      Serial.print("\t\tAvg Co2: "); Serial.print(avgCo2);
+      Serial.print("\t\tChip ID: "); Serial.println(getSensorUID());
+      Serial.println("");
     }
+
+    // Prepare Data For Send
+    DynamicJsonDocument sensorData = prepareSensorData(msgCount, pollEventCount, avgPpm10, avgPpm25, avgPpm100, avgTemperature, avgHumidity, avgCo2);
     
-    // Reset Loop Values
-    ppm10 = 0;
-    ppm25 = 0;
-    ppm100 = 0;
-    temperature = 0;
-    humidity = 0;
+    // Transmit
+    if (transmitData(sensorData)) {
+      return; // It all worked, values reset, now record new values
+    }
+
+    // Transmission failed, handle re-tries
+    int numRetries = 1;
     
-    pollEventCount = 0;
-    Serial.println("---------------+++++-------------------");
+    while (!transmitData(sensorData) && numRetries < MAX_TRANSMISSION_RETRIES){
+      numRetries++;
+      Serial.println("[-] Failed to send packet, retrying. Attempt " + String(numRetries) + " of " + String(MAX_TRANSMISSION_RETRIES) + "\n");
+      waitRandomDelay(TX_RESERVATION_TIME); // Introduce random delay to avoid another collision
+    }
+
+    // We were able to transmit after retries, values reset, now record new values
+    if (numRetries < MAX_TRANSMISSION_RETRIES) {
+     return;
+    }
 
-    msgCount++;
+    // Failed to transmit - Don't Clear Counters, record more values then try to retransmit on next send
+    Serial.println("[-] Failed to send packet, max retries reached. Aborting");
+    return;
   }
 
-  delay(pollingFrequency);
+  delay(POLLING_FREQUENCY);
 }