Jade Perreault
Created July 21, 2019 © CC BY-NC-SA

HoverGames: Drone Deployable Monitoring and Alerts System

Drone Deployable Weather and Alert Station : Alerts for changing temperature/humidity /wind conditions/Smoke Levels

AdvancedFull instructions providedOver 4 days181

Things used in this project

Hardware components

Electron
Particle Electron
×1
SparkFun Atmospheric Sensor Breakout - BME280
SparkFun Atmospheric Sensor Breakout - BME280
×1
SparkFun Particle Sensor Breakout - MAX30105
SparkFun Particle Sensor Breakout - MAX30105
×1
RDDRONE-FMUK66
NXP RDDRONE-FMUK66
×1
KIT-HGDRONEK66
NXP KIT-HGDRONEK66
×1

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE

Hand tools and fabrication machines

Drill / Driver, Cordless
Drill / Driver, Cordless
Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Hot Air Station, Industrial
Hot Air Station, Industrial
3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Lid

Base

Schematics

BOM

Buy it

Code

SparkFun_AS3935.cpp

C/C++
/*
  This is a library for the ASM AS3935 Franklin Lightning Detector
  By: Elias Santistevan
  SparkFun Electronics
  Date: January, 2019
  License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).

  Feel like supporting our work? Buy a board from SparkFun!
*/


#include "SparkFun_AS3935.h"

// Default constructor, to be used with SPI
SparkFun_AS3935::SparkFun_AS3935() { }

// Another constructor with I2C but receives address from user.  
SparkFun_AS3935::SparkFun_AS3935( i2cAddress address ) { _address = address; }

bool SparkFun_AS3935::begin( TwoWire &wirePort )
{
  // Startup time requires 2ms for the LCO and 2ms more for the RC oscillators
  // which occurs only after the LCO settles. See "Timing" under "Electrical
  // Characteristics" in the datasheet.  
  delay(4); 
  _i2cPort = &wirePort;
  //  _i2cPort->begin(); A call to Wire.begin should occur in sketch 
  //  to avoid multiple begins with other sketches.

  // A return of 0 indicates success, else an error occurred. 
  _i2cPort->beginTransmission(_address);
  uint8_t _ret = _i2cPort->endTransmission();
  if(!_ret)
    return true;
  else
    return false; 
}

bool SparkFun_AS3935::beginSPI(uint8_t user_CSPin, uint32_t spiPortSpeed, SPIClass &spiPort) 
{
  // Startup time requires 2ms for the LCO and 2ms more for the RC oscillators
  // which occurs only after the LCO settles. See "Timing" under "Electrical
  // Characteristics" in the datasheet.  
  delay(4);
  // I'll be using this as my indicator that SPI is to be used and not I2C.   
  _i2cPort = NULL; 
  _spiPort = &spiPort; 
  _spiPortSpeed = spiPortSpeed; // Make sure it's not 500kHz or it will cause feedback with antenna.
  _cs = user_CSPin;
  pinMode(_cs, OUTPUT); 
  digitalWrite(_cs, HIGH);// Deselect the Lightning Detector. 
  _spiPort->begin(); // Set up the SPI pins. 

  // Bit order is different for ESP32
#ifdef ESP32 
    SPI.setBitOrder(SPI_MSBFIRST);
#else
    SPI.setBitOrder(MSBFIRST);
#endif

  return true; 
}
// REG0x00, bit[0], manufacturer default: 0. 
// The product consumes 1-2uA while powered down. If the board is powered down 
// the the TRCO will need to be recalibrated: REG0x08[5] = 1, wait 2 ms, REG0x08[5] = 0.
// SPI and I-squared-C remain active when the chip is powered down. 
void SparkFun_AS3935::powerDown()
{
  _writeRegister(AFE_GAIN, POWER_MASK, 1, 0); 
}

// REG0x3A bit[7].
// This register holds the state of the timer RC oscillator (TRCO),
// after it has been calibrated. The TRCO in this case needs to be recalibrated
// after power down. The following function wakes the IC, sends the "Direct Command" to 
// CALIB_RCO register REG0x3D, waits 2ms and then checks that it has been successfully
// calibrated. Note that I-squared-C and SPI are active during power down. 
bool SparkFun_AS3935::wakeUp()
{

  _writeRegister(AFE_GAIN, POWER_MASK, 0, 0); // Set the power down bit to zero to wake it up

  if(calibrateOsc())
    return true; 
  else 
    return false; 
}

// REG0x00, bits [5:1], manufacturer default: 10010 (INDOOR). 
// This function changes toggles the chip's settings for Indoors and Outdoors. 
void SparkFun_AS3935::setIndoorOutdoor( uint8_t _setting )
{
  if((_setting == INDOOR) || (_setting == OUTDOOR))
    { }
  else
    return;

  if(_setting == INDOOR)
    _writeRegister(AFE_GAIN, GAIN_MASK, INDOOR, 1); 
  if(_setting == OUTDOOR)
    _writeRegister(AFE_GAIN, GAIN_MASK, OUTDOOR, 1); 
}

// REG0x00, bits [5:1], manufacturer default: 10010 (INDOOR). 
// This function returns the indoor/outdoor settting. 
uint8_t SparkFun_AS3935::readIndoorOutdoor(){

  uint8_t regVal = _readRegister(AFE_GAIN); 
  return ((regVal &= ~GAIN_MASK) >> 1); 

}

// REG0x01, bits[3:0], manufacturer default: 0010 (2). 
// This setting determines the threshold for events that trigger the 
// IRQ Pin.  
void SparkFun_AS3935::watchdogThreshold( uint8_t _sensitivity )
{
  if( (_sensitivity < 1) || (_sensitivity > 10) )// 10 is the max sensitivity setting
    return; 
  _writeRegister(THRESHOLD, THRESH_MASK, _sensitivity, 0);
}

// REG0x01, bits[3:0], manufacturer default: 0010 (2). 
// This function returns the threshold for events that trigger the 
// IRQ Pin.  
uint8_t SparkFun_AS3935::readWatchdogThreshold(){

  uint8_t regVal = _readRegister(THRESHOLD);
  return (regVal &= (~THRESH_MASK));
}

// REG0x01, bits [6:4], manufacturer default: 010 (2).
// The noise floor level is compared to a known reference voltage. If this
// level is exceeded the chip will issue an interrupt to the IRQ pin,
// broadcasting that it can not operate properly due to noise (INT_NH).
// Check datasheet for specific noise level tolerances when setting this register. 
void SparkFun_AS3935::setNoiseLevel( uint8_t _floor )
{
  if( (_floor < 1) || (_floor > 7) )
    return; 
  
  _writeRegister(THRESHOLD, NOISE_FLOOR_MASK, _floor, 4); 
}

// REG0x01, bits [6:4], manufacturer default: 010 (2).
// This function will return the set noise level threshold: default is 2.
uint8_t SparkFun_AS3935::readNoiseLevel(){

  uint8_t regVal = _readRegister(THRESHOLD);
  return (regVal & ~NOISE_FLOOR_MASK) >> 4;

}

// REG0x02, bits [3:0], manufacturer default: 0010 (2).
// This setting, like the watchdog threshold, can help determine between false
// events and actual lightning. The shape of the spike is analyzed during the
// chip's signal validation routine. Increasing this value increases robustness
// at the cost of sensitivity to distant events. 
void SparkFun_AS3935::spikeRejection( uint8_t _spSensitivity )
{
  if( (_spSensitivity < 1) || (_spSensitivity > 11) )
    return; 

  _writeRegister(LIGHTNING_REG, SPIKE_MASK, _spSensitivity, 0); 
}

// REG0x02, bits [3:0], manufacturer default: 0010 (2).
// This function returns the value of the spike rejection register. This value
// helps to differentiate between events and acutal lightning, by analyzing the 
// shape of the spike during  chip's signal validation routine. 
// Increasing this value increases robustness at the cost of sensitivity to distant events. 
uint8_t SparkFun_AS3935::readSpikeRejection(){

  uint8_t regVal = _readRegister(LIGHTNING_REG);
  return (regVal &= ~SPIKE_MASK);

}
// REG0x02, bits [5:4], manufacturer default: 0 (single lightning strike).
// The number of lightning events before IRQ is set high. 15 minutes is The 
// window of time before the number of detected lightning events is reset. 
// The number of lightning strikes can be set to 1,5,9, or 16. 
void SparkFun_AS3935::lightningThreshold( uint8_t _strikes )
{

  uint8_t bits; 

  if( _strikes == 1)
    bits = 0;
  else if( _strikes == 5)
    bits = 1;
  else if( _strikes == 9)
    bits = 2; 
  else if( _strikes == 16)
    bits = 3; 
  else
    return;

  _writeRegister(LIGHTNING_REG, LIGHT_MASK, bits, 4); 
}

// REG0x02, bits [5:4], manufacturer default: 0 (single lightning strike).
// This function will return the number of lightning strikes must strike within
// a 15 minute window before it triggers an event on the IRQ pin. Default is 1. 
uint8_t SparkFun_AS3935::readLightningThreshold(){

  uint8_t regVal = _readRegister(LIGHTNING_REG);

  regVal &= ~LIGHT_MASK;
  regVal >>= 4; // Front of the line. 

  if(regVal == 0)
    return 1; 
  else if(regVal == 1)
    return 5;
  else if(regVal == 2)
    return 9; 
  else if(regVal == 3)
    return 16; 
  else 
    return regVal;
  
}

// REG0x02, bit [6], manufacturer default: 1. 
// This register clears the number of lightning strikes that has been read in
// the last 15 minute block. 
void SparkFun_AS3935::clearStatistics(bool _clearStat)
{
  if(_clearStat != true)
    return;
  //Write high, then low, then high to clear.
  _writeRegister(LIGHTNING_REG, STAT_MASK, 1, 6);
  _writeRegister(LIGHTNING_REG, STAT_MASK, 0, 6); 
  _writeRegister(LIGHTNING_REG, STAT_MASK, 1, 6);
}

// REG0x03, bits [3:0], manufacturer default: 0. 
// When there is an event that exceeds the watchdog threshold, the register is written
// with the type of event. This consists of two messages: INT_D (disturber detected) and 
// INT_L (Lightning detected). A third interrupt INT_NH (noise level too HIGH) 
// indicates that the noise level has been exceeded and will persist until the
// noise has ended. Events are active HIGH. There is a one second window of time to
// read the interrupt register after lightning is detected, and 1.5 after
// disturber.  
uint8_t SparkFun_AS3935::readInterruptReg()
{
    // A 2ms delay is added to allow for the memory register to be populated 
    // after the interrupt pin goes HIGH. See "Interrupt Management" in
    // datasheet. 
    delay(2);

    uint8_t _interValue; 
    _interValue = _readRegister(INT_MASK_ANT); 
    _interValue &= INT_MASK; 

    return(_interValue); 

}

// REG0x03, bit [5], manufacturere default: 0.
// This setting will change whether or not disturbers trigger the IRQ Pin. 
void SparkFun_AS3935::maskDisturber(bool _state)
{
  if(_state == true || _state == false)
    { }
  else
    return;
  
  _writeRegister(INT_MASK_ANT, DISTURB_MASK, _state, 5); 
  
}


// REG0x03, bit [5], manufacturere default: 0.
// This setting will return whether or not disturbers trigger the IRQ Pin. 
uint8_t SparkFun_AS3935::readMaskDisturber(){

  uint8_t regVal = _readRegister(INT_MASK_ANT);
  return (regVal &= ~DISTURB_MASK) >> 5;

}

// REG0x03, bit [7:6], manufacturer default: 0 (16 division ratio). 
// The antenna is designed to resonate at 500kHz and so can be tuned with the
// following setting. The accuracy of the antenna must be within 3.5 percent of
// that value for proper signal validation and distance estimation.
void SparkFun_AS3935::changeDivRatio(uint8_t _divisionRatio)
{

  uint8_t bits; 

  if(_divisionRatio == 16) 
    bits = 0; 
  else if(_divisionRatio == 32) 
    bits = 1;
  else if(_divisionRatio == 64) 
    bits = 2;
  else if(_divisionRatio == 128) 
    bits = 3; 
  else
    return; 

  _writeRegister(INT_MASK_ANT, DIV_MASK, bits, 6); 

}

// REG0x03, bit [7:6], manufacturer default: 0 (16 division ratio). 
// This function returns the current division ratio of the resonance frequency.
// The antenna resonance frequency should be within 3.5 percent of 500kHz, and
// so when modifying the resonance frequency with the internal capacitors
// (tuneCap()) it's important to keep in mind that the displayed frequency on
// the IRQ pin is divided by this number. 
uint8_t SparkFun_AS3935::readDivRatio(){

  uint8_t regVal = _readRegister(INT_MASK_ANT); 
  regVal &= ~DIV_MASK; 
  regVal >>= 6; // Front of the line. 

  if( regVal == 0 )
    return 16; 
  else if(regVal == 1) 
    return 32;
  else if(regVal == 2)
    return 64;
  else if (regVal == 3) 
    return 128; 
  else
    return UNKNOWN_ERROR;

}

// REG0x07, bit [5:0], manufacturer default: 0. 
// This register holds the distance to the front of the storm and not the
// distance to a lightning strike.  
uint8_t SparkFun_AS3935::distanceToStorm()
{

  uint8_t _dist = _readRegister(DISTANCE); 
  _dist &= DISTANCE_MASK; 
  return(_dist); 

}

// REG0x08, bits [5,6,7], manufacturer default: 0. 
// This will send the frequency of the oscillators to the IRQ pin. 
//  _osc 1, bit[5] = TRCO - System RCO at 32.768kHz
//  _osc 2, bit[6] = SRCO - Timer RCO Oscillators 1.1MHz
//  _osc 3, bit[7] = LCO - Frequency of the Antenna
void SparkFun_AS3935::displayOscillator(bool _state, uint8_t _osc)
{
  if( (_osc < 1) || (_osc > 3) )
    return;

  if(_state == true){
    if(_osc == 1)
      _writeRegister(FREQ_DISP_IRQ, OSC_MASK, 1, 5); 
    if(_osc == 2)
      _writeRegister(FREQ_DISP_IRQ, OSC_MASK, 1, 6); 
    if(_osc == 3)
      _writeRegister(FREQ_DISP_IRQ, OSC_MASK, 1, 7); 
  }

  if(_state == false){
    if(_osc == 1)
      _writeRegister(FREQ_DISP_IRQ, OSC_MASK, 0, 5); //Demonstrative
    if(_osc == 2)
      _writeRegister(FREQ_DISP_IRQ, OSC_MASK, 0, 6); 
    if(_osc == 3)
      _writeRegister(FREQ_DISP_IRQ, OSC_MASK, 0, 7); 
  }

}

// REG0x08, bits [3:0], manufacturer default: 0. 
// This setting will add capacitance to the series RLC antenna on the product
// to help tune its resonance. The datasheet specifies being within 3.5 percent
// of 500kHz to get optimal lightning detection and distance sensing.  
// It's possible to add up to 120pF in steps of 8pF to the antenna. 
void SparkFun_AS3935::tuneCap(uint8_t farad)
{
  if (farad < 0 || farad > 120)
    return; 
  else if ( farad % 8 != 0)
    return; 
  else 
    farad /= 8; 
    

  _writeRegister(FREQ_DISP_IRQ, CAP_MASK, farad, 0);    
}

// REG0x08, bits [3:0], manufacturer default: 0. 
// This setting will return the capacitance of the internal capacitors. It will
// return a value from one to 15 multiplied by the 8pF steps of the internal
// capacitance.
uint8_t SparkFun_AS3935::readTuneCap(){

  uint8_t regVal = _readRegister(FREQ_DISP_IRQ);
  return ((regVal &= ~CAP_MASK) * 8); //Multiplied by 8pF

}

// LSB =  REG0x04, bits[7:0]
// MSB =  REG0x05, bits[7:0]
// MMSB = REG0x06, bits[4:0]
// This returns a 20 bit value that is the 'energy' of the lightning strike.
// According to the datasheet this is only a pure value that doesn't have any
// physical meaning. 
uint32_t SparkFun_AS3935::lightningEnergy()
{

  uint32_t _pureLight = _readRegister(ENERGY_LIGHT_MMSB);
  _pureLight &= ENERGY_MASK; 
  _pureLight <<= 16;
  _pureLight |= _readRegister(ENERGY_LIGHT_MSB);
  _pureLight <<= 8;
  _pureLight |= _readRegister(ENERGY_LIGHT_LSB);
  return _pureLight;

}

