/*Pocobo 2- Pollution Collecting Bottle 2
 * 
 * v1.0
 * 
 * Devices & Parts: 
 * - Arduino MKR NB 1500 (https://www.arduino.cc/en/Guide/MKRNB1500, https://store.arduino.cc/products/arduino-mkr-nb-1500?_gl=1%2Ajmo3e2%2A_ga%2AMTgyMjQxNTg5MC4xNjM3MTQ1NTIw%2A_ga_NEXN8H46L5%2AMTYzNzc0NjM2My4yLjEuMTYzNzc0NjYwOS4w)
 * - Sensirion SEN54 PM Sensor (https://www.sensirion.com/products/catalog/SEN54)
 * - Adafruit Mini GPS PA1010D Module (https://learn.adafruit.com/adafruit-mini-gps-pa1010d-module)
 * - Dipole Pentaband Waterproff antenna (https://store.arduino.cc/en-at/products/dipole-pentaband-waterproof-antenna?queryID=undefined
 * - Eckstein  3.7V, 2000mAh Battery (https://eckstein-shop.de/LiPo-Akku-Lithium-Ion-Polymer-Batterie-37V-2000mAh-mit-JST-PHR-2-Stecker-LP803860)
 * - Adafruit Powerboost 500 Charger (https://www.adafruit.com/product/1944) (needed because the Arduino MKR NB can only charge batteries from 700-1500mAh; see also (scroll down): https://store.arduino.cc/products/arduino-mkr-nb-1500?_gl=1%2Ads0y0w%2A_ga%2AMTgyMjQxNTg5MC4xNjM3MTQ1NTIw%2A_ga_NEXN8H46L5%2AMTYzODQ1MzI5My4xNy4xLjE2Mzg0NTUwMzkuMA..)
 * - Adafruit RGB On/Off Switch (https://www.adafruit.com/product/3426)
 * 
 * 
 * 
 * Infos & Libraries: 
 * - Arduino MKR NB Library:https://www.arduino.cc/en/Reference/MKRNB
 * - Arduino MKR NB Features: https://docs.arduino.cc/hardware/mkr-nb-1500
 * - Sensirion SEN54 Library Github: https://github.com/Sensirion/arduino-i2c-sen5x
 * - Sensirion SEN54 Library Arduino: https://www.arduino.cc/reference/en/libraries/sensirion-i2c-sen5x/
 * - Comparison for PM2.5 values: https://kachelmannwetter.com/de/luftqualitaet/wien-umgebung/pm25-feinstaub/20211126-1100z.html
 * - Comparison for PM10 values: https://kachelmannwetter.com/de/luftqualitaet/wien-umgebung/pm10-feinstaub/20211126-1100z.html
 * - Available Radio Access Technology: https://www.gsma.com/iot/deployment-map/
 * - Sparkfun_I2C_GPS_Arduino_Library: For using TinyGPS++ Library with I2C
 * - Tiny GPS++ Library: http://arduiniana.org/libraries/tinygpsplus/
 * - Switch with Powerboost 500: https://learn.adafruit.com/adafruit-powerboost-500-plus-charger/on-slash-off-switch
 * - Wiring the Button: https://learn.adafruit.com/ambient-color-controller/build-the-circuit
 * - Example for lighting the button LED: https://create.arduino.cc/projecthub/102550/rgb-light-control-with-arduino-9979df
 * - Read Battery voltage on MKR Board: https://docs.arduino.cc/tutorials/mkr-wifi-1010/mkr-battery-app-note
 * - TinyGPS++ i2c example: https://github.com/sparkfun/SparkFun_GPS_Breakout_XA1110_Qwiic/blob/master/Libraries/SparkFun%20I2C%20GPS/examples/Example2-TinyGPS/Example2-TinyGPS.ino
 

 * 
 * WIRING:
 *  Arduino                     SCL  <->   SCL    Adafruit Mini GPS, SEN54 (4, yellow)
 *  Arduino                     SDA  <->   SDA    Adafruit Mini GPS, SEN54 (3, green)
 *  Arduino                     VIN  <->   5V
 *  Arduino                     GND  <->   GND
 *  SEN54 (1, white)            VCC  <->   5V
 *  SEN54 (2, blue)             GND  <->   GND
 *  SEN54 (5, black)            SEL  <->   GND
 *  Adafruit Mini GPS           VIN  <->   5V
 *  Adadruit Mini GPS           GND  <->   GND
 *  Adafruit Power Boost 500    5V   <->   5V   
 *  Adafruit Power Boost 500    GND  <->   GND
 *  Adafruit Power Boost 500    LBO  <->   5      Arduino //DOES NOT REALLY WORK CHECK WIRING AND FUNCTIONALITY
 *  Adafruit RGB ON/OFF Switch  C+   <->   5V          
 *  Adafruit RGB ON/OFF Switch  C    <->   GND
 *  Adafruit RGB ON/OFF Switch  R    <->   6      Arduino   
 *  Adafruit RGB ON/OFF Switch  G    <->   7      Arduino
 *  Adafruit RGB ON/OFF Switch  B    <->   8      Arduino
 *  Adafruit RGB ON/OFF Switch  NC   <->   EN     Adafruit Power Boost 500
 *  
 * Wiring Button (Colors)
 * R = Orange
 * G = Grey
 * B = Violett
 * C+ = White
 * C = Blue
 * NC = Yellow
 * NO (Center) = Green

 * Wiring 22mm Button
 * 1 -> EN
 * 2 -> GND
 * 
 * 
 * Challenges, Problems, Errors, etc.
 * - SOLVED:  Standard SoftwareSerial Library does not work with SAMD Controllers. (Serial1 is now passing through)
 * - SOLVED:    HTTP Post: https://forum.arduino.cc/t/mkr-nb-1500-http-post/680965
 *            -> SIM-Card does not support NB-IoT: https://www.a1.net/nb-iot
 *            -> Sending SMS works, but no GPRS connection via NB-IoT.
 * - SOLVED:  GPS needs 3minutes to get a fix. (Using TinyGPS++ Library)
 * - SOLVED:  Arduino does not work with battery (Use Power Boost 500 Module)
 * - SOLVED:  Button LED is not lighting correct if system runs on battery (arduino not connect to Power via USB).
 *            -> it's color is white ??? WHY; Turns green if arduino is connected to USB
 *            -> Moved activation of Button to the top of setup()
              -> see aml_pocobo2_testledbutton.ino
 * - OPEN:    LED stays green for a long time if sensor is inside (NO GPS).
 * - OPEN:    Indicating low battery level by use of LBO pin
 *            -> https://forums.adafruit.com/viewtopic.php?f=19&p=757864
              -> How to use the LB Pin on the Power boost?
              -> https://forums.adafruit.com/viewtopic.php?f=19&t=58281
              -> https://forums.adafruit.com/viewtopic.php?f=19&t=80260
              -> https://forums.adafruit.com/viewtopic.php?p=379644
              -> https://forum.arduino.cc/t/adafruit-powerboost-500c-arduino-micro-lbo/251123
              -> https://electronics.stackexchange.com/questions/334472/general-electrical-question-with-regards-to-the-lbo-pin-on-the-adafruit-boost-5
              
 * - SOlVED:  After switching OFF and ON again, the SEN54 delivers nan results
 *            -> works after uploading the code, but not after switching OFF and ON again with the button.
 * - SOLVED:    LED Works correctly with battery if Arduino is powered via VCC 
              -> This should not be correct, as this is the 3.3v Power I/O
              -> Check wiring
              = Works also via VIN 9.1.24
 * - TODO:    Send status messages to the Datahub.
 * - TODO:    Get Battery status
 * - SOLVED:     Device restarts every 20 secs (sampling interval)
              - Battery is charged
              - LED is connected to the LBO
 * - SOLVED:   GPS sends only two decimals
              - https://forum.arduino.cc/t/tinygpsplus-gives-out-the-location-only-up-to-two-decimals/601442 
              - String function seems to cut decimals: String(double) https://forum.arduino.cc/t/string-function-seems-to-be-rounding-doubles/642177
              - include numer of decimals (6) when casting to String.
 * - BUG:     After putting sensor inside (no gps, LED green) and then outside again pocobo does not send data to server. gets stuck
              - does not get accross this line in sendtodatahub: if (client.connect(server, port))   
 * TODO:      If an error occurs update the led immediately. (and make sure it is not overridden);      
              

 *         
 *
 *
 *
 *
 * 
 * SOURCE TOKENS:
 *    POCOBO 2 - 0: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjE5LCJpYXQiOjE2Mzc5MzIzMjF9.fGMMPIMp-yHKX7yqddMKU3ItpcwiGD4srkY1-vj0Pz4
      POCOBO 2 - 1: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjIxLCJpYXQiOjE3MTIwNjc4NTZ9._YmIoqeE98XWNu629MFS1OOGGzWxPnKH-zI06zFWOnI
      POCOBO 2 - 2: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjIzLCJpYXQiOjE3NDUzMTgxOTJ9.n2c2huqnC5p4MRaBf-NksLlNKcW976t1mwZMOPhiBa4
      Pocobo 2 - 3: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjI0LCJpYXQiOjE3NDY3MDgxOTJ9.pKcHaTF88j6usI55lnWdgdXk9PESK4mGbKn6nf9Vv7A
      Pocobo 2 - 4: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjI1LCJpYXQiOjE3NDY3MDgyNzF9.UzfhKlXXXO8uFUsBgvVkLg19dRZBHP5rjXvncYZ2JeA
 *    
 */



