/*
 * AML TMP+NOISE
 * 
 * VERSION: 1.03
 * 
 * The Sensorbox measures Temperature and Noise-Level and sends this information with the GPS-position to a webservice.
 * 
 * ************************************************        
 * GENERAL INFO AND COMPONENTS 
 * 
 * Measuring Battery: https://learn.adafruit.com/adafruit-feather-32u4-fona/power-management
 * Gettin Noise: http://arduinolearning.com/code/arduino-and-max4466-electret-module-example.php
 * Getting GSM Location: https://www.instructables.com/id/How-to-make-a-Mobile-Cellular-Location-Logger-with/
 * 
 * Tiny GPS++ Library: http://arduiniana.org/libraries/tinygpsplus/
 * 
 * 
 * Battery saving:
 * 
 * GPS Board:   - Use EN Pin to turn of the board when not needed (https://learn.adafruit.com/adafruit-ultimate-gps/overview)
 *              - EN soll laut der Quelle auf GND gesetzt werden. Es dauert dann aber länger bis wieder ein Fix gefunden wurde.
 *              
 * 
 * Feather FONA:  - Cut the key trac on the bottom of the board (https://learn.adafruit.com/adafruit-feather-32u4-fona/power-management)
 *                - If you want to depower the cell module, cut the KEY trace on the bottom of the board, wire KEY to an unused pad, 
 *                  and toggle the pin low for 100ms to completely turn on/off the module.
 *                - https://arduino.stackexchange.com/questions/54001/adafruit-feather-32u4-fona-key-pin
 *                  cut the trace and wire it to a micro controller pin
 *                - Key - this is by default tied to ground, cut the trace on the bottom and wire to a microcontroller pin to manually turn the module on and off. 
 *                  (Pulse low for a few seconds to change from on to off) This is the only way to truly disable the cellular module.
 *                
 *************************************************      
 * TODOS & BUGS:               
 *      - Die Ladeanzeige über die LED funktioniert nicht immer korrekt. Besonders gelbes Blinken (Aufladen) ist davon betroffen.  
 *      
 ************************************************      
 *      
 * IMPORTANT INFO: 
 *  - Be sure to use the correct Token and device number before uplading the code
 *  - For A1 Sim-Cards the APN needs to be set in initFONA(). Uncomment the line if other SIM-Card is used or set the correct APN
 *  
 * SOURCE TOKENS:
 *      BOX 1: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjcsImlhdCI6MTYwMDg0NjI2MH0.YKzbg-8_hFuw1-W0Hqa5eLZAncfqlqT2rf2c95Abi7k
 *      BOX 2: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjE1LCJpYXQiOjE2MDA4NDY3MTl9.Jk1CXJXeB6cm4T0QARoj_1BtxvsZF3jl5UhvPRMEBvQ
 *      BOX 3: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEwLCJpYXQiOjE2MDA4NDYyMDZ9.rrA8lZ18SThb4L8mf18Krz7dK5ioCwqWd10BstT9GnE
 *      BOX 4: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjE0LCJpYXQiOjE2MDA4NDY0MDR9.0BPUK7NVTlqi7i9QFx9kcVY55jsn0qskPWDL1PKyKec
 *      BOX 5: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlhdCI6MTYwMDg0NjY2NH0.d5xvkTVDcHJl-K6KkohIWcUC5XcYNRZ_wzSLMvqAyvw
 *      BOX 6: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjMsImlhdCI6MTYwMDg0NTg4M30.OjsoqjcAMhCh9WLkBpBtXyOlOL25mBXSxT1Uy3tM01M
 *      BOX 7: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjUsImlhdCI6MTYwMDg0NjUwMH0.XqBhCwt9V6YTNE8fwfxrXahBxGGNHgLkXgO3XaqDcwI
 *      BOX 8: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjExLCJpYXQiOjE2MDA4NDYxMjd9.uJPST-APKEpsS-CIimNh3ePNce2PaabuX5c3HNnVsfo
 *      BOX 9: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjE2LCJpYXQiOjE2MTA3MTg5MjF9.2lk9xY4Gy0CR65V9giXPHrDR7Eei-O3R5sS1oD6hzsM
 *  
 *  
 ************************************************  
 *  
 */

 #include <Adafruit_NeoPixel.h>
 #include "Adafruit_FONA.h"
 #include <SoftwareSerial.h>
 #include <TinyGPS++.h>
 #include <AES.h>

 #define LEDPIN 13
 #define ANALOG_TEMP_PIN A0
 #define ANALOG_NOISE_PIN A1
 #define KEYPIN 12
 #define GPSENABLE 11

 
 #define FONA_RX 9
 #define FONA_TX 8
 #define FONA_RST 4
 #define FONA_RI 7

 #define aref_voltage 3.3  

 Adafruit_NeoPixel pixel(1, LEDPIN, NEO_GRB + NEO_KHZ800);

 //Name of this Box
 String boxID = "BOX 009";
 
 //GSM
 SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);
 SoftwareSerial *fonaSerial = &fonaSS;
 Adafruit_FONA fona = Adafruit_FONA(FONA_RST);
 uint8_t type;
 //char URL[] = "http://airquality.media.tuwien.ac.at:10005/measurements";
 char URL[] = "http://aml.media.tuwien.ac.at:11312/api/sensordata/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjE2LCJpYXQiOjE2MTA3MTg5MjF9.2lk9xY4Gy0CR65V9giXPHrDR7Eei-O3R5sS1oD6hzsM";
 char URL2[] = "http://www.mobillab.wien/sensorbox/write/";
 //char URL[] = "www.kunstmedienkultur.com/sensorbox/";
 unsigned long ATtimeOut = 10000; // How long we will give an AT command to complete
 boolean initFONAagain = false;