// REG0x3D, bits[7:0]
// This function calibrates both internal oscillators The oscillators are tuned
// based on the resonance frequency of the antenna and so it should be trimmed
// before the calibration is done. 
bool SparkFun_AS3935::calibrateOsc(){

  _writeRegister(CALIB_RCO, WIPE_ALL, DIRECT_COMMAND, 0); // Send command to calibrate the oscillators 
  Serial.println("Calibrating Oscillators");

  displayOscillator(true, 2);
  delay(2); // Give time for the internal oscillators to start up.  
  displayOscillator(false, 2); 

  // Check it they were calibrated successfully.   
  uint8_t regValSrco = _readRegister(CALIB_SRCO);
  uint8_t regValTrco = _readRegister(CALIB_TRCO);

  regValSrco &= CALIB_MASK; 
  regValSrco >>= 6;
  regValTrco &= CALIB_MASK; 
  regValTrco >>= 6;

  if(!regValSrco && !regValTrco) // Zero upon success
    return true;
  else
    return false; 
}

// REG0x3C, bits[7:0]
// This function resets all settings to their default values. 
void SparkFun_AS3935::resetSettings(){
      
  _writeRegister(RESET, WIPE_ALL, DIRECT_COMMAND, 0);

}

// This function handles all I2C write commands. It takes the register to write
// to, then will mask the part of the register that coincides with the
// given register, and then write the given bits to the register starting at
// the given start position.  
void SparkFun_AS3935::_writeRegister(uint8_t _wReg, uint8_t _mask, uint8_t _bits, uint8_t _startPosition)
{
  if(_i2cPort == NULL) {
    _spiWrite = _readRegister(_wReg); // Get the current value of the register
    _spiWrite &= _mask; // Mask the position we want to write to
    _spiWrite |= (_bits << _startPosition); // Write the given bits to the variable
    _spiPort->beginTransaction(SPISettings(_spiPortSpeed, MSBFIRST, SPI_MODE1)); 
    digitalWrite(_cs, LOW); // Start communication
    _spiPort->transfer(_wReg); // Start write command at given register
    _spiPort->transfer(_spiWrite); // Write to register
    digitalWrite(_cs, HIGH); // End communcation
    _spiPort->endTransaction();
  }
  else { 
    _i2cWrite = _readRegister(_wReg); // Get the current value of the register
    _i2cWrite &= _mask; // Mask the position we want to write to.
    _i2cWrite |= (_bits << _startPosition);  // Write the given bits to the variable
    _i2cPort->beginTransmission(_address); // Start communication.
    _i2cPort->write(_wReg); // at register....
    _i2cPort->write(_i2cWrite); // Write register...
    _i2cPort->endTransmission(); // End communcation.
  }
}

// This function reads the given register. 
uint8_t SparkFun_AS3935::_readRegister(uint8_t _reg)
{

  if(_i2cPort == NULL) {
    _spiPort->beginTransaction(SPISettings(_spiPortSpeed, MSBFIRST, SPI_MODE1)); 
    digitalWrite(_cs, LOW); // Start communication.
    _spiPort->transfer(_reg |= SPI_READ_M);  // Register OR'ed with SPI read command. 
    _regValue = _spiPort->transfer(0); // Get data from register.  
    // According to datsheet, the chip select must be written HIGH, LOW, HIGH
    // to correctly end the READ command. 
    digitalWrite(_cs, HIGH); 
    digitalWrite(_cs, LOW); 
    digitalWrite(_cs, HIGH); 
    _spiPort->endTransaction();
    return(_regValue); 
  }
  else {
    _i2cPort->beginTransmission(_address); 
    _i2cPort->write(_reg); // Moves pointer to register.
    _i2cPort->endTransmission(false); // 'False' here sends a restart message so that bus is not released
    _i2cPort->requestFrom(_address, 1); // Read the register, only ever once. 
    _regValue = _i2cPort->read();
    return(_regValue);
  }
}

SparkFunBME280.h

C/C++
/******************************************************************************
SparkFunBME280.h
BME280 Particle Photon and Core Driver
Orginal by: Marshall Taylor @ SparkFun Electronics
Particle adaption by: Markus Haack (https://github.com/mhaack)
https://github.com/mhaack/SparkFun_BME280

Development environment specifics:
Particle IDE or Web IDE

This code is released under the [MIT License](http://opensource.org/licenses/MIT).
Please review the LICENSE.md file included with this example. If you have any questions
or concerns with licensing, please contact techsupport@sparkfun.com.
Distributed as-is; no warranty is given.
******************************************************************************/

// Test derived class for base class SparkFunIMU
#ifndef __BME280_H__
#define __BME280_H__

#include "application.h"

#define I2C_MODE 0
#define SPI_MODE 1

//Register names:
#define BME280_DIG_T1_LSB_REG			0x88
#define BME280_DIG_T1_MSB_REG			0x89
#define BME280_DIG_T2_LSB_REG			0x8A
#define BME280_DIG_T2_MSB_REG			0x8B
#define BME280_DIG_T3_LSB_REG			0x8C
#define BME280_DIG_T3_MSB_REG			0x8D
#define BME280_DIG_P1_LSB_REG			0x8E
#define BME280_DIG_P1_MSB_REG			0x8F
#define BME280_DIG_P2_LSB_REG			0x90
#define BME280_DIG_P2_MSB_REG			0x91
#define BME280_DIG_P3_LSB_REG			0x92
#define BME280_DIG_P3_MSB_REG			0x93
#define BME280_DIG_P4_LSB_REG			0x94
#define BME280_DIG_P4_MSB_REG			0x95
#define BME280_DIG_P5_LSB_REG			0x96
#define BME280_DIG_P5_MSB_REG			0x97
#define BME280_DIG_P6_LSB_REG			0x98
#define BME280_DIG_P6_MSB_REG			0x99
#define BME280_DIG_P7_LSB_REG			0x9A
#define BME280_DIG_P7_MSB_REG			0x9B
#define BME280_DIG_P8_LSB_REG			0x9C
#define BME280_DIG_P8_MSB_REG			0x9D
#define BME280_DIG_P9_LSB_REG			0x9E
#define BME280_DIG_P9_MSB_REG			0x9F
#define BME280_DIG_H1_REG				0xA1
#define BME280_CHIP_ID_REG				0xD0 //Chip ID
#define BME280_RST_REG					0xE0 //Softreset Reg
#define BME280_DIG_H2_LSB_REG			0xE1
#define BME280_DIG_H2_MSB_REG			0xE2
#define BME280_DIG_H3_REG				0xE3
#define BME280_DIG_H4_MSB_REG			0xE4
#define BME280_DIG_H4_LSB_REG			0xE5
#define BME280_DIG_H5_MSB_REG			0xE6
#define BME280_DIG_H6_REG				0xE7
#define BME280_CTRL_HUMIDITY_REG		0xF2 //Ctrl Humidity Reg
#define BME280_STAT_REG					0xF3 //Status Reg
#define BME280_CTRL_MEAS_REG			0xF4 //Ctrl Measure Reg
#define BME280_CONFIG_REG				0xF5 //Configuration Reg
#define BME280_PRESSURE_MSB_REG			0xF7 //Pressure MSB
#define BME280_PRESSURE_LSB_REG			0xF8 //Pressure LSB
#define BME280_PRESSURE_XLSB_REG		0xF9 //Pressure XLSB
#define BME280_TEMPERATURE_MSB_REG		0xFA //Temperature MSB
#define BME280_TEMPERATURE_LSB_REG		0xFB //Temperature LSB
#define BME280_TEMPERATURE_XLSB_REG		0xFC //Temperature XLSB
#define BME280_HUMIDITY_MSB_REG			0xFD //Humidity MSB
#define BME280_HUMIDITY_LSB_REG			0xFE //Humidity LSB


//Class SensorSettings.  This object is used to hold settings data.  The application
//uses this classes' data directly.  The settings are adopted and sent to the sensor
//at special times, such as .begin.  Some are used for doing math.
//
//This is a kind of bloated way to do this.  The trade-off is that the user doesn't
//need to deal with #defines or enums with bizarre names.
//
//A power user would strip out SensorSettings entirely, and send specific read and
//write command directly to the IC. (ST #defines below)
//
struct SensorSettings
{
  public:

  //Main Interface and mode settings
    uint8_t commInterface;
    uint8_t I2CAddress;
    uint8_t chipSelectPin;

	uint8_t runMode;
	uint8_t tStandby;
	uint8_t filter;
	uint8_t tempOverSample;
	uint8_t pressOverSample;
	uint8_t humidOverSample;

};

//Used to hold the calibration constants.  These are used
//by the driver as measurements are being taking
struct SensorCalibration
{
  public:
	uint16_t dig_T1;
	int16_t dig_T2;
	int16_t dig_T3;

	uint16_t dig_P1;
	int16_t dig_P2;
	int16_t dig_P3;
	int16_t dig_P4;
	int16_t dig_P5;
	int16_t dig_P6;
	int16_t dig_P7;
	int16_t dig_P8;
	int16_t dig_P9;

	uint8_t dig_H1;
	int16_t dig_H2;
	uint8_t dig_H3;
	int16_t dig_H4;
	int16_t dig_H5;
	uint8_t dig_H6;

};

//This is the man operational class of the driver.

class BME280
{
  public:
    //settings
    SensorSettings settings;
	SensorCalibration calibration;
	int32_t t_fine;

	//Constructor generates default SensorSettings.
	//(over-ride after construction if desired)
    BME280( void );
    //~BME280() = default;

	//Call to apply SensorSettings.
	//This also gets the SensorCalibration constants
    uint8_t begin( void );

	//Software reset routine
	void reset( void );

    //Returns the values as floats.
    float readFloatPressure( void );
	float readFloatAltitudeMeters( void );
	float readFloatAltitudeFeet( void );

	float readFloatHumidity( void );

	//Temperature related methods
    float readTempC( void );
    float readTempF( void );

    //The following utilities read and write

	//ReadRegisterRegion takes a uint8 array address as input and reads
	//a chunk of memory into that array.
    void readRegisterRegion(uint8_t*, uint8_t, uint8_t );
	//readRegister reads one register
    uint8_t readRegister(uint8_t);
    //Reads two regs, LSByte then MSByte order, and concatenates them
	//Used for two-byte reads
	int16_t readRegisterInt16( uint8_t offset );
	//Writes a byte;
    void writeRegister(uint8_t, uint8_t);

};



#endif  // End of __BME280_H__ definition check

Test code November with WInd Direction Speed

C/C++
#include <SF_CCS811.h>
#include <Adafruit_BME280.h>
#include "MAX30105.h"
#include <SPI.h>
#include <Wire.h>
#include "SparkFun_AS3935.h"
#define AS3935_ADDR 0x03 
#define INDOOR 0x12 
#define OUTDOOR 0xE
#define LIGHTNING_INT 0x08
#define DISTURBER_INT 0x04
#define NOISE_INT 0x01
//======================================
#define BME_SCK D4
#define BME_MISO D3
#define BME_MOSI D2
#define BME_CS D5
#define SEALEVELPRESSURE_HPA (1013.25)
//======================================
uint32_t lastReset = 0;

#define CCS811_ADDR 0x5B //Default I2C Address
//#define CCS811_ADDR 0x5A //Alternate I2C Address

CCS811 mySensor(CCS811_ADDR);
//======================================

Adafruit_BME280 bme; // I2C
unsigned long delayTime;
unsigned long duration;
//=======================================
SparkFun_AS3935 lightning(AS3935_ADDR);
MAX30105 particleSensor;
const int lightningInt = 4; 
int intVal = 0;
int noise = 5; // Value between 1-7 
int disturber = 10; // Value between 1-10
//======================================
long startTime;
long samplesTaken = 0; //Counter for calculating the Hz or read rate
//=================**WeatherVane**=====================
int button = A4;                   // button is connected to D0
int LED = D7;                      // LED is connected to D1
int val = 0;
//========================================
 unsigned long timeSinceLastTick = 0;
 unsigned long lastTick = 0; 
String windDir = "";
float windSpeed = 0;
long secsClock = 0;
#define WIND_SPD_PIN A4
#define WIND_DIR_PIN A5
#define AIR_RST      4
#define AIR_WAKE     15
#define DONE_LED     7
//============================================

void setup() {
     Serial.begin(9600); 
     WiFi.connect(WIFI_CONNECT_SKIP_LISTEN);
     Wire.begin();
     lastReset = millis();
//======================================
  pinMode(LED, OUTPUT);            // sets pin as output
  pinMode(button, INPUT_PULLDOWN); // sets pin as input
  pinMode(A4, INPUT_PULLDOWN);
  pinMode(WIND_SPD_PIN, INPUT_PULLDOWN);     // Wind speed sensor
  attachInterrupt(digitalPinToInterrupt(WIND_SPD_PIN), windTick, FALLING);
//=========================================
 Serial.println("MAX30105 Basic Readings Example");
  particleSensor.begin();
  particleSensor.setup(); //Configure sensor. Use 6.4mA for LED drive
  byte ledBrightness = 255; //Options: 0=Off to 255=50mA
  byte sampleAverage = 2; //Options: 1, 2, 4, 8, 16, 32
  byte ledMode = 3; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
  int sampleRate = 50; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
  int pulseWidth = 118; //Options: 69, 118, 215, 411
  int adcRange = 4096; //Options: 2048, 4096, 8192, 16384
  particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
  particleSensor.enableDIETEMPRDY();
  startTime = millis();
//=======================================
bme.begin();
mySensor.begin();
//========================================
  pinMode(lightningInt, INPUT); 
  Serial.println("AS3935 Franklin Lightning Detector"); 
  if( !lightning.begin() ) { // Initialize the sensor. 
    Serial.println ("Lightning Detector did not start up, freezing!"); 
    while(1); 
  }
  else
    Serial.println("Schmow-ZoW, Lightning Detector Ready!");
  lightning.setIndoorOutdoor(OUTDOOR);
//=======================================
}

void loop() {
    
     char message1[120]; 
    char message2[120];
    char message3[120];
    
     if (millis() - lastReset > 5*60000UL) {
    System.reset();
  }
//======================================
  static unsigned long outLoopTimer = 0;
  static unsigned long clockTimer = 0;
  static unsigned long tempMSClock = 0;

  tempMSClock += millis() - clockTimer;
  clockTimer = millis();
  while (tempMSClock >= 1000)
  {
    secsClock++;
    tempMSClock -= 1000;
  }
   
    Serial.print("Wind dir: ");
    windDirCalc(analogRead(WIND_DIR_PIN));
    Serial.print("  ");
    Serial.print(windDir);
    Serial.println(" Degrees ");
    
    if (timeSinceLastTick != 0) windSpeed = 1000.0/timeSinceLastTick;
    Serial.print("Windspeed: ");
    duration = pulseIn(A4, LOW);
    timeSinceLastTick = 0;
    Serial.print(windSpeed*1.94);
    Serial.print(" mph ");
    Serial.print(windSpeed*2.4);
    Serial.println(" kph ");
    delay(10);
//=====================================   

  val = pinReadFast(button);       // read the input pin
  digitalWriteFast(LED, val);
  delay(1);
  duration = pulseIn(A4, HIGH);
 // Serial.printlnf("%d us", duration);

//==================**BME280**====================
    Serial.print("Temperature = ");
    Serial.print(bme.readTemperature());
    Serial.print(" *C ");
    Serial.print("Pressure = ");
    Serial.print(bme.readPressure() / 100.0F);
    Serial.print(" hPa ");
    Serial.print("Approx. Altitude = ");
    Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    Serial.print(" m ");
    Serial.print("Humidity = ");
    Serial.print(bme.readHumidity());
    Serial.print(" % ");
    Serial.println();
  //========================**MAX30105***===============================
    samplesTaken++;
    Serial.print(" R[");
    Serial.print(particleSensor.getRed());
    float RED = particleSensor.getRed();
    Serial.print("] IR[");
    float IR = particleSensor.getIR();
    Serial.print(particleSensor.getIR());
    Serial.print("] G[");
    float GRN = particleSensor.getGreen();
    Serial.print(particleSensor.getGreen());
    Serial.print("] Hz[");
    Serial.print((float)samplesTaken / ((millis() - startTime) / 1000.0), 2);
    Serial.print("]");
    float temperature = particleSensor.readTemperature();
    Serial.print("temperatureC=");
    Serial.print(temperature, 4);
    Serial.println();
    particleSensor.nextSample(); //We're finished with this sample so move to next sample

  //=======================**Lightning***================================
  if(digitalRead(lightningInt) == HIGH){
 
    intVal = lightning.readInterruptReg();
    if(intVal == NOISE_INT){
      Serial.println("Noise."); 
 
    }
    else if(intVal == DISTURBER_INT){
      Serial.println("Disturber."); 

    }
    else if(intVal == LIGHTNING_INT){
      Serial.println("Lightning Strike Detected!"); 

      byte distance = lightning.distanceToStorm(); 
      Serial.print("Approximately: "); 
      Serial.print(distance); 
      Serial.println("km away!"); 
    }
  }

  //===============================**CCS811**==============================
   mySensor.readAlgorithmResults();
    Serial.print("CO2[");
    Serial.print(mySensor.getCO2());
    Serial.print("] tVOC[");
    Serial.print(mySensor.getTVOC());
    Serial.print("]");
    Serial.println();
    
  //========================================================================    

sprintf(message3,"CO2 %d,TVOC %d,WNDSP %.2f,WNDDIR %.2f,RED %.2f,IR %.2f,GRN %.2f,IR_TMP %f",mySensor.getCO2(),mySensor.getTVOC(),(windSpeed*2.4),windDir,RED,IR,GRN,temperature);
sprintf(message1,"BME_TMP %.1f,BME_PRESS %f,BME_ALT %.2f,BME_HUM %.2f",bme.readTemperature(),(bme.readPressure() / 100.0F),(bme.readAltitude(SEALEVELPRESSURE_HPA)),bme.readHumidity());


Particle.publish("String2", message1, PRIVATE);
Particle.publish("String1", message3 , PRIVATE);
  //========================================================================
  delay(1000); // Slow it down.
}