#include <MKRNB.h>
#include <TinyGPS++.h>
#include <SparkFun_I2C_GPS_Arduino_Library.h>
#include <RGBLed.h>
#include <Arduino.h>
#include <SensirionI2CSen5x.h>
#include <Wire.h>

// The used commands use up to 48 bytes. On some Arduino's the default buffer
// space is not large enough
#define MAXBUF_REQUIREMENT 48

#if (defined(I2C_BUFFER_LENGTH) && (I2C_BUFFER_LENGTH >= MAXBUF_REQUIREMENT)) || (defined(BUFFER_LENGTH) && BUFFER_LENGTH >= MAXBUF_REQUIREMENT)
#define USE_PRODUCT_INFO
#endif



#define rLEDPIN 6  // Pin for red Button LED
#define gLEDPIN 7  // Pin for green Button LED
#define bLEDPIN 8  // Pin for blue Button LED
#define batPin 5   // Pin to detect if battery gets low

RGBLed ledButton(rLEDPIN, gLEDPIN, bLEDPIN, RGBLed::COMMON_ANODE);
byte curR = 0;
byte curG = 0;
byte curB = 0;

SensirionI2CSen5x sen5x;

// The TinyGPS++ object
I2CGPS myI2CGPS;  //Hook object to the library
TinyGPSPlus gps;
boolean encodeGPSAgain = false;