//GPS
 static const uint32_t GPSBaud = 9600;
 TinyGPSPlus gps;
 boolean encodeGPSAgain = false;
 
//Timing
unsigned long currentMillis =0;

unsigned long singleBlinkDuration = 500;            //Duration for 1 Blink
unsigned long lastSingleBlinkStart = 0;             
unsigned long blinkSequenceDuration = 4200;            //Duration for an blink sequence (not sure if needed)
unsigned long lastBlinkSequenceStart = 0;
unsigned long blinkPauseDuration = 200;             //Duration for blink pause 
unsigned long lastBlinkPauseStart = 0;
unsigned long blinkInterval = 10000;                 //Interval to blink

unsigned long checkBatInterval = 15000;             //Interval for Checking the Battery -> long interval: every 10 Minutes (if everthing is ok) 600000; short interval: every 15 seconds (150000) 
unsigned long lastBatCheck = 0;

unsigned long sendToWebInterval = 1200000;         //20 min = 1200000;   5min = 300000;         //Interval for sending data to webservice
unsigned long lastSendToWeb = 0;

unsigned long pullLowDuration = 2000;            //3 seconds for pulling KEYPIN low to turn off/on Fona

unsigned long GPSUpdateInterval = 21600000;      // 6hrs         
unsigned long lastGPSupdate = 0;
unsigned long GPSUpdateTimeout = 180000; //Time for trying to find a fix! If over, then no fix found (indoor).

unsigned long noiseSameplingInterval = 2000;    //Start sampling 
unsigned long lastNoiseSampling = 0;


//Temp
byte tempPin = A0;
float temperature;

//Noise
const int sampleWindow = 500; // Sample window width in mS (50 mS = 20Hz)
unsigned int sample;
int noiseAVG = 0;

const int numReadings = 20;     // change for higher smoothing
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total

double boxDB = 115;

//Status Flags
byte statusInfo[] = {
                1,      //Value = 1 - status everything ok (white)
                0,      //Value = 1 - no Position   (blue)
                0,      //Value = 1 - no connection to server (GSM) (pink)
                0,      //Value = 1 - low battery (red)
                0,      //Value = 1 - is charging   (yellow)
                0,      //Value = 1 - battery fully charged (green)
};
uint16_t lastBatteryLevel = 0;
uint16_t lastMilliVolts = 0;
boolean lastMessageSent = true;


