Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Jade Perreault
Published © CC BY-NC-SA

Helium Air Quality Sensor

A Helium-based air quality sensor and solar setup.

AdvancedFull instructions providedOver 6 days3,177
Helium Air Quality Sensor

Things used in this project

Story

Read more

Custom parts and enclosures

Base

Lid

Side Brackets

Solar Panel Mount

Leg 1

Leg 2

Leg 3

Leg 4

Schematics

Arduino Helium Gerber

Voltage Coverter

Code

Arduino Code

C/C++
/*
 * Copyright 2017, Helium Systems, Inc.
 * All Rights Reserved. See LICENCE.txt for license information
 */
#include "SparkFunCCS811.h"
#include "Arduino.h"
#include "Board.h"
#include "Helium.h"
#include "HeliumUtil.h"
#include <OneWire.h>
#include<stdio.h>
#include <DS18B20.h>//(Temperature Sensor)
#include "Wire.h"
// NOTE: Please ensure you've created a channel with the above
// CHANNEL_NAME as it's name.
#define CHANNEL_NAME "Helium MQTT"
#define BUFSIZE 9
//=============================
Helium  helium(&atom_serial);
Channel channel(&helium);
//=============================
#define LTCADDR B1101111//Table 1 both LOW (7bit address)
//=============================
#define CCS811_ADDR 0x5B //Default I2C Address
//#define CCS811_ADDR 0x5A //Alternate I2C Address

CCS811 mySensor(CCS811_ADDR);
//=============================
DS18B20 ds(7);
uint8_t address[] = {40, 250, 31, 218, 4, 0, 0, 52};
uint8_t selected;
//=============================
byte ADCvinMSB, ADCvinLSB, curSenseMSB, curSenseLSB, AinVMSB, AinVLSB;
unsigned int ADCvin, ADCcur, AinV;
float inputVoltage, ADCvoltage, current10, current1, current0p1, current0p01;
//=============================
char status;
void connect() {
  while (!helium.connected())
  {
    Serial.print("Connecting - ");
    int status = helium.connect();
    if (report_status(status) != helium_status_OK)
    {
      delay(1000);
    }
  }
}

void channel_create(const char * channel_name ) {
  int8_t result;
  //    int    status;
  do
  {
    connect();
    Serial.println("Creating Channel - ");
    status = channel.begin(channel_name, &result);
  } while (helium_status_OK != status || result != 0);
}

void channel_poll(void * data1, size_t len, size_t * used) {
  char status;
  do
  {
    status = channel.poll_data(data1, len, used);
  } while (report_status(status) != helium_status_OK);
}

void setup(){
    Serial.begin(9600);
    DBG_PRINTLN(F("Starting"));
    Wire.begin();
    // Begin communication with the Helium Atom
    // The baud rate differs per supported board
    // and is configured in Board.h
    helium.begin(HELIUM_BAUD_RATE);

    // Connect the Atom to the Helium Network
    helium_connect(&helium);

    // Begin communicating with the channel. This should only need to
    // be done once. The HeliumUtil functions add simple retry logic
    // to re-create a channel if it disconnects.
    channel_create(&channel, CHANNEL_NAME);
  Serial.print("Devices: ");
  Serial.println(ds.getNumberOfDevices());
  Serial.println();

    //=======================
 //It is recommended to check return status on .begin(), but it is not
  //required.
  CCS811Core::status returnCode = mySensor.begin();
  if (returnCode != CCS811Core::SENSOR_SUCCESS)
  {
    Serial.println(".begin() returned with an error.");
    while (1); //Hang if there was a problem.
  //======================
}
}