//Connection to DATAhub
//NBClient client(false);
//Otherwise the NBClient.connect() method waits until the internet connection gets ready, if you explicitly prohibit this it will wait forever.
NBClient client(false);
GPRS gprs;
NB nbAccess;
boolean connected = false;

//Forwarder address for non SSL: http://forwarder.aml.media.tuwien.ac.at:11313/receive-reading/{token}
// URL, path and port (for example: example.org
// URL DATAHUB = "http://aml.media.tuwien.ac.at:11312/api/sensordata/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjI0LCJpYXQiOjE3NDY3MDgxOTJ9.pKcHaTF88j6usI55lnWdgdXk9PESK4mGbKn6nf9Vv7A";
char server[] = "aml.media.tuwien.ac.at";
//char server[] = "forwarder.aml.media.tuwien.ac.at";
// IMPORTANT: Place correct source Token here: THIS IS POCOBO 2 - 3
char path[] = "/api/sensordata/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjI0LCJpYXQiOjE3NDY3MDgxOTJ9.pKcHaTF88j6usI55lnWdgdXk9PESK4mGbKn6nf9Vv7A";
int port = 11312;  // port 80 is the default for HTTP


//Measured Data
float lastPM1 = 0;
float lastPM25 = 0;
float lastPM4 = 0;
float lastPM10 = 0;
float lastHumidity = 0;
float lastTemperature = 0;
float lastVocIndex = 0;
float lastNoxIndex = 0;