//encryption
AES aes;
byte *key = (unsigned char*)"0123491889010123";


void setup() {

   //while (!Serial);
   Serial.begin(115200);
   Serial1.begin(GPSBaud);

   //Inbit LED and turn on pixel
   pixel.begin();
   pixel.setBrightness(30);
   pixel.setPixelColor(0, pixel.Color(255, 255, 255));  //light up while startup
   pixel.show();
  
   analogReference(EXTERNAL);    //sets analog reference voltage to AREF pin

   // initialize the readings for Noise to 0:
   for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
   }

   //Pins to turn on and off gsm and gps
   pinMode(KEYPIN, OUTPUT);
   digitalWrite(KEYPIN, HIGH);
   pinMode(GPSENABLE, OUTPUT);
   digitalWrite(GPSENABLE, LOW);

   encodeGPSAgain = true;


   /*
   //Get initial position
   lastGPSupdate = millis();
   encodeGPS();


   //Switch FONA on to initially check battery status
   initFONA();
   if(initFONAagain) initFONA();        //Try again. In case FONA was turned off in 1st attempt
   lastMilliVolts = getBatteryVoltage();
   switchFONA(); 
   */

   delay(900000); //15minutes to charge the battery before anything else is done
   
   //Turn off pixel
   pixel.setPixelColor(0, pixel.Color(0, 0, 0));
   pixel.show();

   
   
}

