Jade Perreault
Published © CC BY-NC-SA

Netduino Environmental Sensor Project

Sensing, power, air quality, temperature, humidity, and air pressure displayed on an LCD and transmitted by MQTT.

AdvancedFull instructions providedOver 1 day1,084
Netduino Environmental Sensor Project

Things used in this project

Hardware components

Sparkfun SEN-14193
×1
LTC2945
×1
SparkFun MicroView - OLED Arduino Module
SparkFun MicroView - OLED Arduino Module
×1

Software apps and online services

Visual Studio 2015
Microsoft Visual Studio 2015

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

DS18B20 Sensor

Data Sheet Sparkfun

Netduino3 Wifi Base

Voltage Sensor

SI7021

Netduino Wifi Top with Added printing supports

Bill of Materials

Copy paste Part number at digikey.com

Netduino Wifi Top

Schematics

Fritzing Board Gerber

Code

Arduino Test Code

C/C++
Plug Board into an arduino and test the I2C Outputs
#include <Adafruit_ST7735.h>
#include <Adafruit_ST7789.h>
#include <Adafruit_ST77xx.h>
#include <OneWire.h>
#include <DS18B20.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "SparkFunCCS811.h"
#include <Adafruit_GFX.h>    // Core graphics library
#include <SPI.h>
#include <SD.h>
//===================================
// MicroOLED Definition //
//===================================

//=======================================
#define LTCADDR 0x69
byte ADCvinMSB, ADCvinLSB, curSenseMSB, curSenseLSB, AinVMSB, AinVLSB;
unsigned int ADCvin, ADCcur, AinV;
float inputVoltage, ADCvoltage, current10, current1, current0p1, current0p01;
#define CCS811_ADDR 0x66 //Default I2C Address
DS18B20 ds(7);
CCS811 mySensor(CCS811_ADDR);
//===================================
#define STATUS     0x00
#define OUT_P_MSB  0x01
#define OUT_P_CSB  0x02
#define OUT_P_LSB  0x03
#define OUT_T_MSB  0x04
#define OUT_T_LSB  0x05
#define DR_STATUS  0x06
#define OUT_P_DELTA_MSB  0x07
#define OUT_P_DELTA_CSB  0x08
#define OUT_P_DELTA_LSB  0x09
#define OUT_T_DELTA_MSB  0x0A
#define OUT_T_DELTA_LSB  0x0B
#define WHO_AM_I   0x0C
#define F_STATUS   0x0D
#define F_DATA     0x0E
#define F_SETUP    0x0F
#define TIME_DLY   0x10
#define SYSMOD     0x11
#define INT_SOURCE 0x12
#define PT_DATA_CFG 0x13
#define BAR_IN_MSB 0x14
#define BAR_IN_LSB 0x15
#define P_TGT_MSB  0x16
#define P_TGT_LSB  0x17
#define T_TGT      0x18
#define P_WND_MSB  0x19
#define P_WND_LSB  0x1A
#define T_WND      0x1B
#define P_MIN_MSB  0x1C
#define P_MIN_CSB  0x1D
#define P_MIN_LSB  0x1E
#define T_MIN_MSB  0x1F
#define T_MIN_LSB  0x20
#define P_MAX_MSB  0x21
#define P_MAX_CSB  0x22
#define P_MAX_LSB  0x23
#define T_MAX_MSB  0x24
#define T_MAX_LSB  0x25
#define CTRL_REG1  0x26
#define CTRL_REG2  0x27
#define CTRL_REG3  0x28
#define CTRL_REG4  0x29
#define CTRL_REG5  0x2A
#define OFF_P      0x2B
#define OFF_T      0x2C
#define OFF_H      0x2D

#define MPL3115A2_ADDRESS 0x60 // 7-bit I2C address
long startTime;
//=====================================
void setup() {
  Serial.begin(9600);
  Wire.begin();
  CCS811Core::status returnCode = mySensor.begin();
  Serial.print("Devices: ");
  Serial.println(ds.getNumberOfDevices());
  Serial.println();
  //==================================
  if (IIC_Read(WHO_AM_I) == 196)
    Serial.println("MPL3115A2 online!");
  else
    Serial.println("No response - check connections");

  // Configure the sensor
  setModeAltimeter(); // Measure altitude above sea level in meters
  //setModeBarometer(); // Measure pressure in Pascals from 20 to 110 kPa

  setOversampleRate(7); // Set Oversample to the recommended 128
  enableEventFlags(); // Enable all three pressure and temp event flags
  //=================================

  //================================
}