//Timing
unsigned long currentMillis = 0;

uint32_t printTimer = millis();  //Just a timer for printing out values

unsigned long sendToDATAhubInterval = 20000;  //20sek; Interval for sending data to webservice = Data sampling interval
unsigned long lastSendToDATAhub = 0;

//String URL = "http://aml.media.tuwien.ac.at:11312/api/sensordata/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjE0LCJpYXQiOjE2MDA4NDY0MDR9.0BPUK7NVTlqi7i9QFx9kcVY55jsn0qskPWDL1PKyKec";


// Sensor Status
// used for the LED
// 0 = Startup
// 1 = Sensor Running / Everything is fine
// 2 = Measuring Data (Optional)
// 3 = No GPS
// 4 = No cell network
// 5 = Battery Low
// 6 = Sensor Error
// 7 = Testoutput
byte statusInfo = 0;

//int sendcount = 0;



/********************************************* SETUP *********************************************************/
void setup() {

  updateLED(statusInfo);

  pinMode(batPin, INPUT_PULLUP);

  Serial.begin(115200);
  Serial1.begin(9600);

  //sendcount = 0;

  //while(!Serial);                  // UNCOMMENT(remove)


  // CONNECT Cell network
  //Serial.println("Starting connection to NB-IOT Service (A1)");
  // connection state
  //updateLED(4);

  connected = false;

    // After starting the modem with NB.begin()
  // attach to the GPRS network with the APN, login and password
  connectToNetwork();
  updateLED(statusInfo);


  Wire.begin();

  //Connect GPS to I2C
  //updateLED(3);
  if (myI2CGPS.begin() == false) {
    //Serial.println("GPS Module failed to respond. Please check wiring.");
    statusInfo = 3;
    updateLED(statusInfo);
    while (1)
      ;  //Freeze!
  }

  encodeGPS();
  updateLED(statusInfo);


  // Init SEN54
  //updateLED(6);


  sen5x.begin(Wire);

  uint16_t error;
  char errorMessage[256];
  error = sen5x.deviceReset();
  if (error) {
    //Serial.print("Error trying to execute deviceReset(): ");
    errorToString(error, errorMessage, 256);
    //Serial.println(errorMessage);
    statusInfo = 6;
    updateLED(statusInfo);
  }


  // set a temperature offset in degrees celsius
  // Note: supported by SEN54 and SEN55 sensors
  // By default, the temperature and humidity outputs from the sensor
  // are compensated for the modules self-heating. If the module is
  // designed into a device, the temperature compensation might need
  // to be adapted to incorporate the change in thermal coupling and
  // self-heating of other device components.
  //
  // A guide to achieve optimal performance, including references
  // to mechanical design-in examples can be found in the app note
  // “SEN5x – Temperature Compensation Instruction” at www.sensirion.com.
  // Please refer to those application notes for further information
  // on the advanced compensation settings used
  // in `setTemperatureOffsetParameters`, `setWarmStartParameter` and
  // `setRhtAccelerationMode`.
  //
  // Adjust tempOffset to account for additional temperature offsets
  // exceeding the SEN module's self heating.
  float tempOffset = 0.0;
  error = sen5x.setTemperatureOffsetSimple(tempOffset);
  if (error) {
    //Serial.print("Error trying to execute setTemperatureOffsetSimple(): ");
    errorToString(error, errorMessage, 256);
    //Serial.println(errorMessage);
    statusInfo = 6;
    updateLED(statusInfo);

  } else {
    //Serial.print("Temperature Offset set to ");
    //Serial.print(tempOffset);
    //Serial.println(" deg. Celsius (SEN54/SEN55 only");
  }


  //Do an initial reading
  readSEN();
  updateLED(statusInfo);

  if (statusInfo == 0) statusInfo = 1;  //Init finished without error. Statusinfo is still 0

  updateLED(statusInfo);

}