void loop() {


    
   blinkStatus();    //After the last message led is constantly red

   if(!lastMessageSent) { //Turn off everything thats not needed
   
     if(encodeGPSAgain) encodeGPS();    //Encoding again if no valid or updated signal found; no rekursion here
     if(initFONAagain) initFONA();      //Init FONA again if FONA was off while first attempt
     
     //Sampling Noise in a specific interval.
     currentMillis = millis();
     if (currentMillis - lastNoiseSampling >= noiseSameplingInterval) {
      lastNoiseSampling = currentMillis;
      noiseAVG = getNoise();
      //Serial.println("Info: Noise: " + String(noiseAVG) + "dB");
     }
     
     //Getting GPS data in a specific interval.
     currentMillis = millis();
     if (currentMillis - lastGPSupdate >= GPSUpdateInterval) {
      //Serial.println("Checking GPS");
      lastGPSupdate = currentMillis;
      encodeGPS();
     }
   }
   

   //Send to Web in a specific interval
   currentMillis = millis();
   if ((currentMillis - lastSendToWeb >= sendToWebInterval)) {

    lastSendToWeb = currentMillis;

    
    //Send to Webservice, if battery is good enough and GPS was once valid
    //INFO: Checking for valid GPS Signal only makes sense for static use on one place. 
    //      For dynamic use with changing places the check should use gps.location.isUpdated()
    if(gps.location.isValid() && (lastMilliVolts >= 3500) && !lastMessageSent) {
      switchFONA();
      initFONA();
      lastMilliVolts = getBatteryVoltage(); 
      //Serial.println("INFO: Sending to URL");
      sendToWebService(getGeoJSONDataString(), URL);
      //(Serial.println("INFO: Sending to URL2");
      //sendToWebService(getGeoJSONDataString(), URL2);
      switchFONA();
    } else {      //Check Battery
      switchFONA();
      initFONA();
      lastMilliVolts = getBatteryVoltage(); 
      switchFONA();
    }


    //SERVICE MESSAGES
    //Power too low, but last message not yet sent -> send last message; BUT make sure, that mV is plausible -> >500
    if ((lastMilliVolts < 3480) && (lastMilliVolts > 500) && !lastMessageSent) { 
      switchFONA();
      initFONA();
      //sendToWebService(getServiceJSONDataString(" LAST MESSAGE - NO BATTERY"));
      sendToWebService(boxID + "Bat: " + lastMilliVolts + "mV ------- LAST MESSAGE - NO BATTERY", URL2);
      lastMessageSent = true;            //set to false; needed if battery is charging and sensor is sending again
      switchFONA();
    } else if((lastMilliVolts > 3550) && lastMessageSent) {   //Power sufficient again (after charging) -> back to work msg (just a service msg) (3520mV to avoid pending between last msg and back to work msg)
      switchFONA();
      initFONA();
      sendToWebService(boxID + "Bat: " + lastMilliVolts + "mV ------- BACK TO WORK", URL2);  
      //sendToWebService(getGeoJSONDataString(), URL);
      lastMessageSent = false;            //set to false; needed if battery is charging and sensor is sending again
      switchFONA();
    }
    
    //Try to get a valid GPS location
    if(!gps.location.isValid() && (lastMilliVolts >= 3550) && !lastMessageSent) {
      switchFONA();
      initFONA();
      sendToWebService(boxID + "Bat: " + lastMilliVolts + "mV ------- NO GPS", URL2); //TODO: Change to service message
      //sendToWebService(getGeoJSONDataString(), URL);
      switchFONA();
      
      GPSUpdateInterval = sendToWebInterval;
      //Serial.println("INFO: NO GPS (sendtowebservice; 258)");
    } 

   }
   
}


 void initFONA() {

      //Serial.println("INFO: Starting FONA init");

      fonaSerial->begin(4800);
      if (!fona.begin(*fonaSerial)) {
        //Serial.println(F("INIT - Couldn't find FONA"));
        switchFONA();             //In case Phona is turned off (happens wenn battery runs completely out and is charged up again.
        initFONAagain = true;     //after switching ON try to init again
        //Serial.println("ERROR: init FONA again; FONA not found");
        

        statusInfo[2] = 1;
        // Try to turn it on
        //turnOn();
        if (!fona.begin(*fonaSerial)) {
          while (1)
            ;
        }
      } else {
        statusInfo[2] = 0;
        initFONAagain = false;
      }
      
      type = fona.type();

       // Set APN (needed for A1 Sim cards) -> Uncomment if other SIM-Cards are in use
      fona.setGPRSNetworkSettings(F("A1.net"), F("ppp@a1plus.at"), F("ppp"));
          
      // turn GPRS on
      delay(10000);
      
      if (!fona.enableGPRS(true)) {
        //Serial.println(F("INIT - Failed to turn on GPRS"));
        statusInfo[2] = 1;
        //Serial.println("ERROR: init FONA again; no GPRS");
        initFONAagain = true;    //Try to init FONA again in the next loop
      } else {
        statusInfo[2] = 0;
        initFONAagain = false;
      }

      /* NOT  WORKING: Sending timestamp couses HTTP POST ERROR
      // enable NTP time sync 
      fona.enableNTPTimeSync(true, F("pool.ntp.org"));
          //Serial.println(F("INFO: Failed to enable NTP time sync"));
      */

      
      //turnOnOffFona();
}

void switchFONA() {

  //Switching FONA on and off through pulling the KEYPIN LOW for a while
  //Each pull to LOW switches the FONA, either on or off (depending on the current state)
  
  //Serial.println("INFO: Switching FONA");
  unsigned long startMillis = millis();

  while (millis() - startMillis < pullLowDuration) {

    digitalWrite (KEYPIN, LOW);
  }

  digitalWrite(KEYPIN, HIGH);
  
}


