aml_sensorbox_tmpNoise.ino 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. /*
  2. * AML TMP+NOISE
  3. *
  4. * VERSION: 1.11
  5. *
  6. *
  7. * The Sensorbox measures Temperature and Noise-Level and sends this information with the GPS-position to a webservice.
  8. *
  9. * ************************************************
  10. * GENERAL INFO AND COMPONENTS
  11. *
  12. * Measuring Battery: https://learn.adafruit.com/adafruit-feather-32u4-fona/power-management
  13. * Gettin Noise: http://arduinolearning.com/code/arduino-and-max4466-electret-module-example.php
  14. * Getting GSM Location: https://www.instructables.com/id/How-to-make-a-Mobile-Cellular-Location-Logger-with/
  15. *
  16. * Tiny GPS++ Library: http://arduiniana.org/libraries/tinygpsplus/
  17. *
  18. *
  19. * Battery saving:
  20. *
  21. * GPS Board: - Use EN Pin to turn of the board when not needed (https://learn.adafruit.com/adafruit-ultimate-gps/overview)
  22. * - EN soll laut der Quelle auf GND gesetzt werden. Es dauert dann aber länger bis wieder ein Fix gefunden wurde.
  23. *
  24. *
  25. * Feather FONA: - Cut the key trac on the bottom of the board (https://learn.adafruit.com/adafruit-feather-32u4-fona/power-management)
  26. * - If you want to depower the cell module, cut the KEY trace on the bottom of the board, wire KEY to an unused pad,
  27. * and toggle the pin low for 100ms to completely turn on/off the module.
  28. * - https://arduino.stackexchange.com/questions/54001/adafruit-feather-32u4-fona-key-pin
  29. * cut the trace and wire it to a micro controller pin
  30. * - 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.
  31. * (Pulse low for a few seconds to change from on to off) This is the only way to truly disable the cellular module.
  32. *
  33. *************************************************
  34. * TODOS & BUGS:
  35. * - Die Ladeanzeige über die LED funktioniert nicht immer korrekt. Besonders gelbes Blinken (Aufladen) ist davon betroffen.
  36. * - GPS fix ist noch valide obwohl die Box offline ist. Wenn der Akku aus ist, sollte ein NO-FIX gesendet werden.
  37. *
  38. ************************************************
  39. *
  40. * IMPORTANT INFO:
  41. * - Be sure to use the correct Token and device number before uplading the code
  42. * - 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
  43. *
  44. * SOURCE TOKENS:
  45. *
  46. * BOX 5: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlhdCI6MTYwMDg0NjY2NH0.d5xvkTVDcHJl-K6KkohIWcUC5XcYNRZ_wzSLMvqAyvw
  47. *
  48. *
  49. *
  50. ************************************************
  51. *
  52. */
  53. #include <Adafruit_NeoPixel.h>
  54. #include "Adafruit_FONA.h"
  55. #include <SoftwareSerial.h>
  56. #include <TinyGPS++.h>
  57. #include <AES.h>
  58. #define LEDPIN 13
  59. #define ANALOG_TEMP_PIN A0
  60. #define ANALOG_NOISE_PIN A1
  61. #define KEYPIN 12
  62. #define GPSENABLE 11
  63. #define FONA_RX 9
  64. #define FONA_TX 8
  65. #define FONA_RST 4
  66. #define FONA_RI 7
  67. #define aref_voltage 3.3
  68. Adafruit_NeoPixel pixel(1, LEDPIN, NEO_GRB + NEO_KHZ800);
  69. //GSM
  70. SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);
  71. SoftwareSerial *fonaSerial = &fonaSS;
  72. Adafruit_FONA fona = Adafruit_FONA(FONA_RST);
  73. uint8_t type;
  74. //This is: Box 5
  75. char URL[] = "http://aml.media.tuwien.ac.at:11312/api/sensordata/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlhdCI6MTYwMDg0NjY2NH0.d5xvkTVDcHJl-K6KkohIWcUC5XcYNRZ_wzSLMvqAyvw";
  76. //char URL2[] = "http://www.mobillab.wien/sensorbox/write/";
  77. char URL3[] = "http://aml.media.tuwien.ac.at:11312/api/sensorstatus/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlhdCI6MTYwMDg0NjY2NH0.d5xvkTVDcHJl-K6KkohIWcUC5XcYNRZ_wzSLMvqAyvw";
  78. unsigned long ATtimeOut = 10000; // How long we will give an AT command to complete
  79. boolean initFONAagain = false;
  80. //Limits
  81. int offlineMv = 3520; //Lowest Millivolts before box shuts down
  82. int onlineMv = 3570; //Millivolts to go online again
  83. //GPS
  84. static const uint32_t GPSBaud = 9600;
  85. TinyGPSPlus gps;
  86. boolean encodeGPSAgain = false;
  87. //Timing
  88. unsigned long currentMillis = 0;
  89. unsigned long singleBlinkDuration = 500; //Duration for 1 Blink
  90. unsigned long lastSingleBlinkStart = 0;
  91. unsigned long blinkSequenceDuration = 4200; //Duration for an blink sequence (not sure if needed)
  92. unsigned long lastBlinkSequenceStart = 0;
  93. unsigned long blinkPauseDuration = 200; //Duration for blink pause
  94. unsigned long lastBlinkPauseStart = 0;
  95. unsigned long blinkInterval = 10000; //Interval to blink
  96. 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)
  97. unsigned long lastBatCheck = 0;
  98. unsigned long sendToWebInterval = 1200000; //20 min = 1200000; 5min = 300000; //Interval for sending data to webservice
  99. unsigned long lastSendToWeb = 0;
  100. unsigned long pullLowDuration = 2000; //3 seconds for pulling KEYPIN low to turn off/on Fona
  101. unsigned long GPSUpdateInterval = 21600000; // 6hrs
  102. unsigned long lastGPSupdate = 0;
  103. unsigned long GPSUpdateTimeout = 180000; //Time for trying to find a fix! If over, then no fix found (indoor).
  104. unsigned long noiseSameplingInterval = 2000; //Start sampling
  105. unsigned long lastNoiseSampling = 0;
  106. //Temp
  107. byte tempPin = A0;
  108. float temperature;
  109. //Noise
  110. const int sampleWindow = 500; // Sample window width in mS (50 mS = 20Hz)
  111. unsigned int sample;
  112. int noiseAVG = 0;
  113. const int numReadings = 20; // change for higher smoothing
  114. int readings[numReadings]; // the readings from the analog input
  115. int readIndex = 0; // the index of the current reading
  116. int total = 0; // the running total
  117. double boxDB = 115;
  118. //Status Flags
  119. byte statusInfo[] = {
  120. 1, //Value = 1 - status everything ok (white)
  121. 0, //Value = 1 - no Position (blue)
  122. 0, //Value = 1 - no connection to server (GSM) (pink)
  123. 0, //Value = 1 - low battery (red)
  124. 0, //Value = 1 - is charging (yellow)
  125. 0, //Value = 1 - battery fully charged (green)
  126. };
  127. uint16_t lastBatteryLevel = 0;
  128. uint16_t lastMilliVolts = 0;
  129. boolean lastMessageSent = true;
  130. //encryption
  131. //AES aes;
  132. //byte *key = (unsigned char*)"0123491889010123";
  133. void setup() {
  134. //while (!Serial);
  135. Serial.begin(115200);
  136. Serial1.begin(GPSBaud);
  137. //Init LED and turn on pixel
  138. pixel.begin();
  139. pixel.setBrightness(30);
  140. pixel.setPixelColor(0, pixel.Color(255, 255, 255)); //light up while startup
  141. pixel.show();
  142. analogReference(EXTERNAL); //sets analog reference voltage to AREF pin
  143. // initialize the readings for Noise to 0:
  144. for (int thisReading = 0; thisReading < numReadings; thisReading++) {
  145. readings[thisReading] = 0;
  146. }
  147. //Pins to turn on and off gsm and gps
  148. pinMode(KEYPIN, OUTPUT);
  149. digitalWrite(KEYPIN, HIGH);
  150. pinMode(GPSENABLE, OUTPUT);
  151. digitalWrite(GPSENABLE, LOW);
  152. encodeGPSAgain = true;
  153. delay(900000); //15minutes to charge the battery before anything else is done 900000
  154. //Turn off pixel
  155. pixel.setPixelColor(0, pixel.Color(0, 0, 0));
  156. pixel.show();
  157. }
  158. void loop() {
  159. if (!lastMessageSent) { //Turn off everything thats not needed
  160. blinkStatus();
  161. if (encodeGPSAgain) encodeGPS(); //Encoding again if no valid or updated signal found; no rekursion here
  162. /*if(initFONAagain) { //NOT NEEDED?????
  163. initFONA(); //Init FONA again if FONA was off while first attempt
  164. Serial.println("INFO: Init Fona again before noise sampling; Line 211");
  165. }*/
  166. //Sampling Noise in a specific interval.
  167. currentMillis = millis();
  168. if (currentMillis - lastNoiseSampling >= noiseSameplingInterval) {
  169. lastNoiseSampling = currentMillis;
  170. noiseAVG = getNoise();
  171. //Serial.println("Info: Noise: " + String(noiseAVG) + "dB");
  172. }
  173. //Getting GPS data in a specific interval.
  174. currentMillis = millis();
  175. if (currentMillis - lastGPSupdate >= GPSUpdateInterval) {
  176. //Serial.println("Checking GPS");
  177. lastGPSupdate = currentMillis;
  178. encodeGPS();
  179. }
  180. }
  181. //Send to Web in a specific interval
  182. currentMillis = millis();
  183. if ((currentMillis - lastSendToWeb >= sendToWebInterval)) {
  184. //Serial.println("foo");
  185. lastSendToWeb = currentMillis;
  186. switchFONA();
  187. //Serial.println(" INFO: Switching FONA (in begin of sendtoweb); Line 239");
  188. initFONA();
  189. if (initFONAagain) {
  190. initFONA();
  191. //Serial.println(" Init FONA again (in begin of sendtoweb); Line 243");
  192. }
  193. //Send to Webservice, if battery is good enough and GPS was once valid
  194. //INFO: Checking for valid GPS Signal only makes sense for static use on one place.
  195. // For dynamic use with changing places the check should use gps.location.isUpdated()
  196. if (gps.location.isValid() && (lastMilliVolts >= offlineMv) && !lastMessageSent) {
  197. lastMilliVolts = getBatteryVoltage();
  198. lastBatteryLevel = getBatteryLevel();
  199. //delay(10000);
  200. //Serial.println("INFO: Sending Data!");
  201. sendToWebService(getGeoJSONDataString(), URL);
  202. //delay(10000);
  203. //Serial.println("INFO: Sending Status!");
  204. sendToWebService("{\"battery\": " + String(getBatteryLevel()) + ", \"millivolt\": " + String(getBatteryVoltage()) + ", \"gps_fix\": 1, \"online\": 1}", URL3);
  205. } else { //Check Battery
  206. lastMilliVolts = getBatteryVoltage();
  207. lastBatteryLevel = getBatteryLevel();
  208. }
  209. //SERVICE MESSAGES
  210. //Power too low, but last message not yet sent -> send last message; BUT make sure, that mV is plausible -> >500
  211. byte gpsFix = 1;
  212. if (statusInfo[1] == 1) { //Statusinfo is 1 in case of error -> no gps
  213. gpsFix = 0;
  214. }
  215. if ((lastMilliVolts < offlineMv) && (lastMilliVolts > 500) && !lastMessageSent) {
  216. //Serial.println("INFO: Sending last message to DATAHUB");
  217. //Tell the server that there is no GPS Fix (although there might still be one).
  218. //TODO: Clear the tinyGPS object (so that there is actually no fix)
  219. sendToWebService("{\"battery\": " + String(getBatteryLevel()) + ", \"millivolt\": " + getBatteryVoltage() + ", \"gps_fix\": 0" + ", \"online\": 0}", URL3);
  220. lastMessageSent = true; //set to false; needed if battery is charging and sensor is sending again
  221. } else if ((lastMilliVolts > onlineMv) && 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)
  222. //Serial.println("INFO: Sending Back to Work message to DATAHUB");
  223. sendToWebService("{\"battery\": " + String(getBatteryLevel()) + ", \"millivolt\": " + getBatteryVoltage() + ", \"gps_fix\": " + gpsFix + ", \"online\": 1}", URL3);
  224. lastMessageSent = false; //set to false; needed if battery is charging and sensor is sending again
  225. }
  226. //Try to get a valid GPS location
  227. if (!gps.location.isValid() && (lastMilliVolts >= onlineMv) && !lastMessageSent) {
  228. //Serial.println("INFO: Sending NO GPS Message to DATAHUB");
  229. sendToWebService("{\"battery\": " + String(getBatteryLevel()) + ", \"millivolt\": " + getBatteryVoltage() + ", \"gps_fix\": 0" + ", \"online\": 1}", URL3);
  230. GPSUpdateInterval = sendToWebInterval;
  231. }
  232. switchFONA();
  233. //Serial.println("INFO: Switching FONA after sending; Line 300");
  234. }
  235. }
  236. void initFONA() {
  237. //Serial.println("INFO: Starting FONA init");
  238. fonaSerial->begin(4800);
  239. if (!fona.begin(*fonaSerial)) {
  240. //Serial.println(F("INIT - Couldn't find FONA"));
  241. switchFONA(); //In case Phona is turned off (happens wenn battery runs completely out and is charged up again.
  242. //Serial.println("INFO: Switching FONA in initFONA; Line 314");
  243. initFONAagain = true; //after switching ON try to init again
  244. //Serial.println("ERROR: init FONA again; FONA not found");
  245. statusInfo[2] = 1;
  246. // Try to turn it on
  247. //turnOn();
  248. if (!fona.begin(*fonaSerial)) {
  249. while (1)
  250. ;
  251. }
  252. } else {
  253. statusInfo[2] = 0;
  254. initFONAagain = false;
  255. }
  256. type = fona.type();
  257. // Set APN (needed for A1 Sim cards) -> Uncomment if other SIM-Cards are in use
  258. fona.setGPRSNetworkSettings(F("A1.net"), F("ppp@a1plus.at"), F("ppp"));
  259. // turn GPRS on
  260. delay(10000);
  261. if (!fona.enableGPRS(true)) {
  262. //Serial.println(F("INIT - Failed to turn on GPRS"));
  263. statusInfo[2] = 1;
  264. //Serial.println("ERROR: init FONA again; no GPRS");
  265. initFONAagain = true; //Try to init FONA again in the next loop
  266. } else {
  267. statusInfo[2] = 0;
  268. initFONAagain = false;
  269. }
  270. /* NOT WORKING: Sending timestamp couses HTTP POST ERROR
  271. // enable NTP time sync
  272. fona.enableNTPTimeSync(true, F("pool.ntp.org"));
  273. //Serial.println(F("INFO: Failed to enable NTP time sync"));
  274. */
  275. //turnOnOffFona();
  276. }
  277. void switchFONA() {
  278. //Switching FONA on and off through pulling the KEYPIN LOW for a while
  279. //Each pull to LOW switches the FONA, either on or off (depending on the current state)
  280. //Serial.println("INFO: Switching FONA");
  281. unsigned long startMillis = millis();
  282. while (millis() - startMillis < pullLowDuration) {
  283. digitalWrite(KEYPIN, LOW);
  284. }
  285. digitalWrite(KEYPIN, HIGH);
  286. }
  287. void sendToWebService(String message, char URL[]) {
  288. //Serial.println("INFO - Start sending to Webservice");
  289. uint16_t statuscode;
  290. int16_t length;
  291. //String datastring = getGeoJSON();
  292. //String datastring = getSimpleDataString(); //For readable output on Server
  293. String datastring = message;
  294. //******with encryption start******
  295. /*
  296. byte *key = (unsigned char*)"0123491889010123"; //needs to be randomized!!!!
  297. unsigned long long int my_iv = 36712162; //needs to be randomized!!!!
  298. byte plain[datastring.length()];
  299. datastring.getBytes(plain, datastring.length());
  300. int plainLength = sizeof(plain)-1; // don't count the trailing /0 of the string !
  301. int padedLength = plainLength + N_BLOCK - plainLength % N_BLOCK;
  302. aes.iv_inc();
  303. byte iv [N_BLOCK] ;
  304. byte plain_p[padedLength];
  305. byte cipher [padedLength] ;
  306. aes.set_IV(my_iv);
  307. aes.get_IV(iv);
  308. aes.do_aes_encrypt(plain,plainLength,cipher,key,128,iv);
  309. flushSerial();
  310. bool success = true;
  311. if (!fona.HTTP_POST_start(URL, F("text/plain"), (uint8_t *) plain, strlen(plain), &statuscode, (uint16_t *)&length)) {
  312. //Serial.println("HTTP POST FAILED!");
  313. statusInfo[2] = 1;
  314. } else {
  315. statusInfo[2] = 0;
  316. }
  317. */
  318. //******with encryption end******
  319. //******without encryption start******
  320. unsigned int len = datastring.length() + 1;
  321. char data[len];
  322. datastring.toCharArray(data, len);
  323. flushSerial();
  324. //char myData[] = "{\"simple\":\"json\"}";
  325. bool success = true;
  326. String test = "fona";
  327. unsigned int lenToken = test.length() + 1;
  328. char tokenData[lenToken];
  329. test.toCharArray(tokenData, lenToken);
  330. if (!fona.HTTP_POST_start(URL, F("text/plain"), (uint8_t *)data, strlen(data), &statuscode, (uint16_t *)&length)) {
  331. statusInfo[2] = 1; //HTTP Post failed
  332. //Serial.println("ERROR: HTTP post failed");
  333. //pixel.setPixelColor(0, pixel.Color(255 ,255, 255)); //white, just for testing
  334. //pixel.show();
  335. } else {
  336. statusInfo[2] = 0;
  337. }
  338. //******without encryption end******
  339. fona.HTTP_POST_end();
  340. //Serial.println("INFO - End sending to Webservice");
  341. }
  342. String getGeoJSONDataString() {
  343. /* GeoJSON should look like this:
  344. {
  345. "type": "Feature",
  346. "geometry": {
  347. "type": "Point",
  348. "coordinates": [100.0, 0.0]
  349. },
  350. "properties": {
  351. "Temperature": [24.5, "°C"],
  352. "Noise": [100.0, "dB"],
  353. "timestamp": "123456723495",
  354. }
  355. }
  356. */
  357. 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() + "\"}}";
  358. return json;
  359. }
  360. String getTimeString() {
  361. /* NOT WORKING: Sending timestamp couses HTTP POST ERROR
  362. *
  363. *
  364. // read the time
  365. char timeBuffer[23];
  366. fona.getTime(timeBuffer, 23); // make sure replybuffer is at least 23 bytes!
  367. //Format timebuffer: YY/MM/DD,HH:MM+00
  368. //Serial.println("INFO: Time: " + String(timeBuffer));
  369. String timestamp = timeBuffer;
  370. */
  371. return "";
  372. }
  373. /* JUST USED FOR TESTING
  374. String getSimpleDataString() {
  375. return ("GPS: " + floatToString(gps.location.lng()) + ", " + floatToString(gps.location.lat()) +
  376. "; Temperature: " + String(getTemperature()) + " C; Noise: " + String(noiseAVG) + " DB; Battery: " + String(getBatteryLevel()) + " %; Voltage: " + String(getBatteryVoltage()) +" mV; " + boxID);
  377. }
  378. */
  379. String floatToString(float val) {
  380. int i;
  381. char buff[10];
  382. String valueString = "";
  383. dtostrf(val, 4, 6, buff); //4 is mininum width, 6 is precision
  384. valueString += buff;
  385. return valueString;
  386. }
  387. //////////// Get Sensor Data ///////////////////
  388. void encodeGPS() {
  389. //Serial.println("INFO: Turning on GPS module");
  390. digitalWrite(GPSENABLE, HIGH);
  391. unsigned long start = millis();
  392. // For one second we parse GPS data and report some key values
  393. for (start; millis() - start < 1000;) {
  394. while (Serial1.available()) {
  395. char c = Serial1.read();
  396. //Serial.write(c); // uncomment this line if you want to see the GPS data flowing
  397. gps.encode(c); // Did a new valid sentence come in?
  398. }
  399. }
  400. //Serial.println();
  401. // DEBUGGING: Re-encoding for a certain time
  402. if (millis() - lastGPSupdate <= GPSUpdateTimeout) {
  403. //if we haven't seen lots of data in 5 seconds, something's wrong.
  404. if (start > 5000 && gps.charsProcessed() < 10) {
  405. //Serial.println("ERROR: Not getting any GPS data! Encoding again");
  406. encodeGPSAgain = true;
  407. //TODO: Turning Module OFF here?
  408. } else if (!gps.location.isValid()) { //also add timer for circumstances when there is definately no signal to find
  409. //Serial.println("ERROR: GPS Data not valid! Encoding again");
  410. encodeGPSAgain = true;
  411. GPSUpdateInterval = sendToWebInterval; //SendToWebinterval is usually shorter.
  412. } else if (!gps.location.isUpdated()) {
  413. //Serial.println("ERROR: GPS Data not updated! Encoding again");
  414. encodeGPSAgain = true;
  415. } else {
  416. //Serial.println("INFO: GPS: " + floatToString(gps.location.lat()) + ", " + floatToString(gps.location.lng()));
  417. //Serial.println("INFO: Turning off GPS-Module");
  418. digitalWrite(GPSENABLE, LOW); //turn gps module off
  419. encodeGPSAgain = false;
  420. GPSUpdateInterval = 21600000;
  421. statusInfo[1] = 0;
  422. }
  423. } else {
  424. //Serial.println("ERROR: Timeout - No GPS-Data");
  425. statusInfo[1] = 1;
  426. //TODO: Send Error Message to Server
  427. digitalWrite(GPSENABLE, LOW); //turn gps module off
  428. encodeGPSAgain = false;
  429. GPSUpdateInterval = sendToWebInterval;
  430. }
  431. }
  432. float getTemperature() {
  433. int pinReading = analogRead(ANALOG_TEMP_PIN);
  434. float voltage = pinReading * aref_voltage;
  435. voltage /= 1024.0;
  436. float temp = (voltage - 0.5) * 100;
  437. //Serial.println("Temperature: " + String(temp));
  438. //(((analogRead(ANALOG_TEMP_PIN) * 3.3)/1024.0) - 0.5) * 100;
  439. return temp;
  440. }
  441. //source: http://arduinolearning.com/code/arduino-and-max4466-electret-module-example.php
  442. float getNoiseReading() {
  443. unsigned long startMillis = millis(); // Start of sample window
  444. unsigned int peakToPeak = 0; // peak-to-peak level
  445. unsigned int signalMax = 0;
  446. unsigned int signalMin = 1024;
  447. while (millis() - startMillis < sampleWindow) {
  448. sample = analogRead(ANALOG_NOISE_PIN);
  449. if (sample < 1024) { // toss out spurious readings
  450. if (sample > signalMax) {
  451. signalMax = sample; // save just the max levels
  452. } else if (sample < signalMin) {
  453. signalMin = sample; // save just the min levels
  454. }
  455. }
  456. }
  457. peakToPeak = signalMax - signalMin; // max - min = peak-peak amplitude
  458. return peakToPeak; //* 3.3) / 1024; // convert to volts
  459. }
  460. int getNoise() {
  461. // subtract the last reading:
  462. total = total - readings[readIndex];
  463. double noiseReading = getNoiseReading();
  464. // read from the sensor:
  465. // https://forum.arduino.cc/index.php?topic=318908.0
  466. // 80 = db on SPL-Meter, 120 = Noise from Noise Sensor at 80 db on the SPL-Meter
  467. //How to calibrate:
  468. //take the SPL-Meter and the Sensorbox
  469. //uncomment this:
  470. //Serial.println("Noise:");
  471. //Serial.println(noiseReading);
  472. //Serial.println("");
  473. //uncomment the seria println before the return
  474. //play a sound to reach 80 db on the SPL Meter
  475. // change boxDB on top to Value which is showing for the Sensorbox
  476. readings[readIndex] = (20 * log(noiseReading / boxDB) + 80);
  477. // add the reading to the total:
  478. total = total + readings[readIndex];
  479. // advance to the next position in the array:
  480. readIndex = readIndex + 1;
  481. // if we're at the end of the array...
  482. if (readIndex >= numReadings) {
  483. // ...wrap around to the beginning:
  484. readIndex = 0;
  485. }
  486. // calculate the average:
  487. //for calibration of Noise
  488. //Serial.println(total / numReadings);
  489. return (total / numReadings);
  490. // send it to the computer as ASCII digits
  491. }
  492. uint16_t getBatteryLevel() {
  493. uint16_t vbat;
  494. fona.getBattPercent(&vbat);
  495. lastBatteryLevel = vbat;
  496. //Serial.println("Battery Level: " + String(vbat));
  497. return vbat;
  498. }
  499. uint16_t getBatteryVoltage() {
  500. uint16_t vbat;
  501. //uint16_t batStat; //NOT WORKING
  502. fona.getBattVoltage(&vbat);
  503. //ATTENTION: This function doesn NOT work with public FONA Library.!!!!
  504. //Status: 0 = not charging; 1 = charging; 2 = finished charging
  505. //More Info in SIM800 Series AT Command Manual
  506. //NOT WORKING: getBattStatus always returns 0
  507. //fona.getBattStatus(&batStat);
  508. //Serial.println("Battery Status: " + String(batStat));
  509. //Serial.println("MilliVolts: " + String(vbat));
  510. //Clear false readings with too high (unrealistic) values
  511. if (vbat > 4700) vbat = 0;
  512. //Battery low
  513. if (vbat <= 3600 && vbat != 0) statusInfo[3] = 1;
  514. else statusInfo[3] = 0;
  515. //Battery charging
  516. if (vbat > lastMilliVolts && vbat != 0) statusInfo[4] = 1;
  517. else statusInfo[4] = 0;
  518. //Battery fully charged
  519. if (vbat > 4170 && vbat != 0) {
  520. statusInfo[5] = 1;
  521. statusInfo[4] = 0;
  522. } else {
  523. statusInfo[5] = 0;
  524. }
  525. return vbat;
  526. }
  527. //////////// Helpers & Output ///////////////////
  528. void blinkStatus() {
  529. //get number of statusInfo
  530. boolean blinkSequenceActive = false;
  531. currentMillis = millis();
  532. if (currentMillis - lastBlinkSequenceStart >= blinkInterval) { //start blinking
  533. blinkSequenceActive = true;
  534. lastBlinkSequenceStart = currentMillis;
  535. }
  536. if (blinkSequenceActive) { //do the blink sequence
  537. lastSingleBlinkStart = currentMillis;
  538. for (int i = 0; i < sizeof(statusInfo);) {
  539. switch (i) {
  540. case 0:
  541. pixel.setPixelColor(0, pixel.Color(255, 255, 255)); //white -> status blink
  542. break;
  543. case 1:
  544. pixel.setPixelColor(0, pixel.Color(0, 200, 255)); //blue -> no GPS
  545. break;
  546. case 2:
  547. pixel.setPixelColor(0, pixel.Color(255, 0, 255)); //pink -> no GSM
  548. break;
  549. case 3:
  550. pixel.setPixelColor(0, pixel.Color(255, 0, 0)); //red -> low Battery
  551. break;
  552. case 4:
  553. pixel.setPixelColor(0, pixel.Color(255, 188, 0)); //orange -> bat charging
  554. break;
  555. case 5:
  556. pixel.setPixelColor(0, pixel.Color(0, 255, 0)); //green -> bat fully charged
  557. break;
  558. }
  559. if (statusInfo[i] != 0) pixel.show();
  560. currentMillis = millis();
  561. if (currentMillis - lastSingleBlinkStart >= singleBlinkDuration) {
  562. i++; //increase counter to get to the next info
  563. lastSingleBlinkStart = currentMillis;
  564. pixel.setPixelColor(0, pixel.Color(0, 0, 0)); //green -> bat fully charged
  565. pixel.show();
  566. }
  567. }
  568. }
  569. if (currentMillis - lastBlinkSequenceStart >= blinkSequenceDuration) { //turn off again
  570. pixel.setPixelColor(0, pixel.Color(0, 0, 0));
  571. pixel.show();
  572. blinkSequenceActive = false;
  573. }
  574. }
  575. /*
  576. void printSensorData(){
  577. Serial.println("SensorData:");
  578. Serial.print("\nTemperature: "); Serial.println(getTemperature());
  579. Serial.print("\nNoise: "); Serial.println(getNoise());
  580. Serial.print("\nBattery: "); Serial.print(getBatteryLevel()); Serial.println(" %");
  581. Serial.print("\nLocation: "); Serial.print(gps.location.lat(), 6); Serial.print(", "); Serial.println(gps.location.lng(), 6);
  582. Serial.println("---------------------------------------");
  583. }
  584. String sensorDataToString() {
  585. String data = "Sensordata: Temperature: " + String(getTemperature()) + " Noise: " + String(getNoise()) + " Battery: " + String(getBatteryLevel()) + "% Location: " + String(gps.location.lat()) + ", " + String(gps.location.lng());
  586. return data;
  587. }
  588. */
  589. void flushSerial() {
  590. while (Serial.available()) {
  591. Serial.read();
  592. }
  593. }