void loop() {
  //=======================================


  //=======================================
  Wire.beginTransmission(LTCADDR);//first get Input Voltage - 80V max
  Wire.write(0x1E);
  Wire.endTransmission(false);
  Wire.requestFrom(LTCADDR, 2, true);
  delay(1);
  ADCvinMSB = Wire.read();
  ADCvinLSB = Wire.read();
  ADCvin = ((unsigned int)(ADCvinMSB) << 4) + ((ADCvinLSB >> 4) & 0x0F);//formats into 12bit integer
  inputVoltage = ADCvin * 0.025; //25mV resolution

  Wire.beginTransmission(LTCADDR);//get ADC Input 2V max
  Wire.write(0x28);
  Wire.endTransmission(false);
  Wire.requestFrom(LTCADDR, 2, true);
  delay(1);
  AinVMSB = Wire.read();
  AinVLSB = Wire.read();
  AinV = ((unsigned int)(AinVMSB) << 4) + ((AinVLSB >> 4) & 0x0F);//12 bit format
  ADCvoltage = AinV * 0.5E-3; //500uV resolution

  Wire.beginTransmission(LTCADDR);//get sense current
  Wire.write(0x14);
  Wire.endTransmission(false);
  Wire.requestFrom(LTCADDR, 2, true);
  delay(1);
  curSenseMSB = Wire.read();
  curSenseLSB = Wire.read();
  ADCcur = ((unsigned int)(curSenseMSB) << 4) + ((curSenseLSB >> 4) & 0x0F);//12 bit format
  //gets voltage across, 25uV resolution, then this converts to voltage for each sense resistor
  current10 = ADCcur * (25E-3) / 10.0; //10mA max, unit is mA
  current1 = ADCcur * (25E-3) / 1.0; //100mA max, unit is mA
  current0p1 = ADCcur * (25E-3) / 0.1; //1A max, unit is mA
  current0p01 = ADCcur * (25E-6) / 0.01;//10A max, unit is A

  Serial.print(inputVoltage, 2);
  Serial.println("V");
  delay(1000);

  while (ds.selectNext())
    Serial.print("Temperature: ");
  Serial.print(ds.getTempC());
  Serial.println(" C / ");

  mySensor.readAlgorithmResults();
  Serial.print("CO2[");
  //Returns calculated CO2 reading
  Serial.print(mySensor.getCO2());


  Serial.print("] tVOC[");
  //Returns calculated TVOC reading
  Serial.print(mySensor.getTVOC());

  Serial.print("] millis[");
  //Simply the time since program start
  Serial.print(millis());
  Serial.print("]");
  Serial.println();

  delay(10); //Don't spam the I2C bus

  //=============================================
  startTime = millis();

  float altitude = readAltitude();
  Serial.print("Altitude(m):");
  Serial.print(altitude, 2);

  Serial.print(" time diff:");
  Serial.print(millis() - startTime);

  Serial.println();
}

//Returns the number of meters above sea level
float readAltitude()
{
  toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading

  //Wait for PDR bit, indicates we have new pressure data
  int counter = 0;
  while ( (IIC_Read(STATUS) & (1 << 1)) == 0)
  {
    if (++counter > 100) return (-999); //Error out
    delay(1);
  }

  // Read pressure registers
  Wire.beginTransmission(MPL3115A2_ADDRESS);
  Wire.write(OUT_P_MSB);  // Address of data to get
  Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1!
  Wire.requestFrom(MPL3115A2_ADDRESS, 3); // Request three bytes

  //Wait for data to become available
  counter = 0;
  while (Wire.available() < 3)
  {
    if (counter++ > 100) return (-999); //Error out
    delay(1);
  }

  byte msb, csb, lsb;
  msb = Wire.read();
  csb = Wire.read();
  lsb = Wire.read();

  toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading
  float tempcsb = (lsb >> 4) / 16.0;
  float altitude = (float)( (msb << 8) | csb) + tempcsb;

  return (altitude);
}

//Returns the number of feet above sea level
float readAltitudeFt()
{
  return (readAltitude() * 3.28084);
}

float readPressure()
{
  toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading

  //Wait for PDR bit, indicates we have new pressure data
  int counter = 0;
  while ( (IIC_Read(STATUS) & (1 << 2)) == 0)
  {
    if (++counter > 100) return (-999); //Error out
    delay(1);
  }

  // Read pressure registers
  Wire.beginTransmission(MPL3115A2_ADDRESS);
  Wire.write(OUT_P_MSB);  // Address of data to get
  Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1!
  Wire.requestFrom(MPL3115A2_ADDRESS, 3); // Request three bytes

  //Wait for data to become available
  counter = 0;
  while (Wire.available() < 3)
  {
    if (counter++ > 100) return (-999); //Error out
    delay(1);
  }

  byte msb, csb, lsb;
  msb = Wire.read();
  csb = Wire.read();
  lsb = Wire.read();

  toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading

  // Pressure comes back as a left shifted 20 bit number
  long pressure_whole = (long)msb << 16 | (long)csb << 8 | (long)lsb;
  pressure_whole >>= 6; //Pressure is an 18 bit number with 2 bits of decimal. Get rid of decimal portion.

  lsb &= 0b00110000; //Bits 5/4 represent the fractional component
  lsb >>= 4; //Get it right aligned
  float pressure_decimal = (float)lsb / 4.0; //Turn it into fraction

  float pressure = (float)pressure_whole + pressure_decimal;

  return (pressure);
}