void sendToWebService(String message, char URL[]) {


      //Serial.println("INFO - Start sending to Webservice");
      
      uint16_t statuscode;
      int16_t length;
      //String datastring = getGeoJSON();
      //String datastring = getSimpleDataString();      //For readable output on Server
      String datastring = message;
      
      
      //******with encryption start******
      /*
      byte *key = (unsigned char*)"0123491889010123"; //needs to be randomized!!!!
      unsigned long long int my_iv = 36712162; //needs to be randomized!!!!

      byte plain[datastring.length()];
      datastring.getBytes(plain, datastring.length());

      int plainLength = sizeof(plain)-1;  // don't count the trailing /0 of the string !
      int padedLength = plainLength + N_BLOCK - plainLength % N_BLOCK;

      aes.iv_inc();
      byte iv [N_BLOCK] ;
      byte plain_p[padedLength];
      byte cipher [padedLength] ;
      aes.set_IV(my_iv);
      aes.get_IV(iv);
      aes.do_aes_encrypt(plain,plainLength,cipher,key,128,iv);

      flushSerial();
      bool success = true;
      if (!fona.HTTP_POST_start(URL, F("text/plain"), (uint8_t *) plain, strlen(plain), &statuscode, (uint16_t *)&length)) {
         //Serial.println("HTTP POST FAILED!");
          statusInfo[2] = 1;
      } else {
          statusInfo[2] = 0;
      }
      
      */
      //******with encryption end******

      //******without encryption start******
      
      unsigned int len = datastring.length() + 1;
      char data[len];
      datastring.toCharArray(data, len);

      flushSerial();

      //char myData[] = "{\"simple\":\"json\"}";
      bool success = true;
      String test = "fona";
      unsigned int lenToken = test.length() + 1;
      char tokenData[lenToken];
      test.toCharArray(tokenData, lenToken);
      
      
      if (!fona.HTTP_POST_start(URL, F("text/plain"), (uint8_t *) data, strlen(data), &statuscode, (uint16_t *)&length)) {
        statusInfo[2] = 1;      //HTTP Post failed
        //Serial.println("ERROR: HTTP post failed");
        //pixel.setPixelColor(0, pixel.Color(255 ,255, 255));     //white, just for testing
        //pixel.show();
        
      } else {
        statusInfo[2] = 0;
      }
      
      //******without encryption end******
      
      fona.HTTP_POST_end();

      //Serial.println("INFO - End sending to Webservice");

      

}

String getGeoJSONDataString() {

  
  /* 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\":[" + 
                  floatToString(gps.location.lng()) + ", " + floatToString(gps.location.lat()) + "]}, \"properties\": {\"Temperature\":[" +  
                  String(getTemperature()) + ", \"°C\"], \"Noise\":[" + String(noiseAVG) + ", \"dB\"], \"timestamp\":\"" + getTimeString() + "\"}}";
  
  
  return json;
 
}

String getServiceJSONDataString(uint16_t batLevel, String message) {

  /* Service JSON should Look like this

    {
      "battery": 17.4,
      "status_message": "failed to get GPS fix"
    }
    */
    
    String json =   "{\"battery\": " + String(batLevel) + 
                    ", \"status_message\": \"" + message + "\"}";


    return json;


  
}

String getTimeString() {
  
  /* NOT  WORKING: Sending timestamp couses HTTP POST ERROR
   *  
   *  
  // read the time
  char timeBuffer[23];
  fona.getTime(timeBuffer, 23);  // make sure replybuffer is at least 23 bytes!

  //Format timebuffer: YY/MM/DD,HH:MM+00
  
  //Serial.println("INFO: Time: " + String(timeBuffer));
  String timestamp = timeBuffer;
  */
  return "";
}

String getSimpleDataString() {
  
 
  return ("GPS: " + floatToString(gps.location.lng()) + ", " + floatToString(gps.location.lat()) + 
                      "; Temperature: " + String(getTemperature())  + " C; Noise: " + String(noiseAVG) + " DB; Battery: " + String(getBatteryLevel()) + " %; Voltage: " + String(getBatteryVoltage()) +" mV; " + boxID);
      

}

String floatToString(float val) {
  
  int i;
  char buff[10]; 
  String valueString = "";

  dtostrf(val, 4, 6, buff);  //4 is mininum width, 6 is precision
  valueString += buff;

  return valueString;
  
}

////////////  Get Sensor Data ///////////////////


void encodeGPS(){
  //Serial.println("INFO: Turning on GPS module");
  digitalWrite(GPSENABLE, HIGH);

  unsigned long start = millis();
  // For one second we parse GPS data and report some key values
  for (start; millis() - start < 1000;)
  {
    while (Serial1.available())
    {
      char c = Serial1.read();
      //Serial.write(c); // uncomment this line if you want to see the GPS data flowing
      gps.encode(c); // Did a new valid sentence come in?
    }
  }
  //Serial.println();

  
  // DEBUGGING: Re-encoding for a certain time
  if(millis() - lastGPSupdate <= GPSUpdateTimeout) {
    
    //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");
      encodeGPSAgain = true;
      
      //TODO: Turning Module OFF here?
    } 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");
      encodeGPSAgain = true;
      GPSUpdateInterval = sendToWebInterval;      //SendToWebinterval is usually shorter.
    } else if(!gps.location.isUpdated()) {
      //Serial.println("ERROR: GPS Data not updated! Encoding again");
      encodeGPSAgain = true;
    } else {
      //Serial.println("INFO: GPS: " + floatToString(gps.location.lat()) + ", " + floatToString(gps.location.lng())); 
      //Serial.println("INFO: Turning off GPS-Module");
      digitalWrite(GPSENABLE, LOW);     //turn gps module off
      encodeGPSAgain = false;
      GPSUpdateInterval = 21600000;
      statusInfo[1] = 0;
    }
 
  } else {
    //Serial.println("ERROR: Timeout - No GPS-Data");
    statusInfo[1] = 1;
    //TODO: Send Error Message to Server
    digitalWrite(GPSENABLE, LOW);     //turn gps module off
    encodeGPSAgain = false;
    GPSUpdateInterval = sendToWebInterval;
   
  }
}



float getTemperature(){
  
  int pinReading = analogRead(ANALOG_TEMP_PIN);

  float voltage = pinReading * aref_voltage;
  voltage /= 1024.0;

  float temp = (voltage - 0.5) * 100;
  
  //Serial.println("Temperature: " + String(temp));
  
  //(((analogRead(ANALOG_TEMP_PIN) * 3.3)/1024.0) - 0.5) * 100;

  return temp;
}

//source: http://arduinolearning.com/code/arduino-and-max4466-electret-module-example.php
float getNoiseReading(){
   unsigned long startMillis= millis();  // Start of sample window
   unsigned int peakToPeak = 0;   // peak-to-peak level
   unsigned int signalMax = 0;
   unsigned int signalMin = 1024;
   while (millis() - startMillis < sampleWindow){
      sample = analogRead(ANALOG_NOISE_PIN);
      if (sample < 1024){  // toss out spurious readings
         if (sample > signalMax){           
            signalMax = sample;  // save just the max levels
         }
         else if (sample < signalMin){
            signalMin = sample;  // save just the min levels
         }
      }
   }
   peakToPeak = signalMax - signalMin;  // max - min = peak-peak amplitude
   return peakToPeak; //* 3.3) / 1024;  // convert to volts  
}



int getNoise(){
  // subtract the last reading:
  total = total - readings[readIndex];
  double noiseReading = getNoiseReading();
  // read from the sensor:
  // https://forum.arduino.cc/index.php?topic=318908.0
  // 80 = db on SPL-Meter, 120 = Noise from Noise Sensor at 80 db on the SPL-Meter
  
  //How to calibrate:
  //take the SPL-Meter and the Sensorbox
  //uncomment this:
  //Serial.println("Noise:");
  //Serial.println(noiseReading);
  //Serial.println("");
  //uncomment the seria println before the return
  //play a sound to reach 80 db on the SPL Meter
  // change boxDB on top to Value which is showing for the Sensorbox
  
  readings[readIndex] = (20 * log(noiseReading / boxDB) + 80);
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;

  // if we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;
  }

  // calculate the average:

  //for calibration of Noise
  //Serial.println(total / numReadings);
  
  return  (total / numReadings);
  // send it to the computer as ASCII digits
}


uint16_t getBatteryLevel() {
     
      uint16_t vbat;
      
      fona.getBattPercent(&vbat);

      lastBatteryLevel = vbat;
      //Serial.println("Battery Level: " + String(vbat));
      return vbat;
}

