Our planet is our home, our only home. Where should we go if we destroy it?
The Dalai Lama, 2004
This quote of great man marks the beginning of our project story. No-one has been able to provide an answer to this essential question so far. Thus, it is our task to preserve and protect Earth as it is the only planet we can live on if humankind is to survive and thrive. Thanks to today’s state-of-the-art technology, whether it is seen as a curse or as a bliss, we are able to design and put to use effective protection systems that can help to save planet Earth.
It is in particular the Internet of Things, and its sector the Green Internet of Things (Green IoT) which is gaining momentum, which is expected to contribute significantly to the preservation of habitats for wildlife, plants and people.
In our project, we will present a universal monitoring system which is based on Sigfox and offers a wide range of applications to monitor environmental aspects of soil, water and air.
The challengeThe idea to develop this system was born in the summer of 2018. That summer was an extremely long hot and dry season, very unusual for Germany, and saw an overheating of many rivers and lakes leading to large amounts of fish dying. This is shown in Figure 1:
Figure 1: Dead fish in the pond of a municipal park ((c) waz.de, Andreas Rorowski)
The causes of these deaths can be explained easily, and they are lethal for any creatures living in water: the temperature of the water rises, reaches critical values and the oxygen dissolved in the water decreases. Additionally, the water volume in a lake or pond reduces due to increased evaporation, and the habitat of the fish shrinks dramatically.
However, it is just as simple to remedy such critical situations: large volumes of fresh water need to be supplied into the lakes and ponds; additionally, air (i.e. oxygen) can be injected, too.
These two measures alone would suffice to save the fish, but: such critical water conditions have to be recognized in due time by the responsible bodies (environmental agencies, local authorities, fire brigades, technical support organizations etc.) to allow for a prompt remediation.
And this is where modern environmental monitoring systems take center stage: measuring stations, set up along the water bodies, cyclically record the crucial water parameters and transmit them to central control stations for further evaluation.
Individual measuring intervals can be selected (e.g. every 10 minutes, every 12 hours, once a day) and once the freely defined limit values are exceeded, corresponding alarm emails are sent to the people responsible.
Thus, a swift response is possible before life-threatening situations occur.
The solutionThe environmental monitoring system which we have developed at the university of applied sciences, Technische Hochschule Georg Agricola (THGA), in Bochum, Germany, uses measuring stations to initially determine two relevant water parameters (oxygen level in the water and water temperature) and transmitted to the Sigfox backend via the Sigfox network.
From there, the data will be transmitted further using the internet to a web-based dashboard program (freeware) which allows the visualization and further evaluation by the user.
If the values measured reach critical levels, emails can be sent immediately. Sensors to determine additional water parameters (conductivity, pH-value, turbidity, etc.) can simply be added and integrated into the system.
Nevertheless, this monitoring system is not only suitable for monitoring waterbodies: it can also be used in many areas of active environmental protection, as the core electronic system remains the same and only the sensors have to be selected and adjusted for the specific purpose.
The software used can be freely designed to record, process and transmit all kinds of environmental protection cases.
The only thing that needs to be in place on the spot is the Sigfox network.
The core electronic system of the measuring stationsThe central microcontroller module of the measuring stations is the well-known Sigfox Arduino Maker Board MKR FOX1200, Figure 2:
Figure 2: The Sigfox Arduino Maker Board MKR FOX1200
In order to enhance the performance of this module, we have developed a motherboard that features additional components and to which the MKR FOX1200 is mounted, Figure 3:
Figure 3: The motherboard forming the core of the Sigfox environmental measuring stations
Figure 4 shows the block diagram of the motherboard:
*
Figure 4: Block diagram of the motherboard
(see ´Enclosure - Missing figures´)
*
The essential components of the boards are listed below:
- kernel system: Sigfox Arduino MKR FOX1200 Board
- 2.9inePaper display (296 * 128 pixels) for local display of information
- three photo-MOS semiconductor relays (60V DC/AV, 1100 mA) to connect relays, gates, solenoid valves, motors, sirens, warning lights etc.
- a piezo-buzzer to emit acoustic signals
- an LDR to measure the ambient brightness
- an impedance converter for the analog output: buffered output of analog (control) voltages
- an integrator to convert, if necessary, the PWM signals to direct voltage
- a slot for BMP280 breakout boards (measuring air pressure and temperature)
- two slots for MAX31865 breakout boards (measuring temperatures by means of Pt100-RTD)
- interfaces connected to sockets for I2C(5V), I2C(3.3V), UART(3.3V); here, for example, additional sensors or actuators can be simply connected
- the remaining port pins of the MKR FOX1200 are connected to screw-type terminals
- voltage supply: 9-15 VDC
This motherboard is the central unit of all our high-performing Sigfox environmental measuring stations and, due to its interfaces, provides the opportunity to connect a large number of sensors which can be used to measure different environmental data such as:
- temperature
- humidity
- oxygen (O2)
- carbon dioxide (CO2)
- methane (CH4)
- carbon monoxide (CO)
- pH-value
- conductivity
- water level
- particle dust
- UV radiation
- precipitation
- ….
Thus, our universal measuring stations are ideally suitable for use in many areas of environmental monitoring (Green IoT.
SmartLake – practical usesThe first application of the Project SmartLake was to implement the monitoring of stagnant waters (lakes). Figure 5 shows one measuring station of this project:
Figure 5: A measuring station of the SmartLake Project
Figure 6 shows the block diagram of the station, and Figure 7 shows the path the sensor signal takes to the user dashboard:
*
Figure 6: The block diagram of the measuring station
(see ´Enclosure - Missing figures´)
*
*
Figure 7: The path of the sensor signal to the user dashboard
(see ´Enclosure - Missing figures´)
*
The kernel of the measuring station is the MKR FOX1200 motherboard, supplemented by a solar power supply unit (solar panel, solar charge controller and rechargeable battery).
These components are installed into a waterproof enclosure of ingress protection IP67 (water and dust).
The solar generator enables a smooth 24/7 year-round permanent operation of the station.
The different sensors are connected using external plugs. The solar panel cables are led directly into the station, and the connection to the development computer is done using a water-tight USB slot.
How it worksThe water parameters to be measured are recorded by sensors suitable for the respective purpose:
- A sensor made by AtlasSientific ( Figure 8) is used to measure the oxygen level in the water. Such sensors are used in professional applications (e.g. fish farming, hydroponics, environmental monitoring) and thus are rather expensive. However, they provide the advantage of being particularly robust which is very useful for extreme outdoor uses such as lake monitoring: these reliable sensors run maintenance-free and over long periods of time. Of course, it is also possible to use other, less expensive senor units provided they can be actuated via the I2C-bus. The sensor output signal is processed by an appropriate measuring amplifier PCB, digitized and transmitted to the MKR FOX1200 board via the I2C-bus.
- The water temperature is measured using a Pt100-RTD which is water-tight encapsulated, Figure 9. A MAX31865 breakout board is used to record the values measured by Pt100; these are then digitized and transmitted to the MKR FOX1200 via the SPI-bus. The motherboard accommodates two of these Pt100 measuring points; at the moment, however, only one is in use.
- The breakout board of the BMP280, as shown in Figure 10, is used to determine the ambient temperature (the BMP280 also measures the current air pressure, but this values is not used further for this purpose).
Figure 8: O2 sensor and its measuring amplifier PCB (AtlasScientific)
Figure 9: Pt100 with breakout board MAX31865
Figure 10: Breakout board with BMP280
The two sensors are connected to the measuring station by means of a BNC connector and and a circular connector as shown in Figure 11:
Figure 11: Sensors connected to the measuring station
The cycle time of recording the values measured can be set and defined in the program; according to the Sigfox specifications the minimum interval is 10 minutes, Figure 12:
/***********************************************************/
/*** Waiting time for Sigfox transmission (in minutes) ***/
/***********************************************************/
unsigned int steps = 180; // Steps
signed int k = steps;
/* With a time delay of delay(10) in the loop-function
the following applies to the cycle time:
steps Time between two transmission
52 3:02 min
180 10:01 min
270 14:56 min
540 28:56 min
*/
Figure 12: Defining the measuring cycle time in the program
The values measured are then processed and displayed permanently on a 2.9in ePaper display permanent as local signals.
Once the defined cycle time is over, the data will be transmitted to the Sigfox back-end, Figure 13:
Figure 13: Data received by the Sigfox back-end
Additionally, alarm emails can be sent immediately once pre-set limit values are exceeded.
Power to the entire measuring station is supplied by a solar generator which consists of a solar panel, a charge controller and a rechargeable battery as shown in Figure 14:
Figure 14: Solar generator
System advantagesThe measuring station which is the result of this development process can be used in many areas where monitoring is required. Those areas can include industrial systems or processes; monitoring in agriculture and forestry; monitoring of grids supply gas, water or electricity, oil and gas pipelines and such.
Due to the flexible options to connect numerous different sensors and actuators, from very simple designs to sophisticated professional units, monitoring assignments of very different natures can be optimally designed and operated.
The use of the solar generator ensures that operation does not need to rely on local power supply; the stations can be erected in a field, on a building or in a vehicle 24/7, year-round.
The hardware-related design of the station can be implemented easily and at low cost, and the cost of the Sigfox network are also very low.
The only thing that needs to be individually adjusted is the software used for recording the values measured and for entering these values into the Sigfox payload.
To sum up, there is an almost unlimited number of possible applications of these Sigfox-monitoring stations and the Sigfox network enables a simple and nearly worldwide access to data.
Design of the measuring stationThe assembly of the motherboardFigure 15 shows the circuit diagram of the motherboard:
Figure 15: Motherboard circuit diagram
For this motherboard, we designed a special PCB using the EAGLE CAD system.
If people want to rebuild this PCB, they can buy one of our PCBs or create, modify and enlarge their own PCBs based on the EAGLE files which have been published.
Below, we are going to show step by step how the motherboard is assembled, Figure 16; simple soldering knowledge is sufficient to carry out this work.
*
Figure 16: Assembly of the motherboard
(see ´Enclosure - Missing figures´)
*Mounting the measuring stationOnce a suitable enclosure has been found for the measuring station, Figure 17, modification of the enclosure can begin, Figure 18:
Figure 17: A suitable enclosure for the measuring station
Figure 18: Modifying the enclosure
Once the enclosure has been modified, the motherboard will be installed into the enclosure together with the rechargeable battery, the solar charge controller and the measuring amplifier card for the O2 sensor.
The O2 sensor and the Pt100 will be connected using a appropriate BNC connector and a circular connector; the cables of the solar panel will be led into the enclosure using cable glands, Figure 19:
Figure 19: installing the components
By doing so, the hardware installation of the measuring station is complete which now takes us to the operating software for the MKR FOX1200 board.
Calibrating the sensorsThe sensors used – O2-sensor, BMP280 and Pt100 – can be easily calibrated.
The Pt100 is simply connected to the measuring amplifier in two-wire system; the amplifier processes the value measured and sends it to the MKR FOX1200 board via the I2C bus. There, the data are processed further.
Likewise, the BMP280 (measuring air pressure and air temperature) does not need particular calibration: the breakout board is simply inserted and data is retrieved via the I2C bus.
It is, however, necessary to calibrate the O2-sensor made by AtlasScientific, but this can be implemented very easily.
Please note:
Before installing the O2 meauring amplifier, the data transfer mode needs to be switched from UART to I2C.
This can be done without any problems and is described in the data sheet of the measuring amplifier (p.41)
For the calibration of the sensor we have written a little Arduino sketch (DO_I2C-V5_en.ino, based of a sketch from AtlasScientific), which allows to operate, address and configure the sensor, Figure 20:
/*************************************************************************/
/* */
/* Sigfox-Challenge 2019 */
/* */
/* SmartLake */
/* */
/* Demoprogramm for the AtlasScientific-Sensor */
/* for Dissolved Oxygen (DO) */
/* Developed by: Frank Schleking, Philipp Krienke */
/* Filip Schmachtenberger, Bernd vom Berg */
/* */
/* University Georg Agricola, Bochum, Germany */
/* */
/* DO_I2C-V5_en.ino Version 5.0, 12.10.2019 */
/* */
/*************************************************************************/
//Include the required standard libraries
#include <Wire.h> //I2C-Library
#define address 97 //default I2C ID adress for EZO DO Circuit.
// Global Variables
char do_data[20]; // Data from the DO sensor as a string
unsigned char taste;
/*******************************************************************/
/*********** Setup - Function ************************************/
/*******************************************************************/
void setup() // called once
{
Serial.begin(9600); //enable serial port.
Wire.begin(); //enable I2C port.
delay(2000); //Switching time for ser. monitor
}
/*********************************************************************************/
/*********************************************************************************/
void loop() //the main infinite loop
{
char wahl;
// clear screen
clear_screen();
// Output title Screen on serial monitor
titel_bild();
// Wait for input
while (Serial.available() == 0);
// Evaluation of the input
wahl = Serial.read();
Serial.print(wahl);
delay(1000); // short waiting time that you can see the input
// Evaluation of the input
switch (wahl)
{
case '1': DO_information();
break;
case '2': DO_mess();
break;
case '3': DO_rueck();
break;
case'4': DO_cal_luft();
break;
default: Serial.print("\n\nInvaild selection. Please try again!");
delay(4000);
break;
}
}
/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/*** Clear serial monitor ´tricky´ ***/
void clear_screen(void)
{
unsigned char i;
for (i = 0; i < 35; i++) Serial.println(" ");
}
/*******************************************************************/
/*** Output title Screen on serial monitor***/
void titel_bild(void)
{
clear_screen();
Serial.println("**************************************************************");
Serial.println("***** Demoprogramm for the AtlasScientific-Sensor *****");
Serial.println("***** for Dissolved Oxygen (DO) *****");
Serial.println("***** *****");
Serial.println("***** I2C-Bus-Adress : 97 (dez) *****");
Serial.println("**************************************************************");
Serial.println("\nPlease choose:\n");
Serial.println(" 1) Read DO-Sensor-Information");
Serial.println(" 2) Read DO-Values");
Serial.println(" 3) Choose unit for DO-Sensor-Values");
Serial.println(" 4) Calibration to O2 air value: 9,09 - 9,1 mg/l");
Serial.print("\n\nYour Choice: ");
}
/********************************************************************/
void DO_send (char data[20])
{
unsigned char i = 0;
unsigned char code = 0;
unsigned char in_char = 0;
int time_;
// Waiting time for sensor-response
if (data[0] == 'c' || data[0] == 'r')time_ = 575; //if a command has been sent to calibrate or take a reading we wait 575ms so that the circuit has time to take the reading.
else time_ = 300; //if any other command has been sent we wait only 300ms.
// !!!! Anders als im Original-Programm !!!!
// Start I2C communication
Wire.beginTransmission(address); //call the circuit by its ID number.
// Send: Command
Wire.write(data); //transmit the command that was sent through the serial port.
Wire.endTransmission(); //end the I2C data transmission.
// Receiving the answer if command is not equal to 'sleep'
if (strcmp(data, "sleep") != 0) //if the command that has been sent is NOT the sleep command, wait the correct amount of time and request data.
{ //if it is the sleep command, we do nothing. Issuing a sleep command and then requesting data will wake the D.O. circuit.
delay(time_); //wait the correct amount of time for the circuit to complete its instruction.
// Request of max. 20 bytes of data from the slave
Wire.requestFrom(address, 20, 1); //call the circuit and request 20 bytes (this is more than we need)
// Reading the first byte = status byte
code = Wire.read(); //the first byte is the response code, we read this separately.
/*
// If required, for test purposes: Evaluation of the status byte
switch (code) //switch case based on what the response code is.
{
case 1: //decimal 1.
Serial.println("Success"); //means the command was successful.
break; //exits the switch case.
case 2: //decimal 2.
Serial.println("Failed"); //means the command has failed.
break; //exits the switch case.
case 254: //decimal 254.
Serial.println("Pending"); //means the command has not yet been finished calculating.
break; //exits the switch case.
case 255: //decimal 255.
Serial.println("No Data"); //means there is no further data to send.
break; //exits the switch case.
}
*/
// Reading in and storing the remaining bytes in array do_data
while (Wire.available()) //are there bytes to receive.
{
in_char = Wire.read(); //receive a byte.
do_data[i] = in_char; //load this byte into our array.
i += 1; //incur the counter for the array element.
if (in_char == 0) //if we see that we have been sent a null command.
{
i = 0; //reset the counter i to 0.
Wire.endTransmission(); //end the I2C data transmission.
break; //exit the while loop.
}
}
/*
// If required, for test purposes: Output of the received data
Serial.print("Return Value: ");
Serial.println(do_data); //print the data.
Serial.println(); //this just makes the output easier to read by adding an extra blank line
*/
}
}
/************************************************************************************/
/*** DO - Read DO-Sensor-Information ***/
void DO_information(void)
{
clear_screen();
Serial.println("DO - Reading Information");
Serial.println("(Cancel with key... )\n\n");
delay(2000);
while(1)
{
// Reading Sensor-Value
DO_send("i");
Serial.print("DO-Info: ");
Serial.println(do_data);
Serial.println();
delay(500);
if (Serial.available() > 0) // Waiting for keystroke
{
taste = Serial.read();
break;
}
}
}
/************************************************************************************/
/*** Get DO-Values ***/
void DO_mess(void)
{
clear_screen();
Serial.println("Read DO-Values");
Serial.println("(Cancel with key... )\n\n");
delay(2000);
while(1)
{
// Query of the sensor
DO_send("r");
Serial.print("DO-Value: ");
Serial.println(do_data);
Serial.println();
delay(1000);
if (Serial.available() > 0) // Waiting for keystroke
{
taste = Serial.read();
break;
}
}
}
/************************************************************************************/
/*** Choose unit for DO-Sensor-Values ***/
void DO_rueck(void)
{
char wahl;
while(1)
{
clear_screen();
Serial.println("Choose unit for DO-Sensor-Values\n");
Serial.println("Please choose:\n");
Serial.println(" 1 = Value in mg/l");
Serial.println(" 2 = Value in % saturation");
Serial.println(" 3 = Both units\n\n");
Serial.print("Your Choice: ");
// Wait for input
while (Serial.available() == 0);
// Evaluation of the input
wahl = Serial.read();
Serial.print(wahl);
// Evaluation of the input
switch (wahl)
{
case '1': DO_send("o,mg,1"); // mg/l enable
DO_send("o,%,0"); // % disable
break;
case '2': DO_send("o,mg,0"); // mg/l disable
DO_send("o,%,1"); // % enable
break;
case '3': DO_send("o,mg,1"); // mg/l enable
DO_send("o,%,1"); // % enable
break;
default: Serial.print("\n\nInvalid Choice. Please try again!");
delay(4000);
break;
}
if ((wahl == 0x31) || (wahl == 0x32) || (wahl == 0x33))
{
Serial.println("\n\nSet! Continue with any key ...");
while (Serial.available() == 0); // Waiting for keystroke
taste = Serial.read();
break;
}
}
}
/*****************************************************************************/
/*** DO - Calibration to O2 air value ***/
void DO_cal_luft(void)
{
while(1)
{
clear_screen();
Serial.println("DO - Calibration to O2 air value: 9,09 - 9,1 mg/l\n");
Serial.println("Dry sensor and hang vertically in air.\n");
Serial.println("Observe readings and if stable (no matter what value),");
Serial.println("Press the 'c' key ...\n");
Serial.println("Start the measurements now with a keystroke ...\n\n");
// Waiting for input
while (Serial.available() == 0);
// Evaluation of the input
taste = Serial.read();
// Continuous measurement
while(1)
{
// Query of the sensor
DO_send("r");
Serial.print("DO-Value: ");
Serial.println(do_data);
delay(1000);
if (Serial.available() > 0) // Waiting for keystroke
{
taste = Serial.read();
break;
}
}
// Call calibration
DO_send("cal");
Serial.println("\n\nWaiting ...");
delay(1300);
Serial.println("\nCalibration done! \n\n");
Serial.println("Exit with any key ... ");
// Continuous measurement
while(1)
{
// Reading the sensor
DO_send("r");
Serial.print("DO-Value: ");
Serial.println(do_data);
delay(1000);
if (Serial.available() > 0) // Waiting for keystroke
{
taste = Serial.read();
break;
}
}
// Return
return;
}
}
/************************************************************************************/
Figure 20: The Arduino sketch for operating the O2-sensor
Figure 21 shows the main menu of this sketch:
Figure 21: Main menu for sketch operating the O2-sensor
To calibrate the sensor, it should be suspended vertically and dry in the air. Then, you need to wait a few minutes before selecting menu item "4) Calibration to O2 air value: 9, 09 - 9, 1 mg/l" to start the recording of the measuring values. Once the value measured is stable (no matter which value is shown), you press the ‘c’ key. This value measured is now equivalent to the median oxygen level in air (9.09 mg/l); accordingly, the current value is recorded as 9.09 mg/l and thus stored in the sensor.
The sensor will now convert all values measured subsequently to that value.
The other menu items of Figure 21 are needed for the test operation of the sensor and can be used anytime.
After all these steps have been completed, the measuring station is ready for use within the Sigfox network.
The operating software for the monitoring stationThe entire operating software for the MKR FOX1200 has been created in C using the Arduino IDE.
Figure 22 shows the flowchart of the sketch:
*
Figure 22: Flowchart of the Arduino sketch
(see ´Enclosure - Missing figures´)
*
The Arduino sketch has been extensively documented and is provided in the Attachments; here, we would only like to explain a few core features.
Firstly, the following libraries have to be embedded in the IDE:
Arduino libraries:
- Arduino Low Power
- Arduino Sigfox for MKRFOX1200
- RTCZero
External libraries:
- BMx280MI
- GxEPD
- Adafruit Circuit Playground
- Adafruit GFX library
- Adafruit MAX31865 library
Then, the Arduino sketch starts with embedding the libraries, defining the specific variables for the individual system components and then also defining the global variables, Figure 23:
/*************************************************************************/
/* */
/* Sigfox-Challenge 2019 */
/* */
/* SmartLake */
/* */
/* An all-round and powerfull monitoring-system for the Green IoT */
/* */
/* Developed by: Frank Schleking, Philipp Krienke */
/* Filip Schmachtenberger, Bernd vom Berg */
/* */
/* University Georg Agricola, Bochum, Germany */
/* */
/* SF-Cha-5_en.ino Version 5.0, 12.10.2019 */
/* */
/*************************************************************************/
//Include the required standard libraries
#include <ArduinoLowPower.h> // Contains features for sleep and deep-sleep
#include <SigFox.h> // Contains the Sigfox functions
#include <SPI.h> // Library for SPI-BUS
#include <Wire.h> // I2C-Library
#include <Adafruit_MAX31865.h> // for the pt100 - MAX31865-Board
// MAX31865-Board (Pt100)____________________________________________________
// use hardware SPI, just pass in the CS pin (active low)
Adafruit_MAX31865 pt100_1 = Adafruit_MAX31865(A5); // for Pt100_1
Adafruit_MAX31865 pt100_2 = Adafruit_MAX31865(A6); // for Pt100_2
// The value of the Rref resistor. Use 430.0 for PT100
#define RREF 430.0
// The 'nominal' 0-degrees-C resistance of the sensor
// 100.0 for PT100
#define RNOMINAL 100.0
// E-PAPER-Display___________________________________________________________
//Include the e-paper display library and additional required libraries
#include <GxEPD.h>
//Include the Library for the selected Display: 2,9´´ black/white
#include <GxGDEH029A1/GxGDEH029A1.h> // for 2.9 inch s/w Display
//Include the Communication-Libraries for the Displays
#include <GxIO/GxIO_SPI/GxIO_SPI.h>
#include <GxIO/GxIO.h>
// Include fonts of the Adafruit-GFX Library
#include <Fonts/FreeMonoBold9pt7b.h>
#include <Fonts/FreeMonoBold12pt7b.h>
// Create an object of the GxIO-Class
// Defining the pins used for CS, DC, and Reset.
// The name of the object is "io"
GxIO_Class io(SPI, /*CS=*/ 4, /*DC=*/ 7, /*RST=*/ A1);
// Create an object of the GxEPD-Class an define the pins
// for Reset an Busy and the previously created object of the GxIO-class "io"
// The name of the object is "display"
GxEPD_Class display(io, /*RST=*/ A1, /*BUSY=*/ 5);
// BMP280-Ambient-Pressure-Sensor_____________________________________________________
// Include the BMP280-Library
#include <BMx280MI.h>
//The default I2C-Adress of the BMP280 is 0x76
#define I2C_ADDRESS 0x76
// Create a BMx280I2C object using the I2C interface with I2C Address 0x76
// The name of the object is "bmx280"
BMx280I2C bmx280(I2C_ADDRESS);
// Pt100 - MAX31865__________________________________________________________
#define Pt_address 97 //default I2C ID number for EZO DO Circuit.
//Global Variables___________________________________________________________
int versorgungs_pin = 0; // Port pin for switching the power supply for
// the ePaper display
// Variables for the measured values
float luftdruck = 0; // Air pressure from the BMP280
float temperatur = 0; // Temperature from the BMP280
float temp_pt100_1 = 0; // Pt100-value 1
float temp_pt100_2 = 0; // Pt100-value 2
char do_data[20]; // Data from the DO sensor as a string
float O2; // DO-value as a float
// General variables
unsigned char taste;
unsigned char first = 1;
unsigned long time_new, time_old; // For the time measurement between
// the transmissions
/***********************************************************/
/*** Waiting time for Sigfox transmission (in minutes) ***/
/***********************************************************/
unsigned int steps = 180; // Steps
signed int k = steps;
/* With a time delay of delay(10) in the loop-function
the following applies to the cycle time:
steps Time between two transmission
52 3:02 min
180 10:01 min
270 14:56 min
540 28:56 min
*/
Figure 23: Embedding the libraries and defining the specific variables
In the setup part of the sketch which now follows the individual assemblies are initialized, Figure 24:
void setup()
{
//Initialisiere the serial Interface
Serial.begin(9600);
//Initialize the I2C-Interface
Wire.begin();
//Declare power supply pin as output (MOSFETs Pin 1)
pinMode(versorgungs_pin, OUTPUT);
// Switch on the power supply for the Display
digitalWrite(versorgungs_pin, HIGH);
//Initialize the E-Paper-Display
display.init();
// Waiting for communication via serial monitor
delay(2000);
/********* Initialization BMP280 ******/
if (!bmx280.begin())
{
Serial.println("Initialization failed. Please check BMP280 and I2C-Adress");
while (1);
}
/******** Initialization for MAX31865 ******/
//set to 2WIRE or 4WIRE as necessary; here: 2WIRE
pt100_1.begin(MAX31865_2WIRE);
pt100_2.begin(MAX31865_2WIRE);
// Show start-up sequence on ePaper display
start_up();
//Creates the mask for the ePaper display with the measurement names and units
display_maske();
}
Figure 24: Setup part: initializing the individual assemblies
The main program (loop part of the sketch) carries out the measuring of the three values and displays them for checking on the ePaper display and via the serial monitor, Figure 25:
void loop()
{
// Required variables
unsigned int z;
// Start measurement of BMP280
bmx280.writeOversamplingPressure(BMx280MI::OSRS_P_x16);
bmx280.writeOversamplingTemperature(BMx280MI::OSRS_T_x16);
/***** Get measured values from BMP280 *****/
if (first)
{
luftdruck = bmx280.readPressure()/100; // Ignore the first value
first = 0;
}
delay(10);
temperatur = bmx280.readTemperature(); // Temperature reading
delay(10);
luftdruck = bmx280.readPressure()/100; // Air pressure reading
// From the Pt100 measuring point 1
temp_pt100_1=pt100_1.temperature(RNOMINAL, RREF);
delay(10);
// From the O2 sensor
DO_send("r");
// Convert do_data to float
O2 = (do_data[0]-0x30) + ((do_data[2]-0x30)/10.0) + ((do_data[3]-0x30)/100.0);
// Output measured value on serial monitor
Serial.print("O2-Water: ");
Serial.print(do_data);
Serial.println(" mg/l");
Serial.print("Temp.-Water: ");
Serial.print(temp_pt100_1,1);
Serial.println(" °C");
Serial.print("Temp.-Amb.: ");
Serial.print(temperatur,1);
Serial.println(" °C");
Serial.print("Press.-Amb.: ");
Serial.print(luftdruck,1);
Serial.println(" hPa\n");
// Output measured value on the ePaper-Display
display_messwerte(luftdruck, temperatur, temp_pt100_1, temp_pt100_2);
Figure 25: Recording the values measured and displaying them via the serial monitor and on the ePaper display
Then it will be checked whether the transmitting cycle time has already run out. If that is the case, the data measured will be sent to the Sigfox cloud via the Sigfox network (using the function send data(…)).
If the cycle time has not finished yet, the time counter (the steps variable) is raised by 1 and displayed on the ePaper display and the serial monitor. Then, the recording cycle of the values measured starts again, Figure 26:
// Send readings to the Sigfox cloud when the wait time has expired
if (k == steps)
{
daten_senden(O2, temp_pt100_1, temperatur);
display.update(); // refresh Display
k = -1;
}
// Waiting time between the transmissions
delay(10); // Wait 10 ms
k = k + 1;
z = steps-k;
Serial.println("Next transmission in: " + String(z) + " Steps\n");
/***** Step counter on the ePaper display *****/
// White rectangle for deleting the measured value field
display.fillRect(185, 40, 68, 15, GxEPD_WHITE);
// Output residual value
display.setCursor(185,51);
display.println(z);
// Partial refresh from the display: Range for the measured values
display.updateWindow(185, 40, 68, 15, true);
Figure 26: Checking the transmitting cycle time and the transmission of the Sigfox telegraph
A few functions written by our team complete the program for the MKR FOX1200.
The essential functions written by us are these:
- recording the measuring value of the oxygen sensor and
- transmitting data to the Sigfox cloud
Figure 27 shows the function of how the data of the oxygen sensor is retrieved (DO = Dissolved Oxygen) via the I2C bus:
// Read from the O2 sensor
DO_send("r");
// Convert do_data to float
O2 = (do_data[0]-0x30) + ((do_data[2]-0x30)/10.0) + ((do_data[3]-0x30)/100.0);
//
//
//And here the function:
//
//
/*** Read the oxygen sensor via I2C: Process and send the telegram ***/
void DO_send (char data[20])
{
unsigned char i = 0;
unsigned char code = 0;
unsigned char in_char = 0;
int time_;
// Set the waiting time for sensor response
if (data[0] == 'c' || data[0] == 'r')time_ = 575; //if a command has been sent to calibrate or take a reading we wait 575ms so that the circuit has time to take the reading.
else time_ = 300; //if any other command has been sent we wait only 300ms.
// !!!! Anders als im Original-Programm !!!!
// Start I2C communication
Wire.beginTransmission(Pt_address); //call the circuit by its ID number.
// Send: Command
Wire.write(data); //transmit the command that was sent through the serial port.
Wire.endTransmission(); //end the I2C data transmission.
// Receiving the answer if command is not equal to 'sleep'
if (strcmp(data, "sleep") != 0) //if the command that has been sent is NOT the sleep command, wait the correct amount of time and request data.
{ //if it is the sleep command, we do nothing. Issuing a sleep command and then requesting data will wake the D.O. circuit.
delay(time_); //wait the correct amount of time for the circuit to complete its instruction.
// Request of max. 20 bytes of data from the slave
Wire.requestFrom(Pt_address, 20, 1); //call the circuit and request 20 bytes (this is more than we need)
// Reading the first byte = status byte
code = Wire.read(); //the first byte is the response code, we read this separately.
/*
// If required, for test purposes: Evaluation of the status byte
switch (code) //switch case based on what the response code is.
{
case 1: //decimal 1.
Serial.println("Success"); //means the command was successful.
break; //exits the switch case.
case 2: //decimal 2.
Serial.println("Failed"); //means the command has failed.
break; //exits the switch case.
case 254: //decimal 254.
Serial.println("Pending"); //means the command has not yet been finished calculating.
break; //exits the switch case.
case 255: //decimal 255.
Serial.println("No Data"); //means there is no further data to send.
break; //exits the switch case.
}
*/
// Reading in and storing the remaining bytes in array do_data
while (Wire.available()) //are there bytes to receive.
{
in_char = Wire.read(); //receive a byte.
do_data[i] = in_char; //load this byte into our array.
i += 1; //incur the counter for the array element.
if (in_char == 0) //if we see that we have been sent a null command.
{
i = 0; //reset the counter i to 0.
Wire.endTransmission(); //end the I2C data transmission.
break; //exit the while loop.
}
}
/*
// If required, for test purposes: Output of the received data
Serial.print("Return Value: ");
Serial.println(do_data); //print the data.
Serial.println();
*/
}
Figure 27: Retrieving data from the oxygen sensor
Figure 28 illustrates the routine of transmitting data to the Sigfox back-end:
/*** Send data to the Sigfox cloud ***/
void daten_senden(float O2, float temp_water, float temp_amb)
{
/*** Determine and output time between two transmissions ***/
time_old = time_new;
time_new = millis();
Serial.print("Time between transmissions: ");
Serial.print((time_new-time_old)/60000.0);
Serial.println(" min.");
/*** transmissions begins ***/
Serial.println("\n TRANSMISSION !!\n");
// Initialize the Sigfox modem
SigFox.begin();
delay(100);
// Enable debug led and disable automatic deep sleep
SigFox.debug();
// Clears all pending interrupts
SigFox.status();
delay(1);
// The structure to be sent must be declared as "packed".
// This removes the padding bytes.
typedef struct __attribute__ ((packed)) sigfox_message {
float O2; // 4 Bytes
float Temp_Water; // 4 Bytes
float Temp_Amb; // 4 Bytes
} SigfoxMessage;
// Create the variable "reading" from the previously created structure type
SigfoxMessage reading;
// Write the measured values into the structure
reading.O2 = O2;
reading.Temp_Water = temp_water;
reading.Temp_Amb = temp_amb;
//Preparing to send a package
SigFox.beginPacket();
// Send the message to the Sigfox backend
SigFox.write((char*)&reading, sizeof(reading));
// If endPacket() returns 'true' then error message
int ret = SigFox.endPacket();
if (ret > 0)
{
Serial.println("Error: no transmission!");
}
else
{
Serial.println("TRANSMISSION OK !!\n\n");
}
//De-Initializes the Sigfox library and the module
SigFox.end();
}
Figure 28: Sending data to the Sigfox back-end
Here, the structure of the Sigfox payload requires special attention as only a maximum of 12 user data bytes are transmitted in the payload.
In our application, the payload is completely filled with the three float measuring values of 4 bytes each:
- 4 bytes for the oxygen value (O2)
- 4 bytes for the water temperature (temp_water)
- 4 bytes for the ambient temperature (temp_amb)
To enable a simple writing of these values into the payload, we have defined an own structure in C (called sigfox_message), added an own data type (called SigfoxMessage) and a suitable variable of this data type (called reading), as shown in Figure 29:
// The structure to be sent must be declared as "packed".
// This removes the padding bytes.
typedef struct __attribute__ ((packed)) sigfox_message {
float O2; // 4 Bytes
float Temp_Water; // 4 Bytes
float Temp_Amb; // 4 Bytes
} SigfoxMessage;
// Create the variable "reading" from the previously created structure type
SigfoxMessage reading;
// Write the measured values into the structure
reading.O2 = O2;
reading.Temp_Water = temp_water;
reading.Temp_Amb = temp_amb;
Figure 29: Self-defined structure, data type and variable plus how the values measured are written into this variable
Now, the three values measured can very easily be written into this variable.
Then, the variable itself is just transferred to the Sigfox “send” function which transmits the data:
// Send the message to the Sigfox backend
SigFox.write((char*)&reading, sizeof(reading));
Working with such C-structures means that the transmission of the Sigfox payload can easily be adapted to other applications: only the design of the structure needs to be modified (i.e. to adjust the configuration of the 12 payload bytes with regard to the application), the new measuring values have to be written into the variable and then passed on for transmission.
Registering the station with the Sigfox back-endFirst of all, the measuring station needs to be registered with the Sigfox back-end.
To do so, you need the internal ID and the PAC number of the Sigfox modem on the MKR FOX1200 board used.
To be able to read these data, we wrote again a little Arduino demo sketch (Sigfox-3_0_en.ino) as shown in Figure 30:
/*** Read out and display the configuration data of the Sigfox modem ***/
void sf_konfig_daten(void)
{
// Clear screen
clear_screen();
// Activate and initialize the Sigfox modem
if (!SigFox.begin())
{
Serial.println("Sigfox modem not found! - Continue with RESET!");
while (1); // In this case: infinite loop
}
else
{
Serial.println("Sigfox-Modem OK !\n");
}
//Read and save the firmware version, ID, PAC and Temp. of the modem
String version = SigFox.SigVersion();
String ID = SigFox.ID();
String PAC = SigFox.PAC();
float temp = SigFox.internalTemperature();
// Send modem information to the serial monitor
Serial.println("MKR FOX 1200 - read configuration data:\n");
Serial.println("Firmware-Version: " + version);
Serial.println("ID = " + ID);
Serial.println("PAC = " + PAC);
Serial.println("Int. Temperature = " + String(temp, 1) + " °C");
delay(100); //Wait 100ms until the serial transfer is finished
SigFox.end(); // send the modem into sleep
Serial.println("\n\nPlease press the button!");
while (Serial.available() == 0); // Waiting for keystroke
taste = Serial.read();
}
Figure 30: The Arduino sketch for the Sigfox demo operation - part: Read out and display the configuration data of the Sigfox modem
Load the sketch and select menu item "1) Read Sigfox configuration data” from the main menu, Figure 31:
Figure 31: Main menu of Sigfox demo sketch
Now the data needed are read from the Sigfox modem of the MKR FOX1200 board which need to be noted for later use.
Next, the MKR FOX1200 board can be registered with the Sigfox backend using the address below:
https://buy.sigfox.com/activate
There, you need to carry out all steps necessary.
Now you can log in yourself as a user to the Sigfox backend providing your e-mail address and a password:
https://backend.sigfox.com/auth/login
Click the tab “Device” on the Sigfox portal to go to the device overview; there you will see that your MKR FOX1200 board has already been registered as shown in Figure 32:
Figure 32: Sigfox device overview
Everything looks fine and you can leave the Sigfox back-end.
Registering with thinger.ioWe operate our measuring stations via Sigfox network in conjunction with the freeware dashboard program thinger.io (www.thinger.io).
thinger.io is an open-source IoT visualization platform which allows to create quickly and simply clear and illustrative data visualizations. For smaller projects, this software is free of charge.
To develop an individual dashboard for your own PC, laptop or smart phone just a few more simple steps are needed:
- create a free user account with thinger.io
- set up a data bucket with thinger.io which receives the measured values sent by the Sigfox backend
- define the token, i.e. the access point for the backend of thinger.io and set up an authentication for the access token at thinger.io which then authorizes the Sigfox backend to transfer data to thinger.io.
- configure a callback on the Sigfox backend to transfer the data from the Sigfox backend to your data bucket in thinger.io via the internet.
- design a pretty dashboard on the thinger.io site to visualize your data.
Now, go to thinger.io using this URL:
The pictures shown in Figure 33 below explain how to:
- register with thinger.io
- set up a data bucket and
- create a proper access point (token) with thinger.io
Figure 33: Registration and set up with thinger.io
If you finished those steps, the first part of your thinger.io work is done.
Configuration of the Sigfox call-back on the Sigfox backend
Configuration of the Sigfox call-back on the Sigfox backendAs soon as the MKR FOX1200 sends data to the Sigfox backend, these data are to be transmitted automatically to thinger.io for further processing (display). For that purpose, the Sigfox backend provides callbacks.
To create a pertinent callback, start the Sigfox backend.
The pictures shown in Figure 34 below illustrate in detail the configuration of the callback needed:
Figure 34: Creating a callback on the Sigfox backend
And now you have to fill in the fields of callback page very carefully, Figure 35:
Figure 35: Fill in the callback fields
You have to fill in:
- Type: ´DATA´ and ´UPLINK´
- Channel: ´URL´
- Custom payload config: here you put the names of the variables to transfer and their data types. Fill in exactly: O2::float:32:little-endian Temp_Water::float:32:little-endian Temp_Amb::float:32:little-endian
- Url pattern: here you fill in the URL of your data bucket on thinger.io. Fill in: https://api.thinger.io/v1/users/XXXXX/buckets/ZZZZZ/data with: XXXXX: your user id of your tinger.io account and ZZZZZ: your data bucket id in thinger.io
- Use HTTP Method: ´POST´
- Headers: ´Authorization´ and then: Bearer yJhbGciOiJIUzI1........... with yJhbGciOiJIUzI1........... your specific ´Access Token´ from thinger.io
- Content type: ´application/json´
- Body: here you have to put the data to be transfered in the json-format:
{
"Device-ID" : "{device}",
"O2" : {customData#O2},
"Temp_Water" : {customData#Temp_Water},
"Temp_Amb" : {customData#Temp_Amb}
}
After fill in the fields click the ´Ok´ button.
Now this callback is used to send the measuring data to thinger.io as soon as they have been received by the Sigfox backend.
At thinger.io, they will then be displayed on a suitable dashboard.
Developing up a dashboard on thinger.ioA Dashboard on thinger.io can easily be designed with just a few mouse clicks. Especially for the initial application, the user does not need any programming skills: everything is done purely graphically and by filling the appropriate configuration boxes!
The information buttons (i) provided can be clicked anytime to receive useful tips on how to fill in the boxes.
Figure 36 shows the individual steps of how to set up dashboard which is still empty:
Figure 36: Setting up an empty dashboard
Once the empty dashboard has been set up, suitable display and presentation elements (called widgets) as needed by the user are selected, configured and placed on the dashboard.This process is also completed with a few mouse clicks.
For our three measuring values, we only need two types of display elements:
- a time series chart widget which show the timeline of the values measured
- a number display of the last value measured (Text/Value widget)
The pictures in Figure 37 and Figure 38 show how these widgets are used on the dashboard:
Figure 37: Using the ´Time Series Chart´ - widget on the dashboard
Figure 38: Using the ´Text/Value´ - widget on the dashboard
To save your dashboard switch off and on the edit button.
Repeat all this steps for the other measured values ´Temperature of the water´ and ´Temperature of the ambient´.
Now your project is finished: the measured values, send via the Sigfox backend to thinger.io, will display on your owndashboard!
Publishing the dashboardNow you can publish your dashboard worldwide via the internet:
Figure 39: Publishing the dashboard
Results of our SmartLake ProjectFigure 40 shows how the measuring station is used at a municipal lake:
Figure 40: Measuring station used at a municipal lake
Figure 41 shows the dashboard on the user’s PC and the dashboard on his smartphone:
Figure 41: The dashboards
The people responsible at the respective authorities of the city receive information on the condition of the lake around the clock and can respond accordingly and in due time (providing freshwater inflow or oxygen) before critical situations may occur endangering the animals and plants living in the lake.
Now, a huge step has been taken to protect and preserve the lake.
You can find the dashboard of this project under: https://bit.ly/2P41j1V
ConclusionOur planet is our home, our only home. Where should we go if we destroy it?
The Dalai Lama, 2004
By using the global Sigfox network and applying our universal and flexible monitoring measuring stations we are able to make a significant contribution to protecting the habitats of animals, plants and people.
The entire mobile and independent system can easily be enlarged and extended for new and different application purposes:
- use of sensors to measure different values
- configuration of alarm emails if preset limit values are exceeded
- simple use of several measuring stations at one site to record additional measuring data: such additional stations can be integrated into the Sigfox network at just a few mouse clicks, and the pertinent dashboards can also be set up quickly
- cross-border monitoring as the Sigfox network is being developed further in many countries
- …
In other words, the technical conditions and solutions to protect our environment do exist right here, right now.
Now, it is up to the decision-makers in politics and society to take serious and sustainable action!
Comments