float readTemp()
{
  toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading

  //Wait for TDR bit, indicates we have new temp data
  int counter = 0;
  while ( (IIC_Read(STATUS) & (1 << 1)) == 0)
  {
    if (++counter > 100) return (-999); //Error out
    delay(1);
  }

  // Read temperature registers
  Wire.beginTransmission(MPL3115A2_ADDRESS);
  Wire.write(OUT_T_MSB);  // Address of data to get
  Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1!
  Wire.requestFrom(MPL3115A2_ADDRESS, 2); // Request two bytes

  //Wait for data to become available
  counter = 0;
  while (Wire.available() < 2)
  {
    if (++counter > 100) return (-999); //Error out
    delay(1);
  }

  byte msb, lsb;
  msb = Wire.read();
  lsb = Wire.read();
  float templsb = (lsb >> 4) / 16.0; //temp, fraction of a degree

  float temperature = (float)(msb + templsb);

  return (temperature);
}

//Give me temperature in fahrenheit!
float readTempF()
{
  return ((readTemp() * 9.0) / 5.0 + 32.0); // Convert celsius to fahrenheit
}
void setModeBarometer()
{
  byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings
  tempSetting &= ~(1 << 7); //Clear ALT bit
  IIC_Write(CTRL_REG1, tempSetting);
}

void setModeAltimeter()
{
  byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings
  tempSetting |= (1 << 7); //Set ALT bit
  IIC_Write(CTRL_REG1, tempSetting);
}

void setModeStandby()
{
  byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings
  tempSetting &= ~(1 << 0); //Clear SBYB bit for Standby mode
  IIC_Write(CTRL_REG1, tempSetting);
}
void setModeActive()
{
  byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings
  tempSetting |= (1 << 0); //Set SBYB bit for Active mode
  IIC_Write(CTRL_REG1, tempSetting);
}

//Setup FIFO mode to one of three modes. See page 26, table 31
//From user jr4284
void setFIFOMode(byte f_Mode)
{
  if (f_Mode > 3) f_Mode = 3; // FIFO value cannot exceed 3.
  f_Mode <<= 6; // Shift FIFO byte left 6 to put it in bits 6, 7.

  byte tempSetting = IIC_Read(F_SETUP); //Read current settings
  tempSetting &= ~(3 << 6); // clear bits 6, 7
  tempSetting |= f_Mode; //Mask in new FIFO bits
  IIC_Write(F_SETUP, tempSetting);
}
void setOversampleRate(byte sampleRate)
{
  if (sampleRate > 7) sampleRate = 7; //OS cannot be larger than 0b.0111
  sampleRate <<= 3; //Align it for the CTRL_REG1 register

  byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings
  tempSetting &= 0b11000111; //Clear out old OS bits
  tempSetting |= sampleRate; //Mask in new OS bits
  IIC_Write(CTRL_REG1, tempSetting);
}
void toggleOneShot(void)
{
  byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings
  tempSetting &= ~(1 << 1); //Clear OST bit
  IIC_Write(CTRL_REG1, tempSetting);

  tempSetting = IIC_Read(CTRL_REG1); //Read current settings to be safe
  tempSetting |= (1 << 1); //Set OST bit
  IIC_Write(CTRL_REG1, tempSetting);
}

void enableEventFlags()
{
  IIC_Write(PT_DATA_CFG, 0x07); // Enable all three pressure and temp event flags
}

// These are the two I2C functions in this sketch.
byte IIC_Read(byte regAddr)
{
  // This function reads one byte over IIC
  Wire.beginTransmission(MPL3115A2_ADDRESS);
  Wire.write(regAddr);  // Address of CTRL_REG1
  Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1!
  Wire.requestFrom(MPL3115A2_ADDRESS, 1); // Request the data...
  return Wire.read();
}

void IIC_Write(byte regAddr, byte value)
{
  // This function writes one byto over IIC
  Wire.beginTransmission(MPL3115A2_ADDRESS);
  Wire.write(regAddr);
  Wire.write(value);
  Wire.endTransmission(true);

  //================================================
  
}

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