/********************************************* LOOP *********************************************************/
void loop() {

  //updateLED(7);


  //Sending to DATAhub
  currentMillis = millis();

  if ((currentMillis - lastSendToDATAhub >= sendToDATAhubInterval)) {

    lastSendToDATAhub = currentMillis;

    updateLED(2);

    Serial.println("start sending");

    


    //TODO: Do this in a certain time interval
    //Serial.println("Start checking battery!");
    //checkBattery(); //does not really work ???

    //Encode GPS
    //Serial.println("Start encoding gps!");
    encodeGPS();

    //Read Sensor Data
    //INFO: Perhaps better to put in a timed interval; Also see sds.setCustomWorkingPeriod
    //Serial.println("Start reading sensor data!");
    readSEN();
    //Serial.println("After Sensor read; before DELAY");
    delay(1000);

    //Sending data to DATAhub Webservice
    //Serial.print("Start sending to DATAhub. statusInfo: ");
    //Serial.println(statusInfo);
    /*if (!gps.location.isUpdated()) {
      Serial.println("ERROR: GPS data not updated (no new signal)");
      statusInfo = 3;
      updateLED(statusInfo);
    }*/

    int satNum = gps.satellites.value();
    Serial.println("Satellites: " + String(satNum));

    //encodeGPS();  //Encode again
    if (statusInfo != 3 && statusInfo != 4 && statusInfo != 6) {  //no gps, mobile connection and sensor error -> send

      sendToDATAhub();
    }
    
    //sendToDATAhub();

    updateLED(statusInfo);
  }
}

/********************************************* sendToDATAhub *********************************************************/
void sendToDATAhub() {



  //Serial.println("connecting to DATAhub...");
  //Serial.println("Getting geo json datastring");
  String dataString = getGeoJSONDataString();
  //Serial.println("GOT Geo json datastring");

  unsigned int len = dataString.length() + 1;
  char data[len];
  dataString.toCharArray(data, len);
  
  //Serial.println("Transformed geo json data string to char array.");
  Serial.println("Server: " + String(server) + " Port: " + String(port));
  // if you get a connection, report back via serial:
  if (client.connect(server, port)) {
    
    Serial.println("connected to server -> YAY I CAME ACROSS THE CONNECTION PART!");
    
    //if (statusInfo != 3 && statusInfo != 4 && statusInfo != 6) {
      // Make a HTTP request:
      client.print("POST ");
      client.print(path);
      client.println(" HTTP/1.1");
      client.print("Host: ");
      client.println(server);
      client.println("Content-Type: text/plain");
      client.print("Content-Length: ");
      client.println(strlen(data));
      client.println();
      client.println(data);
      client.println("Connection: close");
      client.println();
    //}

    Serial.println("...sent to DATAhub!");

    if(statusInfo ==2) statusInfo = 1;
    

  } else {
    // if you didn't get a connection to the server:
    Serial.println("sendToDATAhub: connection failed");
    statusInfo = 4;
    updateLED(statusInfo);
    connected = false;
    Serial.println("Shutting down the modem");
    nbAccess.shutdown();   //restart the modem
    delay(5000);
    connectToNetwork();
    
    
  }
}