//========================================================================
//========================================================================
//========================================================================
//========================================================================
//========================================================================

void windTick(void) {
    
  timeSinceLastTick = millis() - lastTick;
  lastTick = millis();
  
}

void windDirCalc(int vin) {
    
  if      (vin < 1.50) windDir="202.5";
  else if (vin < 1.4) windDir = "180";
  else if (vin < 2.93) windDir = "247.5";
  else if (vin < 3.08) windDir = "225";
  else if (vin < 3.0) windDir = "292.5";
  else if (vin < 3.1) windDir = "270";
  else if (vin < 0.32) windDir = "112.5";
  else if (vin < 0.90) windDir = "135";
  else if (vin < 3.2) windDir = "337.5";
  else if (vin < 3.3) windDir = "315";
  else if (vin < 0.41) windDir = "67.5";
  else if (vin < 0.45) windDir = "90";
  else if (vin < 1.98) windDir = "22.5";
  else if (vin < 2.25) windDir = "45";
  else if (vin < 3.25) windDir = "0";
  else windDir = "0";
}

Main Test Program with all the Sensors

C/C++
www.particle.io .. Set up User Name Password and Navigate to WEB IDE and Paste the COde into a new Program Sketch
https://go.particle.io/shared_apps/5d8bc8c2fb89bf000593c1ba (Shareable Code)
#include <SPI.h>
#include <Wire.h>
#include "SparkFun_AS3935.h"
#include "SparkFun_AS3935.h"
#include "SparkFunBME280.h"
#include <Adafruit_CCS811.h>
#include "MAX30105.h"
#if defined(PARTICLE)
 SYSTEM_THREAD(ENABLED)
#endif
const long interval = 1000;
unsigned long previousMillis =  0;
//==========================
MAX30105 particleSensor;
#define debug Serial
//==========================
Adafruit_CCS811 ccs;
//===========================
  BME280 mySensor;
//=======================
#include <SPI.h>
#include <Wire.h>
#include "SparkFun_AS3935.h"
// 0x03 is default, but the address can also be 0x02, or 0x01.
#define AS3935_ADDR 0x03 
#define INDOOR 0x12 
#define OUTDOOR 0xE
#define LIGHTNING_INT 0x08
#define DISTURBER_INT 0x04
#define NOISE_INT 0x01

SparkFun_AS3935 lightning(AS3935_ADDR);
const int lightningInt = 4; 
byte noiseFloor = 2;
byte watchDogVal = 2;
byte spike = 2;
byte lightningThresh = 1; 
byte intVal = 0;
//========================
//****Weather Station***********
//========================
#define ENABLE_LIGHTNING

//SoftwareSerial imp(8, 9); // RX, TX into Imp pin 7

//Hardware pin definitions
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// digital I/O pins
const byte WSPEED = 3;
const byte RAIN = 2;
const byte STAT1 = 7;

#ifdef ENABLE_LIGHTNING
const byte LIGHTNING_IRQ = 4; //Not really an interrupt pin, we will catch it in software
const byte slaveSelectPin = 10; //SS for AS3935
#endif

// analog I/O pins
const byte WDIR = A0;
const byte LIGHT = A1;
const byte BATT = A2;
const byte REFERENCE_3V3 = A3;
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#ifdef ENABLE_LIGHTNING
//#include "AS3935.h" //Lighting dtector
#include <SPI.h> //Needed for lighting sensor

byte SPItransfer(byte sendByte);

AS3935 AS3935(SPItransfer, slaveSelectPin, LIGHTNING_IRQ);
#endif
long lastSecond; //The millis counter to see when a second rolls by
unsigned int minutesSinceLastReset; //Used to reset variables after 24 hours. Imp should tell us when it's midnight, this is backup.
byte seconds; //When it hits 60, increase the current minute
byte seconds_2m; //Keeps track of the "wind speed/dir avg" over last 2 minutes array of data
byte minutes; //Keeps track of where we are in various arrays of data
byte minutes_10m; //Keeps track of where we are in wind gust/dir over last 10 minutes array of data

long lastWindCheck = 0;
volatile long lastWindIRQ = 0;
volatile byte windClicks = 0;

#ifdef ENABLE_LIGHTNING
byte lightning_distance = 0;
#endif

byte windspdavg[120]; //120 bytes to keep track of 2 minute average
#define WIND_DIR_AVG_SIZE 120
int winddiravg[WIND_DIR_AVG_SIZE]; //120 ints to keep track of 2 minute average
float windgust_10m[10]; //10 floats to keep track of largest gust in the last 10 minutes
int windgustdirection_10m[10]; //10 ints to keep track of 10 minute max
volatile float rainHour[60]; //60 floating numbers to keep track of 60 minutes of rain

//These are all the weather values that wunderground expects:
int winddir; // [0-360 instantaneous wind direction]
float windspeedmph; // [mph instantaneous wind speed]
float windgustmph; // [mph current wind gust, using software specific time period]
int windgustdir; // [0-360 using software specific time period]
float windspdmph_avg2m; // [mph 2 minute average wind speed mph]
int winddir_avg2m; // [0-360 2 minute average wind direction]
float windgustmph_10m; // [mph past 10 minutes wind gust mph ]
int windgustdir_10m; // [0-360 past 10 minutes wind gust direction]
float humidity; // [%]
float tempf; // [temperature F]
float rainin; // [rain inches over the past hour)] -- the accumulated rainfall in the past 60 min
volatile float dailyrainin; // [rain inches so far today in local time]
//float baromin = 30.03;// [barom in] - It's hard to calculate baromin locally, do this in the agent
float pressure;
//float dewptf; // [dewpoint F] - It's hard to calculate dewpoint locally, do this in the agent

//These are not wunderground values, they are just for us
float batt_lvl = 11.8;
float light_lvl = 0.72;

// volatiles are subject to modification by IRQs
volatile unsigned long raintime, rainlast, raininterval, rain;

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

//Interrupt routines (these are called by the hardware interrupts, not by the main code)
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void rainIRQ()
// Count rain gauge bucket tips as they occur
// Activated by the magnet and reed switch in the rain gauge, attached to input D2
{
	raintime = millis(); // grab current time
	raininterval = raintime - rainlast; // calculate interval between this and last event

	if (raininterval > 10) // ignore switch-bounce glitches less than 10mS after initial edge
	{
		dailyrainin += 0.011; //Each dump is 0.011" of water
		rainHour[minutes] += 0.011; //Increase this minute's amount of rain

		rainlast = raintime; // set up for next event
	}
}

void wspeedIRQ()
// Activated by the magnet in the anemometer (2 ticks per rotation), attached to input D3
{
	if (millis() - lastWindIRQ > 10) // Ignore switch-bounce glitches less than 10ms (142MPH max reading) after the reed switch closes
	{
		lastWindIRQ = millis(); //Grab the current time
		windClicks++; //There is 1.492MPH for each click per second.
	}
}
//========================
void setup() {
   Serial.begin(9600);
//=========================   
   Serial.println("CCS811 test");
  
(ccs.begin());
 float temp = ccs.calculateTemperature();
  ccs.setTempOffset(temp - 25.0);
  //=========================
    debug.begin(9600);
   debug.println("MAX30105 Basic Readings Example");
 // Initialize sensor
 if (particleSensor.begin() == false) {
   debug.println("MAX30105 was not found. Please check wiring/power. ");
   while (1);
 }
 particleSensor.setup(); //Configure sensor. Use 6.4mA for LED drive
  //=========================
	mySensor.settings.commInterface = I2C_MODE;
	mySensor.settings.I2CAddress = 0x77;
	//  0, Sleep mode
	//  1 or 2, Forced mode
	//  3, Normal mode
	mySensor.settings.runMode = 3; //Normal mode
	//  0, 0.5ms
	//  1, 62.5ms
	//  2, 125ms
	//  3, 250ms
	//  4, 500ms
	//  5, 1000ms
	//  6, 10ms
	//  7, 20ms
	mySensor.settings.tStandby = 0;
	//  0, filter off
	//  1, coefficients = 2
	//  2, coefficients = 4
	//  3, coefficients = 8
	//  4, coefficients = 16
	mySensor.settings.filter = 4;
	//tempOverSample can be:
	//  0, skipped
	//  1 through 5, oversampling *1, *2, *4, *8, *16 respectively
	mySensor.settings.tempOverSample = 5;
	//pressOverSample can be:
	//  0, skipped
	//  1 through 5, oversampling *1, *2, *4, *8, *16 respectively
    mySensor.settings.pressOverSample = 5;
	//humidOverSample can be:
	//  0, skipped
	//  1 through 5, oversampling *1, *2, *4, *8, *16 respectively
	mySensor.settings.humidOverSample = 5;
	Serial.begin(57600);
	Serial.print("Program Started\n");
	Serial.print("Starting BME280... result of .begin(): 0x");
	//Calling .begin() causes the settings to be loaded
	delay(10);  //Make sure sensor had enough time to turn on. BME280 requires 2ms to start up.
	Serial.println(mySensor.begin(), HEX);
	Serial.print("Displaying ID, reset and ctrl regs\n");
	Serial.print("ID(0xD0): 0x");
	Serial.println(mySensor.readRegister(BME280_CHIP_ID_REG), HEX);
	Serial.print("Reset register(0xE0): 0x");
	Serial.println(mySensor.readRegister(BME280_RST_REG), HEX);
	Serial.print("ctrl_meas(0xF4): 0x");
	Serial.println(mySensor.readRegister(BME280_CTRL_MEAS_REG), HEX);
	Serial.print("ctrl_hum(0xF2): 0x");
	Serial.println(mySensor.readRegister(BME280_CTRL_HUMIDITY_REG), HEX);
	Serial.print("\n\n");
	Serial.print("Displaying all regs\n");
	uint8_t memCounter = 0x80;
	uint8_t tempReadData;
	for(int rowi = 8; rowi < 16; rowi++ )
	{
		Serial.print("0x");
		Serial.print(rowi, HEX);
		Serial.print("0:");
		for(int coli = 0; coli < 16; coli++ )
		{
			tempReadData = mySensor.readRegister(memCounter);
			Serial.print((tempReadData >> 4) & 0x0F, HEX);//Print first hex nibble
			Serial.print(tempReadData & 0x0F, HEX);//Print second hex nibble
			Serial.print(" ");
			memCounter++;
		}
		Serial.print("\n");
	}
	///*
	Serial.print("\n\n");
	Serial.print("Displaying concatenated calibration words\n");
	Serial.print("dig_T1, uint16: ");
	Serial.println(mySensor.calibration.dig_T1);
	Serial.print("dig_T2, int16: ");
	Serial.println(mySensor.calibration.dig_T2);
	Serial.print("dig_T3, int16: ");
	Serial.println(mySensor.calibration.dig_T3);

	Serial.print("dig_P1, uint16: ");
	Serial.println(mySensor.calibration.dig_P1);
	Serial.print("dig_P2, int16: ");
	Serial.println(mySensor.calibration.dig_P2);
	Serial.print("dig_P3, int16: ");
	Serial.println(mySensor.calibration.dig_P3);
	Serial.print("dig_P4, int16: ");
	Serial.println(mySensor.calibration.dig_P4);
	Serial.print("dig_P5, int16: ");
	Serial.println(mySensor.calibration.dig_P5);
	Serial.print("dig_P6, int16: ");
	Serial.println(mySensor.calibration.dig_P6);
	Serial.print("dig_P7, int16: ");
	Serial.println(mySensor.calibration.dig_P7);
	Serial.print("dig_P8, int16: ");
	Serial.println(mySensor.calibration.dig_P8);
	Serial.print("dig_P9, int16: ");
	Serial.println(mySensor.calibration.dig_P9);
	Serial.print("dig_H1, uint8: ");
	Serial.println(mySensor.calibration.dig_H1);
	Serial.print("dig_H2, int16: ");
	Serial.println(mySensor.calibration.dig_H2);
	Serial.print("dig_H3, uint8: ");
	Serial.println(mySensor.calibration.dig_H3);
	Serial.print("dig_H4, int16: ");
	Serial.println(mySensor.calibration.dig_H4);
	Serial.print("dig_H5, int16: ");
	Serial.println(mySensor.calibration.dig_H5);
	Serial.print("dig_H6, uint8: ");
	Serial.println(mySensor.calibration.dig_H6);
	Serial.println();
	 //*/
  //==========================
   // When lightning is detected the interrupt pin goes HIGH.
  pinMode(lightningInt, INPUT); 

  Serial.begin(115200); 
  Serial.println("AS3935 Franklin Lightning Detector"); 

  Wire.begin(); // Begin Wire before lightning sensor. 
  if( !lightning.begin() ){ // Initialize the sensor. 
    Serial.println ("Lightning Detector did not start up, freezing!"); 
    while(1); 
  }
  else
    Serial.println("Schmow-ZoW, Lightning Detector Ready!\n");
  // "Disturbers" are events that are false lightning events. If you find
  // yourself seeing a lot of disturbers you can have the chip not report those
  // events on the interrupt lines. 
  lightning.maskDisturber(true); 
  int maskVal = lightning.readMaskDisturber();
  Serial.print("Are disturbers being masked: "); 
  if (maskVal == 1)
    Serial.println("YES"); 
  else if (maskVal == 0)
    Serial.println("NO"); 

  // The lightning detector defaults to an indoor setting (less
  // gain/sensitivity), if you plan on using this outdoors 
  // uncomment the following line:
  //lightning.setIndoorOutdoor(OUTDOOR); 
  int enviVal = lightning.readIndoorOutdoor();
  Serial.print("Are we set for indoor or outdoor: ");  
  if( enviVal == INDOOR )
    Serial.println("Indoor.");  
  else if( enviVal == OUTDOOR )
    Serial.println("Outdoor.");  
  else 
    Serial.println(enviVal, BIN); 
 
  // Noise floor setting from 1-7, one being the lowest. Default setting is
  // two. If you need to check the setting, the corresponding function for
  // reading the function follows.    

  lightning.setNoiseLevel(noiseFloor);  

  int noiseVal = lightning.readNoiseLevel();
  Serial.print("Noise Level is set at: ");
  Serial.println(noiseVal);

  // Watchdog threshold setting can be from 1-10, one being the lowest. Default setting is
  // two. If you need to check the setting, the corresponding function for
  // reading the function follows.    

  lightning.watchdogThreshold(watchDogVal); 

  int watchVal = lightning.readWatchdogThreshold();
  Serial.print("Watchdog Threshold is set to: ");
  Serial.println(watchVal);

  // Spike Rejection setting from 1-11, one being the lowest. Default setting is
  // two. If you need to check the setting, the corresponding function for
  // reading the function follows.    
  // The shape of the spike is analyzed during the chip's
  // validation routine. You can round this spike at the cost of sensitivity to
  // distant events. 

  lightning.spikeRejection(spike); 

  int spikeVal = lightning.readSpikeRejection();
  Serial.print("Spike Rejection is set to: ");
  Serial.println(spikeVal);

  // This setting will change when the lightning detector issues an interrupt.
  // For example you will only get an interrupt after five lightning strikes
  // instead of one. Default is one, and it takes settings of 1, 5, 9 and 16.   
  // Followed by its corresponding read function. Default is zero. 

  lightning.lightningThreshold(lightningThresh); 

  uint8_t lightVal = lightning.readLightningThreshold();
  Serial.print("The number of strikes before interrupt is triggerd: "); 
  Serial.println(lightVal); 

  // When the distance to the storm is estimated, it takes into account other
  // lightning that was sensed in the past 15 minutes. If you want to reset
  // time, then you can call this function. 

  //lightning.clearStatistics(); 

  // The power down function has a BIG "gotcha". When you wake up the board
  // after power down, the internal oscillators will be recalibrated. They are
  // recalibrated according to the resonance frequency of the antenna - which
  // should be around 500kHz. It's highly recommended that you calibrate your
  // antenna before using these two functions, or you run the risk of schewing
  // the timing of the chip. 

  //lightning.powerDown(); 
  //delay(1000);
  //if( lightning.wakeUp() ) 
   // Serial.println("Successfully woken up!");  
  //else 
    //Serial.println("Error recalibrating internal osciallator on wake up."); 
  
  // Set too many features? Reset them all with the following function.
  lightning.resetSettings();
  
  //==========================
//  wdt_reset(); //Pet the dog
//	wdt_disable(); //We don't want the watchdog during ini

	pinMode(WSPEED, INPUT_PULLUP); // input from wind meters windspeed sensor
	pinMode(RAIN, INPUT_PULLUP); // input from wind meters rain gauge sensor

	pinMode(WDIR, INPUT);
	pinMode(LIGHT, INPUT);
	pinMode(BATT, INPUT);
	pinMode(REFERENCE_3V3, INPUT);

	pinMode(STAT1, OUTPUT);

	midnightReset(); //Reset rain totals

#ifdef ENABLE_LIGHTNING
	startLightning(); //Init the lighting sensor
#endif

	seconds = 0;
	lastSecond = millis();

	// attach external interrupt pins to IRQ functions
	attachInterrupt(0, rainIRQ, FALLING);
	attachInterrupt(1, wspeedIRQ, FALLING);

	// turn on interrupts
	interrupts();

	Serial.println("Wimp Weather Station online!");
	reportWeather();

	//  wdt_enable(WDTO_1S); //Unleash the beast
  //==========================
}

