Commit f24d2814 authored by Melroy van den Berg's avatar Melroy van den Berg

Introduced controller.cc

parent 23488748
Pipeline #1067 passed with stages
in 27 seconds
......@@ -36,7 +36,7 @@ add_library(mh_z19b_lib src/mh_z19b.cc)
add_library(pmsa003_lib src/pmsa003.cc)
add_library(led_lib src/led.cc)
add_executable(quality_meter src/main.cc)
add_executable(quality_meter src/main.cc src/controller.cc)
# Key idea: SEPARATE OUT your main() function into its own file so it can be its
# own executable. Separating out main() means you can add this library to be
# used elsewhere.
......
......@@ -35,7 +35,6 @@ Storing the time series data can be done with InfluxDB or any other database (th
For visualization the data (graphs) itself we use Grafana.
## Development
### Requirements
......
......@@ -6,7 +6,44 @@
],
"settings": {
"files.associations": {
"cmath": "cpp"
"cmath": "cpp",
"exception": "cpp",
"stdexcept": "cpp",
"iostream": "cpp",
"array": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cstdarg": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"string_view": "cpp",
"fstream": "cpp",
"functional": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"limits": "cpp",
"new": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"streambuf": "cpp",
"system_error": "cpp",
"cinttypes": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"utility": "cpp",
"optional": "cpp",
"algorithm": "cpp"
}
}
}
\ No newline at end of file
/**
* Copyright (c) 2019 AIR Quality developers
*
* \file controller.h
* \brief Class for Controller
* \author Melroy van den Berg <webmaster1989@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <stdio.h>
#include <exception>
#include <stdexcept>
#include <iostream>
#include "sgp30.h"
#include "bme280.h"
#include "pmsa003.h"
#include "mh_z19b.h"
#include "weather_env.h"
// Use I2C-2
#define I2C_PATH_LEN 20 /**< max length of /dev/i2c-N */
#define I2C2_BUS 2 /**< Connected to /dev/i2c-2 */
/**
* \class Controller
* \brief This class will be take care of controlling the sensors
*/
class Controller
{
public:
Controller();
~Controller();
void Loop();
unsigned long int Start();
private:
int i2c_fd;
uint32_t absoluteHumidity; // Store temp. absolute humidity (used by gas sensor later on)
int OpenI2C();
bool DoGasMeasurement();
bool DoDustMeasurement();
bool DoCO2Measurement();
bool DoTempMeasurement();
};
#endif
\ No newline at end of file
......@@ -25,6 +25,8 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <exception>
#include <stdexcept>
// Constants
#define BRIGHTNESS_LED0 "/sys/devices/platform/leds/leds/beaglebone:green:usr0/brightness" /**< Brightness for LED #1 */
......
/**
* Copyright (c) 2019 AIR Quality developers
*
* \file controller.cc
* \brief Class for controlling the sensors
* \author Melroy van den Berg <webmaster1989@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "controller.h"
/**
* \brief Constructor
*/
Controller::Controller() {
this->absoluteHumidity = 0;
this->i2c_fd = OpenI2C();
// TODO: Init all other sensors as well during constructor
}
/**
* \brief Destructor close the I2C file descriptor
*/
Controller::~Controller() {
if(this->i2c_fd != -1) {
close(this->i2c_fd);
}
}
/**
* \brief Main loop of the application
*/
void Controller::Loop() {
// TODO: Main loop, which will wait until all sensors are 'stable'
// TODO: Fetch all sensor data in regular intervals
// TOOD: Send to InfluxDB database (query)
//DoDustMeasurement();
//DoCO2Measurement();
DoTempMeasurement();
DoGasMeasurement();
}
/**
* \brief Make I2C connection
* \return file description for the new file
*/
int Controller::OpenI2C() {
int i2c_fd;
// Open the I2C device file
char device[I2C_PATH_LEN];
sprintf(device, "/dev/i2c-%d", I2C2_BUS);
if ((i2c_fd = open(device, O_RDWR, 0)) < 0) {
throw std::runtime_error("Could not open I2C bus #" + std::to_string(I2C2_BUS));
}
return i2c_fd;
}
/**
* \brief Execute gas measurement using SGP30
* \return true if everything went successfully
*/
bool Controller::DoGasMeasurement() {
SGP30 sgp(this->i2c_fd);
// Select the sensor on the bus
if(!sgp.Select()) {
printf("Error: could not select the gas sensor!\n");
return false;
}
// Init the sensor
if(!sgp.Init()) {
printf("Error: Could not init the gas sensor!\n");
return false;
}
// Set humidity compensation value
sgp.SetHumidity(this->absoluteHumidity);
// TODO: first 15 seconds after init, returns fixed values (so should be ignored)
// Also: According to the specs we need to sent regular internals of 1s to ensure proper dynamic baseline compensation algo.
// This time interval needs to be done forever.
int counter = 0;
while(counter < 40) {
// Do measurement
if(!sgp.DoMeasurement()) {
printf("Error: Air quality measurement failed.");
return false;
}
printf("TVOC : %dppb\n", sgp.TVOC);
printf("eCO2 : %dppm\n", sgp.eCO2);
// Sleep 1 second
// TODO: embedded a delay within DoMeasurement()?
usleep(1000000);
counter++;
}
return true;
}
/**
* \brief Execute fine dust measurement using PMSA003
* \return true if everything went successfully
*/
bool Controller::DoDustMeasurement() {
PMSA003 pmsa;
if(!pmsa.Init()) {
printf("Error: Init of PMSA003 failed!\n");
return false;
}
int counter = 0;
while(counter < 10) {
if(pmsa.DoMeasurement()) {
printf("%.3f μ g/m3 PM1.0\n", pmsa.pm1p0);
printf("%.3f μ g/m3 PM2.5\n", pmsa.pm2p5);
printf("%.3f μ g/m3 PM10\n\n", pmsa.pm10);
}
// Sleep 1 second
// TODO: embedded a delay within DoMeasurement()?
usleep(1000000);
counter++;
}
return true;
}
/**
* \brief Execute CO2 concentration measurement using MH-Z19B
* \return true if everything went successfully
*/
bool Controller::DoCO2Measurement() {
MHZ19B mhz19b;
int counter = 0;
while(counter < 10) {
if(mhz19b.DoMeasurement()) {
printf("%d ppm CO2\n", mhz19b.CO2);
printf("%d degrees C\n\n", mhz19b.temperature);
}
// Sleep 1 second
// TODO: embedded a delay within DoMeasurement()?
usleep(1000000);
counter++;
}
return true;
}
/**
* \brief Execute temperature, rel. humidity & pressure measurement using BME280
* \return true if everything went successfully
*/
bool Controller::DoTempMeasurement() {
// The OSR settings (force mode, host in control so no standby settings needed)
uint8_t settings_sel = BME280_OSR_PRESS_SEL | BME280_OSR_TEMP_SEL | BME280_OSR_HUM_SEL | BME280_FILTER_SEL;
bme280_settings settings;
// Set indoor settings
settings.osr_h = BME280_OVERSAMPLING_1X;
settings.osr_p = BME280_OVERSAMPLING_16X;
settings.osr_t = BME280_OVERSAMPLING_2X;
settings.filter = BME280_FILTER_COEFF_16;
BME280 bme(this->i2c_fd, &settings);
// Select sensor on the bus
if(!bme.Select()) {
printf("Error: could not select the temp/humid/pressure sensor!\n");
return false;
}
if(!bme.Init()) {
printf("Error: BME280 init failed!\n");
return false;
}
if(!bme.SetSensorSettings(settings_sel)) {
printf("Error: Set sensor settings failed!\n");
return false;
}
int counter = 0;
while(counter < 60) {
bme.SetSensorMode(BME280_FORCED_MODE);
// 1/60Hz = 60000 ms
// 25Hz = 40ms (use 45ms on the safe side)
usleep(45000);
bme280_data data = bme.getAllSensorData();
printf("%.2f °C\n", data.temperature);
printf("%.2f %%\n", data.humidity);
printf("%.2f hPa\n", data.pressure);
// Absolute humidity in (mg/m3)
this->absoluteHumidity = static_cast<uint32_t>(1000.0f * WeatherEnvironment::GetAbsoluteHumidity(data.temperature, data.humidity));
counter++;
}
return true;
}
......@@ -31,7 +31,7 @@ LED::LED() {
file_led4 = NULL;
if(!OpenLeds()) {
printf("Failed to open LED device files.\n");
throw std::runtime_error("Failed to open LED linux tree device files");
} else {
InitSequence();
}
......
......@@ -2,7 +2,7 @@
* Copyright (c) 2019 AIR Quality developers
*
* \file main.cc
* \brief Main, where everything comes together
* \brief Main, where it all starts
* \author Melroy van den Berg <webmaster1989@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
......@@ -18,197 +18,33 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sgp30.h"
#include "bme280.h"
#include "pmsa003.h"
#include "mh_z19b.h"
#include "weather_env.h"
#include "led.h"
#include <stdio.h>
// Use I2C-2
#define I2C_PATH_LEN 20 /**< max length of /dev/i2c-N */
#define I2C2_BUS 2 /**< Connected to /dev/i2c-2 */
// Hack solution, global
uint32_t absoluteHumidity = {0};
/**
* \brief Execute gas measurement using SGP30
* \param i2c_fd I2C bus file descriptor
* \return true if everything went successfully
*/
bool DoGasMeasurement(int i2c_fd) {
SGP30 sgp(i2c_fd);
// Select the sensor on the bus
if(!sgp.Select()) {
printf("Error: could not select the gas sensor!\n");
return false;
}
// Init the sensor
if(!sgp.Init()) {
printf("Error: Could not init the gas sensor!\n");
return false;
}
// Set humidity compensation value
sgp.SetHumidity(absoluteHumidity);
// TODO: first 15 seconds after init, returns fixed values (so should be ignored)
// Also: According to the specs we need to sent regular internals of 1s to ensure proper dynamic baseline compensation algo.
// This time interval needs to be done forever.
int counter = 0;
while(counter < 40) {
// Do measurement
if(!sgp.DoMeasurement()) {
printf("Error: Air quality measurement failed.");
return false;
}
printf("TVOC : %dppb\n", sgp.TVOC);
printf("eCO2 : %dppm\n", sgp.eCO2);
// Sleep 1 second
// TODO: embedded a delay within DoMeasurement()?
usleep(1000000);
counter++;
}
return true;
}
#include <iostream>
/**
* \brief Execute fine dust measurement using PMSA003
* \return true if everything went successfully
*/
bool DoDustMeasurement() {
PMSA003 pmsa;
if(!pmsa.Init()) {
printf("Error: Init of PMSA003 failed!\n");
return false;
}
int counter = 0;
while(counter < 10) {
if(pmsa.DoMeasurement()) {
printf("%.3f μ g/m3 PM1.0\n", pmsa.pm1p0);
printf("%.3f μ g/m3 PM2.5\n", pmsa.pm2p5);
printf("%.3f μ g/m3 PM10\n\n", pmsa.pm10);
}
// Sleep 1 second
// TODO: embedded a delay within DoMeasurement()?
usleep(1000000);
counter++;
}
return true;
}
/**
* \brief Execute CO2 concentration measurement using MH-Z19B
* \return true if everything went successfully
*/
bool DoCO2Measurement() {
MHZ19B mhz19b;
int counter = 0;
while(counter < 10) {
if(mhz19b.DoMeasurement()) {
printf("%d ppm CO2\n", mhz19b.CO2);
printf("%d degrees C\n\n", mhz19b.temperature);
}
#include "led.h"
#include "controller.h"
// Sleep 1 second
// TODO: embedded a delay within DoMeasurement()?
usleep(1000000);
counter++;
}
return true;
}
/**
* \brief Execute temperature, rel. humidity & pressure measurement using BME280
* \param i2c_fd I2C bus file descriptor
* \return true if everything went successfully
* \brief Main entry point
* \return status code
*/
bool DoTempMeasurement(int i2c_fd) {
// The OSR settings (force mode, host in control so no standby settings needed)
uint8_t settings_sel = BME280_OSR_PRESS_SEL | BME280_OSR_TEMP_SEL | BME280_OSR_HUM_SEL | BME280_FILTER_SEL;
bme280_settings settings;
// Set indoor settings
settings.osr_h = BME280_OVERSAMPLING_1X;
settings.osr_p = BME280_OVERSAMPLING_16X;
settings.osr_t = BME280_OVERSAMPLING_2X;
settings.filter = BME280_FILTER_COEFF_16;
BME280 bme(i2c_fd, &settings);
// Select sensor on the bus
if(!bme.Select()) {
printf("Error: could not select the temp/humid/pressure sensor!\n");
return false;
}
if(!bme.Init()) {
printf("Error: BME280 init failed!\n");
return false;
}
if(!bme.SetSensorSettings(settings_sel)) {
printf("Error: Set sensor settings failed!\n");
return false;
int main() {
try {
LED led; // default init seq
} catch (...) {
// Ignore :)
}
int counter = 0;
while(counter < 60) {
bme.SetSensorMode(BME280_FORCED_MODE);
// 1/60Hz = 60000 ms
// 25Hz = 40ms (use 45ms on the safe side)
usleep(45000);
bme280_data data = bme.getAllSensorData();
printf("%.2f °C\n", data.temperature);
printf("%.2f %%\n", data.humidity);
printf("%.2f hPa\n", data.pressure);
// Absolute humidity in (mg/m3)
absoluteHumidity = static_cast<uint32_t>(1000.0f * WeatherEnvironment::GetAbsoluteHumidity(data.temperature, data.humidity));
counter++;
try {
Controller control;
control.Loop();
}
return true;
}
/**
* \brief Make I2C connection
* \return file description for the new file
*/
int OpenI2C() {
int i2c_fd;
// Open the I2C device file
char device[I2C_PATH_LEN];
sprintf(device, "/dev/i2c-%d", I2C2_BUS);
if ((i2c_fd = open(device, O_RDWR, 0)) < 0) {
printf("FATAL: Could not open I2C bus %d\n", I2C2_BUS);
catch(const std::exception &exc) {
std::cerr << "Main loop error caught: " << exc.what() << std::endl;
exit(1);
// TODO: Retry?
}
return i2c_fd;
}
/**
* \brief Main entry point
* \return status code
*/
int main() {
LED led; // default init seq
//DoDustMeasurement();
//DoCO2Measurement();
int i2c_fd = OpenI2C();
DoTempMeasurement(i2c_fd);
DoGasMeasurement(i2c_fd);
// Close the I2C file descriptor
close(i2c_fd);
return 0;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment