From 6ba5fcdd902a6bb27973bb19139fb901fde69306 Mon Sep 17 00:00:00 2001 From: Zanshin Date: Sun, 5 Jul 2020 11:46:19 +0200 Subject: [PATCH] Issue #25 Changed source to adhere to "clang-format" with Google standards --- .../ESP32FeatherWiFiDemo.ino | 387 +++++---- examples/I2CDemo/I2CDemo.ino | 220 ++--- examples/SDLoggerSPIDemo/SDLoggerSPIDemo.ino | 1 + examples/SPIDemo/SPIDemo.ino | 228 ++--- examples/SoftSPIDemo/SoftSPIDemo.ino | 256 +++--- .../TwoDevicesI2CDemo/TwoDevicesI2CDemo.ino | 207 ++--- src/Zanshin_BME680.cpp | 788 +++++++++--------- 7 files changed, 1048 insertions(+), 1039 deletions(-) diff --git a/examples/ESP32FeatherWiFiDemo/ESP32FeatherWiFiDemo.ino b/examples/ESP32FeatherWiFiDemo/ESP32FeatherWiFiDemo.ino index 05b04ad..f678fb1 100644 --- a/examples/ESP32FeatherWiFiDemo/ESP32FeatherWiFiDemo.ino +++ b/examples/ESP32FeatherWiFiDemo/ESP32FeatherWiFiDemo.ino @@ -2,54 +2,58 @@ @section ESP32FeatherWiFiDemo_intro_section Description -Example program for using an Arduino ESP32 based system (this sketch was developed and tested on a ESP32 Huzzah32 -Feather board from https://www.adafruit.com/product/3405) along with a BME680 connected via I2C to monitor the -temperature, pressure and humidity and report the values in a dynamic chart on a web page hosted by the ESP32 -and connected to a local network. +Example program for using an Arduino ESP32 based system (this sketch was developed and tested on a +ESP32 Huzzah32 Feather board from https://www.adafruit.com/product/3405) along with a BME680 +connected via I2C to monitor the temperature, pressure and humidity and report the values in a +dynamic chart on a web page hosted by the ESP32 and connected to a local network. -Prior to compiling the program, the contents of the include file "Authentication.h" need to be updated to reflect -the local WiFi network to use and the corresponding authentication code. +Prior to compiling the program, the contents of the include file "Authentication.h" need to be +updated to reflect the local WiFi network to use and the corresponding authentication code. -Once started, the IP-Address is set by the WiFi router and displayed on the serial output of the ESP32, this IP -address should then be entered as the URL in a web browser of a computer attached to the same network and the -data should be presented there, updated every 10 seconds. +Once started, the IP-Address is set by the WiFi router and displayed on the serial output of the +ESP32, this IP address should then be entered as the URL in a web browser of a computer attached to +the same network and the data should be presented there, updated every 10 seconds. The Bosch BME680 sensor measures temperature, pressure, humidity and air quality and is described at -https://www.bosch-sensortec.com/bst/products/all_products/BME680. The datasheet is available from Bosch at -https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680_DS001-11.pdf \n\n +https://www.bosch-sensortec.com/bst/products/all_products/BME680. The datasheet is available from +Bosch at https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680_DS001-11.pdf \n\n -The most recent version of the BME680 library is available at https://github.com/SV-Zanshin/BME680 and the -documentation of the library as well as example programs are described in the project's wiki pages located at -https://github.com/SV-Zanshin/BME680/wiki. \n\n +The most recent version of the BME680 library is available at https://github.com/SV-Zanshin/BME680 +and the documentation of the library as well as example programs are described in the project's wiki +pages located at https://github.com/SV-Zanshin/BME680/wiki. \n\n -The BME680 is an extremely small physical package that is so tiny as to be impossible to solder at home, hence it -will be used as part of a third-party breakout board. There are several such boards available at this time, for -example \n -Company | Link +The BME680 is an extremely small physical package that is so tiny as to be impossible to solder at +home, hence it will be used as part of a third-party breakout board. There are several such boards +available at this time, for example \n Company | Link ------- | ---------- Sparkfun | https://www.sparkfun.com/products/14570 BlueDot | https://www.bluedot.space/sensor-boards/bme680/ -Adafruit | https://learn.adafruit.com/adafruit-BME680-humidity-barometric-pressure-temperature-sensor-breakout \n\n +Adafruit | +https://learn.adafruit.com/adafruit-BME680-humidity-barometric-pressure-temperature-sensor-breakout +\n\n -Bosch supplies sample software that runs on various platforms, including the Arduino family; this can be downloaed -at https://github.com/BoschSensortec/BSEC-Arduino-library . This software is part of the Bosch "BSEC" (Bosch -Sensortec Environmental Cluster) framework and somewhat bulky and unwieldy for typical Arduino applications, hence -the choice to make a more compact and rather less abstract library. +Bosch supplies sample software that runs on various platforms, including the Arduino family; this +can be downloaed at https://github.com/BoschSensortec/BSEC-Arduino-library . This software is part +of the Bosch "BSEC" (Bosch Sensortec Environmental Cluster) framework and somewhat bulky and +unwieldy for typical Arduino applications, hence the choice to make a more compact and rather less +abstract library. -The pressure reading needs to be adjusted for altitude to get the adjusted pressure reading. There are numerous -sources on the internet for formulae converting from standard sea-level pressure to altitude, see the data sheet -for the BME180 on page 16 of http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf. Rather than put a -floating-point function in the library which may not be used but which would use space, an example altitude +The pressure reading needs to be adjusted for altitude to get the adjusted pressure reading. There +are numerous sources on the internet for formulae converting from standard sea-level pressure to +altitude, see the data sheet for the BME180 on page 16 of +http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf. Rather than put a floating-point +function in the library which may not be used but which would use space, an example altitude computation function has been added to this example program to show how it might be done. @section ESP32FeatherWiFiDemolicense License -This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License -along with this program. If not, see . +This program is free software: you can redistribute it and/or modify it under the terms of the GNU +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 General Public License for more details. You should have +received a copy of the GNU General Public License along with this program. If not, see +. @section ESP32FeatherWiFiDemoauthor Author @@ -57,248 +61,229 @@ Written by https://github.com/SV-Zanshin @section ESP32FeatherWiFiDemoversions Changelog -Version | Date | Developer | Comments -------- | ---------- | ----------------------------- | ------------------------------------------- -1.0.0 | 2020-06-02 | https://github.com/SV-Zanshin | Ready to commit and publish as Issue #20 -1.0.0b | 2020-05-30 | https://github.com/SV-Zanshin | Initial coding +Version | Date | Developer | Comments +------- | ---------- | ---------- | --------------------------------------------------------------- +1.0.1 | 2020-07-04 | SV-Zanshin | Issue #25 - implement clang-formatting +1.0.0 | 2020-06-02 | SV-Zanshin | Ready to commit and publish as Issue #20 +1.0.0b | 2020-05-30 | SV-Zanshin | Initial coding */ #if !defined(ESP32) - #error This example program is designed specifically for the ESP32 platform and might not work on other platforms +#error This program is designed for the ESP32 platform and might not work on other platforms #endif -/******************************************************************************************************************* -** Declare all include files required ** -*******************************************************************************************************************/ -#include ///< Include the SPI standard library -#include ///< Include the SD Card standard library -#include ///< ESP32 WiFi Library -#include ///< WiFi Client library -#include ///< WiFi Web Server library -#include "WebPageContents.h" ///< Include external HTML and JavaScript definitions -#include "Zanshin_BME680.h" ///< The BME680 sensor library +/************************************************************************************************** +** Declare all include files required ** +**************************************************************************************************/ +#include ///< Include the SD Card standard library +#include ///< Include the SPI standard library +#include ///< WiFi Web Server library +#include ///< ESP32 WiFi Library +#include ///< WiFi Client library -/******************************************************************************************************************* -** Declare all program constants ** -*******************************************************************************************************************/ +#include "WebPageContents.h" ///< Include external HTML and JavaScript definitions +#include "Zanshin_BME680.h" ///< The BME680 sensor library - /************************************************************************* - ** Please change the "WIFI_SSID", "WIFI_PASSWORD", "SD_CARD_SPI_CS_PIN" ** - ** and "SD_CARD_SPI_CD_PIN" constants to match your installation ** - *************************************************************************/ +/************************************************************************************************** +** Declare all program constants ** +**************************************************************************************************/ +/************************************************************************* +** Please change the "WIFI_SSID", "WIFI_PASSWORD", "SD_CARD_SPI_CS_PIN" ** +** and "SD_CARD_SPI_CD_PIN" constants to match your installation ** +*************************************************************************/ +const char* FILE_NAME = "/BME_680.csv"; ///< Filename on SD-Card +const char* HOSTNAME = "BME680"; ///< Give the device a name +const char* WIFI_SSID = "NetworkSSID"; ///< Network SSID for connection +const char* WIFI_PASSWORD = "Password"; ///< Network authentication code +const uint32_t SERIAL_SPEED = 115200; ///< Set the baud rate for Serial I/O +const uint8_t POWER_PIN = A13; ///< Supply voltage through a divider +const uint8_t LED_PIN = 13; ///< This pin is the on-board red LED +const uint8_t SD_CARD_SPI_CS_PIN = 21; ///< (general GPIO) on ESP32 for chip select +const uint8_t SD_CARD_SPI_CD_PIN = A5; ///< (general GPIO) on ESP32 for carrier detect +const uint32_t SD_LOG_INTERVAL = 1000; ///< Milliseconds between writes to SD-Card +const uint8_t SD_FLUSH_INTERVAL = 60; ///< do a "flush" after this number of writes +const float SEA_LEVEL_PRESSURE = 1013.25; ///< Standard atmosphere sea level pressure +/************************************************************************************************** +** Declare all global variables ** +**************************************************************************************************/ +WebServer server(80); ///< Instantiate a web server on port 80 +BME680_Class BME680; ///< Create an instance of the BME680 class +File dataFile; ///< Class for a SD-Card file +int32_t temperature; ///< BME680 temperature value +int32_t humidity; ///< BME680 humidity value +int32_t pressure; ///< BME680 pressure value +int32_t gas; ///< BME680 gas resistance value +int32_t start_pressure; ///< Initial pressure reading +bool sd_card_present = false; ///< Switch set when SD-Card detected +String jsonData; ///< JSON data string +uint16_t loopCounter = 0; ///< Counter for number of write operations since startup -const char* FILE_NAME = "/BME_680.csv"; ///< Filename on SD-Card -const char* HOSTNAME = "BME680"; ///< Give the device a name -const char* WIFI_SSID = "NetworkSSID"; ///< Network SSID for connection -const char* WIFI_PASSWORD = "Password"; ///< Network authentication code -const uint32_t SERIAL_SPEED = 115200; ///< Set the baud rate for Serial I/O -const uint8_t POWER_PIN = A13; ///< Supply voltage through a divider -const uint8_t LED_PIN = 13; ///< This pin is the on-board red LED -const uint8_t SD_CARD_SPI_CS_PIN = 21; ///< Use Pin 21 (general GPIO) on the ESP32 for chip select -const uint8_t SD_CARD_SPI_CD_PIN = A5; ///< Use Pin A5 (general GPIO) on the ESP32 for carrier detect -const uint32_t SD_LOG_INTERVAL = 1000; ///< Milliseconds between measurements to SD-Card -const uint8_t SD_FLUSH_INTERVAL = 60; ///< do a "flush" after this number of writes -const float SEA_LEVEL_PRESSURE = 1013.25; ///< Standard atmosphere sea level pressure -/******************************************************************************************************************* -** Declare all global variables ** -*******************************************************************************************************************/ -WebServer server(80); ///< Instantiate a web server on port 80 -BME680_Class BME680; ///< Create an instance of the BME680 class -File dataFile; ///< Class for a SD-Card file -int32_t temperature; ///< BME680 temperature value -int32_t humidity; ///< BME680 humidity value -int32_t pressure; ///< BME680 pressure value -int32_t gas; ///< BME680 gas resistance value -int32_t start_pressure; ///< Initial pressure reading -bool sd_card_present = false; ///< Switch set when SD-Card detected -String jsonData; ///< JSON data string -uint16_t loopCounter = 0; ///< Counter for number of write operations since startup +/************************************************************************************************** +** Macro that returns the floating point voltage. The formula is the analog value of POWER_PIN. ** +** since this goes through a voltage divider the value is multiplied by 2. The register is 12-bit** +** (0-4095) so we divide by 4095 and then by the 3.3V reference voltage and, finally, by the ADC ** +** reference voltage of 1.1V. ** +**************************************************************************************************/ +#define VOLTAGE (analogRead(POWER_PIN) * 2 / 4095.0 * 3.3 * 1.1) -/******************************************************************************************************************* -** Macro that returns the floating point voltage. The formula is the analog value of POWER_PIN. Since this goes ** -** through a voltage divider the value is multiplied by 2. The register is 12-bit (0-4095) so we divide by 4095 ** -** and then by the 3.3V reference voltage and, finally, by the ADC reference voltage of 1.1V. ** -*******************************************************************************************************************/ -#define VOLTAGE ( analogRead(POWER_PIN) * 2 / 4095.0 * 3.3 * 1.1) - -void setup() -{ +void setup() { /*! @brief Arduino method called once at startup to initialize the system - @details This is an Arduino IDE method which is called first upon boot or restart. It is only called one time - and then control goes to the main "loop()" method, from which control never returns. The BME680, then - the SD-Card (if present, if not present then it is ignored), then the Wi-Fi connection are initialized - and configured here. + @details This is an Arduino IDE method which is called first upon boot or restart. It is only + called one time and then control goes to the main "loop()" method, from which control + never returns. The BME680, then the SD-Card (if present, if not present then it is + ignored), then the Wi-Fi connection are initialized and configured here. @return void */ - pinMode(SD_CARD_SPI_CS_PIN, OUTPUT); // Chip-Select pin for the SD Card as output - pinMode(SD_CARD_SPI_CD_PIN, INPUT_PULLUP); // Carrier detect pin for the SD Card as input - digitalWrite(SD_CARD_SPI_CS_PIN, HIGH); // Deselect device by writing HIGH to it - pinMode(LED_PIN, OUTPUT); // Make the on-board LED an output pin - Serial.begin(SERIAL_SPEED); // Start the Serial comms at specified speed + pinMode(SD_CARD_SPI_CS_PIN, OUTPUT); // Chip-Select pin for the SD Card as output + pinMode(SD_CARD_SPI_CD_PIN, INPUT_PULLUP); // Carrier detect pin for the SD Card as input + digitalWrite(SD_CARD_SPI_CS_PIN, HIGH); // Deselect device by writing HIGH to it + pinMode(LED_PIN, OUTPUT); // Make the on-board LED an output pin + Serial.begin(SERIAL_SPEED); // Start the Serial comms at specified speed /********************** ** Initialize BME680 ** **********************/ Serial.print("Starting ESP32FeatherWiFiDemo for BME680\n"); Serial.print("- Initializing BME680 sensor\n"); - while (!BME680.begin(I2C_STANDARD_MODE)) // Start using I2C, use first device found - { + while (!BME680.begin(I2C_STANDARD_MODE)) { // Start using I2C, use first device found Serial.print("- Unable to find BME680. Trying again in 5 seconds.\n"); delay(5000); - } // of loop until device is located + } // of loop until device is located Serial.print("- Setting 16x oversampling for all sensors\n"); Serial.print("- Setting IIR filter to a value of 4 samples\n"); Serial.print("- Turning off gas measurements\n"); - BME680.setOversampling(TemperatureSensor, Oversample16); // Use enumerated type values to set value - BME680.setOversampling(HumiditySensor, Oversample16); // Use enumerated type values to set value - BME680.setOversampling(PressureSensor, Oversample16); // Use enumerated type values to set value - BME680.setIIRFilter(IIR4); // Use enumerated type values to set value - BME680.setGas(0, 0); // Setting either to 0 turns off gas + BME680.setOversampling(TemperatureSensor, Oversample16); + BME680.setOversampling(HumiditySensor, Oversample16); + BME680.setOversampling(PressureSensor, Oversample16); + BME680.setIIRFilter(IIR4); + BME680.setGas(0, 0); // Setting either to 0 turns off gas BME680.getSensorData(temperature, humidity, start_pressure, gas); // Get most recent readings /*************************************************************** ** Initialize SD-Card for logging, if not found then continue ** ***************************************************************/ Serial.print("- Checking to see if SD Card connected\n"); - if (!digitalRead(SD_CARD_SPI_CD_PIN)) - { - sd_card_present = false; // if pin is high then no card inserted - } - else - { - uint8_t loopCounter = 10; ///< countdown to 0 to detect car - while (--loopCounter && !SD.begin(SD_CARD_SPI_CS_PIN)) // Try to start card using SPI - { + if (!digitalRead(SD_CARD_SPI_CD_PIN)) { // if pin is high then no card is inserted + sd_card_present = false; + } else { + uint8_t loopCounter = 10; ///< countdown to 0 to detect card + while (--loopCounter && !SD.begin(SD_CARD_SPI_CS_PIN)) { // Try to start card using SPI Serial.print("- Unable to find SD Card. Trying again in 5 seconds.\n"); - for (uint8_t i = 0; i < 50; i++) - { + for (uint8_t i = 0; i < 50; i++) { digitalWrite(LED_PIN, !digitalRead(LED_PIN)); delay(100); - } // loop to toggle LED light 10 times - } // of loop until device is located + } // loop to toggle LED light 10 times + } // of loop until device is located if (loopCounter) sd_card_present = true; - } // if-then card is inserted - if (!sd_card_present) - { + } // if-then card is inserted + if (!sd_card_present) { Serial.print("- No SD-Card detected, continuing\n"); - } - else - { + } else { Serial.print("- SD-Card Initialized\n"); - dataFile = SD.open(FILE_NAME, FILE_WRITE); // Open the logfile for writing and position to end-of-file - if (!dataFile) - { + dataFile = SD.open(FILE_NAME, FILE_WRITE); // Open the logfile for writing and position to EOF + if (!dataFile) { Serial.print("Unable to open file \""); Serial.print(FILE_NAME); Serial.print("\" on SD-Card. Error. Skipping SD writes."); - sd_card_present = false; // turn off card - } - else - { + sd_card_present = false; // turn off card + } else { Serial.print("- File \""); Serial.print(FILE_NAME); Serial.print("\" successfully opened. Appending data.\n"); dataFile.print("Seconds,SupplyVoltage,Temperature,Humidity,Altitude\n"); - } // if-then-else the file could be opened - } // if-then-else SD-Card located + } // if-then-else the file could be opened + } // if-then-else SD-Card located /********************************************** ** Connect to the specified wireless network ** **********************************************/ Serial.print("- Connecting to Wireless network \""); Serial.print(WIFI_SSID); Serial.print("\"."); - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // Connect to the specified WiFi router - while (WiFi.status() != WL_CONNECTED) // Loop until the connection is established - { - delay(1000); // wait one second before trying again - Serial.print("."); - } // for-next connection not established - WiFi.setHostname(HOSTNAME); // Give our device a name + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // Connect to the specified WiFi router + while (WiFi.status() != WL_CONNECTED) { // Loop until the connection is established + delay(1000); // wait one second before trying again + Serial.print("."); + } // for-next connection not established + WiFi.setHostname(HOSTNAME); // Give our device a name // Show connection data Serial.print("\n- Open browser to IP address \""); Serial.print(WiFi.localIP()); Serial.print("\"\n- or to \"http://BME680\" to view data.\n"); - server.on("/", handleRoot); // Routine for root page call - server.on("/readADC", handleADC); // Page called by AJAX - server.begin(); // Start server + server.on("/", handleRoot); // Routine for root page call + server.on("/readADC", handleADC); // Page called by AJAX + server.begin(); // Start server Serial.print("HTTP server started\n"); - digitalWrite(LED_PIN, false); // Turn the LED off -} // of method "setup()" -void handleRoot() -{ + digitalWrite(LED_PIN, false); // Turn the LED off +} // of method "setup()" +void handleRoot() { /*! @brief Called when a request is sent from a browser client - @details When the ESP32 gets an IP address and that address is entered in a browser then this ISR get called to - handle that request. The response is to send the HTML page which is contained in the variable "MAIN_page" - which is set in the "WebPageContents.h" include file + @details When the ESP32 gets an IP address and that address is entered in a browser then this + ISR get called to handle that request. The response is to send the HTML page which is + contained in the variable "MAIN_page" which is set in the "WebPageContents.h" file @return void */ - String s = MAIN_page; // Read HTML contents into a string - server.send(200, "text/html", s); // Send the string as HTML to the requester -} // of method "handleRoot()" -void handleADC() -{ + String s = MAIN_page; // Read HTML contents into a string + server.send(200, "text/html", s); // Send the string as HTML to the requester +} // of method "handleRoot()" +void handleADC() { /*! @brief Called when a "readADC"" request is sent from a browser client - @details When the browser client sends a GET request for a "readADC" this ISR is called, which then returns - the current value in the jsonData string, which is continuously updated in the main loop + @details When the browser client sends a GET request for a "readADC" this ISR is called, which + then returns the current value in the jsonData string, which is continuously updated + in the main loop @return void */ - if (!sd_card_present) // If no SD-Card is present then we need to get data, - { // if there is an SD-Card then the data has already been fetched - getSensorData(); // get the BME680 data - } // if-then SD-Card not present - server.send(200, "text/plain", jsonData); // Send JSON to client ajax request -} // of method "handleADC()" -void loop() -{ + if (!sd_card_present) // If no SD-Card is present then we need to get data, + { // if there is an SD-Card then the data has already been fetched + getSensorData(); // get the BME680 data + } // if-then SD-Card not present + server.send(200, "text/plain", jsonData); // Send JSON to client ajax request +} // of method "handleADC()" +void loop() { /*! @brief Arduino method for the main program loop - @details This is the main program for the Arduino IDE, it is an infinite loop and keeps on repeating. - The timed measurements from the BME680 are handled here, while the actual web page serving and - responses are handled by the 2 ISRs - "handleADC()" and "handleRoot()". + @details This is the main program for the Arduino IDE, it is an infinite loop and keeps on + repeating. The timed measurements from the BME680 are handled here, while the actual + web page serving and responses are handled by the ISRs "handleADC()" and "handleRoot()" @return void */ - static uint32_t next_log_millis; ///< Millis() value for next SD-Card measurement time - server.handleClient(); // Handle client requests - if (millis() > next_log_millis) // if time to get another measurement from the BME680 - { // - next_log_millis = millis() + SD_LOG_INTERVAL; // set next time to get a reading - getSensorData(); // get the BME680 data - } // if-then time to get a measurement -} // of method "loop()" -void getSensorData() -{ + static uint32_t next_log_millis; ///< Millis() value for next SD-Card write time + server.handleClient(); // Handle client requests + if (millis() > next_log_millis) { // if it is time to get another measurement + next_log_millis = millis() + SD_LOG_INTERVAL; // set next time to get a reading + getSensorData(); // get the BME680 data + } // if-then time to get a measurement +} // of method "loop()" +void getSensorData() { /*! @brief Function to read the BME680 data and optionally write it to the SD-Card - @details The BME680 data is read in and if an SD-Card is present the values are written to it. The "jsonData" - string is also put together here so that a client request can immediately send the current data. This - is done because the frequency with which the data is written to the SD-Card is higher than that of the - web page refresh. + @details The BME680 data is read in and if an SD-Card is present the values are written to it. + The "jsonData" string is also put together here so that a client request can + immediately send the current data. This is done because the frequency with which the + data is written to the SD-Card is higher than that of the web page refresh. @return void */ - BME680.getSensorData(temperature, humidity, pressure, gas); // Get the most recent readings from the BME680 - float altitude = 44330.0 * (1.0 - pow(((float)pressure/100) / SEA_LEVEL_PRESSURE, 0.1903)); - if (sd_card_present) // Log data to the SD-Card, if present - { - dataFile.print(millis() / 1000); + BME680.getSensorData(temperature, humidity, pressure, gas); // Get readings + float altitude = 44330.0 * (1.0 - pow(((float)pressure / 100) / SEA_LEVEL_PRESSURE, 0.1903)); + if (sd_card_present) { // Log data to SD-Card, if present + dataFile.print(millis() / 1000); dataFile.print(","); dataFile.print(VOLTAGE); dataFile.print(","); - dataFile.print(temperature / 100.0, 2); + dataFile.print(temperature / 100.0, 2); dataFile.print(","); - dataFile.print(humidity / 1000.0, 2); + dataFile.print(humidity / 1000.0, 2); dataFile.print(","); dataFile.print(altitude, 2); dataFile.print("\n"); - if (++loopCounter % SD_FLUSH_INTERVAL == 0) // Flush every N-Iterations - { // - digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Toggle LED before flushing buffer - dataFile.flush(); // flush pending writes to SD - digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Toggle LED after flushing is complete + if (++loopCounter % SD_FLUSH_INTERVAL == 0) { // Flush every N-Iterations + digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Toggle LED before flushing buffer + dataFile.flush(); // flush pending writes to SD + digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Toggle LED after flushing buffer Serial.print("Flushed data to SD-Card\n"); - } // flush the buffer - } // if-then SD card is present + } // flush the buffer + } // if-then SD card is present jsonData = "{\"SupplyVoltage\":\"" + String(VOLTAGE) + "\", \"Temperature\":\"" + - String(temperature / 100.0) + "\", \"Humidity\":\"" + String(humidity / 1000.0) + - "\", \"Altitude\":\"" + String(altitude) + "\"}"; -} // of method "getSensorData()" + String(temperature / 100.0) + "\", \"Humidity\":\"" + String(humidity / 1000.0) + + "\", \"Altitude\":\"" + String(altitude) + "\"}"; +} // of method "getSensorData()" diff --git a/examples/I2CDemo/I2CDemo.ino b/examples/I2CDemo/I2CDemo.ino index c008f47..9bd276e 100644 --- a/examples/I2CDemo/I2CDemo.ino +++ b/examples/I2CDemo/I2CDemo.ino @@ -2,48 +2,54 @@ @section I2CDemo_intro_section Description -Example program for using I2C to set and read the Bosch BME680 sensor. The sensor measures temperature, pressure and -humidity and is described at https://www.bosch-sensortec.com/bst/products/all_products/BME680. The datasheet is -available from Bosch at https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680_DS001-11.pdf \n\n +Example program for using I2C to set and read the Bosch BME680 sensor. The sensor measures +temperature, pressure and humidity and is described at +https://www.bosch-sensortec.com/bst/products/all_products/BME680. The datasheet is available from +Bosch at https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680_DS001-11.pdf \n\n -The most recent version of the BME680 library is available at https://github.com/SV-Zanshin/BME680 and the -documentation of the library as well as example programs are described in the project's wiki pages located at -https://github.com/SV-Zanshin/BME680/wiki. \n\n +The most recent version of the BME680 library is available at https://github.com/SV-Zanshin/BME680 +and the documentation of the library as well as example programs are described in the project's wiki +pages located at https://github.com/SV-Zanshin/BME680/wiki. \n\n -The BME680 is an extremely small physical package that is so tiny as to be impossible to solder at home, hence it -will be used as part of a third-party breakout board. There are several such boards available at this time, for -example \n -Company | Link +The BME680 is an extremely small physical package that is so tiny as to be impossible to solder at +home, hence it will be used as part of a third-party breakout board. There are several such boards +available at this time, for example \n Company | Link ------- | ---------- Sparkfun | https://www.sparkfun.com/products/14570 BlueDot | https://www.bluedot.space/sensor-boards/bme680/ -Adafruit | https://learn.adafruit.com/adafruit-BME680-humidity-barometric-pressure-temperature-sensor-breakout \n\n +Adafruit | +https://learn.adafruit.com/adafruit-BME680-humidity-barometric-pressure-temperature-sensor-breakout +\n\n -Bosch supplies sample software that runs on various platforms, including the Arduino family; this can be downloaed -at https://github.com/BoschSensortec/BSEC-Arduino-library . This software is part of the Bosch "BSEC" (Bosch -Sensortec Environmental Cluster) framework and somewhat bulky and unwieldy for typical Arduino applications, hence -the choice to make a more compact and rather less abstract library. - -This example program initializes the BME680 to use I2C for communications. The library does not using floating -point numbers to save on memory space and computation time. The values for Temperature, Pressure and Humidity are -returned in deci-units, e.g. a Temperature reading of "2731" means "27.31" degrees Celsius. The display in the -example program uses floating point for demonstration purposes only. Note that the temperature reading is -generally higher than the ambient temperature due to die and PCB temperature and self-heating of the element.\n\n +Bosch supplies sample software that runs on various platforms, including the Arduino family; this +can be downloaed at https://github.com/BoschSensortec/BSEC-Arduino-library . This software is part +of the Bosch "BSEC" (Bosch Sensortec Environmental Cluster) framework and somewhat bulky and +unwieldy for typical Arduino applications, hence the choice to make a more compact and rather less +abstract library. -The pressure reading needs to be adjusted for altitude to get the adjusted pressure reading. There are numerous -sources on the internet for formulae converting from standard sea-level pressure to altitude, see the data sheet -for the BME180 on page 16 of http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf. Rather than put a -floating-point function in the library which may not be used but which would use space, an example altitude +This example program initializes the BME680 to use I2C for communications. The library does not +using floating point numbers to save on memory space and computation time. The values for +Temperature, Pressure and Humidity are returned in deci-units, e.g. a Temperature reading of "2731" +means "27.31" degrees Celsius. The display in the example program uses floating point for +demonstration purposes only. Note that the temperature reading is generally higher than the ambient +temperature due to die and PCB temperature and self-heating of the element.\n\n + +The pressure reading needs to be adjusted for altitude to get the adjusted pressure reading. There +are numerous sources on the internet for formulae converting from standard sea-level pressure to +altitude, see the data sheet for the BME180 on page 16 of +http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf. Rather than put a floating-point +function in the library which may not be used but which would use space, an example altitude computation function has been added to this example program to show how it might be done. @section I2CDemolicense License -This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License -along with this program. If not, see . +This program is free software: you can redistribute it and/or modify it under the terms of the GNU +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 General Public License for more details. You should have +received a copy of the GNU General Public License along with this program. If not, see +. @section I2CDemoauthor Author @@ -51,98 +57,100 @@ Written by Arnd\@SV-Zanshin @section I2CDemoversions Changelog -Version | Date | Developer | Comments -------- | ---------- | ----------------------------- | ------------------------------------------- -1.0.2 | 2020-05-09 | https://github.com/SV-Zanshin | Issue #8 - clean up comments and code -1.0.1 | 2019-01-30 | https://github.com/SV-Zanshin | Removed old comments -1.0.1 | 2019-01-26 | https://github.com/SV-Zanshin | Issue #3 - convert documentation to Doxygen -1.0.0b | 2018-06-30 | https://github.com/SV-Zanshin | Cloned from original BME280 program +Version | Date | Developer | Comments +------- | ---------- | ---------- | --------------------------------------------------------------- +1.0.3 | 2020-07-04 | SV-Zanshin | Issue #25 - implement clang-formatting +1.0.2 | 2020-05-09 | SV-Zanshin | Issue #8 - clean up comments and code +1.0.1 | 2019-01-30 | SV-Zanshin | Removed old comments +1.0.1 | 2019-01-26 | SV-Zanshin | Issue #3 - convert documentation to Doxygen +1.0.0b | 2018-06-30 | SV-Zanshin | Cloned from original BME280 program */ -#include "Zanshin_BME680.h" // Include the BME680 Sensor library -/******************************************************************************************************************* -** Declare all program constants ** -*******************************************************************************************************************/ -const uint32_t SERIAL_SPEED = 115200; ///< Set the baud rate for Serial I/O - -/******************************************************************************************************************* -** Declare global variables and instantiate classes ** -*******************************************************************************************************************/ -BME680_Class BME680; ///< Create an instance of the BME680 class +#include "Zanshin_BME680.h" // Include the BME680 Sensor library +/************************************************************************************************** +** Declare all program constants ** +**************************************************************************************************/ +const uint32_t SERIAL_SPEED = 115200; ///< Set the baud rate for Serial I/O -float altitude(const int32_t press, const float seaLevel = 1013.25); ///< Forward function declaration with default value for sea level -float altitude(const int32_t press, const float seaLevel) -{ +/************************************************************************************************** +** Declare global variables and instantiate classes ** +**************************************************************************************************/ +BME680_Class BME680; ///< Create an instance of the BME680 class +///< Forward function declaration with default value for sea level +float altitude(const int32_t press, const float seaLevel = 1013.25); +float altitude(const int32_t press, const float seaLevel) { /*! - * @brief This converts a pressure measurement into a height in meters - * @details The corrected sea-level pressure can be passed into the function if it is know, otherwise the standard - * atmospheric pressure of 1013.25hPa is used (see https://en.wikipedia.org/wiki/Atmospheric_pressure) - * @param[in] press Pressure reading from BME680 - * @param[in] seaLevel Sea-Level pressure in millibars - * @return floating point altitude in meters. + @brief This converts a pressure measurement into a height in meters + @details The corrected sea-level pressure can be passed into the function if it is known, + otherwise the standard atmospheric pressure of 1013.25hPa is used (see + https://en.wikipedia.org/wiki/Atmospheric_pressure) for details. + @param[in] press Pressure reading from BME680 + @param[in] seaLevel Sea-Level pressure in millibars + @return floating point altitude in meters. */ static float Altitude; - Altitude = 44330.0*(1.0-pow(((float)press/100.0)/seaLevel,0.1903)); // Convert into altitude in meters - return(Altitude); -} // of method altitude() + Altitude = + 44330.0 * (1.0 - pow(((float)press / 100.0) / seaLevel, 0.1903)); // Convert into meters + return (Altitude); +} // of method altitude() -void setup() -{ +void setup() { /*! @brief Arduino method called once at startup to initialize the system - @details This is an Arduino IDE method which is called first upon boot or restart. It is only called one time - and then control goes to the main "loop()" method, from which control never returns + @details This is an Arduino IDE method which is called first upon boot or restart. It is only + called one time and then control goes to the main "loop()" method, from which + control never returns @return void */ - Serial.begin(SERIAL_SPEED); // Start serial port at Baud rate - #ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, then wait 3 seconds to initialize USB port - delay(3000); - #endif + Serial.begin(SERIAL_SPEED); // Start serial port at Baud rate +#ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, then wait 3 seconds to init USB port + delay(3000); +#endif Serial.print(F("Starting I2CDemo example program for BME680\n")); Serial.print(F("- Initializing BME680 sensor\n")); - while (!BME680.begin(I2C_STANDARD_MODE)) // Start BME680 using I2C, use first device found - { + while (!BME680.begin(I2C_STANDARD_MODE)) { // Start BME680 using I2C, use first device found Serial.print(F("- Unable to find BME680. Trying again in 5 seconds.\n")); delay(5000); - } // of loop until device is located + } // of loop until device is located Serial.print(F("- Setting 16x oversampling for all sensors\n")); - BME680.setOversampling(TemperatureSensor,Oversample16); // Use enumerated type values - BME680.setOversampling(HumiditySensor, Oversample16); // Use enumerated type values - BME680.setOversampling(PressureSensor, Oversample16); // Use enumerated type values + BME680.setOversampling(TemperatureSensor, Oversample16); // Use enumerated type values + BME680.setOversampling(HumiditySensor, Oversample16); // Use enumerated type values + BME680.setOversampling(PressureSensor, Oversample16); // Use enumerated type values Serial.print(F("- Setting IIR filter to a value of 4 samples\n")); - BME680.setIIRFilter(IIR4); // Use enumerated type values - Serial.print(F("- Setting gas measurement to 320\xC2\xB0\x43 for 150ms\n")); // "°C" symbols - BME680.setGas(320,150); // 320°c for 150 milliseconds -} // of method setup() -void loop() -{ + BME680.setIIRFilter(IIR4); // Use enumerated type values + Serial.print(F("- Setting gas measurement to 320\xC2\xB0\x43 for 150ms\n")); // "°C" symbols + BME680.setGas(320, 150); // 320°c for 150 milliseconds +} // of method setup() +void loop() { /*! @brief Arduino method for the main program loop - @details This is the main program for the Arduino IDE, it is an infinite loop and keeps on repeating. - The "sprintf()" function is to pretty-print the values, since floating point is not supported on the - Arduino, split the values into those before and those after the decimal point. + @details This is the main program for the Arduino IDE, it is an infinite loop and keeps on + repeating. The "sprintf()" function is to pretty-print the values, since floating + point is not supported on the Arduino, split the values into those before and those + after the decimal point. @return void */ - static int32_t temp, humidity, pressure, gas; // Variable to store readings - static char buf[16]; // Text buffer for sprintf - static float alt; // temp variable for altitude - static uint16_t loopCounter = 0; // Display iterations - if (loopCounter % 25 == 0) // Display header every 25 loops - { // - Serial.print(F("\nLoop Temp\xC2\xB0\x43 Humid% Press hPa Alt m Air m")); // Show header plus unicode "°C" - Serial.print(F("\xE2\x84\xA6\n==== ====== ====== ========= ======= ======\n")); // and "?" symbols - } // if-then time to show headers // - BME680.getSensorData(temp,humidity,pressure,gas); // Get the most recent readings - sprintf(buf, "%4d %3d.%02d", ++loopCounter%9999, // Clamp iterations to 9999, - (int8_t)(temp/100),(uint8_t)(temp%100)); // Temperature in decidegrees - Serial.print(buf); // - sprintf(buf, "%3d.%03d", (int8_t)(humidity/1000),(uint16_t)(humidity%1000)); // Humidity in milli-percent - Serial.print(buf); // - sprintf(buf, "%7d.%02d", (int16_t)(pressure/100),(uint8_t)(pressure%100)); // Pressure in Pascals - Serial.print(buf); // - alt = altitude(pressure); // temp variable for altitude - sprintf(buf, "%5d.%02d", (int16_t)(alt),((uint8_t)(alt*100)%100)); // Altitude in meters - Serial.print(buf); // - sprintf(buf, "%4d.%02d\n", (int16_t)(gas/100),(uint8_t)(gas%100)); // Resistance in milliohms - Serial.print(buf); // - delay(10000); // Wait 10s before repeating -} // of method loop() \ No newline at end of file + static int32_t temp, humidity, pressure, gas; // BME readings + static char buf[16]; // sprintf text buffer + static float alt; // Temporary variable + static uint16_t loopCounter = 0; // Display iterations + if (loopCounter % 25 == 0) { // Show header @25 loops + Serial.print(F("\nLoop Temp\xC2\xB0\x43 Humid% Press hPa Alt m Air m")); + Serial.print(F("\xE2\x84\xA6\n==== ====== ====== ========= ======= ======\n")); // "°C" symbol + } // if-then time to show headers + BME680.getSensorData(temp, humidity, pressure, gas); // Get readings + sprintf(buf, "%4d %3d.%02d", ++loopCounter % 9999, // Clamp to 9999, + (int8_t)(temp / 100), (uint8_t)(temp % 100)); // Temp in decidegrees + Serial.print(buf); + sprintf(buf, "%3d.%03d", (int8_t)(humidity / 1000), + (uint16_t)(humidity % 1000)); // Humidity milli-pct + Serial.print(buf); + sprintf(buf, "%7d.%02d", (int16_t)(pressure / 100), + (uint8_t)(pressure % 100)); // Pressure Pascals + Serial.print(buf); + alt = altitude(pressure); // temp altitude + sprintf(buf, "%5d.%02d", (int16_t)(alt), ((uint8_t)(alt * 100) % 100)); // Altitude meters + Serial.print(buf); + sprintf(buf, "%4d.%02d\n", (int16_t)(gas / 100), (uint8_t)(gas % 100)); // Resistance milliohms + Serial.print(buf); + delay(10000); // Wait 10s +} // of method loop() diff --git a/examples/SDLoggerSPIDemo/SDLoggerSPIDemo.ino b/examples/SDLoggerSPIDemo/SDLoggerSPIDemo.ino index 50768ce..e985171 100644 --- a/examples/SDLoggerSPIDemo/SDLoggerSPIDemo.ino +++ b/examples/SDLoggerSPIDemo/SDLoggerSPIDemo.ino @@ -80,6 +80,7 @@ Written by https://github.com/SV-Zanshin Version | Date | Developer | Comments ------- | ---------- | ---------- | --------------------------------------------------------------- +1.0.3 | 2020-07-05 | SV-Zanshin | Issue #25 - implement clang-formatting 1.0.2 | 2020-06-28 | SV-Zanshin | Changed to adhere to clang Google formatting 1.0.1 | 2020-06-01 | SV-Zanshin | Added Doxygen commenting for redefine of serial class 1.0.0 | 2020-05-27 | SV-Zanshin | Completed and tested diff --git a/examples/SPIDemo/SPIDemo.ino b/examples/SPIDemo/SPIDemo.ino index 6d42d48..8c16c14 100644 --- a/examples/SPIDemo/SPIDemo.ino +++ b/examples/SPIDemo/SPIDemo.ino @@ -2,50 +2,55 @@ @section SPIDemo_intro_section Description -Example program for using hardware SPI to set and read the Bosch BME680 sensor. The sensor measures temperature, -pressure and humidity and is described at https://www.bosch-sensortec.com/bst/products/all_products/BME680. The -datasheet is available from Bosch at -https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680_DS001-11.pdf +Example program for using hardware SPI to set and read the Bosch BME680 sensor. The sensor measures +temperature, pressure and humidity and is described at +https://www.bosch-sensortec.com/bst/products/all_products/BME680. The datasheet is available from +Bosch at https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680_DS001-11.pdf \n\n -The most recent version of the BME680 library is available at https://github.com/SV-Zanshin/BME680 and the -documentation of the library as well as example programs are described in the project's wiki pages located at -https://github.com/SV-Zanshin/BME680/wiki. \n\n +The most recent version of the BME680 library is available at https://github.com/SV-Zanshin/BME680 +and the documentation of the library as well as example programs are described in the project's wiki +pages located at https://github.com/SV-Zanshin/BME680/wiki. \n\n -The BME680 is an extremely small physical package that is so tiny as to be impossible to solder at home, hence it -will be used as part of a third-party breakout board. There are several such boards available at this time, for -example \n -Company | Link +The BME680 is an extremely small physical package that is so tiny as to be impossible to solder at +home, hence it will be used as part of a third-party breakout board. There are several such boards +available at this time, for example \n Company | Link ------- | ---------- Sparkfun | https://www.sparkfun.com/products/14570 BlueDot | https://www.bluedot.space/sensor-boards/bme680/ -Adafruit | https://learn.adafruit.com/adafruit-BME680-humidity-barometric-pressure-temperature-sensor-breakout \n\n - -Bosch supplies sample software that runs on various platforms, including the Arduino family; this can be downloaed -at https://github.com/BoschSensortec/BSEC-Arduino-library . This software is part of the Bosch "BSEC" (Bosch -Sensortec Environmental Cluster) framework and somewhat bulky and unwieldy for typical Arduino applications, hence -the choice to make a more compact and rather less abstract library. - -This example program initializes the BME680 to use hardware SPI for communications. The library does not use -floating point mathematics to save on computation space and time, the values for Temperature, Pressure and Humidity -are returned in deci-units, e.g. a Temperature reading of "2731" means "27.31" degrees Celsius. The display in the -example program uses floating point for demonstration purposes only. Note that the temperature reading is -generally higher than the ambient temperature due to die and PCB temperature and self-heating of the element.\n\n - -The pressure reading needs to be adjusted for altitude to get the adjusted pressure reading. There are numerous -sources on the internet for formula converting from standard sea-level pressure to altitude, see the data sheet -for the BME180 on page 16 of http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf. Rather than put a -floating-point function in the library which may not be used but which would use space, an example altitude +Adafruit | +https://learn.adafruit.com/adafruit-BME680-humidity-barometric-pressure-temperature-sensor-breakout +\n\n + +Bosch supplies sample software that runs on various platforms, including the Arduino family; this +can be downloaed at https://github.com/BoschSensortec/BSEC-Arduino-library . This software is part +of the Bosch "BSEC" (Bosch Sensortec Environmental Cluster) framework and somewhat bulky and +unwieldy for typical Arduino applications, hence the choice to make a more compact and rather less +abstract library. + +This example program initializes the BME680 to use hardware SPI for communications. The library does +not use floating point mathematics to save on computation space and time, the values for +Temperature, Pressure and Humidity are returned in deci-units, e.g. a Temperature reading of "2731" +means "27.31" degrees Celsius. The display in the example program uses floating point for +demonstration purposes only. Note that the temperature reading is generally higher than the ambient +temperature due to die and PCB temperature and self-heating of the element.\n\n + +The pressure reading needs to be adjusted for altitude to get the adjusted pressure reading. There +are numerous sources on the internet for formula converting from standard sea-level pressure to +altitude, see the data sheet for the BME180 on page 16 of +http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf. Rather than put a floating-point +function in the library which may not be used but which would use space, an example altitude computation function has been added to this example program to show how it might be done. @section SPIDemolicense License -This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License -along with this program. If not, see . +This program is free software: you can redistribute it and/or modify it under the terms of the GNU +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 General Public License for more details. You should have +received a copy of the GNU General Public License along with this program. If not, see +. @section SPIDemoauthor Author @@ -53,102 +58,103 @@ Written by Arnd\@SV-Zanshin @section SPIDemoversions Changelog -Version | Date | Developer | Comments -------- | ---------- | ----------------------------- | --------------------------------------------------- -1.0.2 | 2020-05-09 | https://github.com/SV-Zanshin | Issue #8 - clean up comments and code -1.0.1 | 2019-01-26 | https://github.com/SV-Zanshin | Issue #3 - convert documentation to Doxygen -1.0.0 | 2017-07-01 | https://github.com/SV-Zanshin | Cloned from original SPIDemo.ino program for BME280 +Version | Date | Developer | Comments +------- | ---------- | ---------- | --------------------------------------------------------------- +1.0.3 | 2020-07-05 | SV-Zanshin | Issue #25 - implement clang-formatting +1.0.2 | 2020-05-09 | SV-Zanshin | Issue #8 - clean up comments and code +1.0.1 | 2019-01-26 | SV-Zanshin | Issue #3 - convert documentation to Doxygen +1.0.0 | 2017-07-01 | SV-Zanshin | Cloned from original SPIDemo.ino program for BME280 */ -#include "Zanshin_BME680.h" // Include the BME680 Sensor library -/******************************************************************************************************************* -** Declare all program constants ** -*******************************************************************************************************************/ -const uint32_t SERIAL_SPEED = 115200; ///< Set the baud rate for Serial I/O - -/*! -* The pin used for slave-select can be freely chosen from the digital pins available. This is default -* pin used on an Arduino Micro -*/ -const uint8_t SPI_CS_PIN = SS; -/******************************************************************************************************************* -** Declare global variables and instantiate classes ** -*******************************************************************************************************************/ -BME680_Class BME680; ///< Create an instance of the BME680 - -float altitude(const int32_t press, const float seaLevel = 1013.25); ///< Forward declaration with default value -float altitude(const int32_t press, const float seaLevel) -{ +#include "Zanshin_BME680.h" // Include the BME680 Sensor library +/************************************************************************************************** +** Declare all program constants ** +**************************************************************************************************/ +const uint32_t SERIAL_SPEED = 115200; ///< Set the baud rate for Serial I/O + +/*! The pin used for slave-select can be freely chosen from the digital pins available. This is + the default pin used on an Arduino Micro */ +const uint8_t SPI_CS_PIN = SS; +/************************************************************************************************** +** Declare global variables and instantiate classes ** +**************************************************************************************************/ +BME680_Class BME680; ///< Create an instance of the BME680 + +float altitude(const int32_t press, const float seaLevel = 1013.25); ///< Forward declaration +float altitude(const int32_t press, const float seaLevel) { /*! - * @brief This converts a pressure measurement into a height in meters - * @details The corrected sea-level pressure can be passed into the function if it is know, otherwise the standard - * atmospheric pressure of 1013.25hPa is used (see https://en.wikipedia.org/wiki/Atmospheric_pressure) - * @param[in] press Pressure reading from BME680 - * @param[in] seaLevel Sea-Level pressure in millibars - * @return floating point altitude in meters. + @brief This converts a pressure measurement into a height in meters + @details The corrected sea-level pressure can be passed into the function if it is known, + otherwise the standard atmospheric pressure of 1013.25hPa is used + (see https://en.wikipedia.org/wiki/Atmospheric_pressure for details) + @param[in] press Pressure reading from BME680 + @param[in] seaLevel Sea-Level pressure in millibars + @return floating point altitude in meters. */ static float Altitude; - Altitude = 44330.0*(1.0-pow(((float)press/100.0)/seaLevel,0.1903)); // Convert into altitude in meters - return(Altitude); -} // of method altitude() -void setup() -{ + Altitude = + 44330.0 * (1.0 - pow(((float)press / 100.0) / seaLevel, 0.1903)); // Convert into meters + return (Altitude); +} // of method altitude() +void setup() { /*! @brief Arduino method called once at startup to initialize the system - @details This is an Arduino IDE method which is called first upon boot or restart. It is only called one time - and then control goes to the main "loop()" method, from which control never returns + @details This is an Arduino IDE method which is called first upon boot or restart. It is only + called one time and then control goes to the main "loop()" method, from which control + never returns @return void */ - Serial.begin(SERIAL_SPEED); // Start serial port at Baud rate -#ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, then wait 3 seconds to initialize USB + Serial.begin(SERIAL_SPEED); // Start serial port at Baud rate +#ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, then wait 3 seconds to initialize USB delay(3000); #endif Serial.println(F("Starting Hardware SPIDemo example program for BME680")); Serial.print(F("- Initializing BME680 sensor\n")); - - while (!BME680.begin(SPI_CS_PIN)) // Start using hardware SPI protocol + + while (!BME680.begin(SPI_CS_PIN)) // Start using hardware SPI protocol { Serial.println(F("- Unable to find BME680. Waiting 3 seconds.")); delay(3000); - } // of loop until device is located + } // of loop until device is located Serial.println(F("- Setting 16x oversampling for all sensors")); - BME680.setOversampling(TemperatureSensor,Oversample16); - BME680.setOversampling(HumiditySensor, Oversample16); - BME680.setOversampling(PressureSensor, Oversample16); + BME680.setOversampling(TemperatureSensor, Oversample16); + BME680.setOversampling(HumiditySensor, Oversample16); + BME680.setOversampling(PressureSensor, Oversample16); Serial.println(F("- Setting IIR filter to maximum value of 16 samples")); BME680.setIIRFilter(IIR16); - Serial.print(F("- Setting gas measurement to 320\xC2\xB0\x43 for 150ms\n")); // "°C" symbols - BME680.setGas(320,150); // 320°c for 150 milliseconds -} // of method setup() -void loop() -{ + Serial.print(F("- Setting gas measurement to 320\xC2\xB0\x43 for 150ms\n")); // "°C" symbol + BME680.setGas(320, 150); // 320°c for 150 milliseconds +} // of method setup() +void loop() { /*! @brief Arduino method for the main program loop - @details This is the main program for the Arduino IDE, it is an infinite loop and keeps on repeating. - The "sprintf()" function is to pretty-print the values, since floating point is not supported on the - Arduino, split the values into those before and those after the decimal point. + @details This is the main program for the Arduino IDE, it is an infinite loop + The "sprintf()" function is to pretty-print the values, since floating point is not + supported on the Arduino, split the values into those before and those after the + decimal point. @return void */ - static int32_t temp, humidity, pressure, gas; // Variable to store readings - static char buf[16]; // Text buffer for sprintf - static float alt; // temp variable for altitude - static uint16_t loopCounter = 0; // Display iterations - if (loopCounter % 25 == 0) // Display header every 25 loops - { // - Serial.print(F("\nLoop Temp\xC2\xB0\x43 Humid% Press hPa Alt m Air m")); // Show header plus unicode "°C" - Serial.print(F("\xE2\x84\xA6\n==== ====== ====== ========= ======= =======\n"));// and "?" symbols - } // if-then time to show headers // - BME680.getSensorData(temp,humidity,pressure,gas); // Get the most recent readings - sprintf(buf, "%4d %3d.%02d", ++loopCounter%9999, // Clamp iterations to 9999, - (int8_t)(temp/100),(uint8_t)(temp%100)); // Temperature in decidegrees - Serial.print(buf); // - sprintf(buf, "%3d.%03d", (int8_t)(humidity/1000),(uint16_t)(humidity%1000)); // Humidity in milli-percent - Serial.print(buf); // - sprintf(buf, "%7d.%02d", (int16_t)(pressure/100),(uint8_t)(pressure%100)); // Pressure in Pascals - Serial.print(buf); // - alt = altitude(pressure); // temp variable for altitude - sprintf(buf, "%5d.%02d", (int16_t)(alt),((uint8_t)(alt*100)%100)); // Altitude in meters - Serial.print(buf); // - sprintf(buf, "%5d.%02d\n", (int16_t)(gas/100),(uint8_t)(gas%100)); // Resistance in milliohms - Serial.print(buf); // - delay(10000); // Wait 10s before repeating -} // of method loop() + static int32_t temp, humidity, pressure, gas; // Temp variables + static char buf[16]; // springtf text buffer + static float alt; // temp altitude + static uint16_t loopCounter = 0; // Display iterations + if (loopCounter % 25 == 0) { // Display header + Serial.print(F("\nLoop Temp\xC2\xB0\x43 Humid% Press hPa Alt m Air m")); // Show header "°C" + Serial.print(F("\xE2\x84\xA6\n==== ====== ====== ========= ======= =======\n")); + } // if-then time to show headers + BME680.getSensorData(temp, humidity, pressure, gas); // Get readings + sprintf(buf, "%4d %3d.%02d", ++loopCounter % 9999, // Clamp to 9999, + (int8_t)(temp / 100), (uint8_t)(temp % 100)); // Temp decidegrees + Serial.print(buf); + sprintf(buf, "%3d.%03d", (int8_t)(humidity / 1000), + (uint16_t)(humidity % 1000)); // Humidity milli-% + Serial.print(buf); + sprintf(buf, "%7d.%02d", (int16_t)(pressure / 100), + (uint8_t)(pressure % 100)); // Pressure Pascals + Serial.print(buf); + alt = altitude(pressure); // temp altitude + sprintf(buf, "%5d.%02d", (int16_t)(alt), ((uint8_t)(alt * 100) % 100)); // Altitude in meters + Serial.print(buf); + sprintf(buf, "%5d.%02d\n", (int16_t)(gas / 100), (uint8_t)(gas % 100)); // Resistance milliohms + Serial.print(buf); + delay(10000); // Wait 10s before repeating +} // of method loop() diff --git a/examples/SoftSPIDemo/SoftSPIDemo.ino b/examples/SoftSPIDemo/SoftSPIDemo.ino index 42bf2ee..49b4358 100644 --- a/examples/SoftSPIDemo/SoftSPIDemo.ino +++ b/examples/SoftSPIDemo/SoftSPIDemo.ino @@ -2,48 +2,54 @@ @section SoftSPIDemo_intro_section Description -Example program for using the Bosch BME680 sensor. The sensor measures temperature, pressure and humidity and is -described at https://www.bosch-sensortec.com/bst/products/all_products/BME680. The datasheet is available from Bosch -at https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680_DS001-11.pdf \n\n - -The most recent version of the BME680 library is available at https://github.com/SV-Zanshin/BME680 and the -documentation of the library as well as example programs are described in the project's wiki pages located at -https://github.com/SV-Zanshin/BME680/wiki. \n\n - -The BME680 is an extremely small physical package that is so tiny as to be impossible to solder at home, hence it -will be used as part of a third-party breakout board. There are several such boards available at this time, for -example \n -Company | Link +Example program for using the Bosch BME680 sensor. The sensor measures temperature, pressure and +humidity and is described at https://www.bosch-sensortec.com/bst/products/all_products/BME680. The +datasheet is available from Bosch at +https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680_DS001-11.pdf \n\n + +The most recent version of the BME680 library is available at https://github.com/SV-Zanshin/BME680 +and the documentation of the library as well as example programs are described in the project's wiki +pages located at https://github.com/SV-Zanshin/BME680/wiki. \n\n + +The BME680 is an extremely small physical package that is so tiny as to be impossible to solder at +home, hence it will be used as part of a third-party breakout board. There are several such boards +available at this time, for example \n Company | Link ------- | ---------- Sparkfun | https://www.sparkfun.com/products/14570 BlueDot | https://www.bluedot.space/sensor-boards/bme680/ -Adafruit | https://learn.adafruit.com/adafruit-BME680-humidity-barometric-pressure-temperature-sensor-breakout \n\n - -Bosch supplies sample software that runs on various platforms, including the Arduino family; this can be downloaed -at https://github.com/BoschSensortec/BSEC-Arduino-library . This software is part of the Bosch "BSEC" (Bosch -Sensortec Environmental Cluster) framework and somewhat bulky and unwieldy for typical Arduino applications, hence -the choice to make a more compact and rather less abstract library. - -This example program initializes the BME680 to use software SPI for communications. The library does not use -floating point mathematics to save on computation space and time, the values for Temperature, Pressure and Humidity -are returned in deci-units, e.g. a Temperature reading of "2731" means "27.31" degrees Celsius. The display in the -example program uses floating point for demonstration purposes only. Note that the temperature reading is -generally higher than the ambient temperature due to die and PCB temperature and self-heating of the element.\n\n - -The pressure reading needs to be adjusted for altitude to get the adjusted pressure reading. There are numerous -sources on the internet for formula converting from standard sea-level pressure to altitude, see the data sheet -for the BME180 on page 16 of http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf. Rather than put a -floating-point function in the library which may not be used but which would use space, an example altitude +Adafruit | +https://learn.adafruit.com/adafruit-BME680-humidity-barometric-pressure-temperature-sensor-breakout +\n\n + +Bosch supplies sample software that runs on various platforms, including the Arduino family; this +can be downloaed at https://github.com/BoschSensortec/BSEC-Arduino-library . This software is part +of the Bosch "BSEC" (Bosch Sensortec Environmental Cluster) framework and somewhat bulky and +unwieldy for typical Arduino applications, hence the choice to make a more compact and rather less +abstract library. + +This example program initializes the BME680 to use software SPI for communications. The library does +not use floating point mathematics to save on computation space and time, the values for +Temperature, Pressure and Humidity are returned in deci-units, e.g. a Temperature reading of "2731" +means "27.31" degrees Celsius. The display in the example program uses floating point for +demonstration purposes only. Note that the temperature reading is generally higher than the ambient +temperature due to die and PCB temperature and self-heating of the element.\n\n + +The pressure reading needs to be adjusted for altitude to get the adjusted pressure reading. There +are numerous sources on the internet for formula converting from standard sea-level pressure to +altitude, see the data sheet for the BME180 on page 16 of +http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf. Rather than put a floating-point +function in the library which may not be used but which would use space, an example altitude computation function has been added to this example program to show how it might be done. @section SoftSPIDemolicense License -This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License -along with this program. If not, see . +This program is free software: you can redistribute it and/or modify it under the terms of the GNU +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 General Public License for more details. You should have +received a copy of the GNU General Public License along with this program. If not, see +. @section SoftSPIDemoauthor Author @@ -51,117 +57,121 @@ Written by Arnd\@SV-Zanshin @section SoftSPIDemoversions Changelog -Version | Date | Developer | Comments -------- | ---------- | ----------------------------- | --------------------------------------------------- -1.0.2 | 2020-05-13 | https://github.com/SV-Zanshin | Issue #8 - clean up comments and code -1.0.1 | 2019-01-26 | https://github.com/SV-Zanshin | Issue #3 - convert documentation to Doxygen -1.0.0 | 2017-07-01 | https://github.com/SV-Zanshin | Cloned from original SPIDemo.ino program for BME280 +Version | Date | Developer | Comments +------- | ---------- | ---------- | --------------------------------------------------------------- +1.0.3 | 2020-07-05 | SV-Zanshin | Issue #25 - implement clang-formatting +1.0.2 | 2020-05-13 | SV-Zanshin | Issue #8 - clean up comments and code +1.0.1 | 2019-01-26 | SV-Zanshin | Issue #3 - convert documentation to Doxygen +1.0.0 | 2017-07-01 | SV-Zanshin | Cloned from original SPIDemo.ino program for BME280 */ -#include "Zanshin_BME680.h" // Include the BME680 Sensor library -/******************************************************************************************************************* -** Declare all program constants ** -*******************************************************************************************************************/ -const uint32_t SERIAL_SPEED = 115200; ///< Set the baud rate for Serial I/O // +#include "Zanshin_BME680.h" // Include the BME680 Sensor library +/************************************************************************************************** +** Declare all program constants ** +**************************************************************************************************/ +const uint32_t SERIAL_SPEED = 115200; ///< Set the baud rate for Serial I/O // /*! -* Software SPI requires 4 digital pins to be defined in order to work:\n -* CS - Chip Select or sometimes called SS for Slave-select. Used to address chip\n -* MISO - Master In, Slave Out. Wire used to send data from the BME680 to program\n -* MOSI - Master Out, Slave In. Wire used to send data from the program to BME680\n -* SCK - System Clock. This is used for timing data -*/ + * Software SPI requires 4 digital pins to be defined in order to work:\n + * CS - Chip Select or sometimes called SS for Slave-select. Used to address chip\n + * MISO - Master In, Slave Out. Wire used to send data from the BME680 to program\n + * MOSI - Master Out, Slave In. Wire used to send data from the program to BME680\n + * SCK - System Clock. This is used for timing data + */ #ifdef __AVR_ATmega1280__ - const uint8_t SPI_CS_PIN = 53; ///< Pin for slave-select of BME680 - const uint8_t SPI_SCK_PIN = 52; ///< Pin for clock signal - const uint8_t SPI_MOSI_PIN = 51; ///< Master-out, Slave-in Pin - const uint8_t SPI_MISO_PIN = 50; ///< Master-in, Slave-out Pin +const uint8_t SPI_CS_PIN = 53; ///< Pin for slave-select of BME680 +const uint8_t SPI_SCK_PIN = 52; ///< Pin for clock signal +const uint8_t SPI_MOSI_PIN = 51; ///< Master-out, Slave-in Pin +const uint8_t SPI_MISO_PIN = 50; ///< Master-in, Slave-out Pin #else - const uint8_t SPI_CS_PIN = 10; ///< Pin for slave-select of BME680 - const uint8_t SPI_SCK_PIN = 13; ///< Pin for clock signal - const uint8_t SPI_MOSI_PIN = 11; ///< Master-out, Slave-in Pin - const uint8_t SPI_MISO_PIN = 12; ///< Master-in, Slave-out Pin +const uint8_t SPI_CS_PIN = 10; ///< Pin for slave-select of BME680 +const uint8_t SPI_SCK_PIN = 13; ///< Pin for clock signal +const uint8_t SPI_MOSI_PIN = 11; ///< Master-out, Slave-in Pin +const uint8_t SPI_MISO_PIN = 12; ///< Master-in, Slave-out Pin #endif -/******************************************************************************************************************* -** Declare global variables and instantiate classes ** -*******************************************************************************************************************/ -BME680_Class BME680; ///< Create an instance of the BME680 +/************************************************************************************************** +** Declare global variables and instantiate classes ** +**************************************************************************************************/ +BME680_Class BME680; ///< Create an instance of the BME680 -float altitude(const int32_t press, const float seaLevel = 1013.25); ///< Forward declaration with default value -float altitude(const int32_t press, const float seaLevel) -{ +float altitude(const int32_t press, const float seaLevel = 1013.25); ///< Forward declaration +float altitude(const int32_t press, const float seaLevel) { /*! - * @brief This converts a pressure measurement into a height in meters - * @details The corrected sea-level pressure can be passed into the function if it is know, otherwise the standard - * atmospheric pressure of 1013.25hPa is used (see https://en.wikipedia.org/wiki/Atmospheric_pressure) - * @param[in] press Pressure reading from BME680 - * @param[in] seaLevel Sea-Level pressure in millibars - * @return floating point altitude in meters. + @brief This converts a pressure measurement into a height in meters + @details The corrected sea-level pressure can be passed into the function if it is know, other- + wise the standard atmospheric pressure of 1013.25hPa is used (see + https://en.wikipedia.org/wiki/Atmospheric_pressure) for details + @param[in] press Pressure reading from BME680 + @param[in] seaLevel Sea-Level pressure in millibars + @return floating point altitude in meters. */ static float Altitude; - Altitude = 44330.0*(1.0-pow(((float)press/100.0)/seaLevel,0.1903)); // Convert into altitude in meters - return(Altitude); -} // of method altitude() + Altitude = + 44330.0 * (1.0 - pow(((float)press / 100.0) / seaLevel, 0.1903)); // Convert into meters + return (Altitude); +} // of method altitude() -void setup() -{ -/*! - @brief Arduino method called once at startup to initialize the system - @details This is an Arduino IDE method which is called first upon boot or restart. It is only called one time - and then control goes to the main "loop()" method, from which control never returns - @return void -*/ Serial.begin(SERIAL_SPEED); // Start serial port at Baud rate -#ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, then wait 3 seconds to initialize USB +void setup() { + /*! + @brief Arduino method called once at startup to initialize the system + @details This is an Arduino IDE method which is called first upon boot or restart. It is only + called one time and then control goes to the main "loop()" method, from which + control never returns + @return void + */ + Serial.begin(SERIAL_SPEED); // Start serial port at Baud rate +#ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, then wait 3 seconds to initialize USB delay(3000); #endif Serial.println(F("Starting Software SPIDemo example program for BME680")); Serial.print(F("- Initializing BME680 sensor\n")); - while (!BME680.begin(SPI_CS_PIN,SPI_MOSI_PIN,SPI_MISO_PIN,SPI_SCK_PIN)) // Start using software SPI protocol + while (!BME680.begin(SPI_CS_PIN, SPI_MOSI_PIN, SPI_MISO_PIN, SPI_SCK_PIN)) // Start software SPI { Serial.println(F("- Unable to find BME680. Waiting 5 seconds.")); delay(5000); - } // of loop until device is located + } // of loop until device is located Serial.println(F("- Setting 16x oversampling for all sensors")); - BME680.setOversampling(TemperatureSensor,Oversample16); // Use enumerated type values - BME680.setOversampling(HumiditySensor, Oversample16); - BME680.setOversampling(PressureSensor, Oversample16); + BME680.setOversampling(TemperatureSensor, Oversample16); // Use enumerated type values + BME680.setOversampling(HumiditySensor, Oversample16); + BME680.setOversampling(PressureSensor, Oversample16); Serial.println(F("- Setting IIR filter to value of 2 samples")); - BME680.setIIRFilter(IIR16); // Use enumerated type values - Serial.print(F("- Setting gas measurement to 320\xC2\xB0\x43 for 150ms\n")); // "°C" symbols - BME680.setGas(320,150); // 320°c for 150 milliseconds -} // of method setup() -void loop() -{ + BME680.setIIRFilter(IIR16); // Use enumerated type values + Serial.print(F("- Setting gas measurement to 320\xC2\xB0\x43 for 150ms\n")); // "°C" symbols + BME680.setGas(320, 150); // 320°c for 150 milliseconds +} // of method setup() +void loop() { /*! @brief Arduino method for the main program loop - @details This is the main program for the Arduino IDE, it is an infinite loop and keeps on repeating. - The "sprintf()" function is to pretty-print the values, since floating point is not supported on the - Arduino, split the values into those before and those after the decimal point. + @details This is the main program for the Arduino IDE, it is an infinite loop and keeps on + repeating. The "sprintf()" function is to pretty-print the values, since floating point + is not supported on the Arduino , split the values into those before and those after + the decimal point. @return void */ - static int32_t temp, humidity, pressure, gas; // Variable to store readings - static char buf[16]; // Text buffer for sprintf - static float alt; // temp variable for altitude - static uint16_t loopCounter = 0; // Display iterations - if (loopCounter % 25 == 0) // Display header every 25 loops - { // - Serial.print(F("\nLoop Temp\xC2\xB0\x43 Humid% Press hPa Alt m Air m")); // Show header plus unicode "°C" - Serial.print(F("\xE2\x84\xA6\n==== ====== ====== ========= ======= =======\n"));// and "?" symbols - } // if-then time to show headers // - BME680.getSensorData(temp,humidity,pressure,gas); // Get the most recent readings - sprintf(buf, "%4d %3d.%02d", ++loopCounter%9999, // Clamp iterations to 9999, - (int8_t)(temp/100),(uint8_t)(temp%100)); // Temperature in decidegrees - Serial.print(buf); // - sprintf(buf, "%3d.%03d", (int8_t)(humidity/1000),(uint16_t)(humidity%1000)); // Humidity in milli-percent - Serial.print(buf); // - sprintf(buf, "%7d.%02d", (int16_t)(pressure/100),(uint8_t)(pressure%100)); // Pressure in Pascals - Serial.print(buf); // - alt = altitude(pressure); // temp variable for altitude - sprintf(buf, "%5d.%02d", (int16_t)(alt),((uint8_t)(alt*100)%100)); // Altitude in meters - Serial.print(buf); // - sprintf(buf, "%5d.%02d\n", (int16_t)(gas/100),(uint8_t)(gas%100)); // Resistance in milliohms - Serial.print(buf); // - delay(10000); // Wait 10s before repeating -} // of method loop() - + static int32_t temp, humidity, pressure, gas; // Variable to store readings + static char buf[16]; // Text buffer for sprintf + static float alt; // Altitude temp variable + static uint16_t loopCounter = 0; // Display iterations + if (loopCounter % 25 == 0) { // Display header @ 25 loops + Serial.print(F("\nLoop Temp\xC2\xB0\x43 Humid% Press hPa Alt m Air m")); // Show header "°C" + Serial.print(F("\xE2\x84\xA6\n==== ====== ====== ========= ======= =======\n")); // and "?" + } // if-then time to show headers + BME680.getSensorData(temp, humidity, pressure, gas); // Get readings + sprintf(buf, "%4d %3d.%02d", ++loopCounter % 9999, // Clamp to 9999, + (int8_t)(temp / 100), (uint8_t)(temp % 100)); // Temperature decidegrees + Serial.print(buf); + sprintf(buf, "%3d.%03d", (int8_t)(humidity / 1000), + (uint16_t)(humidity % 1000)); // Humidity milli-% + Serial.print(buf); + sprintf(buf, "%7d.%02d", (int16_t)(pressure / 100), + (uint8_t)(pressure % 100)); // Pressure Pascals + Serial.print(buf); + alt = altitude(pressure); // temp variable for altitude + sprintf(buf, "%5d.%02d", (int16_t)(alt), ((uint8_t)(alt * 100) % 100)); // Altitude in meters + Serial.print(buf); + sprintf(buf, "%5d.%02d\n", (int16_t)(gas / 100), + (uint8_t)(gas % 100)); // Resistance in milliohms + Serial.print(buf); + delay(10000); // Wait 10s before repeating +} // of method loop() diff --git a/examples/TwoDevicesI2CDemo/TwoDevicesI2CDemo.ino b/examples/TwoDevicesI2CDemo/TwoDevicesI2CDemo.ino index 3ff7113..94d9e55 100644 --- a/examples/TwoDevicesI2CDemo/TwoDevicesI2CDemo.ino +++ b/examples/TwoDevicesI2CDemo/TwoDevicesI2CDemo.ino @@ -2,43 +2,48 @@ @section TwoDevicesI2CDemo_intro_section Description -Example program for using I2C to set and read data from 2 Bosch BME680 sensors. The sensors measure temperature, -pressure, humidity and air quality and are described at https://www.bosch-sensortec.com/bst/products/all_products/BME680. -The datasheet is available from Bosch at https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680_DS001-11.pdf \n\n +Example program for using I2C to set and read data from 2 Bosch BME680 sensors. The sensors measure +temperature, pressure, humidity and air quality and are described at +https://www.bosch-sensortec.com/bst/products/all_products/BME680. The datasheet is available from +Bosch at https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680_DS001-11.pdf \n\n -The most recent version of the BME680 library is available at https://github.com/SV-Zanshin/BME680 and the -documentation of the library as well as example programs are described in the project's wiki pages located at -https://github.com/SV-Zanshin/BME680/wiki. \n\n +The most recent version of the BME680 library is available at https://github.com/SV-Zanshin/BME680 +and the documentation of the library as well as example programs are described in the project's wiki +pages located at https://github.com/SV-Zanshin/BME680/wiki. \n\n -The BME680 is an extremely small physical package that is so tiny as to be impossible to solder at home, hence it -will be used as part of a third-party breakout board. There are several such boards available at this time, for -example \n -Company | Link +The BME680 is an extremely small physical package that is so tiny as to be impossible to solder at +home, hence it will be used as part of a third-party breakout board. There are several such boards +available at this time, for example \n Company | Link ------- | ---------- Sparkfun | https://www.sparkfun.com/products/14570 BlueDot | https://www.bluedot.space/sensor-boards/bme680/ -Adafruit | https://learn.adafruit.com/adafruit-BME680-humidity-barometric-pressure-temperature-sensor-breakout \n\n +Adafruit | +https://learn.adafruit.com/adafruit-BME680-humidity-barometric-pressure-temperature-sensor-breakout +\n\n + +Bosch supplies sample software that runs on various platforms, including the Arduino family; this +can be downloaed at https://github.com/BoschSensortec/BSEC-Arduino-library . This software is part +of the Bosch "BSEC" (Bosch Sensortec Environmental Cluster) framework and somewhat bulky and +unwieldy for typical Arduino applications, hence the choice to make a more compact and rather less +abstract library. -Bosch supplies sample software that runs on various platforms, including the Arduino family; this can be downloaed -at https://github.com/BoschSensortec/BSEC-Arduino-library . This software is part of the Bosch "BSEC" (Bosch -Sensortec Environmental Cluster) framework and somewhat bulky and unwieldy for typical Arduino applications, hence -the choice to make a more compact and rather less abstract library. - -This example program explicitly initializes each BME680 - one at address 0x76 and the other at 0x77. The BME680s are -initialized in separate library instances. The library does not using floating point numbers to save on memory space -and computation time. The values for Temperature, Pressure and Humidity are returned in deci-units, e.g. a -Temperature reading of "2731" means "27.31" degrees Celsius.\n -The program measures only the temperature and humidity on the two devices and outputs the values. +This example program explicitly initializes each BME680 - one at address 0x76 and the other at 0x77. +The BME680s are initialized in separate library instances. The library does not using floating point +numbers to save on memory space and computation time. The values for Temperature, Pressure and +Humidity are returned in deci-units, e.g. a Temperature reading of "2731" means "27.31" degrees +Celsius.\n The program measures only the temperature and humidity on the two devices and outputs the +values. \n\n @section TwoDevicesI2CDemolicense License -This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License -along with this program. If not, see . +This program is free software: you can redistribute it and/or modify it under the terms of the GNU +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 General Public License for more details. You should have +received a copy of the GNU General Public License along with this program. If not, see +. @section TwoDevicesI2CDemoauthor Author @@ -46,99 +51,99 @@ Written by Arnd\@SV-Zanshin @section TwoDevicesI2CDemoversions Changelog -Version | Date | Developer | Comments -------- | ---------- | ----------------------------- | ---------------------------------------------- -1.0.0 | 2020-05-16 | https://github.com/SV-Zanshin | Issue #11. Cloned and adapted from I2CDemo.ino +Version | Date | Developer | Comments +------- | ---------- | ---------- | --------------------------------------------------------------- +1.0.3 | 2020-07-05 | SV-Zanshin | Issue #25 - implement clang-formatting +1.0.0 | 2020-05-16 | SV-Zanshin | Issue #11. Cloned and adapted from I2CDemo.ino */ -#include "Zanshin_BME680.h" // Include the BME680 Sensor library -/******************************************************************************************************************* -** Declare all program constants ** -*******************************************************************************************************************/ -const uint32_t SERIAL_SPEED = 115200; ///< Set the baud rate for Serial I/O +#include "Zanshin_BME680.h" // Include the BME680 Sensor library +/************************************************************************************************** +** Declare all program constants ** +**************************************************************************************************/ +const uint32_t SERIAL_SPEED = 115200; ///< Set the baud rate for Serial I/O -/******************************************************************************************************************* -** Declare global variables and instantiate classes ** -*******************************************************************************************************************/ -BME680_Class BME680_1; ///< Create an instance of the BME680 class for the first device -BME680_Class BME680_2; ///< Create an instance of the BME680 class for the second device +/************************************************************************************************** +** Declare global variables and instantiate classes ** +**************************************************************************************************/ +BME680_Class BME680_1; ///< Create an instance of the BME680 class for the first device +BME680_Class BME680_2; ///< Create an instance of the BME680 class for the second device -void setup() -{ +void setup() { /*! @brief Arduino method called once at startup to initialize the system - @details This is an Arduino IDE method which is called first upon boot or restart. It is only called one time - and then control goes to the main "loop()" method, from which control never returns + @details This is an Arduino IDE method which is called first upon boot or restart. It is only + called one time and then control goes to the main "loop()" method, from which control + never returns @return void */ - Serial.begin(SERIAL_SPEED); // Start serial port at Baud rate - #ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, then wait 3 seconds to initialize USB port - delay(3000); - #endif + Serial.begin(SERIAL_SPEED); // Start serial port at Baud rate +#ifdef __AVR_ATmega32U4__ // If a 32U4 processor, then wait 3 seconds to initialize USB port + delay(3000); +#endif Serial.print(F("\nStarting TwoDevicesI2CDemo example program for 2 BME680s\n\n")); Serial.print(F("- Initializing BME680 sensor 1 at address 0x76\n")); - while (!BME680_1.begin(I2C_STANDARD_MODE,0x76)) // Start BME680 using I2C, use device at address 0x76 - { + while (!BME680_1.begin(I2C_STANDARD_MODE, 0x76)) { // Start BME680 using I2C, use address 0x76 Serial.print(F("- Unable to find BME680. Trying again in 5 seconds.\n")); delay(5000); - } // of loop until device is located + } // of loop until device is located Serial.print(F("- Setting 16x oversampling for all sensors\n")); - BME680_1.setOversampling(TemperatureSensor,Oversample16); // Use enumerated type values - BME680_1.setOversampling(HumiditySensor, Oversample16); // Use enumerated type values - BME680_1.setOversampling(PressureSensor, SensorOff); // Use enumerated type value to turn off + BME680_1.setOversampling(TemperatureSensor, Oversample16); + BME680_1.setOversampling(HumiditySensor, Oversample16); + BME680_1.setOversampling(PressureSensor, SensorOff); Serial.print(F("- Setting IIR filter to a value of 4 samples\n")); - BME680_1.setIIRFilter(IIR4); // Use enumerated type values - Serial.print(F("- Turning off pressure and gas sensors\n")); // "°C" symbols - BME680_1.setGas(0,0); // If either Temp/Time is zero then turn off + BME680_1.setIIRFilter(IIR4); + Serial.print(F("- Turning off pressure and gas sensors\n")); // "°C" symbols + BME680_1.setGas(0, 0); // When either Temp/Time is zero then gas measurements are turned off Serial.print(F("\n- Initializing BME680 sensor 2 at address 0x77\n")); - - while (!BME680_2.begin(I2C_STANDARD_MODE,0x77)) // Start BME680 using I2C, use device at address 0x76 - { + while (!BME680_2.begin(I2C_STANDARD_MODE, 0x77)) { // Start BME680 using I2C, use address 0x76 Serial.print(F("- Unable to find BME680. Trying again in 5 seconds.\n")); delay(5000); - } // of loop until device is located + } // of loop until device is located Serial.print(F("- Setting 16x oversampling for all sensors\n")); - BME680_2.setOversampling(TemperatureSensor,Oversample16); // Use enumerated type values - BME680_2.setOversampling(HumiditySensor, Oversample16); // Use enumerated type values - BME680_2.setOversampling(PressureSensor, SensorOff); // Use enumerated type value to turn off + BME680_2.setOversampling(TemperatureSensor, Oversample16); + BME680_2.setOversampling(HumiditySensor, Oversample16); + BME680_2.setOversampling(PressureSensor, SensorOff); Serial.print(F("- Setting IIR filter to a value of 4 samples\n")); - BME680_2.setIIRFilter(IIR4); // Use enumerated type values - Serial.print(F("- Turning off pressure and gas sensors\n\n")); // "°C" symbols - BME680_2.setGas(0,0); // If either Temp/Time is zero then turn off -} // of method setup() -void loop() -{ + BME680_2.setIIRFilter(IIR4); + Serial.print(F("- Turning off pressure and gas sensors\n\n")); // "°C" symbols + BME680_2.setGas(0, 0); // When either Temp/Time is zero then gas measurements are turned off +} // of method setup() +void loop() { /*! @brief Arduino method for the main program loop - @details This is the main program for the Arduino IDE, it is an infinite loop and keeps on repeating. - The "sprintf()" function is to pretty-print the values, since floating point is not supported on the - Arduino, split the values into those before and those after the decimal point. + @details This is the main program for the Arduino IDE, it is an infinite loop and keeps on + repeating. The "sprintf()" function is to pretty-print the values, since floating + point is not supported on the Arduino, split the values into those before and those + after the decimal point. @return void */ - static int32_t temp1, humidity1, pressure1, gas1; // Variables for BME680 1 - static int32_t temp2, humidity2, pressure2, gas2; // Variables for BME680 1 - static char buf[16]; // Text buffer for sprintf - static uint16_t loopCounter = 0; // Display iterations - if (loopCounter % 25 == 0) // Display header every 25 loops - { // - Serial.print(F("\nLoop Temp1\xC2\xB0\x43 Humid1% | Temp2\xC2\xB0\x43 Humid2%")); // Show header plus unicode "°C" - Serial.print(F(" | \xCE\x94Temp \xCE\x94Humid%")); // and "?" symbols - Serial.print(F("\n==== ======= ======= | ======= ======= | ====== =======\n")); // - } // if-then time to show headers // - BME680_1.getSensorData(temp1,humidity1,pressure1,gas1); // Get the most recent readings - BME680_2.getSensorData(temp2,humidity2,pressure2,gas2); // Get the most recent readings - sprintf(buf, "%4d %4d.%02d", ++loopCounter%9999, // Clamp iterations to 9999, - (int8_t)(temp1/100),(uint8_t)(temp1%100)); // Temperature in decidegrees - Serial.print(buf); // - sprintf(buf, "%4d.%03d", (int8_t)(humidity1/1000),(uint16_t)(humidity1%1000)); // Humidity in milli-percent - Serial.print(buf); // - sprintf(buf, " | %4d.%02d", (int8_t)(temp2/100),(uint8_t)(temp2%100)); // Temperature in decidegrees - Serial.print(buf); // - sprintf(buf, "%4d.%03d ", (int8_t)(humidity2/1000),(uint16_t)(humidity2%1000)); // Humidity in milli-percent - Serial.print(buf); // - temp1 = abs(temp1 - temp2); // Compute the delta temperature - humidity1 = abs(humidity1 - humidity2); // Compute the delta humidity - sprintf(buf,"| %3d.%02d %3d.%02d\n",(int8_t)(temp1/100),(uint8_t)(temp1%100), // - (int8_t)(humidity1/1000),(uint16_t)(humidity1%1000)); // - Serial.print(buf); // - delay(10000); // Wait 10s before repeating -} // of method loop() \ No newline at end of file + static int32_t temp1, humidity1, pressure1, gas1; // Variables for BME680 1 + static int32_t temp2, humidity2, pressure2, gas2; // Variables for BME680 1 + static char buf[16]; // Text buffer for sprintf + static uint16_t loopCounter = 0; // Display iterations + if (loopCounter % 25 == 0) // Display header every 25 loops + { + Serial.print(F("\nLoop Temp1\xC2\xB0\x43 Humid1% | Temp2\xC2\xB0\x43 Humid2%")); // Show header + Serial.print(F(" | \xCE\x94Temp \xCE\x94Humid%")); // and "°C" + Serial.print(F("\n==== ======= ======= | ======= ======= | ====== =======\n")); + } // if-then time to show headers + BME680_1.getSensorData(temp1, humidity1, pressure1, gas1); + BME680_2.getSensorData(temp2, humidity2, pressure2, gas2); + sprintf(buf, "%4d %4d.%02d", ++loopCounter % 9999, // Clamp iterations to 9999, + (int8_t)(temp1 / 100), (uint8_t)(temp1 % 100)); // Temperature in decidegrees + Serial.print(buf); + sprintf(buf, "%4d.%03d", (int8_t)(humidity1 / 1000), + (uint16_t)(humidity1 % 1000)); // Humidity milli% + Serial.print(buf); + sprintf(buf, " | %4d.%02d", (int8_t)(temp2 / 100), (uint8_t)(temp2 % 100)); // Temp decidegrees + Serial.print(buf); + sprintf(buf, "%4d.%03d ", (int8_t)(humidity2 / 1000), + (uint16_t)(humidity2 % 1000)); // Humidity milli% + Serial.print(buf); + temp1 = abs(temp1 - temp2); // Compute the delta temperature + humidity1 = abs(humidity1 - humidity2); // Compute the delta humidity + sprintf(buf, "| %3d.%02d %3d.%02d\n", (int8_t)(temp1 / 100), (uint8_t)(temp1 % 100), + (int8_t)(humidity1 / 1000), (uint16_t)(humidity1 % 1000)); + Serial.print(buf); + delay(10000); // Wait 10s before repeating +} // of method loop() diff --git a/src/Zanshin_BME680.cpp b/src/Zanshin_BME680.cpp index b98d2e1..0856bc8 100644 --- a/src/Zanshin_BME680.cpp +++ b/src/Zanshin_BME680.cpp @@ -28,8 +28,8 @@ bool BME680_Class::begin() { /*! * @brief starts communications with device (overloaded) - * @details When called with no parameters the I2C protocol with I2C_STANDARD_MODE speed is selected. The I2C network - * is searched for the first BME680 device. + * @details When called with no parameters the I2C protocol with I2C_STANDARD_MODE speed is + selected. The I2C network is searched for the first BME680 device. * return "true" if successful otherwise false */ return begin(I2C_STANDARD_MODE,0); // Initialize I2C with standard speed, allow any i2c address @@ -37,105 +37,101 @@ bool BME680_Class::begin() bool BME680_Class::begin(const uint32_t i2cSpeed) { /*! - * @brief starts communications with device (overloaded) - * @details When called with a 32-bit parameter it is assumed that the I2C protocol is to be used and the speed - * setting is determined by the parameter. - * @param[in] i2cSpeed I2C Speed value - * return "true" if successful otherwise false + @brief starts communications with device (overloaded) + @details When called with a 32-bit parameter it is assumed that the I2C protocol is to be used + and the speed setting is determined by the parameter. + @param[in] i2cSpeed I2C Speed value + return "true" if successful otherwise false */ return begin(i2cSpeed,0); // Initialize I2C with given speed, allow any i2c address } // of method begin() bool BME680_Class::begin(const uint32_t i2cSpeed, const uint8_t i2cAddress) { /*! - * @brief starts communications with device (overloaded) - * @details When called with a 32-bit parameter it is assumed that the I2C protocol is to be used and the speed - * setting is determined by the parameter. If the i2cAddress is 0 then check both, otherwise check only - * only the specified address - * @param[in] i2cSpeed I2C Speed value - * @param[in] i2cAddress I2C Address, use 0 to self-determine - * return "true" if successful otherwise false + @brief starts communications with device (overloaded) + @details When called with a 32-bit parameter it is assumed that the I2C protocol is to be used + and the speed setting is determined by the parameter. If the i2cAddress is 0 then check + both, otherwise check only only the specified address + @param[in] i2cSpeed I2C Speed value + @param[in] i2cAddress I2C Address, use 0 to self-determine + return "true" if successful otherwise false */ Wire.begin(); // Start I2C as master - Wire.setClock(i2cSpeed); // Set I2C bus speed - _I2CSpeed = i2cSpeed; // Set private variable to speed - for (_I2CAddress = BME680_I2C_MIN_ADDRESS;_I2CAddress <= BME680_I2C_MAX_ADDRESS; // Loop through all possible - _I2CAddress++) // I2C addresses - { - if (i2cAddress == 0 || _I2CAddress == i2cAddress) // Only check íf relevant - { - Wire.beginTransmission(_I2CAddress); // Check for BME680 here - if (Wire.endTransmission()==0) // We have found a device - { // that could be a BME680, so - return commonInitialization(); // perform common initialization + Wire.setClock(i2cSpeed); // and set bus speed + _I2CSpeed = i2cSpeed; + for (_I2CAddress = BME680_I2C_MIN_ADDRESS;_I2CAddress <= BME680_I2C_MAX_ADDRESS; + _I2CAddress++) { // Loop through all I2C addresses + if (i2cAddress == 0 || _I2CAddress == i2cAddress) { // Only check íf relevant + Wire.beginTransmission(_I2CAddress); // Check for BME680 here + if (Wire.endTransmission()==0) { // We have found a device that could be + return commonInitialization(); // a BME680, so perform common init } // of if-then we have found a device } // of if-then check all or a specific address } // of for-next each I2C address loop - _I2CAddress = 0; // Set to denote no device found - _I2CSpeed = 0; // Set to denote no device found - return false; // return failure if we get here + _I2CAddress = 0; // Set to denote no device found + _I2CSpeed = 0; // Set to denote no device found + return false; // return failure if we get here } // of method begin() -bool BME680_Class::begin(const uint8_t chipSelect) -{ +bool BME680_Class::begin(const uint8_t chipSelect) { /*! - * @brief starts communications with device (overloaded) - * @details When called with a single 8-bit parameter is assumed that hardware SPI is to be used if the - value is not 0x76 or 0x77, in which case it is assumed that this is the I2C address - * @param[in] chipSelect Arduino Pin number for the Slave-Select pin or the I2C address - * return "true" if successful otherwise false + @brief starts communications with device (overloaded) + @details When called with a single 8-bit parameter is assumed that hardware SPI is to be used + if the value is not 0x76 or 0x77, in which case it is assumed that it is the I2C addr + @param[in] chipSelect Arduino Pin number for the Slave-Select pin or the I2C address + return "true" if successful otherwise false */ - if (chipSelect==BME680_I2C_MIN_ADDRESS || chipSelect==BME680_I2C_MAX_ADDRESS) // If 0x76 or 0x77 then we have I2C - { // - return begin(I2C_STANDARD_MODE, chipSelect); // Std speed with explicit address - } // if-then we have an I2C call // - _cs = chipSelect; // Store value for future use - digitalWrite(_cs, HIGH); // High means ignore master - pinMode(_cs, OUTPUT); // Make the chip select pin output - SPI.begin(); // Start hardware SPI - return commonInitialization(); // Perform common initialization -} // of method begin() -bool BME680_Class::begin(const uint8_t chipSelect, const uint8_t mosi, const uint8_t miso, const uint8_t sck) -{ + if (chipSelect == BME680_I2C_MIN_ADDRESS || chipSelect == BME680_I2C_MAX_ADDRESS) { + // If 0x76 or 0x77 then we have I2C + return begin(I2C_STANDARD_MODE, chipSelect); // Std speed with explicit address + } // if-then we have an I2C call + _cs = chipSelect; // Store value for future use + digitalWrite(_cs, HIGH); // High means ignore master + pinMode(_cs, OUTPUT); // Make the chip select pin output + SPI.begin(); // Start hardware SPI + return commonInitialization(); // Perform common initialization +} // of method begin() +bool BME680_Class::begin(const uint8_t chipSelect, const uint8_t mosi, const uint8_t miso, + const uint8_t sck) { /*! - * @brief starts communications with device (overloaded) - * @details When called with 4 parameters then software SPI is assumed - * @param[in] chipSelect Arduino Pin number for the Slave-Select pin - * @param[in] mosi Arduino Pin number for the Master-out-Slave-in pin - * @param[in] miso Arduino Pin number for the Master-in-Slave-out pin - * @param[in] sck Arduino Pin number for the System clock pin - * return "true" if successful otherwise false + @brief starts communications with device (overloaded) + @details When called with 4 parameters then software SPI is assumed + @param[in] chipSelect Arduino Pin number for the Slave-Select pin + @param[in] mosi Arduino Pin number for the Master-out-Slave-in pin + @param[in] miso Arduino Pin number for the Master-in-Slave-out pin + @param[in] sck Arduino Pin number for the System clock pin + return "true" if successful otherwise false */ - _cs = chipSelect; _mosi = mosi; _miso = miso; _sck = sck; // Store SPI pins - digitalWrite(_cs, HIGH); // High means ignore master - pinMode(_cs, OUTPUT); // Make the chip select pin output - pinMode(_sck, OUTPUT); // Make system clock pin output - pinMode(_mosi, OUTPUT); // Make master-out slave-in output - pinMode(_miso, INPUT); // Make master-in slave-out input - return commonInitialization(); // Perform common initialization -} // of method begin() + _cs = chipSelect; + _mosi = mosi; + _miso = miso; + _sck = sck; // Store SPI pins + digitalWrite(_cs, HIGH); // High means ignore master + pinMode(_cs, OUTPUT); // Make the chip select pin output + pinMode(_sck, OUTPUT); // Make system clock pin output + pinMode(_mosi, OUTPUT); // Make master-out slave-in output + pinMode(_miso, INPUT); // Make master-in slave-out input + return commonInitialization(); // Perform common initialization +} // of method begin() bool BME680_Class::commonInitialization() { /*! - * @brief Common BME680 initialization function - * @details Called from all of the overloaded "begin()" functions once the communicationsprotocol has been selected - * return "true" if successful otherwise false + @brief Common BME680 initialization function + @details Called from all of the overloaded "begin()" functions once the communications protocol + has been selected return "true" if successful otherwise false */ - uint8_t SPI_Register = readByte(BME680_SPI_REGISTER); // Read the SPI register byte - if (_I2CAddress==0 && bitRead(SPI_Register,BME680_SPI_MEM_PAGE_POSITION)) // Wrong mode for SPI to read chip ID - { // - bitWrite(SPI_Register,BME680_SPI_MEM_PAGE_POSITION,0); // Turn off Page bit to go to Page 0 - putData(BME680_SPI_REGISTER,SPI_Register); // Write value to register - } // of if-then we are in SPI mode // - if (readByte(BME680_CHIPID_REGISTER)==BME680_CHIPID) // check for correct chip id - { // - getCalibration(); // get the calibration values - if (_I2CAddress==0) // If using SPI, then we need to ensure - { // that we switch to the correct - bitWrite(SPI_Register,BME680_SPI_MEM_PAGE_POSITION,1); // Page "1" again - putData(BME680_SPI_REGISTER, SPI_Register); // Update register value - } // of if-then SPI mode // - uint8_t workRegister = readByte(BME680_CONTROL_MEASURE_REGISTER); // Read the control measure - putData(BME680_CONTROL_MEASURE_REGISTER,(uint8_t)(workRegister|1)); // Trigger start of first measurement + uint8_t SPI_Register = readByte(BME680_SPI_REGISTER); // Read SPI reg. byte + if (_I2CAddress==0 && bitRead(SPI_Register,BME680_SPI_MEM_PAGE_POSITION)) { // Wrong mode for ID + bitWrite(SPI_Register,BME680_SPI_MEM_PAGE_POSITION,0); // Turn off Page bit to go to Page 0 + putData(BME680_SPI_REGISTER,SPI_Register); // Write value to register + } // of if-then we are in SPI mode + if (readByte(BME680_CHIPID_REGISTER)==BME680_CHIPID) { // check for correct chip id + getCalibration(); // get the calibration values + if (_I2CAddress==0) { // If using SPI, switch to correct + bitWrite(SPI_Register,BME680_SPI_MEM_PAGE_POSITION,1); // Page "1" again + putData(BME680_SPI_REGISTER, SPI_Register); // Update register value + } // of if-then SPI mode + uint8_t workRegister = readByte(BME680_CONTROL_MEASURE_REGISTER); // Read the control measure + putData(BME680_CONTROL_MEASURE_REGISTER,(uint8_t)(workRegister|1)); // Trigger 1st measurement return true; } // of if-then device is really a BME680 else return false; @@ -143,11 +139,12 @@ bool BME680_Class::commonInitialization() uint8_t BME680_Class::readByte(const uint8_t addr) { /*! - * @brief Read a single byte from the give address - * @details interlude function to the "getData()" function. This is called so that the return value is the byte - * of data that has just been read rather than the number of bytes read returned by "getData()" - * param[in] addr Address of device - * return single byte of read from addr + @brief Read a single byte from the give address + @details interlude function to the "getData()" function. This is called so that the return value + is the byte of data that has just been read rather than the number of bytes read + returned by "getData()" + param[in] addr Address of device + return single byte of read from addr */ uint8_t returnValue; // Storage for returned value getData(addr,returnValue); // Read just one byte @@ -160,18 +157,16 @@ void BME680_Class::reset() */ putData(BME680_SOFTRESET_REGISTER,BME680_RESET_CODE); // write reset code to device delay(2); // Datasheet states 2ms Start-up time - if (_I2CAddress) // Branch depending on if using I2C or SPI - { // - begin(_I2CSpeed, _I2CAddress); // Start device with same settings again if using I2C - } // - else // - { // - if (_sck) // If _sck is set the use software SPI - { // + if (_I2CAddress) { // Branch depending on if using I2C or SPI + begin(_I2CSpeed, _I2CAddress); // If I2C start device with same settings + } + else + { + if (_sck) { // If _sck is set the use software SPI begin(_cs, _mosi, _miso, _sck); // Use software SPI again - } // - else // - { // + } + else + { begin(_cs); // otherwise it must be hardware SPI } // if-then-else using sw or hw SPI } // if-then-else using I2C @@ -179,54 +174,55 @@ void BME680_Class::reset() void BME680_Class::getCalibration() { /*! - * @brief reads the calibration register data into local variables for use in converting readings - * @details The calibration registers are read into a temporary array and then parsed into the appropriate - * calibration variables, this was taken from the example BOSCH software and minimizes register reads, - * but makes it rather difficult to read. This will be redone for legibility at some point in the future - * param[in] addr Address of device - * return single byte read + @brief reads the calibration register data into local variables for use in converting readings + @details The calibration registers are read into a temporary array and then parsed into the + appropriate calibration variables, this was taken from the example BOSCH software and + minimizes register reads, but makes it rather difficult to read. This will be redone + for legibility at some point in the future + param[in] addr Address of device + return single byte read */ /************************************* ** Temperature related coefficients ** *************************************/ - uint8_t coeff_array1[BME680_COEFF_SIZE1] = { 0 }; // Define temporary array 1 and initialize with zeroes - uint8_t coeff_array2[BME680_COEFF_SIZE2] = { 0 }; // Define temporary array 2 and initialize with zeroes - getData(BME680_COEFF_START_ADDRESS1,coeff_array1); // Split reading registers into 2 - getData(BME680_COEFF_START_ADDRESS2,coeff_array2); // one 25 bytes and the other 16 - _T1 = (uint16_t) (CONCAT_BYTES(coeff_array2[BME680_T1_MSB_REG], coeff_array2[BME680_T1_LSB_REG])); - _T2 = (int16_t) (CONCAT_BYTES(coeff_array1[BME680_T2_MSB_REG], coeff_array1[BME680_T2_LSB_REG])); - _T3 = (int8_t) (coeff_array1[BME680_T3_REG]); + uint8_t coeff_arr1[BME680_COEFF_SIZE1] = { 0 }; // Define temp array 1 and initialize with 0x + uint8_t coeff_arr2[BME680_COEFF_SIZE2] = { 0 }; // Define temp array 2 and initialize with 0s + getData(BME680_COEFF_START_ADDRESS1,coeff_arr1); // Split reading registers into 2 + getData(BME680_COEFF_START_ADDRESS2,coeff_arr2); // one 25 bytes and the other 16 + _T1 = (uint16_t) (CONCAT_BYTES(coeff_arr2[BME680_T1_MSB_REG], coeff_arr2[BME680_T1_LSB_REG])); + _T2 = (int16_t) (CONCAT_BYTES(coeff_arr1[BME680_T2_MSB_REG], coeff_arr1[BME680_T2_LSB_REG])); + _T3 = (int8_t) (coeff_arr1[BME680_T3_REG]); /************************************* ** Pressure related coefficients ** *************************************/ - _P1 = (uint16_t) (CONCAT_BYTES(coeff_array1[BME680_P1_MSB_REG], coeff_array1[BME680_P1_LSB_REG])); - _P2 = (int16_t) (CONCAT_BYTES(coeff_array1[BME680_P2_MSB_REG], coeff_array1[BME680_P2_LSB_REG])); - _P3 = (int8_t) coeff_array1[BME680_P3_REG]; - _P4 = (int16_t) (CONCAT_BYTES(coeff_array1[BME680_P4_MSB_REG], coeff_array1[BME680_P4_LSB_REG])); - _P5 = (int16_t) (CONCAT_BYTES(coeff_array1[BME680_P5_MSB_REG], coeff_array1[BME680_P5_LSB_REG])); - _P6 = (int8_t) (coeff_array1[BME680_P6_REG]); - _P7 = (int8_t) (coeff_array1[BME680_P7_REG]); - _P8 = (int16_t) (CONCAT_BYTES(coeff_array1[BME680_P8_MSB_REG], coeff_array1[BME680_P8_LSB_REG])); - _P9 = (int16_t) (CONCAT_BYTES(coeff_array1[BME680_P9_MSB_REG], coeff_array1[BME680_P9_LSB_REG])); - _P10 = (uint8_t) (coeff_array1[BME680_P10_REG]); + _P1 = (uint16_t) (CONCAT_BYTES(coeff_arr1[BME680_P1_MSB_REG], coeff_arr1[BME680_P1_LSB_REG])); + _P2 = (int16_t) (CONCAT_BYTES(coeff_arr1[BME680_P2_MSB_REG], coeff_arr1[BME680_P2_LSB_REG])); + _P3 = (int8_t) coeff_arr1[BME680_P3_REG]; + _P4 = (int16_t) (CONCAT_BYTES(coeff_arr1[BME680_P4_MSB_REG], coeff_arr1[BME680_P4_LSB_REG])); + _P5 = (int16_t) (CONCAT_BYTES(coeff_arr1[BME680_P5_MSB_REG], coeff_arr1[BME680_P5_LSB_REG])); + _P6 = (int8_t) (coeff_arr1[BME680_P6_REG]); + _P7 = (int8_t) (coeff_arr1[BME680_P7_REG]); + _P8 = (int16_t) (CONCAT_BYTES(coeff_arr1[BME680_P8_MSB_REG], coeff_arr1[BME680_P8_LSB_REG])); + _P9 = (int16_t) (CONCAT_BYTES(coeff_arr1[BME680_P9_MSB_REG], coeff_arr1[BME680_P9_LSB_REG])); + _P10 = (uint8_t) (coeff_arr1[BME680_P10_REG]); /********************************** ** Humidity related coefficients ** **********************************/ - _H1 = (uint16_t) (((uint16_t) coeff_array2[BME680_H1_MSB_REG] << BME680_HUM_REG_SHIFT_VAL) | - (coeff_array2[BME680_H1_LSB_REG]&BME680_BIT_H1_DATA_MSK)); - _H2 = (uint16_t) (((uint16_t) coeff_array2[BME680_H2_MSB_REG] << BME680_HUM_REG_SHIFT_VAL) | - ((coeff_array2[BME680_H2_LSB_REG])>>BME680_HUM_REG_SHIFT_VAL)); - _H3 = (int8_t) coeff_array2[BME680_H3_REG]; - _H4 = (int8_t) coeff_array2[BME680_H4_REG]; - _H5 = (int8_t) coeff_array2[BME680_H5_REG]; - _H6 = (uint8_t) coeff_array2[BME680_H6_REG]; - _H7 = (int8_t) coeff_array2[BME680_H7_REG]; + _H1 = (uint16_t) (((uint16_t) coeff_arr2[BME680_H1_MSB_REG] << BME680_HUM_REG_SHIFT_VAL) | + (coeff_arr2[BME680_H1_LSB_REG]&BME680_BIT_H1_DATA_MSK)); + _H2 = (uint16_t) (((uint16_t) coeff_arr2[BME680_H2_MSB_REG] << BME680_HUM_REG_SHIFT_VAL) | + ((coeff_arr2[BME680_H2_LSB_REG])>>BME680_HUM_REG_SHIFT_VAL)); + _H3 = (int8_t) coeff_arr2[BME680_H3_REG]; + _H4 = (int8_t) coeff_arr2[BME680_H4_REG]; + _H5 = (int8_t) coeff_arr2[BME680_H5_REG]; + _H6 = (uint8_t) coeff_arr2[BME680_H6_REG]; + _H7 = (int8_t) coeff_arr2[BME680_H7_REG]; /************************************ ** Gas heater related coefficients ** ************************************/ - _G1 = (int8_t) coeff_array2[BME680_GH1_REG]; - _G2 = (int16_t) (CONCAT_BYTES(coeff_array2[BME680_GH2_MSB_REG], coeff_array2[BME680_GH2_LSB_REG])); - _G3 = (int8_t) coeff_array2[BME680_GH3_REG]; + _G1 = (int8_t) coeff_arr2[BME680_GH1_REG]; + _G2 = (int16_t) (CONCAT_BYTES(coeff_arr2[BME680_GH2_MSB_REG], coeff_arr2[BME680_GH2_LSB_REG])); + _G3 = (int8_t) coeff_arr2[BME680_GH3_REG]; uint8_t temp_var = 0; getData(BME680_ADDR_RES_HEAT_RANGE_ADDR,temp_var); _res_heat_range = ((temp_var & BME680_RHRANGE_MSK) / 16); @@ -235,272 +231,270 @@ void BME680_Class::getCalibration() getData(BME680_ADDR_RANGE_SW_ERR_ADDR,temp_var); _rng_sw_err = ((int8_t) temp_var & (int8_t) BME680_RSERROR_MSK) / 16; } // of method getCalibration() -uint8_t BME680_Class::setOversampling(const uint8_t sensor, const uint8_t sampling) -{ - /*! - * @brief sets the oversampling mode for the sensor - * @details See enumerated "sensorTypes" for a list of values. Set to a valid oversampling rate as defined in the - * enumerated type oversamplingTypes. If either value is out of range or another error occurs then the - * return value is false. - * param[in] sensor Enumerated sensor type - * param[in] sampling Sampling rate from enumerated type - * return "true" if successful otherwise false - */ - if (sensor >= UnknownSensor || // return an error if sensor is out of - (sampling != UINT8_MAX && sampling >= UnknownOversample)) // range or oversample value is out of - { // range - return(UINT8_MAX); // - } // if then values out of range // - uint8_t tempRegister; // Temporary register variable - uint8_t returnValue = sampling; // Return sampling value - waitForReadings(); // Ensure any active reading is finished - switch (sensor) // Depending upon which sensor is chosen - { // - case HumiditySensor : // Set the humidity oversampling - { // - tempRegister = readByte(BME680_CONTROL_HUMIDITY_REGISTER); // Read the register contents - if (sampling == UINT8_MAX) // If we just want to read the values - { // - returnValue = tempRegister & ~BME680_HUMIDITY_MASK; // Set return value - } // - else // - { // - tempRegister &= BME680_HUMIDITY_MASK; // Mask bits to 0 - tempRegister |= sampling; // Add in the sampling bits - putData(BME680_CONTROL_HUMIDITY_REGISTER, (uint8_t)tempRegister); // Update humidity bits 0:2 - } // if-then return current value or set new value // - break; // - } // of HumiditySensor // - case PressureSensor : // Set the pressure oversampling - { // - tempRegister = readByte(BME680_CONTROL_MEASURE_REGISTER); // Read the register contents - if (sampling == UINT8_MAX) // If we just want to read the values - { // - returnValue = (tempRegister & ~BME680_PRESSURE_MASK)>>2; // Set return value - } // - else // - { // - tempRegister &= BME680_PRESSURE_MASK; // Mask bits to 0 - tempRegister |= (sampling << 2); // Add in the sampling bits at offset - putData(BME680_CONTROL_MEASURE_REGISTER, (uint8_t)tempRegister); // Update register - } // if-then return current value or set new value // - break; // - } // of PressureSensor // - case TemperatureSensor : // Set the temperature oversampling - { // - tempRegister = readByte(BME680_CONTROL_MEASURE_REGISTER); // Read the register contents - if (sampling == UINT8_MAX) // If we just want to read the values - { // - returnValue = (tempRegister & ~BME680_TEMPERATURE_MASK) >> 5; // Set return value - } // - else // - { // - tempRegister &= BME680_TEMPERATURE_MASK; // Mask bits to 0 - tempRegister |= (sampling << 5); // Add in the sampling bits at offset - putData(BME680_CONTROL_MEASURE_REGISTER, (uint8_t)tempRegister); // Update humidity bits 5:7 - } // if-then return current value or set new value // - break; // - } // of TemperatureSensor // - default: return(UINT8_MAX); // Return an error if no match - } // of switch the sensor type // - return(returnValue); // Otherwise return current value -} // of method setOversampling() -uint8_t BME680_Class::setIIRFilter(const uint8_t iirFilterSetting ) -{ - /*! - * @brief Set or return the current IIR filter setting - * @details When called with no parameters returns the current IIR Filter setting, otherwise when called with one - * parameter will set the IIR filter value and return the new setting - * param[in] iirFilterSetting New setting - * return IIR Filter setting - */ - waitForReadings(); // Ensure any active reading is finished - uint8_t returnValue = readByte(BME680_CONFIG_REGISTER); // Get control register byte contents - if (iirFilterSetting != UINT8_MAX) // If the value is to be changed - { // - returnValue = returnValue & B11100011; // mask IIR bits - returnValue |= (iirFilterSetting&B00000111)<<2; // use 3 bits of iirFilterSetting - putData(BME680_CONFIG_REGISTER,returnValue); // Write new control register value - } // if the value is to be changed // - returnValue = (returnValue >> 2) & B00000111; // Extract IIR filter setting from register - return(returnValue); // Return IIR Filter setting -} // of method setIIRFilter() -void BME680_Class::getSensorData(int32_t &temp, int32_t &hum, int32_t &press, int32_t &gas, const bool waitSwitch ) -{ +uint8_t BME680_Class::setOversampling(const uint8_t sensor, const uint8_t sampling) { /*! - * @brief Returns the most recent temperature, humidity and pressure readings - * param[out] temp Temperature reading - * param[out] hum Humidity reading - * param[out] press Pressure reading - * param[out] gas Gas reading - * param[in] waitSwitch Optional switch that, when set to "true" will not return until reading is finished + @brief sets the oversampling mode for the sensor + @details See enumerated "sensorTypes" for a list of values. Set to a valid oversampling rate as + defined in the enumerated type oversamplingTypes. If either value is out of range or + another error occurs then the return value is false. + param[in] sensor Enumerated sensor type + param[in] sampling Sampling rate from enumerated type + return "true" if successful otherwise false */ - readSensors(waitSwitch); // Get compensated data from BME680 - temp = _Temperature; // Copy global variables to parameters - hum = _Humidity; // Copy global variables to parameters - press = _Pressure; // Copy global variables to parameters - gas = _Gas; // Copy global variables to parameters -} // of method getSensorData() -uint8_t BME680_Class::getI2CAddress() -{ - /*! - * @brief Returns the I2C address of the BME680 - * return I2C Address from private variables - */ - return(_I2CAddress); -} // of method getI2CAddress() -void BME680_Class::readSensors(const bool waitSwitch) -{ - /*! - * @brief reads all 4 sensor values from the registers in one operation and then proceeds to convert the raw - * temperature, pressure and humidity readings into standard metric units - * @details The formula is written in the BME680's documentation but the math used below was taken from Adafruit's - * Adafruit_BME680_Library at https://github.com/adafruit/Adafruit_BME680. I think it can be refactored - * into more efficient code at some point in the future, but it does work correctly. - * param[in] waitSwitch Optional switch that, when set to "true" will not return until reading is finished - */ - /*! Lookup table for the possible gas range values */ -const uint32_t lookupTable1[16] = { - UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647), - UINT32_C(2126008810), UINT32_C(2147483647), UINT32_C(2130303777), UINT32_C(2147483647), UINT32_C(2147483647), - UINT32_C(2143188679), UINT32_C(2136746228), UINT32_C(2147483647), UINT32_C(2126008810), UINT32_C(2147483647), - UINT32_C(2147483647) -}; -const uint32_t lookupTable2[16] = { - UINT32_C(4096000000), UINT32_C(2048000000), UINT32_C(1024000000), UINT32_C(512000000), UINT32_C(255744255), - UINT32_C(127110228) , UINT32_C(64000000), UINT32_C(32258064), UINT32_C(16016016), UINT32_C(8000000), - UINT32_C(4000000), UINT32_C(2000000), UINT32_C(1000000), UINT32_C(500000), UINT32_C(250000), - UINT32_C(125000) -}; - - uint8_t buff[15],gas_range,status=0; // declare array for registers - int64_t var1, var2, var3, var4, var5, var6, temp_scaled; // Work variables - uint32_t adc_temp, adc_pres; // Raw ADC temperature and pressure - uint16_t adc_hum, adc_gas_res; // Raw ADC humidity and gas - if (waitSwitch) waitForReadings(); // Doesn't return until the readings are finished - getData(BME680_STATUS_REGISTER,buff); // read all 15 bytes in one go - adc_pres = (uint32_t)(((uint32_t) buff[2]*4096)|((uint32_t)buff[3]*16)| ((uint32_t)buff[4]/16));// put the 3 bytes of Pressure - adc_temp = (uint32_t)(((uint32_t) buff[5]*4096)|((uint32_t)buff[6]*16)| ((uint32_t)buff[7]/16));// put the 3 bytes of Temperature - adc_hum = (uint16_t)(((uint32_t) buff[8]*256)|(uint32_t)buff[9]); // put the 2 bytes of Humidity - adc_gas_res = (uint16_t)((uint32_t) buff[13]*4|(((uint32_t)buff[14])/64)); // put the 2 bytes of Gas - gas_range = buff[14] & 0X0F; // Retrieve the range - status |= buff[14] & 0X20; // See if the gas range is valid - status |= buff[14] & 0X10; // and the measurement is valid - //*******************************// - // First compute the temperature // - //*******************************// - var1 = ((int32_t)adc_temp>>3)-((int32_t)_T1<<1); // Perform calibration/adjustment - var2 = (var1*(int32_t)_T2)>>11; // of Temperature values according - var3 = ((var1>>1)*(var1>>1))>>12; // to formula defined by Bosch - var3 = ((var3)*((int32_t)_T3<<4))>>14; - _tfine = (int32_t)(var2+var3); - _Temperature = (int16_t)(((_tfine*5)+128)>>8); - //*******************************// - // Now compute the pressure // - //*******************************// - var1 = (((int32_t)_tfine) >> 1) - 64000; - var2 = ((((var1 >> 2)*(var1 >> 2)) >> 11)*(int32_t)_P6) >> 2; - var2 = var2 + ((var1 * (int32_t)_P5) << 1); - var2 = (var2 >> 2) + ((int32_t)_P4 << 16); - var1 = (((((var1>>2)*(var1>>2))>>13)*((int32_t)_P3<<5))>>3)+(((int32_t)_P2*var1)>>1); - var1 = var1 >> 18; - var1 = ((32768 + var1) * (int32_t)_P1) >> 15; - _Pressure = 1048576 - adc_pres; - _Pressure = (int32_t)((_Pressure - (var2 >> 12)) * ((uint32_t)3125)); - var4 = ((int32_t)1 << 31); - if (_Pressure >= var4) - _Pressure = ((_Pressure / (uint32_t)var1) << 1); - else - _Pressure = ((_Pressure << 1) / (uint32_t)var1); - var1 = ((int32_t)_P9*(int32_t)(((_Pressure>>3)*(_Pressure>>3))>>13))>>12; - var2 = ((int32_t)(_Pressure >> 2) * (int32_t)_P8) >> 13; - var3 = ((int32_t)(_Pressure >> 8) * (int32_t)(_Pressure >> 8) * (int32_t)(_Pressure >> 8) * (int32_t)_P10) >> 17; - _Pressure = (int32_t)(_Pressure)+((var1+var2+var3+((int32_t)_P7<<7))>>4); - //**********************// - // Compute the humidity // - //**********************// - temp_scaled = (((int32_t) _tfine * 5) + 128) >> 8; - var1 = (int32_t)(adc_hum-((int32_t)((int32_t)_H1*16))) - -(((temp_scaled*(int32_t)_H3)/((int32_t)100))>>1); - var2 = ((int32_t)_H2*(((temp_scaled*(int32_t)_H4)/ - ((int32_t) 100))+(((temp_scaled*((temp_scaled* - (int32_t)_H5)/((int32_t)100)))>>6)/((int32_t)100))+ - (int32_t) (1 << 14))) >> 10; - var3 = var1 * var2; - var4 = (int32_t) _H6 << 7; - var4 = ((var4)+((temp_scaled*(int32_t)_H7)/((int32_t)100)))>>4; - var5 = ((var3 >> 14) * (var3 >> 14)) >> 10; - var6 = (var4 * var5) >> 1; - _Humidity = (((var3 + var6) >> 10) * ((int32_t) 1000)) >> 12; - if (_Humidity > 100000) /* Cap at 100%rH */ - _Humidity = 100000; - else if (_Humidity < 0) - _Humidity = 0; - //**********************// - // Compute the Gas // - //**********************// - uint64_t uvar2; - var1 = (int64_t)((1340+(5*(int64_t)_rng_sw_err))* - ((int64_t) lookupTable1[gas_range])) >> 16; - uvar2 = (((int64_t)((int64_t)adc_gas_res<<15)-(int64_t)(16777216))+var1); - var3 = (((int64_t) lookupTable2[gas_range] * (int64_t) var1) >> 9); - _Gas = (uint32_t) ((var3 + ((int64_t) uvar2 >> 1)) / (int64_t) uvar2); - uint8_t workRegister = readByte(BME680_CONTROL_MEASURE_REGISTER); // Read the control measure - putData(BME680_CONTROL_MEASURE_REGISTER,(uint8_t)(workRegister|1)); // Trigger start of next measurement -} // of method readSensors() -void BME680_Class::waitForReadings() -{ - /*! - * @brief Only returns once a measurement on the BME680 has completed - */ - while ((readByte(BME680_STATUS_REGISTER) & // Loop until the "measuring" bit - _BV(BME680_MEASURING_BIT_POSITION))!=0); // is cleared by BME680 -} // of method waitForReadings -bool BME680_Class::setGas(uint16_t GasTemp, uint16_t GasMillis) -{ - /*! - * @brief sets the gas measurement target temperature and heating time - * param[in] GasTemp Target temperature in Celsius - * param[in] GasMillis Milliseconds to turn on heater - * return Always returns "true" - */ - waitForReadings(); // Ensure any active reading is finished - uint8_t gasRegister = readByte(BME680_CONTROL_GAS_REGISTER2); // Read current register values - if ( GasTemp==0 || GasMillis==0 ) - { - // If either input variable is zero // - putData(BME680_CONTROL_GAS_REGISTER1,(uint8_t)B00001000); // Turn off gas heater - putData(BME680_CONTROL_GAS_REGISTER2,(uint8_t)(gasRegister&B11101111)); // Turn off gas measurements + if (sensor >= UnknownSensor || + (sampling != UINT8_MAX && + sampling >= UnknownOversample)) { // return an error if sensor or oversample is out of range + return (UINT8_MAX); } - else - { - putData(BME680_CONTROL_GAS_REGISTER1,(uint8_t)0); // Turn off heater bit to turn on - uint8_t heatr_res; - int32_t var1,var2,var3,var4,var5,heatr_res_x100; - if (GasTemp < 200) GasTemp = 200; else if (GasTemp > 400) GasTemp = 400; // Clamp temperature to min/max - var1 = (((int32_t)(_Temperature/100)*_H3)/1000)*256; - var2 = (_H1+784)*(((((_H2+154009)*GasTemp*5)/100)+3276800)/10); - var3 = var1 + (var2 / 2); - var4 = (var3 / (_res_heat_range+4)); - var5 = (131 * _res_heat) + 65536; - heatr_res_x100 = (int32_t) (((var4 / var5) - 250) * 34); - heatr_res = (uint8_t) ((heatr_res_x100 + 50) / 100); - putData(BME680_GAS_HEATER_REGISTER0,heatr_res); - uint8_t factor = 0; - uint8_t durval; - if (GasMillis >= 0xfc0) durval = 0xff; // Max duration - else - { - while (GasMillis > 0x3F) + uint8_t tempRegister; // Temporary register variable + uint8_t returnValue = sampling; // Return sampling value + waitForReadings(); // Ensure any active reading is finished + switch (sensor) { // Depending upon which sensor is chosen + case HumiditySensor: // Set the humidity oversampling { - GasMillis = GasMillis / 4; - factor += 1; - } // of while loop - durval = (uint8_t) (GasMillis + (factor * 64)); - } // of if-then-else duration exceeds max - putData(BME680_CONTROL_GAS_REGISTER1,(uint8_t)0); // then turn off gas heater - putData(BME680_GAS_DURATION_REGISTER0,durval); - putData(BME680_CONTROL_GAS_REGISTER2,(uint8_t)(gasRegister|B00010000)); - } // of if-then-else turn gas measurements on or off - return true; -} // of method setGas() \ No newline at end of file + tempRegister = readByte(BME680_CONTROL_HUMIDITY_REGISTER); // Read the register contents + if (sampling == UINT8_MAX) { // If we just want to read values + + returnValue = tempRegister & ~BME680_HUMIDITY_MASK; // Set return value + } else { + tempRegister &= BME680_HUMIDITY_MASK; // Mask bits to 0 + tempRegister |= sampling; // Add in the sampling bits + putData(BME680_CONTROL_HUMIDITY_REGISTER, + (uint8_t)tempRegister); // Update humidity bits 0:2 + } // if-then return current value or set new value + break; + } // of HumiditySensor + case PressureSensor: // Set the pressure oversampling + { + tempRegister = readByte(BME680_CONTROL_MEASURE_REGISTER); // Read the register contents + if (sampling == UINT8_MAX) { // If we just want to read the values + returnValue = (tempRegister & ~BME680_PRESSURE_MASK) >> 2; // Set return value + } else { + tempRegister &= BME680_PRESSURE_MASK; // Mask bits to 0 + tempRegister |= (sampling << 2); // Add in sampling bits at offset + putData(BME680_CONTROL_MEASURE_REGISTER, (uint8_t)tempRegister); // Update register + } // if-then return current value or set new value + break; + } // of PressureSensor + case TemperatureSensor: // Set the temperature oversampling + { + tempRegister = readByte(BME680_CONTROL_MEASURE_REGISTER); // Read the register contents + if (sampling == UINT8_MAX) { // If we just want to read the values + returnValue = (tempRegister & ~BME680_TEMPERATURE_MASK) >> 5; // Set return value + } else { + tempRegister &= BME680_TEMPERATURE_MASK; // Mask bits to 0 + tempRegister |= (sampling << 5); // Add in the sampling bits at offset + putData(BME680_CONTROL_MEASURE_REGISTER, + (uint8_t)tempRegister); // Update humidity bits 5:7 + } // if-then return current value or set new value + break; + } // of TemperatureSensor + default: + return (UINT8_MAX); // Return an error if no match + } // of switch the sensor type + return (returnValue); // Otherwise return current value + } // of method setOversampling() + uint8_t BME680_Class::setIIRFilter(const uint8_t iirFilterSetting) { + /*! + @brief Set or return the current IIR filter setting + @details When called with no parameters returns the current IIR Filter setting, otherwise + when called with one parameter will set the IIR filter value and return the new setting + param[in] iirFilterSetting New setting + return IIR Filter setting + */ + waitForReadings(); // Ensure any active reading is finished + uint8_t returnValue = readByte(BME680_CONFIG_REGISTER); // Get control register byte contents + if (iirFilterSetting != UINT8_MAX) // If the value is to be changed + { // + returnValue = returnValue & B11100011; // mask IIR bits + returnValue |= (iirFilterSetting & B00000111) << 2; // use 3 bits of iirFilterSetting + putData(BME680_CONFIG_REGISTER, returnValue); // Write new control register value + } // if the value is to be changed // + returnValue = (returnValue >> 2) & B00000111; // Extract IIR filter setting from register + return (returnValue); // Return IIR Filter setting + } // of method setIIRFilter() + void BME680_Class::getSensorData(int32_t & temp, int32_t & hum, int32_t & press, int32_t & gas, + const bool waitSwitch) { + /*! + @brief Returns the most recent temperature, humidity and pressure readings + param[out] temp Temperature reading + param[out] hum Humidity reading + param[out] press Pressure reading + param[out] gas Gas reading + param[in] waitSwitch (Optional) When set will not return until reading is finished + */ + readSensors(waitSwitch); // Get compensated data from BME680 + temp = _Temperature; // Copy global variables to parameters + hum = _Humidity; // Copy global variables to parameters + press = _Pressure; // Copy global variables to parameters + gas = _Gas; // Copy global variables to parameters + } // of method getSensorData() + uint8_t BME680_Class::getI2CAddress() { + /*! + @brief Returns the I2C address of the BME680 + return I2C Address from private variables + */ + return (_I2CAddress); + } // of method getI2CAddress() + void BME680_Class::readSensors(const bool waitSwitch) { + /*! + @brief reads all 4 sensor values from the registers in one operation and then proceeds to + convert the raw temperature, pressure & humidity readings into standard metric units + @details The formula is written in the BME680's documentation but the math used below was + taken from Adafruit's Adafruit_BME680_Library at + https://github.com/adafruit/Adafruit_BME680. I think it can be refactored into more + efficient code at some point in the future, but it does work correctly. + param[in] waitSwitch (Optional) When set will not return until reading is finished + */ + /*! Lookup table for the possible gas range values */ + const uint32_t lookupTable1[16] = { + UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647), + UINT32_C(2147483647), UINT32_C(2126008810), UINT32_C(2147483647), UINT32_C(2130303777), + UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2143188679), UINT32_C(2136746228), + UINT32_C(2147483647), UINT32_C(2126008810), UINT32_C(2147483647), UINT32_C(2147483647)}; + const uint32_t lookupTable2[16] = { + UINT32_C(4096000000), UINT32_C(2048000000), UINT32_C(1024000000), UINT32_C(512000000), + UINT32_C(255744255), UINT32_C(127110228), UINT32_C(64000000), UINT32_C(32258064), + UINT32_C(16016016), UINT32_C(8000000), UINT32_C(4000000), UINT32_C(2000000), + UINT32_C(1000000), UINT32_C(500000), UINT32_C(250000), UINT32_C(125000)}; + + uint8_t buff[15], gas_range, status = 0; // declare array for registers + int64_t var1, var2, var3, var4, var5, var6, temp_scaled; // Work variables + uint32_t adc_temp, adc_pres; // Raw ADC temperature and pressure + uint16_t adc_hum, adc_gas_res; // Raw ADC humidity and gas + if (waitSwitch) waitForReadings(); // Doesn't return until the readings are finished + getData(BME680_STATUS_REGISTER, buff); // read all 15 bytes in one go + adc_pres = (uint32_t)(((uint32_t)buff[2] * 4096) | ((uint32_t)buff[3] * 16) | + ((uint32_t)buff[4] / 16)); // put the 3 bytes of Pressure + adc_temp = (uint32_t)(((uint32_t)buff[5] * 4096) | ((uint32_t)buff[6] * 16) | + ((uint32_t)buff[7] / 16)); // put the 3 bytes of Temperature + adc_hum = + (uint16_t)(((uint32_t)buff[8] * 256) | (uint32_t)buff[9]); // put the 2 bytes of Humidity + adc_gas_res = + (uint16_t)((uint32_t)buff[13] * 4 | (((uint32_t)buff[14]) / 64)); // put the 2 bytes of Gas + gas_range = buff[14] & 0X0F; // Retrieve the range + status |= buff[14] & 0X20; // See if the gas range is valid + status |= buff[14] & 0X10; // and the measurement is valid + //*******************************// + // First compute the temperature // + //*******************************// + var1 = ((int32_t)adc_temp >> 3) - ((int32_t)_T1 << 1); // Perform calibration/adjustment + var2 = (var1 * (int32_t)_T2) >> 11; // of Temperature values according + var3 = ((var1 >> 1) * (var1 >> 1)) >> 12; // to formula defined by Bosch + var3 = ((var3) * ((int32_t)_T3 << 4)) >> 14; + _tfine = (int32_t)(var2 + var3); + _Temperature = (int16_t)(((_tfine * 5) + 128) >> 8); + //*******************************// + // Now compute the pressure // + //*******************************// + var1 = (((int32_t)_tfine) >> 1) - 64000; + var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * (int32_t)_P6) >> 2; + var2 = var2 + ((var1 * (int32_t)_P5) << 1); + var2 = (var2 >> 2) + ((int32_t)_P4 << 16); + var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) * ((int32_t)_P3 << 5)) >> 3) + + (((int32_t)_P2 * var1) >> 1); + var1 = var1 >> 18; + var1 = ((32768 + var1) * (int32_t)_P1) >> 15; + _Pressure = 1048576 - adc_pres; + _Pressure = (int32_t)((_Pressure - (var2 >> 12)) * ((uint32_t)3125)); + var4 = ((int32_t)1 << 31); + if (_Pressure >= var4) + _Pressure = ((_Pressure / (uint32_t)var1) << 1); + else + _Pressure = ((_Pressure << 1) / (uint32_t)var1); + var1 = ((int32_t)_P9 * (int32_t)(((_Pressure >> 3) * (_Pressure >> 3)) >> 13)) >> 12; + var2 = ((int32_t)(_Pressure >> 2) * (int32_t)_P8) >> 13; + var3 = ((int32_t)(_Pressure >> 8) * (int32_t)(_Pressure >> 8) * (int32_t)(_Pressure >> 8) * + (int32_t)_P10) >> + 17; + _Pressure = (int32_t)(_Pressure) + ((var1 + var2 + var3 + ((int32_t)_P7 << 7)) >> 4); + //**********************// + // Compute the humidity // + //**********************// + temp_scaled = (((int32_t)_tfine * 5) + 128) >> 8; + var1 = (int32_t)(adc_hum - ((int32_t)((int32_t)_H1 * 16))) - + (((temp_scaled * (int32_t)_H3) / ((int32_t)100)) >> 1); + var2 = + ((int32_t)_H2 * (((temp_scaled * (int32_t)_H4) / ((int32_t)100)) + + (((temp_scaled * ((temp_scaled * (int32_t)_H5) / ((int32_t)100))) >> 6) / + ((int32_t)100)) + + (int32_t)(1 << 14))) >> + 10; + var3 = var1 * var2; + var4 = (int32_t)_H6 << 7; + var4 = ((var4) + ((temp_scaled * (int32_t)_H7) / ((int32_t)100))) >> 4; + var5 = ((var3 >> 14) * (var3 >> 14)) >> 10; + var6 = (var4 * var5) >> 1; + _Humidity = (((var3 + var6) >> 10) * ((int32_t)1000)) >> 12; + if (_Humidity > 100000) /* Cap at 100%rH */ + _Humidity = 100000; + else if (_Humidity < 0) + _Humidity = 0; + //**********************// + // Compute the Gas // + //**********************// + uint64_t uvar2; + var1 = + (int64_t)((1340 + (5 * (int64_t)_rng_sw_err)) * ((int64_t)lookupTable1[gas_range])) >> 16; + uvar2 = (((int64_t)((int64_t)adc_gas_res << 15) - (int64_t)(16777216)) + var1); + var3 = (((int64_t)lookupTable2[gas_range] * (int64_t)var1) >> 9); + _Gas = (uint32_t)((var3 + ((int64_t)uvar2 >> 1)) / (int64_t)uvar2); + uint8_t workRegister = readByte(BME680_CONTROL_MEASURE_REGISTER); // Read the control measure + putData(BME680_CONTROL_MEASURE_REGISTER, + (uint8_t)(workRegister | 1)); // Trigger start of next measurement + } // of method readSensors() + void BME680_Class::waitForReadings() { + /*! + @brief Only returns once a measurement on the BME680 has completed + */ + while ((readByte(BME680_STATUS_REGISTER) & // Loop until the "measuring" bit + _BV(BME680_MEASURING_BIT_POSITION)) != 0) + ; // is cleared by BME680 + } // of method waitForReadings + bool BME680_Class::setGas(uint16_t GasTemp, uint16_t GasMillis) { + /*! + * @brief sets the gas measurement target temperature and heating time + * param[in] GasTemp Target temperature in Celsius + * param[in] GasMillis Milliseconds to turn on heater + * return Always returns "true" + */ + waitForReadings(); // Ensure any active reading is finished + uint8_t gasRegister = readByte(BME680_CONTROL_GAS_REGISTER2); // Read current register values + if (GasTemp == 0 || GasMillis == 0) { + // If either input variable is zero // + putData(BME680_CONTROL_GAS_REGISTER1, (uint8_t)B00001000); // Turn off gas heater + putData(BME680_CONTROL_GAS_REGISTER2, + (uint8_t)(gasRegister & B11101111)); // Turn off gas measurements + } else { + putData(BME680_CONTROL_GAS_REGISTER1, (uint8_t)0); // Turn off heater bit to turn on + uint8_t heatr_res; + int32_t var1, var2, var3, var4, var5, heatr_res_x100; + if (GasTemp < 200) + GasTemp = 200; + else if (GasTemp > 400) + GasTemp = 400; // Clamp temperature to min/max + var1 = (((int32_t)(_Temperature / 100) * _H3) / 1000) * 256; + var2 = (_H1 + 784) * (((((_H2 + 154009) * GasTemp * 5) / 100) + 3276800) / 10); + var3 = var1 + (var2 / 2); + var4 = (var3 / (_res_heat_range + 4)); + var5 = (131 * _res_heat) + 65536; + heatr_res_x100 = (int32_t)(((var4 / var5) - 250) * 34); + heatr_res = (uint8_t)((heatr_res_x100 + 50) / 100); + putData(BME680_GAS_HEATER_REGISTER0, heatr_res); + uint8_t factor = 0; + uint8_t durval; + if (GasMillis >= 0xfc0) + durval = 0xff; // Max duration + else { + while (GasMillis > 0x3F) { + GasMillis = GasMillis / 4; + factor += 1; + } // of while loop + durval = (uint8_t)(GasMillis + (factor * 64)); + } // of if-then-else duration exceeds max + putData(BME680_CONTROL_GAS_REGISTER1, (uint8_t)0); // then turn off gas heater + putData(BME680_GAS_DURATION_REGISTER0, durval); + putData(BME680_CONTROL_GAS_REGISTER2, (uint8_t)(gasRegister | B00010000)); + } // of if-then-else turn gas measurements on or off + return true; + } // of method setGas() \ No newline at end of file