void loop() {
    char message1[120];
    char message3[120];
    if (millis() - previousMillis >= (interval )) {
    //=========================================================================
    
  //================================
  //*************CCS811*************
  //================================
  
  if(ccs.available()){
    float temp = ccs.calculateTemperature();
    if(!ccs.readData()){
      Serial.print("CO2: ");
      Serial.print(ccs.geteCO2());
      Serial.print("ppm, TVOC: ");
      Serial.print(ccs.getTVOC());
      Serial.print("ppb   Temp:");
      Serial.println(temp);
    }
    else{
      Serial.println("ERROR!");
      while(1);
    }
  }
}
//====================================
//**************MAX30105**************
//===================================
byte ledBrightness = 0xFF; //Options: 0=Off to 255=50mA
byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
byte ledMode = 3; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
byte sampleRate = 400; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
int pulseWidth = 411; //Options: 69, 118, 215, 411
int adcRange = 16384; //Options: 2048, 4096, 8192, 16384
int PROX_INT_EN = 0;

particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
float temperature = particleSensor.readTemperature();
  Serial.print("temperatureC=");
  Serial.print(temperature, 4);
///*  
 debug.print(" R[");
 debug.print(particleSensor.getRed());
 debug.print("] IR[");
 debug.print(particleSensor.getIR());
 debug.print("] G[");
 debug.print(particleSensor.getGreen());
 debug.print("]");
 debug.println();
 Serial.print("temperatureC=");
 Serial.print(temperature, 4);
 Serial.println();
 //*/
 //======================================
 //****************BME280****************
 //======================================
 	//Each loop, take a reading.
	//Start with temperature, as that data is needed for accurate compensation.
	//Reading the temperature updates the compensators of the other functions
	//in the background.
///* 
	Serial.print("Temperature: ");
	Serial.print(mySensor.readTempC(), 2);
	Serial.println(" degrees C");

	Serial.print("Temperature: ");
	Serial.print(mySensor.readTempF(), 2);
	Serial.println(" degrees F");

	Serial.print("Pressure: ");
	Serial.print(mySensor.readFloatPressure(), 2);
	Serial.println(" Pa");

	Serial.print("Altitude: ");
	Serial.print(mySensor.readFloatAltitudeMeters(), 2);
	Serial.println("m");

	Serial.print("Altitude: ");
	Serial.print(mySensor.readFloatAltitudeFeet(), 2);
	Serial.println("ft");

	Serial.print("%RH: ");
	Serial.print(mySensor.readFloatHumidity(), 2);
	Serial.println(" %");

	Serial.println();
 //*/ 
 //======================================
 //********Lightning_Detector************
 //=====================================
  if(digitalRead(lightningInt) == HIGH){
    // Hardware has alerted us to an event, now we read the interrupt register
    // to see exactly what it is. 
    intVal = lightning.readInterruptReg();
    if(intVal == NOISE_INT){
      Serial.println("Noise."); 
    }
    else if(intVal == DISTURBER_INT){
      Serial.println("Disturber."); 
    }
    else if(intVal == LIGHTNING_INT){
      Serial.println("Lightning Strike Detected!"); 
      byte distance = lightning.distanceToStorm(); 
      Serial.print("Approximately: "); 
      Serial.print(distance); 
      Serial.println("km away!"); 
      long lightEnergy = lightning.lightningEnergy(); 
      Serial.print("Lightning Energy: "); 
      Serial.println(lightEnergy); 

    }
  }
 //=====================================
 //*****Wind_Direction& Speed***********
 //=====================================


	//Keep track of which minute it is
	if(millis() - lastSecond >= 1000)
	{
		lastSecond += 1000;
		//Take a speed and direction reading every second for 2 minute average
		if(++seconds_2m > 119) seconds_2m = 0;
		//Calc the wind speed and direction every second for 120 second to get 2 minute average
		windspeedmph = get_wind_speed();
		winddir = get_wind_direction();
		windspdavg[seconds_2m] = (int)windspeedmph;
		winddiravg[seconds_2m] = winddir;
		//if(seconds_2m % 10 == 0) displayArrays();
		//Check to see if this is a gust for the minute
		if(windspeedmph > windgust_10m[minutes_10m])
		{
			windgust_10m[minutes_10m] = windspeedmph;
			windgustdirection_10m[minutes_10m] = winddir;
		}
		//Check to see if this is a gust for the day
		//Resets at midnight each night
		if(windspeedmph > windgustmph)
		{
			windgustmph = windspeedmph;
			windgustdir = winddir;
		}
		//Blink stat LED briefly to show we are alive
		digitalWrite(STAT1, HIGH);
		//reportWeather(); //Print the current readings. Takes 172ms.
		delay(25);
		digitalWrite(STAT1, LOW);
		//If we roll over 60 seconds then update the arrays for rain and windgust
		if(++seconds > 59)
		{
			seconds = 0;
			if(++minutes > 59) minutes = 0;
			if(++minutes_10m > 9) minutes_10m = 0;
			rainHour[minutes] = 0; //Zero out this minute's rainfall amount
			windgust_10m[minutes_10m] = 0; //Zero out this minute's gust
			minutesSinceLastReset++; //It's been another minute since last night's midnight reset
		}
	}
	//Check to see if there's been lighting
#ifdef ENABLE_LIGHTNING
	if(digitalRead(LIGHTNING_IRQ) == HIGH)
	{
		//We've got something!
		lightning_distance = readLightning();
	}
#endif
	//Wait for the imp to ping us with the ! character
	if(Serial.available())
	{
		byte incoming = Serial.read();
		if(incoming == '!')
		{
			reportWeather(); //Send all the current readings out the imp and to its agent for posting to wunderground. Takes 196ms
			//Serial.print("Pinged!");

#ifdef ENABLE_LIGHTNING
			//Give imp time to transmit then read any erroneous lightning strike
			delay(1000); //Give the Imp time to transmit
			readLightning(); //Clear any readings and forget it
#endif
		}
		else if(incoming == '@') //Special character from Imp indicating midnight local time
		{
			midnightReset(); //Reset a bunch of variables like rain and daily total rain
			//Serial.print("Midnight reset");
		}
		else if(incoming == '#') //Special character from Imp indicating a hardware reset
		{
			//Serial.print("Watchdog reset");
			delay(5000); //This will cause the system to reset because we don't pet the dog
		}
	}
	//If we go for more than 24 hours without a midnight reset then force a reset
	//24 hours * 60 mins/hr = 1,440 minutes + 10 extra minutes. We hope that Imp is doing it.
	if(minutesSinceLastReset > (1440 + 10))
	{
		midnightReset(); //Reset a bunch of variables like rain and daily total rain
		//Serial.print("Emergency midnight reset");
	}
 
 //=====================================
 sprintf(message1,"RED%.2d,IR%.2d,GREEN%.2d,Heat_Alarm%f",particleSensor.getRed(),particleSensor.getIR(),particleSensor.getGreen(),temperature);
 Particle.publish("Sensor_Array2",message1,PRIVATE);
 
 
previousMillis = millis();
}

//Prints the various arrays for debugging
void displayArrays()
{
	//Windgusts in this hour
	Serial.println();
	Serial.print(minutes);
	Serial.print(":");
	Serial.println(seconds);

	Serial.print("Windgust last 10 minutes:");
	for(int i = 0 ; i < 10 ; i++)
	{
		if(i % 10 == 0) Serial.println();
		Serial.print(" ");
		Serial.print(windgust_10m[i]);
	}

	//Wind speed avg for past 2 minutes
	/*Serial.println();
	 Serial.print("Wind 2 min avg:");
	 for(int i = 0 ; i < 120 ; i++)
	 {
	 if(i % 30 == 0) Serial.println();
	 Serial.print(" ");
	 Serial.print(windspdavg[i]);
	 }*/

	//Rain for last hour
	Serial.println();
	Serial.print("Rain hour:");
	for(int i = 0 ; i < 60 ; i++)
	{
		if(i % 30 == 0) Serial.println();
		Serial.print(" ");
		Serial.print(rainHour[i]);
	}

}

//When the imp tells us it's midnight, reset the total amount of rain and gusts
void midnightReset()
{
	dailyrainin = 0; //Reset daily amount of rain

	windgustmph = 0; //Zero out the windgust for the day
	windgustdir = 0; //Zero out the gust direction for the day

	minutes = 0; //Reset minute tracker
	seconds = 0;
	lastSecond = millis(); //Reset variable used to track minutes

	minutesSinceLastReset = 0; //Zero out the backup midnight reset variable
}

//Calculates each of the variables that wunderground is expecting
void calcWeather()
{
	//current winddir, current windspeed, windgustmph, and windgustdir are calculated every 100ms throughout the day

	//Calc windspdmph_avg2m
	float temp = 0;
	for(int i = 0 ; i < 120 ; i++)
		temp += windspdavg[i];
	temp /= 120.0;
	windspdmph_avg2m = temp;

	//Calc winddir_avg2m, Wind Direction
	//You can't just take the average. Google "mean of circular quantities" for more info
	//We will use the Mitsuta method because it doesn't require trig functions
	//And because it sounds cool.
	//Based on: http://abelian.org/vlf/bearings.html
	//Based on: http://stackoverflow.com/questions/1813483/averaging-angles-again
	long sum = winddiravg[0];
	int D = winddiravg[0];
	for(int i = 1 ; i < WIND_DIR_AVG_SIZE ; i++)
	{
		int delta = winddiravg[i] - D;

		if(delta < -180)
			D += delta + 360;
		else if(delta > 180)
			D += delta - 360;
		else
			D += delta;

		sum += D;
	}
	winddir_avg2m = sum / WIND_DIR_AVG_SIZE;
	if(winddir_avg2m >= 360) winddir_avg2m -= 360;
	if(winddir_avg2m < 0) winddir_avg2m += 360;


	//Calc windgustmph_10m
	//Calc windgustdir_10m
	//Find the largest windgust in the last 10 minutes
	windgustmph_10m = 0;
	windgustdir_10m = 0;
	//Step through the 10 minutes
	for(int i = 0; i < 10 ; i++)
	{
		if(windgust_10m[i] > windgustmph_10m)
		{
			windgustmph_10m = windgust_10m[i];
			windgustdir_10m = windgustdirection_10m[i];
		}
	}


	light_lvl = get_light_level();

	//Calc battery level
	batt_lvl = get_battery_level();

	//Lightning is checked in the main loop
}

//Returns the voltage of the light sensor based on the 3.3V rail
//This allows us to ignore what VCC might be (an Arduino plugged into USB has VCC of 4.5 to 5.2V)
float get_light_level()
{
	float operatingVoltage = averageAnalogRead(REFERENCE_3V3);

	float lightSensor = averageAnalogRead(LIGHT);

	operatingVoltage = 3.3 / operatingVoltage; //The reference voltage is 3.3V

	lightSensor *= operatingVoltage;

	return(lightSensor);
}

//Returns the voltage of the raw pin based on the 3.3V rail
//The battery can ranges from 4.2V down to around 3.3V
//This function allows us to ignore what VCC might be (an Arduino plugged into USB has VCC of 4.5 to 5.2V)
//The weather shield has a pin called RAW (VIN) fed through through two 5% resistors and connected to A2 (BATT):
//3.9K on the high side (R1), and 1K on the low side (R2)
float get_battery_level()
{
	float operatingVoltage = averageAnalogRead(REFERENCE_3V3);

	float rawVoltage = averageAnalogRead(BATT);

	operatingVoltage = 3.30 / operatingVoltage; //The reference voltage is 3.3V

	rawVoltage *= operatingVoltage; //Convert the 0 to 1023 int to actual voltage on BATT pin

	rawVoltage *= 4.90; //(3.9k+1k)/1k - multiply BATT voltage by the voltage divider to get actual system voltage

	return(rawVoltage);
}

//Returns the instataneous wind speed
float get_wind_speed()
{
	float deltaTime = millis() - lastWindCheck; //750ms

	deltaTime /= 1000.0; //Covert to seconds

	float windSpeed = (float)windClicks / deltaTime; //3 / 0.750s = 4

	windClicks = 0; //Reset and start watching for new wind
	lastWindCheck = millis();

	windSpeed *= 1.492; //4 * 1.492 = 5.968MPH

Serial.println();
	 Serial.print("Windspeed:");
	 Serial.println(windSpeed);

	return(windSpeed);
}

int get_wind_direction()
// read the wind direction sensor, return heading in degrees
{
	unsigned int adc;

	adc = averageAnalogRead(WDIR); // get the current reading from the sensor

	// The following table is ADC readings for the wind direction sensor output, sorted from low to high.
	// Each threshold is the midpoint between adjacent headings. The output is degrees for that ADC reading.
	// Note that these are not in compass degree order! See Weather Meters datasheet for more information.

	if (adc < 380) return (113);
	if (adc < 393) return (68);
	if (adc < 414) return (90);
	if (adc < 456) return (158);
	if (adc < 508) return (135);
	if (adc < 551) return (203);
	if (adc < 615) return (180);
	if (adc < 680) return (23);
	if (adc < 746) return (45);
	if (adc < 801) return (248);
	if (adc < 833) return (225);
	if (adc < 878) return (338);
	if (adc < 913) return (0);
	if (adc < 940) return (293);
	if (adc < 967) return (315);
	if (adc < 990) return (270);
	return (-1); // error, disconnected?
}

void reportWeather()
{
	calcWeather(); //Go calc all the various sensors

	Serial.print("$,winddir=");
	Serial.print(winddir);
	Serial.print(",windspeedmph=");
	Serial.print(windspeedmph, 1);
	Serial.print(",windgustmph=");
	Serial.print(windgustmph, 1);
	Serial.print(",windgustdir=");
	Serial.print(windgustdir);
	Serial.print(",windspdmph_avg2m=");
	Serial.print(windspdmph_avg2m, 1);
	Serial.print(",winddir_avg2m=");
	Serial.print(winddir_avg2m);
	Serial.print(",windgustmph_10m=");
	Serial.print(windgustmph_10m, 1);
	Serial.print(",windgustdir_10m=");
	Serial.print(windgustdir_10m);
	Serial.print(",humidity=");
	Serial.print(humidity, 1);
	Serial.print(",tempf=");
	Serial.print(tempf, 1);
	Serial.print(",rainin=");
	Serial.print(rainin, 2);
	Serial.print(",dailyrainin=");
	Serial.print(dailyrainin, 2);
	Serial.print(","); //Don't print pressure= because the agent will be doing calcs on the number
	Serial.print(pressure, 2);
	Serial.print(",batt_lvl=");
	Serial.print(batt_lvl, 2);
	Serial.print(",light_lvl=");
	Serial.print(light_lvl, 2);

#ifdef LIGHTNING_ENABLED
	Serial.print(",lightning_distance=");
	Serial.print(lightning_distance);
#endif

	Serial.print(",");
	Serial.println("#,");

	//Test string
	Serial.println("$,winddir=270,windspeedmph=0.0,windgustmph=0.0,windgustdir=0,windspdmph_avg2m=0.0,winddir_avg2m=12,windgustmph_10m=0.0,windgustdir_10m=0,humidity=998.0,tempf=-1766.2,rainin=0.00,dailyrainin=0.00,-999.00,batt_lvl=16.11,light_lvl=3.32,#,");
}