uint16_t getBatteryVoltage() {
   uint16_t vbat;
   //uint16_t batStat;            //NOT WORKING
   fona.getBattVoltage(&vbat);

   //ATTENTION: This function doesn NOT work with public FONA Library.!!!!
   //Status: 0 = not charging; 1 = charging; 2 = finished charging
   //More Info in SIM800 Series AT Command Manual
   //NOT WORKING: getBattStatus always returns 0
   //fona.getBattStatus(&batStat);
   //Serial.println("Battery Status: " + String(batStat));

   //Serial.println("MilliVolts: " + String(vbat));

   //Clear false readings with too high (unrealistic) values
   if(vbat > 4700) vbat=0;

   
   //Battery low
   if (vbat <= 3600 && vbat!=0 ) statusInfo[3] = 1;  
   else statusInfo[3] = 0;
  
   //Battery charging
   if (vbat > lastMilliVolts && vbat!=0) statusInfo[4] = 1;
   else statusInfo[4] = 0;
  
   //Battery fully charged
   if (vbat > 4170 && vbat!=0) {
    statusInfo[5] = 1;
    statusInfo[4] = 0;
   } else {
    statusInfo[5] = 0;
   }
   

   return vbat;
}



////////////  Helpers & Output ///////////////////



void blinkStatus() {

  //get number of statusInfo
  boolean blinkSequenceActive = false;

  currentMillis = millis();
  
  if(currentMillis - lastBlinkSequenceStart >= blinkInterval) {  //start blinking
    blinkSequenceActive = true;
    lastBlinkSequenceStart = currentMillis;
    
  }


  if(blinkSequenceActive) { //do the blink sequence

      lastSingleBlinkStart = currentMillis;
      
      for(int i=0; i<sizeof(statusInfo); ) {
        
        switch (i) {
          case 0: 
            pixel.setPixelColor(0, pixel.Color(255 ,255, 255));     //white -> status blink
            break;
          case 1: 
            pixel.setPixelColor(0, pixel.Color(0,200, 255));       //blue -> no GPS
            break;
          case 2: 
            pixel.setPixelColor(0, pixel.Color(255,0, 255));       //pink -> no GSM
            break;
          case 3: 
            pixel.setPixelColor(0, pixel.Color(255,0, 0));         //red -> low Battery
            break;
          case 4: 
            pixel.setPixelColor(0, pixel.Color(255,188, 0));       //orange -> bat charging
            break;
          case 5: 
            pixel.setPixelColor(0, pixel.Color(0,255, 0));      //green -> bat fully charged
            break;
        }

        if(statusInfo[i] !=0) pixel.show();

        currentMillis = millis();
        if(currentMillis - lastSingleBlinkStart >= singleBlinkDuration) {
          i++;                                              //increase counter to get to the next info
          lastSingleBlinkStart = currentMillis;
          pixel.setPixelColor(0, pixel.Color(0,0, 0));      //green -> bat fully charged
          pixel.show();
        }
             
      }

      
    }
    
  if(currentMillis - lastBlinkSequenceStart >= blinkSequenceDuration) {   //turn off again
        
        pixel.setPixelColor(0, pixel.Color(0 ,0, 0));    
        pixel.show();
        blinkSequenceActive = false;
   }
  
}

/*
void printSensorData(){
  Serial.println("SensorData:");
  Serial.print("\nTemperature: "); Serial.println(getTemperature());
  Serial.print("\nNoise: "); Serial.println(getNoise());
  Serial.print("\nBattery: "); Serial.print(getBatteryLevel()); Serial.println(" %");
  Serial.print("\nLocation: "); Serial.print(gps.location.lat(), 6); Serial.print(", "); Serial.println(gps.location.lng(), 6); 
  Serial.println("---------------------------------------");
}

String sensorDataToString() {
  String data = "Sensordata: Temperature: " + String(getTemperature()) + " Noise: " + String(getNoise()) + " Battery: " + String(getBatteryLevel()) + "% Location: " + String(gps.location.lat()) + ", " + String(gps.location.lng());
  return data;
}
*/
void flushSerial() {
      while (Serial.available()) {
        Serial.read();
      }
}