void connectToNetwork() {
    // After starting the modem with NB.begin()
  // attach to the GPRS network with the APN, login and password
  //&& (gprs.attachGPRS() == GPRS_READY)
  while (!connected) {
    
    
    Serial.println("Connecting to network!");
    
    if ((nbAccess.begin() == NB_READY)) {
      Serial.println("Connected to NB-IOT Service (A1)");
      connected = true;
      if(statusInfo ==2) statusInfo = 1;
    } else {
      Serial.println("Not connected to NB-IOT Service (A1)");
      delay(1000);
      statusInfo = 4;
      updateLED(statusInfo);
    }
  }
}

/********************************************* encodeGPS *********************************************************/
void encodeGPS() {

  unsigned long start = millis();
  // For one second we parse GPS data and report some key values
  for (start; millis() - start < 1000;) {
    while (myI2CGPS.available()) {  //available() returns the number of new bytes available from the GPS module
      gps.encode(myI2CGPS.read());  //Feed the GPS parser
    }
  }

  //Debugging
  //if we haven't seen lots of data in 5 seconds, something's wrong.
  if (start > 5000 && gps.charsProcessed() < 10) {
    //Serial.println("ERROR: Not getting any GPS data! Encoding again");
    statusInfo = 3;
    updateLED(statusInfo);
    encodeGPS();                         //Encode again
  } else if (!gps.location.isValid()) {  //also add timer for circumstances when there is definately no signal to find
    Serial.println("ERROR: GPS Data not valid! Encoding again");
    statusInfo = 3;
    updateLED(statusInfo);
    encodeGPS();  //Encode again
  } else if (gps.satellites.value() < 3) {
    Serial.println("ERROR: Not enough GPS satellites");
    statusInfo = 3;
    updateLED(statusInfo);
    //encodeGPS();  //Encode again
  } else {
    if(statusInfo ==2) statusInfo = 1; //no error occured
  }


  //printGPSData();
}

/********************************************* PrintGPSDATA *********************************************************/
void printGPSData() {

  //We have new GPS data to deal with!
  Serial.println();

  if (gps.time.isValid()) {
    Serial.print(F("Date: "));
    Serial.print(gps.date.month());
    Serial.print(F("/"));
    Serial.print(gps.date.day());
    Serial.print(F("/"));
    Serial.print(gps.date.year());

    Serial.print((" Time: "));
    if (gps.time.hour() < 10) Serial.print(F("0"));
    Serial.print(gps.time.hour());
    Serial.print(F(":"));
    if (gps.time.minute() < 10) Serial.print(F("0"));
    Serial.print(gps.time.minute());
    Serial.print(F(":"));
    if (gps.time.second() < 10) Serial.print(F("0"));
    Serial.print(gps.time.second());

    Serial.println();  //Done printing time
  } else {
    Serial.println(F("Time not yet valid"));
  }

  if (gps.location.isValid()) {
    Serial.print("Location: ");
    Serial.print(gps.location.lat(), 6);
    Serial.print(F(", "));
    Serial.print(gps.location.lng(), 6);
    Serial.println();
  } else {
    Serial.println(F("Location not yet valid"));
  }
}

/********************************************* readSEN *********************************************************/
void readSEN() {
  
  

  // Start Measurement //TODO: Remove from setup()?
  uint16_t error;
  char errorMessage[256];
  error = sen5x.startMeasurement();
  if (error) {
    //Serial.print("Error trying to execute startMeasurement(): ");
    errorToString(error, errorMessage, 256);
    //Serial.println(errorMessage);
    statusInfo = 6;
    updateLED(statusInfo);
  }

  error = sen5x.readMeasuredValues(
    lastPM1, lastPM25, lastPM4,
    lastPM10, lastHumidity, lastTemperature, lastVocIndex,
    lastNoxIndex);

  if (error) {
    //Serial.print("Error trying to execute readMeasuredValues(): ");
    errorToString(error, errorMessage, 256);
    //Serial.println(errorMessage);
    statusInfo = 6;
    updateLED(statusInfo);
  } else {
    //printSen54Data();
    if(statusInfo ==2) statusInfo = 1;
    
  }

  //status = 7;






  delay(1000);

  //updateLED(statusInfo);
}