int averageAnalogRead(int pinToRead)
{
	byte numberOfReadings = 8;
	unsigned int runningValue = 0;

	for(int x = 0 ; x < numberOfReadings ; x++)
		runningValue += analogRead(pinToRead);
	runningValue /= numberOfReadings;

	return(runningValue);
}

//The following is for the AS3935 lightning sensor
#ifdef ENABLE_LIGHTNING
byte readLightning(void)
{
	byte distance = 0;

	//Check to see if we have lightning!
	if(digitalRead(LIGHTNING_IRQ) == HIGH)
	{
		// first step is to find out what caused interrupt
		// as soon as we read interrupt cause register, irq pin goes low
		int irqSource = AS3935.interruptSource();

		// returned value is bitmap field, bit 0 - noise level too high, bit 2 - disturber detected, and finally bit 3 - lightning!
		if (irqSource & 0b0001)
		{
			//Serial.println("Noise level too high, try adjusting noise floor");
		}

		if (irqSource & 0b0100)
		{
			//Serial.println("Disturber detected");
			distance = 64;
		}

		if (irqSource & 0b1000)
		{
			// need to find how far that lightning stroke, function returns approximate distance in kilometers,
			// where value 1 represents storm in detector's near victinity, and 63 - very distant, out of range stroke
			// everything in between is just distance in kilometers
			distance = AS3935.lightningDistanceKm();

			//Serial.print("Lightning: ");
			//Serial.print(lightning_distance, DEC);
			//Serial.println(" km");

			//The AS3935 remembers the nearest strike distance. For example 15km away then 10, then overhead all following
			//distances (10, 20, 30) will instead output as 'Storm overhead, watch out!'. Resetting the chip erases this.
			lightning_init();
		}
	}

	return(distance);
}

void startLightning(void)
{
	pinMode(slaveSelectPin, OUTPUT); // set the slaveSelectPin as an output:

	pinMode(LIGHTNING_IRQ, INPUT_PULLUP); //Set IRQ pin as input

	SPI.begin(); //Start SPI

	SPI.setDataMode(SPI_MODE1); // NB! chip uses SPI MODE1

	SPI.setClockDivider(SPI_CLOCK_DIV16); //Uno 16MHz / 16 = 1MHz

	SPI.setBitOrder(MSBFIRST); // and chip is MSB first

	lightning_init(); //Setup the values for the sensor

	Serial.println("Lightning sensor online");
}

void lightning_init()
{
	AS3935.reset(); // reset all internal register values to defaults

	// if lightning detector can not tune tank circuit to required tolerance,
	// calibration function will return false
	if(!AS3935.calibrate())
	{
		Serial.println("Tuning out of range, check your wiring, your sensor and make sure physics laws have not changed!");
	}

	AS3935.setOutdoors(); //The weather station is outdoors

	AS3935.enableDisturbers(); //We want to know if a man-made event happens
	AS3935.setNoiseFloor(3); //See table 16 of the AS3935 datasheet. 4-6 works. This was found through experimentation.

	//printAS3935Registers();
}

/*void printAS3935Registers()
{
  int noiseFloor = AS3935.getNoiseFloor();
  int spikeRejection = AS3935.getSpikeRejection();
  int watchdogThreshold = AS3935.getWatchdogThreshold();
  Serial.print("Noise floor is: ");
  Serial.println(noiseFloor, DEC);
  Serial.print("Spike rejection is: ");
  Serial.println(spikeRejection, DEC);
  Serial.print("Watchdog threshold is: ");
  Serial.println(watchdogThreshold, DEC);
}*/

byte SPItransfer(byte sendByte)
{
	return SPI.transfer(sendByte);
}
#endif

Attached .h Files MAX30105.cpp

C/C++
Very Necessary for the Code to Work
/***************************************************
  This is a library written for the Maxim MAX30105 Optical Smoke Detector
  It should also work with the MAX30102. However, the MAX30102 does not have a Green LED.

  These sensors use I2C to communicate, as well as a single (optional)
  interrupt line that is not currently supported in this driver.

  Written by Peter Jansen and Nathan Seidle (SparkFun)
  BSD license, all text above must be included in any redistribution.
 *****************************************************/

#include "MAX30105.h"

// Status Registers
static const uint8_t MAX30105_INTSTAT1 =		0x00;
static const uint8_t MAX30105_INTSTAT2 =		0x01;
static const uint8_t MAX30105_INTENABLE1 =		0x02;
static const uint8_t MAX30105_INTENABLE2 =		0x03;

// FIFO Registers
static const uint8_t MAX30105_FIFOWRITEPTR = 	0x04;
static const uint8_t MAX30105_FIFOOVERFLOW = 	0x05;
static const uint8_t MAX30105_FIFOREADPTR = 	0x06;
static const uint8_t MAX30105_FIFODATA =		0x07;

// Configuration Registers
static const uint8_t MAX30105_FIFOCONFIG = 		0x08;
static const uint8_t MAX30105_MODECONFIG = 		0x09;
static const uint8_t MAX30105_PARTICLECONFIG = 	0x0A;    // Note, sometimes listed as "SPO2" config in datasheet (pg. 11)
static const uint8_t MAX30105_LED1_PULSEAMP = 	0x0C;
static const uint8_t MAX30105_LED2_PULSEAMP = 	0x0D;
static const uint8_t MAX30105_LED3_PULSEAMP = 	0x0E;
static const uint8_t MAX30105_LED_PROX_AMP = 	0x10;
static const uint8_t MAX30105_MULTILEDCONFIG1 = 0x11;
static const uint8_t MAX30105_MULTILEDCONFIG2 = 0x12;

// Die Temperature Registers
static const uint8_t MAX30105_DIETEMPINT = 		0x1F;
static const uint8_t MAX30105_DIETEMPFRAC = 	0x20;
static const uint8_t MAX30105_DIETEMPCONFIG = 	0x21;

// Proximity Function Registers
static const uint8_t MAX30105_PROXINTTHRESH = 	0x30;

// Part ID Registers
static const uint8_t MAX30105_REVISIONID = 		0xFE;
static const uint8_t MAX30105_PARTID = 			0xFF;    // Should always be 0x15. Identical to MAX30102.

// MAX30105 Commands
// Interrupt configuration (pg 13, 14)
static const uint8_t MAX30105_INT_A_FULL_MASK =		(byte)~0b10000000;
static const uint8_t MAX30105_INT_A_FULL_ENABLE = 	0x80;
static const uint8_t MAX30105_INT_A_FULL_DISABLE = 	0x00;

static const uint8_t MAX30105_INT_DATA_RDY_MASK = (byte)~0b01000000;
static const uint8_t MAX30105_INT_DATA_RDY_ENABLE =	0x40;
static const uint8_t MAX30105_INT_DATA_RDY_DISABLE = 0x00;

static const uint8_t MAX30105_INT_ALC_OVF_MASK = (byte)~0b00100000;
static const uint8_t MAX30105_INT_ALC_OVF_ENABLE = 	0x20;
static const uint8_t MAX30105_INT_ALC_OVF_DISABLE = 0x00;

static const uint8_t MAX30105_INT_PROX_INT_MASK = (byte)~0b00010000;
static const uint8_t MAX30105_INT_PROX_INT_ENABLE = 0x10;
static const uint8_t MAX30105_INT_PROX_INT_DISABLE = 0x00;

static const uint8_t MAX30105_INT_DIE_TEMP_RDY_MASK = (byte)~0b00000010;
static const uint8_t MAX30105_INT_DIE_TEMP_RDY_ENABLE = 0x02;
static const uint8_t MAX30105_INT_DIE_TEMP_RDY_DISABLE = 0x00;

static const uint8_t MAX30105_SAMPLEAVG_MASK =	(byte)~0b11100000;
static const uint8_t MAX30105_SAMPLEAVG_1 = 	0x00;
static const uint8_t MAX30105_SAMPLEAVG_2 = 	0x20;
static const uint8_t MAX30105_SAMPLEAVG_4 = 	0x40;
static const uint8_t MAX30105_SAMPLEAVG_8 = 	0x60;
static const uint8_t MAX30105_SAMPLEAVG_16 = 	0x80;
static const uint8_t MAX30105_SAMPLEAVG_32 = 	0xA0;

static const uint8_t MAX30105_ROLLOVER_MASK = 	0xEF;
static const uint8_t MAX30105_ROLLOVER_ENABLE = 0x10;
static const uint8_t MAX30105_ROLLOVER_DISABLE = 0x00;

static const uint8_t MAX30105_A_FULL_MASK = 	0xF0;

// Mode configuration commands (page 19)
static const uint8_t MAX30105_SHUTDOWN_MASK = 	0x7F;
static const uint8_t MAX30105_SHUTDOWN = 		0x80;
static const uint8_t MAX30105_WAKEUP = 			0x00;

static const uint8_t MAX30105_RESET_MASK = 		0xBF;
static const uint8_t MAX30105_RESET = 			0x40;

static const uint8_t MAX30105_MODE_MASK = 		0xF8;
static const uint8_t MAX30105_MODE_REDONLY = 	0x02;
static const uint8_t MAX30105_MODE_REDIRONLY = 	0x03;
static const uint8_t MAX30105_MODE_MULTILED = 	0x07;

// Particle sensing configuration commands (pgs 19-20)
static const uint8_t MAX30105_ADCRANGE_MASK = 	0x9F;
static const uint8_t MAX30105_ADCRANGE_2048 = 	0x00;
static const uint8_t MAX30105_ADCRANGE_4096 = 	0x20;
static const uint8_t MAX30105_ADCRANGE_8192 = 	0x40;
static const uint8_t MAX30105_ADCRANGE_16384 = 	0x60;

static const uint8_t MAX30105_SAMPLERATE_MASK = 0xE3;
static const uint8_t MAX30105_SAMPLERATE_50 = 	0x00;
static const uint8_t MAX30105_SAMPLERATE_100 = 	0x04;
static const uint8_t MAX30105_SAMPLERATE_200 = 	0x08;
static const uint8_t MAX30105_SAMPLERATE_400 = 	0x0C;
static const uint8_t MAX30105_SAMPLERATE_800 = 	0x10;
static const uint8_t MAX30105_SAMPLERATE_1000 = 0x14;
static const uint8_t MAX30105_SAMPLERATE_1600 = 0x18;
static const uint8_t MAX30105_SAMPLERATE_3200 = 0x1C;

static const uint8_t MAX30105_PULSEWIDTH_MASK = 0xFC;
static const uint8_t MAX30105_PULSEWIDTH_69 = 	0x00;
static const uint8_t MAX30105_PULSEWIDTH_118 = 	0x01;
static const uint8_t MAX30105_PULSEWIDTH_215 = 	0x02;
static const uint8_t MAX30105_PULSEWIDTH_411 = 	0x03;

//Multi-LED Mode configuration (pg 22)
static const uint8_t MAX30105_SLOT1_MASK = 		0xF8;
static const uint8_t MAX30105_SLOT2_MASK = 		0x8F;
static const uint8_t MAX30105_SLOT3_MASK = 		0xF8;
static const uint8_t MAX30105_SLOT4_MASK = 		0x8F;

static const uint8_t SLOT_NONE = 				0x00;
static const uint8_t SLOT_RED_LED = 			0x01;
static const uint8_t SLOT_IR_LED = 				0x02;
static const uint8_t SLOT_GREEN_LED = 			0x03;
static const uint8_t SLOT_NONE_PILOT = 			0x04;
static const uint8_t SLOT_RED_PILOT =			0x05;
static const uint8_t SLOT_IR_PILOT = 			0x06;
static const uint8_t SLOT_GREEN_PILOT = 		0x07;

static const uint8_t MAX_30105_EXPECTEDPARTID = 0x15;

MAX30105::MAX30105() {
  // Constructor
}

boolean MAX30105::begin(TwoWire &wirePort, uint32_t i2cSpeed, uint8_t i2caddr) {
//int setClock = i2cSpeed;
  _i2cPort = &wirePort; //Grab which port the user wants us to use

  _i2cPort->begin();
//  _i2cPort->setClock(i2cSpeed);

  _i2caddr = i2caddr;

  // Step 1: Initial Communication and Verification
  // Check that a MAX30105 is connected
  if (readPartID() != MAX_30105_EXPECTEDPARTID) {
    // Error -- Part ID read from MAX30105 does not match expected part ID.
    // This may mean there is a physical connectivity problem (broken wire, unpowered, etc).
    return false;
  }

  // Populate revision ID
  readRevisionID();
  
  return true;
}

//
// Configuration
//

//Begin Interrupt configuration
uint8_t MAX30105::getINT1(void) {
  return (readRegister8(_i2caddr, MAX30105_INTSTAT1));
}
uint8_t MAX30105::getINT2(void) {
  return (readRegister8(_i2caddr, MAX30105_INTSTAT2));
}

void MAX30105::enableAFULL(void) {
  bitMask(MAX30105_INTENABLE1, MAX30105_INT_A_FULL_MASK, MAX30105_INT_A_FULL_ENABLE);
}
void MAX30105::disableAFULL(void) {
  bitMask(MAX30105_INTENABLE1, MAX30105_INT_A_FULL_MASK, MAX30105_INT_A_FULL_DISABLE);
}

void MAX30105::enableDATARDY(void) {
  bitMask(MAX30105_INTENABLE1, MAX30105_INT_DATA_RDY_MASK, MAX30105_INT_DATA_RDY_ENABLE);
}
void MAX30105::disableDATARDY(void) {
  bitMask(MAX30105_INTENABLE1, MAX30105_INT_DATA_RDY_MASK, MAX30105_INT_DATA_RDY_DISABLE);
}

void MAX30105::enableALCOVF(void) {
  bitMask(MAX30105_INTENABLE1, MAX30105_INT_ALC_OVF_MASK, MAX30105_INT_ALC_OVF_ENABLE);
}
void MAX30105::disableALCOVF(void) {
  bitMask(MAX30105_INTENABLE1, MAX30105_INT_ALC_OVF_MASK, MAX30105_INT_ALC_OVF_DISABLE);
}

void MAX30105::enablePROXINT(void) {
  bitMask(MAX30105_INTENABLE1, MAX30105_INT_PROX_INT_MASK, MAX30105_INT_PROX_INT_ENABLE);
}
void MAX30105::disablePROXINT(void) {
  bitMask(MAX30105_INTENABLE1, MAX30105_INT_PROX_INT_MASK, MAX30105_INT_PROX_INT_DISABLE);
}

void MAX30105::enableDIETEMPRDY(void) {
  bitMask(MAX30105_INTENABLE2, MAX30105_INT_DIE_TEMP_RDY_MASK, MAX30105_INT_DIE_TEMP_RDY_ENABLE);
}
void MAX30105::disableDIETEMPRDY(void) {
  bitMask(MAX30105_INTENABLE2, MAX30105_INT_DIE_TEMP_RDY_MASK, MAX30105_INT_DIE_TEMP_RDY_DISABLE);
}

//End Interrupt configuration

void MAX30105::softReset(void) {
  bitMask(MAX30105_MODECONFIG, MAX30105_RESET_MASK, MAX30105_RESET);

  // Poll for bit to clear, reset is then complete
  // Timeout after 100ms
  unsigned long startTime = millis();
  while (millis() - startTime < 100)
  {
    uint8_t response = readRegister8(_i2caddr, MAX30105_MODECONFIG);
    if ((response & MAX30105_RESET) == 0) break; //We're done!
    delay(1); //Let's not over burden the I2C bus
  }
}

void MAX30105::shutDown(void) {
  // Put IC into low power mode (datasheet pg. 19)
  // During shutdown the IC will continue to respond to I2C commands but will
  // not update with or take new readings (such as temperature)
  bitMask(MAX30105_MODECONFIG, MAX30105_SHUTDOWN_MASK, MAX30105_SHUTDOWN);
}

void MAX30105::wakeUp(void) {
  // Pull IC out of low power mode (datasheet pg. 19)
  bitMask(MAX30105_MODECONFIG, MAX30105_SHUTDOWN_MASK, MAX30105_WAKEUP);
}

void MAX30105::setLEDMode(uint8_t mode) {
  // Set which LEDs are used for sampling -- Red only, RED+IR only, or custom.
  // See datasheet, page 19
  bitMask(MAX30105_MODECONFIG, MAX30105_MODE_MASK, mode);
}

void MAX30105::setADCRange(uint8_t adcRange) {
  // adcRange: one of MAX30105_ADCRANGE_2048, _4096, _8192, _16384
  bitMask(MAX30105_PARTICLECONFIG, MAX30105_ADCRANGE_MASK, adcRange);
}

void MAX30105::setSampleRate(uint8_t sampleRate) {
  // sampleRate: one of MAX30105_SAMPLERATE_50, _100, _200, _400, _800, _1000, _1600, _3200
  bitMask(MAX30105_PARTICLECONFIG, MAX30105_SAMPLERATE_MASK, sampleRate);
}

