|
|
@@ -0,0 +1,775 @@
|
|
|
+/*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;
|
|
|
+}
|