/********************************************* printSEN54DATA *********************************************************/
void printSen54Data() {

  Serial.print("PM 1.0: ");
  Serial.print(lastPM1);
  Serial.print("\t");
  Serial.print("PM 2.5: ");
  Serial.print(lastPM25);
  Serial.print("\t");
  Serial.print("PM 4.0: ");
  Serial.print(lastPM4);
  Serial.print("\t");
  Serial.print("PM 10.0: ");
  Serial.print(lastPM10);
  Serial.print("\t");
  Serial.print("Humidity: ");
  if (isnan(lastHumidity)) {
    Serial.print("n/a");
  } else {
    Serial.print(lastHumidity);
  }
  Serial.print("\t");
  Serial.print("Temperature: ");
  if (isnan(lastTemperature)) {
    Serial.print("n/a");
  } else {
    Serial.print(lastTemperature);
  }
  Serial.print("\t");
  Serial.print("VocIndex: ");
  if (isnan(lastVocIndex)) {
    Serial.print("n/a");
  } else {
    Serial.print(lastVocIndex);
  }
  Serial.print("\t");
  Serial.print("NoxIndex: ");
  if (isnan(lastNoxIndex)) {
    Serial.println("n/a");
  } else {
    Serial.println(lastNoxIndex);
  }
}



/********************************************* getGEOJSONDataString *********************************************************/
String getGeoJSONDataString() {


  /* GeoJSON should look like this:
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [100.0, 0.0]
      },
      "properties": {
        "PM 1": [5.5, "myg/m3"],
        "PM 2.5": [43.5, "myg/m3"],
        "PM 4": [10.5, "myg/m3"],
        "PM 10": [21.5, "myg/m3"],
        "Humidity": [70, "%"],
        "Temperature": [23,5, "°C"],
        "VOC": [102, "VOC Index"],
        "timestamp": "123456723495",
      }
    }
  */

  /*
  String json =   "{\"type\":\"Feature\",\"geometry\":{\"type\": \"Point\", \"coordinates\":["+ String(gps.location.lat()) + "," + String(gps.location.lng()) + "]}, \"properties\": {\"PM 1\":[" +  String(lastPM1) + ", \"myg/m3\"], \"PM 2.5\":[" +  String(lastPM25) + ", \"myg/m3\"], \"PM 4\":[" +  String(lastPM4) + ", \"myg/m3\"], \"PM 10\":[" + String(lastPM10) + ", \"myg/m3\"], \"Humidity\":[" + 
                  String(lastHumidity) + ", \"%\"], \"Temperature\":[" + String(lastTemperature) + ", \"°C\"], \"VOC\":[" + String(lastVocIndex) + ", \"VOC Index"], \"timestamp\":\"""\"}}";
  */

  String json = "{\"type\":\"Feature\",\"geometry\":{\"type\": \"Point\", \"coordinates\":[" + String(gps.location.lng(), 6) + "," + String(gps.location.lat(), 6) + "]}, \"properties\": {\"PM 1\":[" + String(lastPM1) + ", \"myg/m3\"], \"PM 2.5\":[" + String(lastPM25) + ", \"myg/m3\"], \"PM 4\":[" + String(lastPM4) + ", \"myg/m3\"], \"PM 10\":[" + String(lastPM10) + ", \"myg/m3\"], \"Humidity\":[" + String(lastHumidity) + ", \"%\"], \"Temperature\":[" + String(lastTemperature) + ", \"°C\"], \"VOC\":[" + String(lastVocIndex) + ", \"VOC Index\"], \"timestamp\":\"""\"}}";

  //TODO: print this out!
  //Serial.println(json);

  //Serial.println("GeoJSON Datastring built");

  return json;
}