void MAX30105::setPulseWidth(uint8_t pulseWidth) {
  // pulseWidth: one of MAX30105_PULSEWIDTH_69, _188, _215, _411
  bitMask(MAX30105_PARTICLECONFIG, MAX30105_PULSEWIDTH_MASK, pulseWidth);
}

// NOTE: Amplitude values: 0x00 = 0mA, 0x7F = 25.4mA, 0xFF = 50mA (typical)
// See datasheet, page 21
void MAX30105::setPulseAmplitudeRed(uint8_t amplitude) {
  writeRegister8(_i2caddr, MAX30105_LED1_PULSEAMP, amplitude);
}

void MAX30105::setPulseAmplitudeIR(uint8_t amplitude) {
  writeRegister8(_i2caddr, MAX30105_LED2_PULSEAMP, amplitude);
}

void MAX30105::setPulseAmplitudeGreen(uint8_t amplitude) {
  writeRegister8(_i2caddr, MAX30105_LED3_PULSEAMP, amplitude);
}

void MAX30105::setPulseAmplitudeProximity(uint8_t amplitude) {
  writeRegister8(_i2caddr, MAX30105_LED_PROX_AMP, amplitude);
}

void MAX30105::setProximityThreshold(uint8_t threshMSB) {
  // Set the IR ADC count that will trigger the beginning of particle-sensing mode.
  // The threshMSB signifies only the 8 most significant-bits of the ADC count.
  // See datasheet, page 24.
  writeRegister8(_i2caddr, MAX30105_PROXINTTHRESH, threshMSB);
}

//Given a slot number assign a thing to it
//Devices are SLOT_RED_LED or SLOT_RED_PILOT (proximity)
//Assigning a SLOT_RED_LED will pulse LED
//Assigning a SLOT_RED_PILOT will ??
void MAX30105::enableSlot(uint8_t slotNumber, uint8_t device) {

  uint8_t originalContents;

  switch (slotNumber) {
    case (1):
      bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT1_MASK, device);
      break;
    case (2):
      bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT2_MASK, device << 4);
      break;
    case (3):
      bitMask(MAX30105_MULTILEDCONFIG2, MAX30105_SLOT3_MASK, device);
      break;
    case (4):
      bitMask(MAX30105_MULTILEDCONFIG2, MAX30105_SLOT4_MASK, device << 4);
      break;
    default:
      //Shouldn't be here!
      break;
  }
}

//Clears all slot assignments
void MAX30105::disableSlots(void) {
  writeRegister8(_i2caddr, MAX30105_MULTILEDCONFIG1, 0);
  writeRegister8(_i2caddr, MAX30105_MULTILEDCONFIG2, 0);
}

//
// FIFO Configuration
//

//Set sample average (Table 3, Page 18)
void MAX30105::setFIFOAverage(uint8_t numberOfSamples) {
  bitMask(MAX30105_FIFOCONFIG, MAX30105_SAMPLEAVG_MASK, numberOfSamples);
}

//Resets all points to start in a known state
//Page 15 recommends clearing FIFO before beginning a read
void MAX30105::clearFIFO(void) {
  writeRegister8(_i2caddr, MAX30105_FIFOWRITEPTR, 0);
  writeRegister8(_i2caddr, MAX30105_FIFOOVERFLOW, 0);
  writeRegister8(_i2caddr, MAX30105_FIFOREADPTR, 0);
}

//Enable roll over if FIFO over flows
void MAX30105::enableFIFORollover(void) {
  bitMask(MAX30105_FIFOCONFIG, MAX30105_ROLLOVER_MASK, MAX30105_ROLLOVER_ENABLE);
}

//Disable roll over if FIFO over flows
void MAX30105::disableFIFORollover(void) {
  bitMask(MAX30105_FIFOCONFIG, MAX30105_ROLLOVER_MASK, MAX30105_ROLLOVER_DISABLE);
}

//Set number of samples to trigger the almost full interrupt (Page 18)
//Power on default is 32 samples
//Note it is reverse: 0x00 is 32 samples, 0x0F is 17 samples
void MAX30105::setFIFOAlmostFull(uint8_t numberOfSamples) {
  bitMask(MAX30105_FIFOCONFIG, MAX30105_A_FULL_MASK, numberOfSamples);
}

//Read the FIFO Write Pointer
uint8_t MAX30105::getWritePointer(void) {
  return (readRegister8(_i2caddr, MAX30105_FIFOWRITEPTR));
}

//Read the FIFO Read Pointer
uint8_t MAX30105::getReadPointer(void) {
  return (readRegister8(_i2caddr, MAX30105_FIFOREADPTR));
}


// Die Temperature
// Returns temp in C
float MAX30105::readTemperature() {
	
  //DIE_TEMP_RDY interrupt must be enabled
  //See issue 19: https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library/issues/19
  
  // Step 1: Config die temperature register to take 1 temperature sample
  writeRegister8(_i2caddr, MAX30105_DIETEMPCONFIG, 0x01);

  // Poll for bit to clear, reading is then complete
  // Timeout after 100ms
  unsigned long startTime = millis();
  while (millis() - startTime < 100)
  {
    //uint8_t response = readRegister8(_i2caddr, MAX30105_DIETEMPCONFIG); //Original way
    //if ((response & 0x01) == 0) break; //We're done!
    
	//Check to see if DIE_TEMP_RDY interrupt is set
	uint8_t response = readRegister8(_i2caddr, MAX30105_INTSTAT2);
    if ((response & MAX30105_INT_DIE_TEMP_RDY_ENABLE) > 0) break; //We're done!
    delay(1); //Let's not over burden the I2C bus
  }
  //TODO How do we want to fail? With what type of error?
  //? if(millis() - startTime >= 100) return(-999.0);

  // Step 2: Read die temperature register (integer)
  int8_t tempInt = readRegister8(_i2caddr, MAX30105_DIETEMPINT);
  uint8_t tempFrac = readRegister8(_i2caddr, MAX30105_DIETEMPFRAC); //Causes the clearing of the DIE_TEMP_RDY interrupt

  // Step 3: Calculate temperature (datasheet pg. 23)
  return (float)tempInt + ((float)tempFrac * 0.0625);
}

// Returns die temp in F
float MAX30105::readTemperatureF() {
  float temp = readTemperature();

  if (temp != -999.0) temp = temp * 1.8 + 32.0;

  return (temp);
}

// Set the PROX_INT_THRESHold
void MAX30105::setPROXINTTHRESH(uint8_t val) {
  writeRegister8(_i2caddr, MAX30105_PROXINTTHRESH, val);
}


//
// Device ID and Revision
//
uint8_t MAX30105::readPartID() {
  return readRegister8(_i2caddr, MAX30105_PARTID);
}

void MAX30105::readRevisionID() {
  revisionID = readRegister8(_i2caddr, MAX30105_REVISIONID);
}

uint8_t MAX30105::getRevisionID() {
  return revisionID;
}


//Setup the sensor
//The MAX30105 has many settings. By default we select:
// Sample Average = 4
// Mode = MultiLED
// ADC Range = 16384 (62.5pA per LSB)
// Sample rate = 50
//Use the default setup if you are just getting started with the MAX30105 sensor
void MAX30105::setup(byte powerLevel, byte sampleAverage, byte ledMode, int sampleRate, int pulseWidth, int adcRange) {
  softReset(); //Reset all configuration, threshold, and data registers to POR values

  //FIFO Configuration
  //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  //The chip will average multiple samples of same type together if you wish
  if (sampleAverage == 1) setFIFOAverage(MAX30105_SAMPLEAVG_1); //No averaging per FIFO record
  else if (sampleAverage == 2) setFIFOAverage(MAX30105_SAMPLEAVG_2);
  else if (sampleAverage == 4) setFIFOAverage(MAX30105_SAMPLEAVG_4);
  else if (sampleAverage == 8) setFIFOAverage(MAX30105_SAMPLEAVG_8);
  else if (sampleAverage == 16) setFIFOAverage(MAX30105_SAMPLEAVG_16);
  else if (sampleAverage == 32) setFIFOAverage(MAX30105_SAMPLEAVG_32);
  else setFIFOAverage(MAX30105_SAMPLEAVG_4);

  //setFIFOAlmostFull(2); //Set to 30 samples to trigger an 'Almost Full' interrupt
  enableFIFORollover(); //Allow FIFO to wrap/roll over
  //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

  //Mode Configuration
  //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  if (ledMode == 3) setLEDMode(MAX30105_MODE_MULTILED); //Watch all three LED channels
  else if (ledMode == 2) setLEDMode(MAX30105_MODE_REDIRONLY); //Red and IR
  else setLEDMode(MAX30105_MODE_REDONLY); //Red only
  activeLEDs = ledMode; //Used to control how many bytes to read from FIFO buffer
  //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

  //Particle Sensing Configuration
  //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  if(adcRange < 4096) setADCRange(MAX30105_ADCRANGE_2048); //7.81pA per LSB
  else if(adcRange < 8192) setADCRange(MAX30105_ADCRANGE_4096); //15.63pA per LSB
  else if(adcRange < 16384) setADCRange(MAX30105_ADCRANGE_8192); //31.25pA per LSB
  else if(adcRange == 16384) setADCRange(MAX30105_ADCRANGE_16384); //62.5pA per LSB
  else setADCRange(MAX30105_ADCRANGE_2048);

  if (sampleRate < 100) setSampleRate(MAX30105_SAMPLERATE_50); //Take 50 samples per second
  else if (sampleRate < 200) setSampleRate(MAX30105_SAMPLERATE_100);
  else if (sampleRate < 400) setSampleRate(MAX30105_SAMPLERATE_200);
  else if (sampleRate < 800) setSampleRate(MAX30105_SAMPLERATE_400);
  else if (sampleRate < 1000) setSampleRate(MAX30105_SAMPLERATE_800);
  else if (sampleRate < 1600) setSampleRate(MAX30105_SAMPLERATE_1000);
  else if (sampleRate < 3200) setSampleRate(MAX30105_SAMPLERATE_1600);
  else if (sampleRate == 3200) setSampleRate(MAX30105_SAMPLERATE_3200);
  else setSampleRate(MAX30105_SAMPLERATE_50);

  //The longer the pulse width the longer range of detection you'll have
  //At 69us and 0.4mA it's about 2 inches
  //At 411us and 0.4mA it's about 6 inches
  if (pulseWidth < 118) setPulseWidth(MAX30105_PULSEWIDTH_69); //Page 26, Gets us 15 bit resolution
  else if (pulseWidth < 215) setPulseWidth(MAX30105_PULSEWIDTH_118); //16 bit resolution
  else if (pulseWidth < 411) setPulseWidth(MAX30105_PULSEWIDTH_215); //17 bit resolution
  else if (pulseWidth == 411) setPulseWidth(MAX30105_PULSEWIDTH_411); //18 bit resolution
  else setPulseWidth(MAX30105_PULSEWIDTH_69);
  //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

  //LED Pulse Amplitude Configuration
  //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  //Default is 0x1F which gets us 6.4mA
  //powerLevel = 0x02, 0.4mA - Presence detection of ~4 inch
  //powerLevel = 0x1F, 6.4mA - Presence detection of ~8 inch
  //powerLevel = 0x7F, 25.4mA - Presence detection of ~8 inch
  //powerLevel = 0xFF, 50.0mA - Presence detection of ~12 inch

  setPulseAmplitudeRed(powerLevel);
  setPulseAmplitudeIR(powerLevel);
  setPulseAmplitudeGreen(powerLevel);
  setPulseAmplitudeProximity(powerLevel);
  //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

  //Multi-LED Mode Configuration, Enable the reading of the three LEDs
  //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  enableSlot(1, SLOT_RED_LED);
  if (ledMode > 1) enableSlot(2, SLOT_IR_LED);
  if (ledMode > 2) enableSlot(3, SLOT_GREEN_LED);
  //enableSlot(1, SLOT_RED_PILOT);
  //enableSlot(2, SLOT_IR_PILOT);
  //enableSlot(3, SLOT_GREEN_PILOT);
  //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

  clearFIFO(); //Reset the FIFO before we begin checking the sensor
}

//
// Data Collection
//

//Tell caller how many samples are available
uint8_t MAX30105::available(void)
{
  int8_t numberOfSamples = sense.head - sense.tail;
  if (numberOfSamples < 0) numberOfSamples += STORAGE_SIZE;

  return (numberOfSamples);
}

//Report the most recent red value
uint32_t MAX30105::getRed(void)
{
  //Check the sensor for new data for 250ms
  if(safeCheck(250))
    return (sense.red[sense.head]);
  else
    return(0); //Sensor failed to find new data
}

//Report the most recent IR value
uint32_t MAX30105::getIR(void)
{
  //Check the sensor for new data for 250ms
  if(safeCheck(250))
    return (sense.IR[sense.head]);
  else
    return(0); //Sensor failed to find new data
}

//Report the most recent Green value
uint32_t MAX30105::getGreen(void)
{
  //Check the sensor for new data for 250ms
  if(safeCheck(250))
    return (sense.green[sense.head]);
  else
    return(0); //Sensor failed to find new data
}

//Report the next Red value in the FIFO
uint32_t MAX30105::getFIFORed(void)
{
  return (sense.red[sense.tail]);
}

//Report the next IR value in the FIFO
uint32_t MAX30105::getFIFOIR(void)
{
  return (sense.IR[sense.tail]);
}

//Report the next Green value in the FIFO
uint32_t MAX30105::getFIFOGreen(void)
{
  return (sense.green[sense.tail]);
}

//Advance the tail
void MAX30105::nextSample(void)
{
  if(available()) //Only advance the tail if new data is available
  {
    sense.tail++;
    sense.tail %= STORAGE_SIZE; //Wrap condition
  }
}

//Polls the sensor for new data
//Call regularly
//If new data is available, it updates the head and tail in the main struct
//Returns number of new samples obtained
uint16_t MAX30105::check(void)
{
  //Read register FIDO_DATA in (3-byte * number of active LED) chunks
  //Until FIFO_RD_PTR = FIFO_WR_PTR

  byte readPointer = getReadPointer();
  byte writePointer = getWritePointer();

  int numberOfSamples = 0;

  //Do we have new data?
  if (readPointer != writePointer)
  {
    //Calculate the number of readings we need to get from sensor
    numberOfSamples = writePointer - readPointer;
    if (numberOfSamples < 0) numberOfSamples += 32; //Wrap condition

    //We now have the number of readings, now calc bytes to read
    //For this example we are just doing Red and IR (3 bytes each)
    int bytesLeftToRead = numberOfSamples * activeLEDs * 3;

    //Get ready to read a burst of data from the FIFO register
    _i2cPort->beginTransmission(MAX30105_ADDRESS);
    _i2cPort->write(MAX30105_FIFODATA);
    _i2cPort->endTransmission();

    //We may need to read as many as 288 bytes so we read in blocks no larger than I2C_BUFFER_LENGTH
    //I2C_BUFFER_LENGTH changes based on the platform. 64 bytes for SAMD21, 32 bytes for Uno.
    //Wire.requestFrom() is limited to BUFFER_LENGTH which is 32 on the Uno
    while (bytesLeftToRead > 0)
    {
      int toGet = bytesLeftToRead;
      if (toGet > I2C_BUFFER_LENGTH)
      {
        //If toGet is 32 this is bad because we read 6 bytes (Red+IR * 3 = 6) at a time
        //32 % 6 = 2 left over. We don't want to request 32 bytes, we want to request 30.
        //32 % 9 (Red+IR+GREEN) = 5 left over. We want to request 27.

        toGet = I2C_BUFFER_LENGTH - (I2C_BUFFER_LENGTH % (activeLEDs * 3)); //Trim toGet to be a multiple of the samples we need to read
      }

      bytesLeftToRead -= toGet;

      //Request toGet number of bytes from sensor
      _i2cPort->requestFrom(MAX30105_ADDRESS, toGet);
      
      while (toGet > 0)
      {
        sense.head++; //Advance the head of the storage struct
        sense.head %= STORAGE_SIZE; //Wrap condition

        byte temp[sizeof(uint32_t)]; //Array of 4 bytes that we will convert into long
        uint32_t tempLong;

        //Burst read three bytes - RED
        temp[3] = 0;
        temp[2] = _i2cPort->read();
        temp[1] = _i2cPort->read();
        temp[0] = _i2cPort->read();

        //Convert array to long
        memcpy(&tempLong, temp, sizeof(tempLong));
		
		tempLong &= 0x3FFFF; //Zero out all but 18 bits

        sense.red[sense.head] = tempLong; //Store this reading into the sense array

        if (activeLEDs > 1)
        {
          //Burst read three more bytes - IR
          temp[3] = 0;
          temp[2] = _i2cPort->read();
          temp[1] = _i2cPort->read();
          temp[0] = _i2cPort->read();

          //Convert array to long
          memcpy(&tempLong, temp, sizeof(tempLong));

		  tempLong &= 0x3FFFF; //Zero out all but 18 bits
          
		  sense.IR[sense.head] = tempLong;
        }

        if (activeLEDs > 2)
        {
          //Burst read three more bytes - Green
          temp[3] = 0;
          temp[2] = _i2cPort->read();
          temp[1] = _i2cPort->read();
          temp[0] = _i2cPort->read();

          //Convert array to long
          memcpy(&tempLong, temp, sizeof(tempLong));

		  tempLong &= 0x3FFFF; //Zero out all but 18 bits

          sense.green[sense.head] = tempLong;
        }

        toGet -= activeLEDs * 3;
      }

    } //End while (bytesLeftToRead > 0)

  } //End readPtr != writePtr

  return (numberOfSamples); //Let the world know how much new data we found
}

//Check for new data but give up after a certain amount of time
//Returns true if new data was found
//Returns false if new data was not found
bool MAX30105::safeCheck(uint8_t maxTimeToCheck)
{
  uint32_t markTime = millis();
  
  while(1)
  {
	if(millis() - markTime > maxTimeToCheck) return(false);

	if(check() == true) //We found new data!
	  return(true);

	delay(1);
  }
}

//Given a register, read it, mask it, and then set the thing
void MAX30105::bitMask(uint8_t reg, uint8_t mask, uint8_t thing)
{
  // Grab current register context
  uint8_t originalContents = readRegister8(_i2caddr, reg);

  // Zero-out the portions of the register we're interested in
  originalContents = originalContents & mask;

  // Change contents
  writeRegister8(_i2caddr, reg, originalContents | thing);
}

//
// Low-level I2C Communication
//
uint8_t MAX30105::readRegister8(uint8_t address, uint8_t reg) {
  _i2cPort->beginTransmission(address);
  _i2cPort->write(reg);
  _i2cPort->endTransmission(false);

  _i2cPort->requestFrom((uint8_t)address, (uint8_t)1); // Request 1 byte
  if (_i2cPort->available())
  {
    return(_i2cPort->read());
  }

  return (0); //Fail

}

void MAX30105::writeRegister8(uint8_t address, uint8_t reg, uint8_t value) {
  _i2cPort->beginTransmission(address);
  _i2cPort->write(reg);
  _i2cPort->write(value);
  _i2cPort->endTransmission();
}

MAX30105.h

C/C++
/*************************************************** 
 This is a library written for the Maxim MAX30105 Optical Smoke Detector
 It should also work with the MAX30102. However, the MAX30102 does not have a Green LED.

 These sensors use I2C to communicate, as well as a single (optional)
 interrupt line that is not currently supported in this driver.
 
 Written by Peter Jansen and Nathan Seidle (SparkFun)
 BSD license, all text above must be included in any redistribution.
 *****************************************************/

#pragma once
#include "Particle.h"
#define MAX30105_ADDRESS          0x57 //7-bit I2C Address
//Note that MAX30102 has the same I2C address and Part ID

#define I2C_SPEED_STANDARD        100000
#define I2C_SPEED_FAST            400000

//Define the size of the I2C buffer based on the platform the user has
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)

  //I2C_BUFFER_LENGTH is defined in Wire.H
  #define I2C_BUFFER_LENGTH BUFFER_LENGTH

#elif defined(__SAMD21G18A__)

  //SAMD21 uses RingBuffer.h
  #define I2C_BUFFER_LENGTH SERIAL_BUFFER_SIZE

#else

  //The catch-all default is 32
  #define I2C_BUFFER_LENGTH 32

#endif

class MAX30105 {
 public: 
  MAX30105(void);

  boolean begin(TwoWire &wirePort = Wire, uint32_t i2cSpeed = I2C_SPEED_STANDARD, uint8_t i2caddr = MAX30105_ADDRESS);

  uint32_t getRed(void); //Returns immediate red value
  uint32_t getIR(void); //Returns immediate IR value
  uint32_t getGreen(void); //Returns immediate green value
  bool safeCheck(uint8_t maxTimeToCheck); //Given a max amount of time, check for new data

  // Configuration
  void softReset();
  void shutDown(); 
  void wakeUp(); 

  void setLEDMode(uint8_t mode);

  void setADCRange(uint8_t adcRange);
  void setSampleRate(uint8_t sampleRate);
  void setPulseWidth(uint8_t pulseWidth);

  void setPulseAmplitudeRed(uint8_t value);
  void setPulseAmplitudeIR(uint8_t value);
  void setPulseAmplitudeGreen(uint8_t value);
  void setPulseAmplitudeProximity(uint8_t value);

  void setProximityThreshold(uint8_t threshMSB);

  //Multi-led configuration mode (page 22)
  void enableSlot(uint8_t slotNumber, uint8_t device); //Given slot number, assign a device to slot
  void disableSlots(void);
  
  // Data Collection

  //Interrupts (page 13, 14)
  uint8_t getINT1(void); //Returns the main interrupt group
  uint8_t getINT2(void); //Returns the temp ready interrupt
  void enableAFULL(void); //Enable/disable individual interrupts
  void disableAFULL(void);
  void enableDATARDY(void);
  void disableDATARDY(void);
  void enableALCOVF(void);
  void disableALCOVF(void);
  void enablePROXINT(void);
  void disablePROXINT(void);
  void enableDIETEMPRDY(void);
  void disableDIETEMPRDY(void);

  //FIFO Configuration (page 18)
  void setFIFOAverage(uint8_t samples);
  void enableFIFORollover();
  void disableFIFORollover();
  void setFIFOAlmostFull(uint8_t samples);
  
  //FIFO Reading
  uint16_t check(void); //Checks for new data and fills FIFO
  uint8_t available(void); //Tells caller how many new samples are available (head - tail)
  void nextSample(void); //Advances the tail of the sense array
  uint32_t getFIFORed(void); //Returns the FIFO sample pointed to by tail
  uint32_t getFIFOIR(void); //Returns the FIFO sample pointed to by tail
  uint32_t getFIFOGreen(void); //Returns the FIFO sample pointed to by tail

  uint8_t getWritePointer(void);
  uint8_t getReadPointer(void);
  void clearFIFO(void); //Sets the read/write pointers to zero

  //Proximity Mode Interrupt Threshold
  void setPROXINTTHRESH(uint8_t val);

  // Die Temperature
  float readTemperature();
  float readTemperatureF();

  // Detecting ID/Revision
  uint8_t getRevisionID();
  uint8_t readPartID();  

  // Setup the IC with user selectable settings
  void setup(byte powerLevel = 0x1F, byte sampleAverage = 4, byte ledMode = 3, int sampleRate = 400, int pulseWidth = 411, int adcRange = 4096);

  // Low-level I2C communication
  uint8_t readRegister8(uint8_t address, uint8_t reg);
  void writeRegister8(uint8_t address, uint8_t reg, uint8_t value);

 private:
  TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware
  uint8_t _i2caddr;

  //activeLEDs is the number of channels turned on, and can be 1 to 3. 2 is common for Red+IR.
  byte activeLEDs; //Gets set during setup. Allows check() to calculate how many bytes to read from FIFO
  
  uint8_t revisionID; 

  void readRevisionID();

  void bitMask(uint8_t reg, uint8_t mask, uint8_t thing);
 
   #define STORAGE_SIZE 4 //Each long is 4 bytes so limit this to fit on your micro
  typedef struct Record
  {
    uint32_t red[STORAGE_SIZE];
    uint32_t IR[STORAGE_SIZE];
    uint32_t green[STORAGE_SIZE];
    byte head;
    byte tail;
  } sense_struct; //This is our circular buffer of readings from the sensor

  sense_struct sense;

};

SparkFun_AS3935

C/C++
#ifndef _SPARKFUN_AS3935_H_
#define _SPARKFUN_AS3935_H_

#include <Wire.h>
#include <SPI.h>
#include <Arduino.h>

typedef uint8_t i2cAddress; 

const i2cAddress defAddr = 0x03; // Default ADD0 and ADD1 are HIGH
const i2cAddress addrOneHigh = 0x02; // ADD1 HIGH, ADD0 LOW
const i2cAddress addrZeroHigh = 0x01;// ADD1 LOW, ADD0 HIGH

enum SF_AS3935_REGISTER_NAMES {

	AFE_GAIN          = 0x00, 
  THRESHOLD,
  LIGHTNING_REG,
  INT_MASK_ANT,
  ENERGY_LIGHT_LSB,
  ENERGY_LIGHT_MSB,
  ENERGY_LIGHT_MMSB,
  DISTANCE,
  FREQ_DISP_IRQ,
  CALIB_TRCO        = 0x3A, 
  CALIB_SRCO        = 0x3B,
 // RESET             = 0x3C,
  CALIB_RCO         = 0x3D

};

// Masks for various registers, there are some redundant values that I kept 
// for the sake of clarity.
enum SF_AS3935_REGSTER_MASKS { 

  WIPE_ALL          = 0x0,
  INT_MASK          = 0xF, 
  ENERGY_MASK       = 0xF, 
  SPI_READ_M        = 0x40,
  CALIB_MASK        = 0x40,
  OSC_MASK          = 0x1F,
  DISTANCE_MASK     = 0x3F,
  DIV_MASK          = 0x3F,
  NOISE_FLOOR_MASK  = 0x8F,
  GAIN_MASK         = 0xC1,
  STAT_MASK         = 0xBF,
  DISTURB_MASK      = 0xDF, 
  LIGHT_MASK        = 0xCF, 
  SPIKE_MASK        = 0xF0,
  THRESH_MASK       = 0xF0, 
  CAP_MASK          = 0xF0,
  POWER_MASK        = 0xFE

};

typedef enum INTERRUPT_STATUS {

  NOISE_TO_HIGH     = 0x01,
  DISTURBER_DETECT  = 0x04,
  LIGHTNING         = 0x08

} lightningStatus;  

#define INDOOR            0x12
#define OUTDOOR           0xE

#define DIRECT_COMMAND    0x96
#define UNKNOWN_ERROR     0xFF

class SparkFun_AS3935
{
  public: 
    // Constructor to be used with SPI
    SparkFun_AS3935();

    // Constructor to be used with I-squared-C. 
    SparkFun_AS3935(i2cAddress address);

    // I-squared-C Begin
    bool begin(TwoWire &wirePort = Wire);

    // SPI begin 
    bool beginSPI(uint8_t user_CSPin, uint32_t spiPortSpeed, SPIClass &spiPort = SPI); 

    // REG0x00, bit[0], manufacturer default: 0. 
    // The product consumes 1-2uA while powered down. If the board is powered down 
    // the the TRCO will need to be recalibrated: REG0x08[5] = 1, wait 2 ms, REG0x08[5] = 0.
    // SPI and I-squared-C remain active when the chip is powered down. 
    void powerDown();

    // REG0x3A bit[7].
    // This register holds the state of the timer RC oscillator (TRCO),
    // after it has been calibrated. The TRCO will need to be recalibrated
    // after power down. The following function wakes the IC, sends the "Direct Command" to 
    // CALIB_RCO register REG0x3D, waits 2ms and then checks that it has been successfully
    // calibrated. Note that I-squared-C and SPI are active during power down. 
    bool wakeUp();

    // REG0x00, bits [5:1], manufacturer default: 10010 (INDOOR). 
    // This funciton changes toggles the chip's settings for Indoors and Outdoors. 
    void setIndoorOutdoor(uint8_t _setting);

    // REG0x00, bits [5:1], manufacturer default: 10010 (INDOOR). 
    // This function returns the indoor/outdoor settting. 
    uint8_t readIndoorOutdoor();

    // REG0x01, bits[3:0], manufacturer default: 0010 (2). 
    // This setting determines the threshold for events that trigger the 
    // IRQ Pin.  
    void watchdogThreshold(uint8_t _sensitivity);

    // REG0x01, bits[3:0], manufacturer default: 0010 (2). 
    // This function returns the threshold for events that trigger the 
    // IRQ Pin.  
    uint8_t readWatchdogThreshold();

    // REG0x01, bits [6:4], manufacturer default: 010 (2).
    // The noise floor level is compared to a known reference voltage. If this
    // level is exceeded the chip will issue an interrupt to the IRQ pin,
    // broadcasting that it can not operate properly due to noise (INT_NH).
    // Check datasheet for specific noise level tolerances when setting this register. 
    void setNoiseLevel(uint8_t _floor);

    // REG0x01, bits [6:4], manufacturer default: 010 (2).
    // This function will return the set noise level threshold: default is 2.
    uint8_t readNoiseLevel();

    // REG0x02, bits [3:0], manufacturer default: 0010 (2).
    // This setting, like the watchdog threshold, can help determine between false
    // events and actual lightning. The shape of the spike is analyzed during the
    // chip's signal validation routine. Increasing this value increases robustness
    // at the cost of sensitivity to distant events. 
    void spikeRejection(uint8_t _spSensitivity);
    
    // REG0x02, bits [3:0], manufacturer default: 0010 (2).
    // This function returns the value of the spike rejection register. This value
    // helps to differentiate between events and acutal lightning, by analyzing the 
    // shape of the spike during  chip's signal validation routine. 
    // Increasing this value increases robustness at the cost of sensitivity to distant events. 
    uint8_t readSpikeRejection();

    // REG0x02, bits [5:4], manufacturer default: 0 (single lightning strike).
    // The number of lightning events before IRQ is set high. 15 minutes is The 
    // window of time before the number of detected lightning events is reset. 
    // The number of lightning strikes can be set to 1,5,9, or 16. 
    void lightningThreshold(uint8_t _strikes);

    // REG0x02, bits [5:4], manufacturer default: 0 (single lightning strike).
    // This function will return the number of lightning strikes must strike within
    // a 15 minute window before it triggers an event on the IRQ pin. Default is 1. 
    uint8_t readLightningThreshold();

    // REG0x02, bit [6], manufacturer default: 1. 
    // This register clears the number of lightning strikes that has been read in
    // the last 15 minute block. 
    void clearStatistics(bool _clearStat);

    // REG0x03, bits [3:0], manufacturer default: 0. 
    // When there is an event that exceeds the watchdog threshold, the register is written
    // with the type of event. This consists of two messages: INT_D (disturber detected) and 
    // INT_L (Lightning detected). A third interrupt INT_NH (noise level too HIGH) 
    // indicates that the noise level has been exceeded and will persist until the
    // noise has ended. Events are active HIGH. There is a one second window of time to
    // read the interrupt register after lightning is detected, and 1.5 after
    // disturber.  
    uint8_t readInterruptReg();

    // REG0x03, bit [5], manufacturere default: 0.
    // This setting will change whether or not disturbers trigger the IRQ Pin. 
    void maskDisturber(bool _state);

    // REG0x03, bit [5], manufacturere default: 0.
    // This setting will return whether or not disturbers trigger the IRQ Pin. 
    uint8_t readMaskDisturber();

    // REG0x03, bit [7:6], manufacturer default: 0 (16 division ratio). 
    // The antenna is designed to resonate at 500kHz and so can be tuned with the
    // following setting. The accuracy of the antenna must be within 3.5 percent of
    // that value for proper signal validation and distance estimation.
    void changeDivRatio(uint8_t _divisionRatio);

    // REG0x03, bit [7:6], manufacturer default: 0 (16 division ratio). 
    // This function returns the current division ratio of the resonance frequency.
    // The antenna resonance frequency should be within 3.5 percent of 500kHz, and
    // so when modifying the resonance frequency with the internal capacitors
    // (tuneCap()) it's important to keep in mind that the displayed frequency on
    // the IRQ pin is divided by this number. 
    uint8_t readDivRatio();

    // REG0x07, bit [5:0], manufacturer default: 0. 
    // This register holds the distance to the front of the storm and not the
    // distance to a lightning strike.  
    uint8_t distanceToStorm();

    // REG0x08, bits [5,6,7], manufacturer default: 0. 
    // This will send the frequency of the oscillators to the IRQ pin. 
    //  _osc 1, bit[5] = TRCO - Timer RCO Oscillators 1.1MHz
    //  _osc 2, bit[6] = SRCO - System RCO at 32.768kHz
    //  _osc 3, bit[7] = LCO - Frequency of the Antenna
    void displayOscillator(bool _state, uint8_t _osc);

    // REG0x08, bits [3:0], manufacturer default: 0. 
    // This setting will add capacitance to the series RLC antenna on the product.
    // It's possible to add 0-120pF in steps of 8pF to the antenna. 
    void tuneCap(uint8_t _farad);

    // REG0x08, bits [3:0], manufacturer default: 0. 
    // This setting will return the capacitance of the internal capacitors. It will
    // return a value from one to 15 multiplied by the 8pF steps of the internal
    // capacitance.
    uint8_t readTuneCap();