void loop(){
//const char * data = "Hello Helium!";
const char data[HELIUM_MAX_DATA_SIZE];
  char data1[HELIUM_MAX_DATA_SIZE];
  size_t data_used;
  char message[56];
  char msg[56];
  int sensorValue;
  int message1;
  int    status;
  int8_t result;
  int v = (inputVoltage);
  int c = (current0p01);
  int t = (ds.getTempC());
  int C0 = (mySensor.getCO2());
  int VOC = (mySensor.getTVOC());
//=============================================================
//========================C0 Sensor============================
//=============================================================
 //Check to see if data is ready with .dataAvailable()
  if (mySensor.dataAvailable())
  {
    //If so, have the sensor read and calculate the results.
    //Get them later
    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
//===========================================================
//=====================Voltage Sensor========================
//===========================================================
 Wire.beginTransmission(LTCADDR);//first get Input Voltage - 30V 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
  current0p1 = ADCcur * (25E-3) / 0.1; //1A max, unit is mA
  Serial.print("Volts_");
  Serial.println(inputVoltage);
  Serial.print("Amps_");
  Serial.println(current0p01, 2);
  delay(100);
//===========================================================
//===================Temp Sensor=============================
//===========================================================
  while (ds.selectNext())
  {
    switch (ds.getFamilyCode())
      // Print address.
      uint8_t address[8];
    ds.getAddress(address);
    for (uint8_t i = 0; i < 8; i++)
      (ds.getResolution());
    Serial.print(ds.getTempC());
    Serial.print(" C / ");
    Serial.print(ds.getTempF());
    Serial.println(" F");
    sensorValue = ds.getTempC();
  }
//=========================================================
    // Send data to channel
    snprintf(data, 75, "V %d:A %d:T %d:C02 %d:VOC %d", v, c, t, C0, VOC); //NOTE: The values V,A,T,C0,VOC are needed for the next step
    channel_send(&channel, CHANNEL_NAME, data, strlen(data));
    delay(5000);
    // Wait about 5 seconds
}

Arduino Code for Google

C/C++
/*
 * Copyright 2017, Helium Systems, Inc.
 * All Rights Reserved. See LICENCE.txt for license information
 */
#include "SparkFunCCS811.h"
#include "Arduino.h"
#include "Board.h"
#include "Helium.h"
#include "HeliumUtil.h"
#include <OneWire.h>
#include<stdio.h>
#include <DS18B20.h>//(Temperature Sensor)
#include "Wire.h"
// NOTE: Please ensure you've created a channel with the above
// CHANNEL_NAME as it's name.
#define CHANNEL_NAME "Helium MQTT"
#define CHANNEL_NAME2 "Helium_Air_Quality"
#define BUFSIZE 9
//=============================
Helium  helium(&atom_serial);
Channel channel(&helium);
Channel channel2(&helium);
//=============================
#define LTCADDR B1101010//Table 1 both nc (7bit address)
//=============================
#define CCS811_ADDR 0x5B //Default I2C Address
//#define CCS811_ADDR 0x5A //Alternate I2C Address

CCS811 mySensor(CCS811_ADDR);
//=============================
DS18B20 ds(7);
uint8_t address[] = {40, 250, 31, 218, 4, 0, 0, 52};
uint8_t selected;
//=============================
byte ADCvinMSB, ADCvinLSB, curSenseMSB, curSenseLSB, AinVMSB, AinVLSB;
unsigned int ADCvin, ADCcur, AinV;
float inputVoltage, ADCvoltage, current10, current1, current0p1, current0p01;
//=============================
char status;
void connect() {
  while (!helium.connected())
  {
    Serial.print("Connecting - ");
    int status = helium.connect();
    if (report_status(status) != helium_status_OK)
    {
      delay(1000);
    }
  }
}

void channel_create(const char * channel_name ) {
  int8_t result;

  do
  {
    connect();
    Serial.println("Creating Channel - ");
    status = channel.begin(channel_name, &result);
  } while (helium_status_OK != status || result != 0);
}
void channel2_create(const char * channel2_name ) {
  int8_t result;

  do
  {
    connect();
    Serial.println("Creating Channel - ");
    status = channel2.begin(channel2_name, &result);
  } while (helium_status_OK != status || result != 0);
}
void channel_poll(void * data1, size_t len, size_t * used) {
  char status;
  do
  {
    status = channel.poll_data(data1, len, used);
  } while (report_status(status) != helium_status_OK);
}

void setup(){
    Serial.begin(9600);
    DBG_PRINTLN(F("Starting"));
    Wire.begin();
    // Begin communication with the Helium Atom
    // The baud rate differs per supported board
    // and is configured in Board.h
    helium.begin(HELIUM_BAUD_RATE);

    // Connect the Atom to the Helium Network
    helium_connect(&helium);

    // Begin communicating with the channel. This should only need to
    // be done once. The HeliumUtil functions add simple retry logic
    // to re-create a channel if it disconnects.
    channel_create(&channel, CHANNEL_NAME);
  Serial.print("Devices: ");
  Serial.println(ds.getNumberOfDevices());
  Serial.println();

    //=======================
 //It is recommended to check return status on .begin(), but it is not
  //required.
  CCS811Core::status returnCode = mySensor.begin();
  if (returnCode != CCS811Core::SENSOR_SUCCESS)
  {
    Serial.println(".begin() returned with an error.");
    while (1); //Hang if there was a problem.
  //======================
}
}

void loop(){
//const char * data = "Hello Helium!";
const char data[HELIUM_MAX_DATA_SIZE];
  char data1[HELIUM_MAX_DATA_SIZE];
  size_t data_used;
  char message[56];
  char msg[56];
  int sensorValue;
  int message1;
  int    status;
  int8_t result;
  int v = (inputVoltage);
  int c = (current0p01);
  int t = (ds.getTempC());
  int C0 = (mySensor.getCO2());
  int VOC = (mySensor.getTVOC());
//=============================================================
//========================C0 Sensor============================
//=============================================================
 //Check to see if data is ready with .dataAvailable()
  if (mySensor.dataAvailable())
  {
    //If so, have the sensor read and calculate the results.
    //Get them later
    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
//===========================================================
//=====================Voltage Sensor========================
//===========================================================
 Wire.beginTransmission(LTCADDR);//first get Input Voltage - 30V 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
  current0p1 = ADCcur * (25E-3) / 0.1; //1A max, unit is mA
  Serial.print("Volts_");
  Serial.println(inputVoltage);
  Serial.print("Amps_");
  Serial.println(current0p01, 2);
  delay(100);
  Serial.print("V 10ohm:2.5uA/10mA>");
  Serial.print(current10, 3);
  Serial.print("mA 1ohm:25uA/100mA>");
  Serial.print(current1, 2);
  Serial.print("mA 0.1ohm:250uA/1A>");
  Serial.print(current0p1, 1);
  Serial.println("mA 0.01ohm:2.5mA/10A>");
  delay(10000);
//===========================================================
//===================Temp Sensor=============================
//===========================================================
  while (ds.selectNext())
  {
    switch (ds.getFamilyCode())
      // Print address.
      uint8_t address[8];
    ds.getAddress(address);
    for (uint8_t i = 0; i < 8; i++)
      (ds.getResolution());
    Serial.print(ds.getTempC());
    Serial.print(" C / ");
    Serial.print(ds.getTempF());
    Serial.println(" F");
    sensorValue = ds.getTempC();
  }
//=========================================================
    // Send data to channel
    snprintf(data, 75, "V %d:A %d:T %d:C02 %d:VOC %d", v, c, t, C0, VOC); //NOTE: The values V,A,T,C0,,Z are needed for the next step
    channel_send(&channel, CHANNEL_NAME, data, strlen(data));
    channel_send(&channel2, CHANNEL_NAME2, data, strlen(data));
    Serial.println(strlen(data));
    delay(5000);
    // Wait about 5 seconds
    delay(5000);
}

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