/********************************************* GetGeoJSONTestString *********************************************************/
String getGeoJSONTestString() {


  /* GeoJSON should look like this:
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [100.0, 0.0]
      },
      "properties": {
        "Temperature": [24.5, "°C"],
        "Noise": [100.0, "dB"],
        "timestamp": "123456723495",
      }
    }
  */

  //String json = "{\"type\":\"Feature\",\"geometry\":{\"type\": \"Point\", \"coordinates\":[16.791124, 47.865672]}, \"properties\": {\"Temperature\":[35, \"°C\"], \"Noise\":[130, \"dB\"], \"timestamp\":\"\"}}";

  String json = "{\"type\":\"Feature\",\"geometry\":{\"type\": \"Point\", \"coordinates\":[16.504511, 48.225247]}, \"properties\": {\"PM 1\":[" + String(lastPM1) + ", \"myg/m3\"], \"PM 2.5\":[" + String(lastPM25) + ", \"myg/m3\"], \"PM 4\":[" + String(lastPM4) + ", \"myg/m3\"], \"PM 10\":[" + String(lastPM10) + ", \"myg/m3\"], \"Humidity\":[" + String(lastHumidity) + ", \"%\"], \"Temperature\":[" + String(lastTemperature) + ", \"°C\"], \"VOC\":[" + String(lastVocIndex) + ", \"VOC Index\"], \"timestamp\":\""
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            "\"}}";
  return json;
}
/********************************************* checkBattery *********************************************************/
void checkBattery() {

  //DOES NOT WORK: LOW LED on the Powerboost is active but batPin reading is 1

  // reads the pin connect to the LBO output of the powerboost 500 Unit
  int batState = digitalRead(batPin);


  if (batState == LOW) {

    //Sets the LED to red if battery is low
    statusInfo = 5;
    updateLED(statusInfo);
  }

  //Serial.println("Battery state: " + batState);
}

/********************************************* updateLED *********************************************************/
void updateLED(byte status) {

  //Status and LED Colours:
  //0 = While Setup/Startup:          YELLOW (255, 225, 0)
  //1 = Running & Everything is OK:   GREEN (0, 255, 0)
  //2 = Measuring DATA:               GREEN (200, 255, 50)
  //3 = NO GPS Signal/Position/Fix:   BLUE (0, 0, 255)
  //4 = NO Cell Network/Connection:   PINK (255, 0, 255)
  //5 = Batterry low:                 RED (255, 0, 0)
  //6 = Sensor Error:                 VIOLETT (170, 0, 255)
  //7 = TESTOUTPUT                    GREEN/PETROL (3, 252, 177)

  statusInfo = status;

  if (status == 0) {
    ledButton.crossFade(curR, curG, curB, 255, 255, 0, 10, 500);
    curR = 255;
    curG = 255;
    curB = 0;
  } else if (status == 1) {
    ledButton.crossFade(curR, curG, curB, 0, 255, 0, 10, 500);
    curR = 0;
    curG = 255;
    curB = 0;
    //} else if (status == 2) { ledButton.flash(200, 255, 50, 50);
  } else if (status == 2) {
    ledButton.crossFade(curR, curG, curB, 200, 255, 50, 10, 500);
    //ledButton.flash(200, 255, 50, 1000);
    curR = 200;
    curG = 255;
    curB = 50;
  } else if (status == 3) {
    ledButton.crossFade(curR, curG, curB, 0, 0, 255, 10, 500);
    curR = 0;
    curG = 0;
    curB = 255;
  } else if (status == 4) {
    ledButton.crossFade(curR, curG, curB, 255, 0, 255, 10, 500);
    curR = 255;
    curG = 0;
    curB = 255;
  } else if (status == 5) {
    ledButton.crossFade(curR, curG, curB, 255, 0, 0, 10, 500);
    curR = 255;
    curG = 0;
    curB = 0;
  } else if (status == 6) {
    ledButton.crossFade(curR, curG, curB, 170, 0, 255, 10, 500);
    curR = 170;
    curG = 0;
    curB = 255;
  } else if (status == 7) {
    ledButton.crossFade(curR, curG, curB, 3, 252, 177, 10, 500);
    curR = 3;
    curG = 252;
    curB = 177;  //Testoutput
  }

  statusInfo = status;
}