    // LSB =  REG0x04, bits[7:0]
    // MSB =  REG0x05, bits[7:0]
    // MMSB = REG0x06, bits[4:0]
    // This returns a 20 bit value that is the 'energy' of the lightning strike.
    // According to the datasheet this is only a pure value that doesn't have any
    // physical meaning. 
    uint32_t lightningEnergy();
  
    // REG0x3D, bits[7:0]
    // This function calibrates both internal oscillators The oscillators are tuned
    // based on the resonance frequency of the antenna and so it should be trimmed
    // before the calibration is done. 
    bool calibrateOsc();

    // REG0x3C, bits[7:0]
    // This function resets all settings to their default values. 
    void resetSettings();

  private:

    uint32_t _spiPortSpeed; // Given sport speed. 
    uint8_t _cs; // Chip select pin
    uint8_t _regValue; // Variable for returned register data. 
    uint8_t _spiWrite; // Variable used for SPI write commands. 
    uint8_t _i2cWrite; // Variable used for SPI write commands. 
    // Address variable. 
    i2cAddress _address; 
    // This function handles all I2C write commands. It takes the register to write
    // to, then will mask the part of the register that coincides with the
    // setting, and then write the given bits to the register at the given
    // start position. 
    void _writeRegister(uint8_t _reg, uint8_t _mask, uint8_t _bits, uint8_t _startPosition);
    // Reads the given register.
    uint8_t _readRegister(uint8_t _reg);
    // I-squared-C and SPI Classes
    TwoWire *_i2cPort; 
    SPIClass *_spiPort; 

};
#endif

SparkFunBME280.cpp

C/C++
/******************************************************************************
SparkFunBME280.cpp
BME280 Particle Photon and Core Driver
Orginal by: Marshall Taylor @ SparkFun Electronics
Particle adaption by: Markus Haack (https://github.com/mhaack)
https://github.com/mhaack/SparkFun_BME280

Development environment specifics:
Particle IDE or Web IDE

This code is released under the [MIT License](http://opensource.org/licenses/MIT).
Please review the LICENSE.md file included with this example. If you have any questions 
or concerns with licensing, please contact techsupport@sparkfun.com.
Distributed as-is; no warranty is given.
******************************************************************************/
//See SparkFunBME280.h for additional topology notes.

#include "SparkFunBME280.h"
#include "application.h"
#include <math.h>

//****************************************************************************//
//
//  Settings and configuration
//
//****************************************************************************//

//Constructor -- Specifies default configuration
BME280::BME280( void )
{
	//Construct with these default settings if nothing is specified

	//Select interface mode
	settings.commInterface = I2C_MODE; //Can be I2C_MODE, SPI_MODE
	//Select address for I2C.  Does nothing for SPI
	settings.I2CAddress = 0x77; //Ignored for SPI_MODE
	//Select CS pin for SPI.  Does nothing for I2C
	settings.chipSelectPin = 10;
	settings.runMode = 0;
	settings.tempOverSample = 0;
	settings.pressOverSample = 0;
	settings.humidOverSample = 0;

}


//****************************************************************************//
//
//  Configuration section
//
//  This uses the stored SensorSettings to start the IMU
//  Use statements such as "mySensor.settings.commInterface = SPI_MODE;" to 
//  configure before calling .begin();
//
//****************************************************************************//
uint8_t BME280::begin()
{
	//Check the settings structure values to determine how to setup the device
	uint8_t dataToWrite = 0;  //Temporary variable

	switch (settings.commInterface)
	{

	case I2C_MODE:
		Wire.begin();
		break;

	case SPI_MODE:
		// start the SPI library:
		SPI.begin();
		// Maximum SPI frequency is 10MHz, could divide by 2 here:
		SPI.setClockDivider(SPI_CLOCK_DIV32);
		// Data is read and written MSb first.
		SPI.setBitOrder(MSBFIRST);
		// Data is captured on rising edge of clock (CPHA = 0)
		// Base value of the clock is HIGH (CPOL = 1)
		// This was SPI_MODE3 for RedBoard, but I had to change to
		// MODE0 for Teensy 3.1 operation
		SPI.setDataMode(SPI_MODE3);
		// initalize the  data ready and chip select pins:
		pinMode(settings.chipSelectPin, OUTPUT);
		digitalWrite(settings.chipSelectPin, HIGH);
		break;

	default:
		break;
	}

	//Reading all compensation data, range 0x88:A1, 0xE1:E7
	
	calibration.dig_T1 = ((uint16_t)((readRegister(BME280_DIG_T1_MSB_REG) << 8) + readRegister(BME280_DIG_T1_LSB_REG)));
	calibration.dig_T2 = ((int16_t)((readRegister(BME280_DIG_T2_MSB_REG) << 8) + readRegister(BME280_DIG_T2_LSB_REG)));
	calibration.dig_T3 = ((int16_t)((readRegister(BME280_DIG_T3_MSB_REG) << 8) + readRegister(BME280_DIG_T3_LSB_REG)));

	calibration.dig_P1 = ((uint16_t)((readRegister(BME280_DIG_P1_MSB_REG) << 8) + readRegister(BME280_DIG_P1_LSB_REG)));
	calibration.dig_P2 = ((int16_t)((readRegister(BME280_DIG_P2_MSB_REG) << 8) + readRegister(BME280_DIG_P2_LSB_REG)));
	calibration.dig_P3 = ((int16_t)((readRegister(BME280_DIG_P3_MSB_REG) << 8) + readRegister(BME280_DIG_P3_LSB_REG)));
	calibration.dig_P4 = ((int16_t)((readRegister(BME280_DIG_P4_MSB_REG) << 8) + readRegister(BME280_DIG_P4_LSB_REG)));
	calibration.dig_P5 = ((int16_t)((readRegister(BME280_DIG_P5_MSB_REG) << 8) + readRegister(BME280_DIG_P5_LSB_REG)));
	calibration.dig_P6 = ((int16_t)((readRegister(BME280_DIG_P6_MSB_REG) << 8) + readRegister(BME280_DIG_P6_LSB_REG)));
	calibration.dig_P7 = ((int16_t)((readRegister(BME280_DIG_P7_MSB_REG) << 8) + readRegister(BME280_DIG_P7_LSB_REG)));
	calibration.dig_P8 = ((int16_t)((readRegister(BME280_DIG_P8_MSB_REG) << 8) + readRegister(BME280_DIG_P8_LSB_REG)));
	calibration.dig_P9 = ((int16_t)((readRegister(BME280_DIG_P9_MSB_REG) << 8) + readRegister(BME280_DIG_P9_LSB_REG)));

	calibration.dig_H1 = ((uint8_t)(readRegister(BME280_DIG_H1_REG)));
	calibration.dig_H2 = ((int16_t)((readRegister(BME280_DIG_H2_MSB_REG) << 8) + readRegister(BME280_DIG_H2_LSB_REG)));
	calibration.dig_H3 = ((uint8_t)(readRegister(BME280_DIG_H3_REG)));
	calibration.dig_H4 = ((int16_t)((readRegister(BME280_DIG_H4_MSB_REG) << 4) + (readRegister(BME280_DIG_H4_LSB_REG) & 0x0F)));
	calibration.dig_H5 = ((int16_t)((readRegister(BME280_DIG_H5_MSB_REG) << 4) + ((readRegister(BME280_DIG_H4_LSB_REG) >> 4) & 0x0F)));
	calibration.dig_H6 = ((uint8_t)readRegister(BME280_DIG_H6_REG));

	//Set the oversampling control words.
	//config will only be writeable in sleep mode, so first insure that.
	writeRegister(BME280_CTRL_MEAS_REG, 0x00);
	
	//Set the config word
	dataToWrite = (settings.tStandby << 0x5) & 0xE0;
	dataToWrite |= (settings.filter << 0x02) & 0x1C;
	writeRegister(BME280_CONFIG_REG, dataToWrite);
	
	//Set ctrl_hum first, then ctrl_meas to activate ctrl_hum
	dataToWrite = settings.humidOverSample & 0x07; //all other bits can be ignored
	writeRegister(BME280_CTRL_HUMIDITY_REG, dataToWrite);
	
	//set ctrl_meas
	//First, set temp oversampling
	dataToWrite = (settings.tempOverSample << 0x5) & 0xE0;
	//Next, pressure oversampling
	dataToWrite |= (settings.pressOverSample << 0x02) & 0x1C;
	//Last, set mode
	dataToWrite |= (settings.runMode) & 0x03;
	//Load the byte
	writeRegister(BME280_CTRL_MEAS_REG, dataToWrite);
	
	return readRegister(0xD0);
}

//Strictly resets.  Run .begin() afterwards
void BME280::reset( void )
{
	writeRegister(BME280_RST_REG, 0xB6);
	
}

//****************************************************************************//
//
//  Pressure Section
//
//****************************************************************************//
float BME280::readFloatPressure( void )
{

	// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
	// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa
	int32_t adc_P = ((uint32_t)readRegister(BME280_PRESSURE_MSB_REG) << 12) | ((uint32_t)readRegister(BME280_PRESSURE_LSB_REG) << 4) | ((readRegister(BME280_PRESSURE_XLSB_REG) >> 4) & 0x0F);
	
	int64_t var1, var2, p_acc;
	var1 = ((int64_t)t_fine) - 128000;
	var2 = var1 * var1 * (int64_t)calibration.dig_P6;
	var2 = var2 + ((var1 * (int64_t)calibration.dig_P5)<<17);
	var2 = var2 + (((int64_t)calibration.dig_P4)<<35);
	var1 = ((var1 * var1 * (int64_t)calibration.dig_P3)>>8) + ((var1 * (int64_t)calibration.dig_P2)<<12);
	var1 = (((((int64_t)1)<<47)+var1))*((int64_t)calibration.dig_P1)>>33;
	if (var1 == 0)
	{
		return 0; // avoid exception caused by division by zero
	}
	p_acc = 1048576 - adc_P;
	p_acc = (((p_acc<<31) - var2)*3125)/var1;
	var1 = (((int64_t)calibration.dig_P9) * (p_acc>>13) * (p_acc>>13)) >> 25;
	var2 = (((int64_t)calibration.dig_P8) * p_acc) >> 19;
	p_acc = ((p_acc + var1 + var2) >> 8) + (((int64_t)calibration.dig_P7)<<4);
	
	return (float)p_acc / 256.0;
	
}

float BME280::readFloatAltitudeMeters( void )
{
	float heightOutput = 0;
	
	heightOutput = ((float)-45846.2)*(pow(((float)readFloatPressure()/(float)101325), 0.190263) - (float)1);
	return heightOutput;
	
}

float BME280::readFloatAltitudeFeet( void )
{
	float heightOutput = 0;
	
	heightOutput = readFloatAltitudeMeters() * 3.28084;
	return heightOutput;
	
}

//****************************************************************************//
//
//  Humidity Section
//
//****************************************************************************//
float BME280::readFloatHumidity( void )
{
	
	// Returns humidity in %RH as unsigned 32 bit integer in Q22. 10 format (22 integer and 10 fractional bits).
	// Output value of “47445” represents 47445/1024 = 46. 333 %RH
	int32_t adc_H = ((uint32_t)readRegister(BME280_HUMIDITY_MSB_REG) << 8) | ((uint32_t)readRegister(BME280_HUMIDITY_LSB_REG));
	
	int32_t var1;
	var1 = (t_fine - ((int32_t)76800));
	var1 = (((((adc_H << 14) - (((int32_t)calibration.dig_H4) << 20) - (((int32_t)calibration.dig_H5) * var1)) +
	((int32_t)16384)) >> 15) * (((((((var1 * ((int32_t)calibration.dig_H6)) >> 10) * (((var1 * ((int32_t)calibration.dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) *
	((int32_t)calibration.dig_H2) + 8192) >> 14));
	var1 = (var1 - (((((var1 >> 15) * (var1 >> 15)) >> 7) * ((int32_t)calibration.dig_H1)) >> 4));
	var1 = (var1 < 0 ? 0 : var1);
	var1 = (var1 > 419430400 ? 419430400 : var1);

	return (float)(var1>>12) / 1024.0;

}



//****************************************************************************//
//
//  Temperature Section
//
//****************************************************************************//

float BME280::readTempC( void )
{
	// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
	// t_fine carries fine temperature as global value

	//get the reading (adc_T);
	int32_t adc_T = ((uint32_t)readRegister(BME280_TEMPERATURE_MSB_REG) << 12) | ((uint32_t)readRegister(BME280_TEMPERATURE_LSB_REG) << 4) | ((readRegister(BME280_TEMPERATURE_XLSB_REG) >> 4) & 0x0F);

	//By datasheet, calibrate
	int64_t var1, var2;

	var1 = ((((adc_T>>3) - ((int32_t)calibration.dig_T1<<1))) * ((int32_t)calibration.dig_T2)) >> 11;
	var2 = (((((adc_T>>4) - ((int32_t)calibration.dig_T1)) * ((adc_T>>4) - ((int32_t)calibration.dig_T1))) >> 12) *
	((int32_t)calibration.dig_T3)) >> 14;
	t_fine = var1 + var2;
	float output = (t_fine * 5 + 128) >> 8;

	output = output / 100;
	
	return output;
}

float BME280::readTempF( void )
{
	float output = readTempC();
	output = (output * 9) / 5 + 32;

	return output;
}

//****************************************************************************//
//
//  Utility
//
//****************************************************************************//
void BME280::readRegisterRegion(uint8_t *outputPointer , uint8_t offset, uint8_t length)
{
	//define pointer that will point to the external space
	uint8_t i = 0;
	char c = 0;

	switch (settings.commInterface)
	{

	case I2C_MODE:
		Wire.beginTransmission(settings.I2CAddress);
		Wire.write(offset);
		Wire.endTransmission();

		// request bytes from slave device
		Wire.requestFrom(settings.I2CAddress, length);
		while ( (Wire.available()) && (i < length))  // slave may send less than requested
		{
			c = Wire.read(); // receive a byte as character
			*outputPointer = c;
			outputPointer++;
			i++;
		}
		break;

	case SPI_MODE:
		// take the chip select low to select the device:
		digitalWrite(settings.chipSelectPin, LOW);
		// send the device the register you want to read:
		SPI.transfer(offset | 0x80);  //Ored with "read request" bit
		while ( i < length ) // slave may send less than requested
		{
			c = SPI.transfer(0x00); // receive a byte as character
			*outputPointer = c;
			outputPointer++;
			i++;
		}
		// take the chip select high to de-select:
		digitalWrite(settings.chipSelectPin, HIGH);
		break;

	default:
		break;
	}

}

uint8_t BME280::readRegister(uint8_t offset)
{
	//Return value
	uint8_t result;
	uint8_t numBytes = 1;
	switch (settings.commInterface) {

	case I2C_MODE:
		Wire.beginTransmission(settings.I2CAddress);
		Wire.write(offset);
		Wire.endTransmission();

		Wire.requestFrom(settings.I2CAddress, numBytes);
		while ( Wire.available() ) // slave may send less than requested
		{
			result = Wire.read(); // receive a byte as a proper uint8_t
		}
		break;

	case SPI_MODE:
		// take the chip select low to select the device:
		digitalWrite(settings.chipSelectPin, LOW);
		// send the device the register you want to read:
		SPI.transfer(offset | 0x80);  //Ored with "read request" bit
		// send a value of 0 to read the first byte returned:
		result = SPI.transfer(0x00);
		// take the chip select high to de-select:
		digitalWrite(settings.chipSelectPin, HIGH);
		break;

	default:
		break;
	}
	return result;
}

int16_t BME280::readRegisterInt16( uint8_t offset )
{
	uint8_t myBuffer[2];
	readRegisterRegion(myBuffer, offset, 2);  //Does memory transfer
	int16_t output = (int16_t)myBuffer[0] | int16_t(myBuffer[1] << 8);
	
	return output;
}

void BME280::writeRegister(uint8_t offset, uint8_t dataToWrite)
{
	switch (settings.commInterface)
	{
	case I2C_MODE:
		//Write the byte
		Wire.beginTransmission(settings.I2CAddress);
		Wire.write(offset);
		Wire.write(dataToWrite);
		Wire.endTransmission();
		break;

	case SPI_MODE:
		// take the chip select low to select the device:
		digitalWrite(settings.chipSelectPin, LOW);
		// send the device the register you want to read:
		SPI.transfer(offset & 0x7F);
		// send a value of 0 to read the first byte returned:
		SPI.transfer(dataToWrite);
		// decrement the number of bytes left to read:
		// take the chip select high to de-select:
		digitalWrite(settings.chipSelectPin, HIGH);
		break;

	default:
		break;
	}
}

Credits

Jade Perreault

Jade Perreault

32 projects • 59 followers
27 Years as a Journeyman Electrician , 4 Years as a PCB design engineer for Penteon Corporation Out of New York ..

Comments