A Dual Purpose Environment and HVAC Monitoring System

A Dual Purpose Environment and HVAC Monitoring System © GPL3+

Improve the health of individuals and monitor the operation of HVAC systems in residential or business settings.

  • 1 comment
  • 13 respects

Components and supplies

Devkit grande 9hzpuvtnaw
Helium Starter Kit
Used to send data to the Google Cloud IoT via the Helium infrastructure.
13676 01
SparkFun Atmospheric Sensor Breakout - BME280
SparkFun Environmental Combo Breakout - CCS811/BME280 (Qwiic)
SparkFun Air Quality Breakout - CCS811
Note this sensor requires a burn in period of 48 hours and a run-in of 20 minutes for accurate measurements. This also applies to the combo board above.
11931 01
SparkFun Digital Temperature Sensor Breakout - TMP102
SparkFun Qwiic Shield for Arduino
Found the Quiic connect system very useful for prototyping. Allowed me to move I2C boards around easily during testing.
SparkFun Qwiic Adapter
Not sure how many are needed. They come in handy for connecting a I2C breakout that doesn't have Qwiic connectors. Also need female headers for the connection but won't list those separately. See text for more information.
Adafruit RGB LCD Shield Kit w/ 16x2 Character Display
Ard due
Arduino Due

Apps and online services

Cloud iot medium 00pm8p0wh8
Google Cloud IoT Core
Helium Dashboard
Eclipse Photon IDE CDT Version
Eclipse is a full capability IDE for C++ (aka Arduino) development. I have a number of years experience with it so prefer it to the Arduino IDE. It requires the Sloeber plugin to support Arduino.
Sloeber Eclipse Arduino Plugin
This plugin adds support for Arduino boards to Eclipse.

About this project

Genesis of Project

The annual AC checkup was a couple of weeks ago. The two units are in the attic and each has a whole house air filter. The technician checks whether the filters are dirty twice a year, at the start of the winter and summer. In between those checkups the state of the filters is unknown. Climbing into the attic is a hassle, so I wondered if this could be automated using IoT?

Yes, it can and there is a commercial product that does this, including reporting the information to the cloud. This product inserts a pressure sensor in the air stream. When a clean filter is installed, a base pressure is set. As the filter becomes dirty the pressure changes, and after sufficient change the filter should be replaced.

During the checkup the AC technician also reported that the temperature differential between the inflow and outflow sides of the AC was good. A similar is check done during heating season. If the temperature differential is low, the system is not working properly.

Temperature sensors could monitor this differential providing an indicator of the health of the AC or furnace, possibly indicating a problem that could be fixed before requiring hefty repair bills.

Starting with these sensors, I began looking at what other sensors were available that might provide useful information. In addition to the status of the HVAC (heating, ventilation, and air conditioning) the suite of sensors I found could be used for general environmental monitoring to help keep people healthy.

Additional Sensors

Humidity - During the AC season humidity is high in some climes, like Houston, TX or Buffalo, NY. High humidity supports the growth of mold and other microscopic organisms. These can trigger allergies, breathing problems, or skin irritation.

Low humidity also causes problems such as dry skin, irritated sinuses, and itchy eyes. Buffalo suffers from this because winter cold air contains little moisture and after heating by a furnace the relative humidity is extremely low. Humidifiers are often installed in northern climes to combat this problem.

Volatile Organic Compounds - Volatile Organic Compounds (VOC) are gases emitted from solids or liquids. There is a multitude of sources in a building, like cleaning products. Some are relatively benign such as from a glass of rum, while the residual dry cleaning chemicals on clothing are known carcinogens.

Particulate - I found a sensor that purported to measure particulates like pollen and dust. It would be a good addition. However the sensor only provides raw data (reflection of LED light) with no analysis.

General Environment

Making these same measurements on the ambient environment in a building, residential or commercial, could also diagnose issues with the overall air handling system. If an individual is especially susceptible to allergies, for instance, measuring the conditions in the rooms most frequented might indicate if they are insufficiently filtered.

Checking rooms could also help identify where outside air is entering the building and bringing in humid, polluted, or ambient temperature air.

System Design

The project system design is:

  • An Arduino processor to collect data from the sensors over I2C. The Arduino is used since the software is in non-volatile storage so not typically subject to corruption.
  • A single Arduino and Helium Atom at a air handler can monitor inflow and outflow sensors.
  • An Arduino with a single set of sensors and Helium Atom can be used for ambient monitoring.
  • The Helium Ethernet Element is used since this is a system in a fixed location.

First Steps

A project like this involves as much research as development. The devices are unknown, so setting up a test to see how they work and if they work together is a big first step.

Here's a look at all the sensors working together.

The Sparkfun RedBoard is collecting this data over I2C and sending it to my desktop over the serial port. Here's a key to the devices and the data following them.

  • B1, B2 - BME280: temperature in Fahrenheit, pressure in millibars, and relative humidity in precentage
  • T1, T2 - TMP102: temperature in Fahrenheit
  • M1 -MAX20105: temperature in Fahrenheit, red, ir, and green LED in arbitrary units (This is a particulate sensor that is not included in the final project.)
  • C1, C2 - CCS811: CO2 and Volatile Organic Compounds in parts per million.

You can see the devices as listed in the hardware components above. At the time of the measurements all devices were lying on my work table within a few inches of one another.

Temperatures: There are four reported temperatures. The original intent was to use the BME280 (BME) to measure the ambient temperatures. These temperatures are not useful without adjustment since they measure the die temperature on the sensor chip. The reading is greater than ambient due to the heat of the processing. Additionally the B1 sensor shares a board with a CCS811 (CCS) C1 chip which uses a heater for measurements. The heater increases the B1 temperature by a couple of degrees. B2 and C2 are on separate boards so they do not interact.

The temperature measurement chips, T1 and T2 are TMP102s (TMP) sensors and only measure temperature. An instant reading digital thermometer near them reads 77.40F. T1 closely agrees and T2 is within the limits of the data sheet for the TMP. This is a situation where testing showed the need to add the TMP for better temperature readings.

Pressure: The two BMPs agree on the pressure within their data sheet limits. They also agree with the pressure sensor on my Nexus 5X smartphone.

CO2/VOC: The CO2 reading from the CCS is not a direct reading but inferred from the VOC readings. The data sheet explains that CO2 can be estimated based on the quantity of VOCs emitted by people. I'm suspicious of the CO2 reading in this system but report it for future study.

The VOC reading is non-specific about the compounds. Anything organic dispersing in the air increases this value. For example, by breathing on the sensor, the reading increases. Slightly warm rum fumes also increase the reading.

While non-specific, the VOC reading is of value, as mentioned above. A higher VOC level is a possible indicator of unhealthy conditions that merit closer examination.

Summary: All these sensors are usable from a single Arduino using I2C. The default addresses worked, as you would expect, and the alternate addresses once configured properly, also worked. In short, the electronics is working.

The only deviation from the original plan is the use of the TMP sensors instead of the temperature readings from other devices. I also dropped the particulate sensor since there was no software to provide interpretation of the raw data, i.e. were there particles?

The system appears stable having run for over a day without problems with the readings.

Simulating the HVAC for Testing

It isn't fun to climb into the attic to test the sensors, climb down to make software changes, and back up to test. One night when climbing in bed I realized my CPAP (Continuous Positive Air Pressure) for sleep apena is a small HVAC.

A CPAP uses a fan to pressurize air that is sent to a mask on my face. The increased pressure keeps open the airways that otherwise close at times during the night. The fan is the same as an HVAC blower. There's a filter on the intake and a heating element that warms a water tank to increase the humidity of the air going to the mask. Luckily, I have an old CPAP so don't have to mess with the system I'm using.

I added boxes, or plenums to the inflow and outflow ports. The sensors for the project are put into these plenums for measurements.

The plenum on the right is the outflow and underneath it is the humidifier. The inflow is low on the back to the left and with a plenum attached there.

Sensors and LCD

Here are two shots of the computer stack and the sensors.

The first set (S1) of sensors is toward the front on the left. The second set is slightly behind to the right. The first set is, from the left, the BME/CCS combination, the MAX (no longer being used), and the TMP boards. The second set is the TMP, BME, and CCS boards, starting on the left. This displays show actual readings.

Following the set name (S1/S2) is the temperature from the TMP followed by the pressure in millibars from the BME. The second line is the BME's relative humidity and then a representation of the VOC from the CCS.

The VOC reading from the CCS is in parts per billion with a range of zero to 1187 ppb. This is meaningless to mere mortals so I converted it to a percentage of the range, in part to save space on the display. That's why the units following the number are "%V" for percentage of volatile. In later versions I added a "%C" reading for the CO2.

Live Testing

The next two images are captured with the CPAP running. They show differences between the input and output plenums.

Temperature: The input temperature (S1) is the ambient temperature of the office. The output temperature (S2) is higher because of the adiabatic increase caused by the compression of the air by the fan.

Pressure: These images show only a 2 millibar difference. I've seen the input as low as 1006 mb and the output as high as 1011 mb. It depends on how well the plenums are sealed. In an HVAC system this sealing is similar to the resistance to airflow of ducts from the return air intake and to the room vents.

That's enough pictures showing the difference in the input and output sensor readings. Some other observations:

Volatile Organic Compounds - Through a small hole in the intake plenum I introduced some VOCs to check the response of the sensor. The readings on both sets of sensors were the same, within the limits of sensor precision. Some results garnered by putting samples on a tissue and placing the tissue over the intake hole:

  • Male cologne: 97%V (I over soaked the tissue!)
  • Wife's hairspray: 23%V
  • Tequila: 28%V

Cooling - Ice placed in the water tank for the humidifier acted like an AC. The temperature on the intake was 76.9ᴼ while the outflow dropped to 71.7ᴼ.

Heating - An important function of the CPAP is to heat and humidify the air for the comfort of the sleeper. With an intake temperature of 76.4ᴼ the temperature rose to 86.3ᴼ.

Summary - The CPAP testing shows it is sufficiently similar to an HVAC to be useful for testing, especially when sending data to the cloud using the Helium Atom and its Arduino board. Testing on an actual HVAC in the attic will occur, just not now. Now it's time to figure out the IOT aspects of the project.

A Due, RedBoard

The original Arduino for this project was a Sparkfun RedBoard which is a Uno Rev. 3 clone. You can see it in the pictures above. Unfortunately, adding the Helium board libraries to the sensor code exceeded the memory capacity of the RedBoard. The system would start but quickly crash. I switched to an Aruduino Due board which was on hand.

I2C Issues - Switching to the Due caused issues with the I2C. The main hardware I2C connections on the Due are different than on the Uno. The underlying Arduino I2C library, Wire, refers to that connection as Wire and a secondary connection as Wire1. I won't go into all the details to explain this. Suffice it to say that Wire1 on the Due uses the SDA/SCL pins near AREF. These do not connect to the A4/A5 pins used by the Uno for I2C.

The LCD library and some of the sensor libraries allowed specifying Wire1. Others did not and required actually editing the library code to use Wire1. Once that was sorted out the Due ran the sensor, LCD, and Helium code fine.

Arduino Code Summary

The code for the Arduino is in a BitBucket repository which is listed in the Code section of the project. Details are in the readme file but here is a quick summary of the software. First a diagram showing the classes.

A main Application class wraps all the other code. Each of the sensors, the LCD, and Helium have classes to wrap their libraries and are members of the Application class. The Application class has members setup() and loop() which are called by the standard Arduino functions of the same name.

A common interface for all the sensors is provided by the SensorAbstract class. A key member of this class reports if the sensor was found during initialization of the I2C devices. This allows the software to automatically reconfigure based on the presence or absence of sensors. Mainly this determines if the S2 set of sensors is present.

A basic scheduler is implemented in the Application class. This allows specifying when sensors are read, the LCD updated, and data sent over the Helium channel.

I use Eclipse with the Sloeber plugin to work with Arduinos. This provides a more robust and capable development environment than the Arduino IDE, which I've not tried to use for this code. It should work with some tweaking to conform to the Arduino IDE's requirements. One potential problem area may be the I2C issue discussed above.

Helium Atom and Element Setup

The setup of the Helium Atom and the Helium Element are well documented, so I won't go into full details here. If the setup is done correctly the Dashboard should show messages in the Atom Debug Log from the Arduino every 30 seconds. If not, check the Dashboard for the status of the Atom and Element to see if they are online. Also check the LCD display. The background turns to red if the Helium is not connecting to the Helium system.

As a test function, pressing any button on the LCD panel causes the Atom to send a message. During sending the Atom flashes a blue LED. If it repeatedly flashes, it is not connected. Check the Element to see if the center orange LED is on solid, indicating a connection to the Internet. The blue LED should be flashing once a second. When the Element receives a message from an Atom the red LED flashes. See the Helium setup documentation mentioned above for troubleshooting assistance.

Google IOT Channel Setup

Once the setup above is complete it is time to create the channel to Google IOT. From the Dashboard select the Channels from the left sidebar. You'll see the following:

Click on the Google Cloud IoT Core icon. That brings up:

Enter the information from setting up your Helium service account. After finishing this step you'll see:

Enter a name for your channel and click next. Explore the information on the next screen and finish by selecting Done. Take a look at the sample code for the Arduino to get an idea how to interact with the Atom. As mentioned above, this is all wrapped by a class in the software.

If everything is working you should see entries in the Event Log toward the bottom of the web page.

Google IoT

Setting up the various parts for the Google IoT capability is like drinking from a fire hose. There are many possibilities and it is often unclear which should be used. A lot of the following I gleaned from numerous other projects on Hackster.io.

The goal is to store the environmental data and present it to the end user in some form. For this project a simple web page is sufficient but longer term providing graphical visualization, notifications of problems, e.g. low temperature differential, and long term trend analysis of reading are desirable.

The starting point for documentation is the Google IoT Core page with the diagram everyone shows in their projects. It not only provides an idea of what is available but also the challenge in determining what to use.

There is an additional useful capability, Firebase, which provides application development capability for mobile platforms and the web. It is the web portion of Firebase used by this project for hosting a web site to display data stored data in a Firebase Realtime Database.

Getting data to Firebase uses the Cloud PubSub capability from the above diagram. The data also is put into a BigQuery database to show how easily this is done should analysis or reporting using Data Studio become of interest.


The Firebase Console is used to create a Firebase project. Select Add Project, enter a project name, accept the conditions, and click Create a Project. I worked through this writeup to verify the steps using the project name "mystic-firebase-example". The actual project has the name "Mystic Lake Air Monitor" and the project id of "mystic-air-monitor". You'll see those used in the project code.

Once the project is created Firebase dashboard opens. Click on the settings wheel icon at the top of the left panel. Change the Project Name or Public-facing name to something more friendly, if you like.

Web App Setup

Now click on the Add Firebase to your web app icon which will provide scripts needed for a web page. Here is the scripting for an example.

<script src="https://www.gstatic.com/firebasejs/5.2.0/firebase.js"></script>
 // Initialize Firebase
 var config = {
   apiKey: "AIzaSyDXhWfXMWZWcosNF4jNBdicLATonktdbiQ",
   authDomain: "mystic-firebase-example.firebaseapp.com",
   databaseURL: "https://mystic-firebase-example.firebaseio.com",
   projectId: "mystic-firebase-example",
   storageBucket: "mystic-firebase-example.appspot.com",
   messagingSenderId: "190671885051"

Save this to a file in your project.

Setup Database

Now go to the left sidebar and select Database. Scroll down to Realtime Database and select Create Database. Select Test Mode to make it easier to get started. If you need secure data the Locked Mode option is preferred for security but it can be setup latter should it be needed. Select Enable and the database is ready but empty.

Setup Web Page Hosting

Back to the sidebar on the left and select Hosting and Get Started. Copy the command that appears to install the Firbase tools and run it from a terminal on your system. If you've not worked with Node.js you need to install it from its web site. After the install is complete select Continue. Close the dialog without running the command lines on the next page of the dialog. The screen should show a web URL in the box labeled Domains.

Before proceeding create a directory for the project and change to it. The second step, the init process, will generate files in this directory so be sure this directory is a good place for working on the project.

Start by running:

firebase login 

This will open a web page where you specify the account used for the project. After you run the next command read the next paragraphs before responding to the prompts. Run:

firebase init 

Select the project to work on. There is probably only the one you created above through the console. After selecting the project the terminal looks like:

(node:6065) ExperimentalWarning: The fs.promises API is experimental
    ######## #### ########  ######## ########     ###     ######  ########
    ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
    ######    ##  ########  ######   ########  #########  ######  ######
    ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
    ##       #### ##     ## ######## ########  ##     ##  ######  ########
You're about to initialize a Firebase project in this directory:
Before we get started, keep in mind:
 * You are initializing in an existing Firebase project directory
? Which Firebase CLI features do you want to setup for this folder? Press Space to select features, then Enter to confirm your choices. 
◉ Database: Deploy Firebase Realtime Database Rules
◯ Firestore: Deploy rules and create indexes for Firestore
◉ Functions: Configure and deploy Cloud Functions
◉ Hosting: Configure and deploy Firebase Hosting sites
❯◉ Storage: Deploy Cloud Storage security rules

As shown, do not select Firestore, the second option, but do select all of the other options. Press enter to select the defaults for all the other prompts that follow.

Examine the directory to see the files that were created. In the public directory is an index.html file which is a skeleton web page. In functions the file index.js is a basic Node.js file.

Now start a local server by running:

firebase serve

You can click on the URL that appears near the end or in a browser window enter the URL:


This will open the default web page. Use ctrl-c to exit the server and run the final command that appeared in the dialog:

firebase deploy

Go to the browser with the Firebase Console open. Select the Hosting menu item and on the page under Domains click the URL. The same web page should appear. Bookmark this web page so you find it later as the project continues.

Setup Functions

Open the index.js file in the functions directory for editing. Remove the double slashes on the last three lines to uncomment them. Save the file and in the terminal run:

firebase deploy --only functions

This does a deploy, just as before, but it only deploys functions. There are options to deploy the separate parts of the Firebase. Run firebase help deploy to see the possibilities.

Now select Functions from the left sidebar. The page should show a function helloWorld and a URL. Copy the URL and paste it into a browser. The page should show Hello from Firebase!. Close it.

Go back to the Firebase console. If not on the Functions page, select it from the sidebar. Click on the Logs tab which will show the function executed. Ignore the billing message if it appears. The Logs tab is useful for debugging when something goes wrong with a function. In the project code you'll see the ability to write debug information to the log.

That's it for Firebase at the moment but bookmark the console URL. You'll be returning frequently to examine the functions logs and the database.

Google PubSub and IoT Core

There is still more to be done to setup Google for the project. Go to the Google Cloud Platform. You should see in the upper left the project you created in Firebase, i.e. "mystic-firebase-example" for this example. Click on the three horizontal bars in the very upper left to get a drop-down menu. Scroll down quite aways and find Pub/Sub. You might want to click on the push-pin which will add it to the very top of this menu. Select Pub/Sub and a dialog should appear with a button Create a Topic. Select the button. Enter a name for the topic, i.e. "mystic-example-topic" to continue the pattern.

From the left sidebar menu select IoT Core. A dialog will appear and you'll need to set up a billing account to proceed further. Work through that process and dialogs until you get to a screen that says, Create a registry. Enter a registry name, i.e. "mystic-example-reg". Select a region appropriate to your location. In the drop down box under Default telemetry topic it says Select a Cloud Pub/Sub Topic. When you click on, it the topic created above should appear, i.e. "mystic-example-topic". Click on Create.


Still one more capability to setup, BigQuery. Go to the BigQuery Console which can also be reached from the Google Cloud Platform Console using its left menu. On the left you should see the project name, i.e. "mystic-firebase-example". Click the small down arrow to the right of the name and select Create new dataset. Enter a name into the dialog, i.e. "mystic_lake_example". Note the use of underscores, not dashes. You can select a region or not and leave the expiration to the default. Once you close the dialog you'll see the dataset under the project. If you select it you can edit the information from the dialog.

Now create a table by hovering over the dataset and selecting the plus sign. Select "Create empty table". Enter a name, i.e. "hvacstatus" for the table. In the Schema section select Edit as text. In the edit box that appears delete the text. Go to the repository, copy the text from the file hvacstatus.json and paste it into the edit box. Do the same with the name "airstatus" and the file "airstatus.json" from the repository.

As each table is created you should get a page displaying the schema for that table.

Google Setup Caveat

This completes the setup of the Google pieces, I hope.

I may not have covered all the steps in setting up Google to work properly. If you encounter problems add a comment. I'll figure out the missing steps. There are a lot of parts, as you've seen, and it is possible, even likely, that I've forgotten something.

Uploading Code to Google

With all this done it is time to upload the real index.js and index.html files to see if everything works. These are in the same repository. The easiest approach is to clone the repository to your system. In index.js edit the name of the BigQuery datase name to the name you used, on the line:

const dataset = bigquery.dataset("mystic_air_monitor");

Also edit the topic name on the line:

exports.airStatusPub = functions.pubsub.topic('air-status').onPublish( (message, event) => {

There are two HTML files to edit, air.html and index.html. At the top of both, in the first <script>, replace it with the HTML you saved way back at the beginning in the section Web App Setup.

Now upload through the terminal by entering:

firebase deploy --only functions,hosting

Let the entire system run for a couple minutes to populate the Firebase database. Go to the Firebase Console Hosting page and click on the URL in the Domain section. A web page should appear that looks like:

If you click on Air Monitor, another page with just the S1 sensor values appears. To populate that data you'll need to disconnect the S2 sensors from the system and let it run in the air monitor mode for a few minutes. You can also look at my projects web site.

As with the Arduino code there is some explanation of these files in the repositories readme.md file.

Some Tests

With the system working it's time to have fun with testing. In the first video the system is on top of the refrigerator. The S1 sensors are mounted on the top of the enclosure. The S2 sensors are inside the refrigerator. The video shows the final temperature readings as the TMP drops from room temperature to the internal temperature. What is interesting is the reading of the VOCs which is unexpected. But, of course, there are a lot of organics in a refrigerator which must be emitting compounds.

S2 Sensors Inside Refrigerator

The next test is not surprising. The system with just the S1 sensors, the environmental sensing mode, is placed under the kitchen sink. There are cleaning supplies, soap, and dishwasher pods under there. A promising environment for finding VOCs.

The picture shows the ambient readings before closing the door.

Now the display of the results after opening the door. There are a fair number of VOCs. This display doesn't appear to change since it is only the S1 sensors but you can see the relative humidity, first number on the second line, change.

If you watch at 16 seconds you'll see a little blue flash and hear a click in the audio. That's the Helium transmitting the data. Apparently the camera's audio circuits are not completely shielded from the RF from the Atom.

Reading of VOCs Under Kitchen Sink

Before the second video zooms in, at about 13 seconds, you can see the enclosure with a LIPO battery, the stack of boards, the antennas, and the S1 sensors on the top of the enclosure. Since this is a research project I simply found a box that would hold everything neatly so it was portable.

And here's a couple more pictures to show the enclosure and the stack of boards.


That wraps it up. Leave comments if you have questions and I'll respond as soon as I can.

This was quite a learning experience. I'd worked with the Arduino with other projects but this introduced me to I2C and sensors, areas I'd not worked with previously. The Google Cloud learning curve was steep, frustrating, and challenging. Just what I needed. The easiest part of the project was working with the Helium web site and devices.


Helium / Arduino HVAC and Environmental Monitoring
Helium / Google IOT HVAC and Environmental Monitoring


Schematic with I2C sensors, Helium Atom connection, and Arduino Due
I2c and due schem ccajlinnpc


Similar projects you might like

Windows 10 IoT Plant Monitoring System

Project tutorial by Team BME-AUT

  • 55 respects

Home Monitoring System and Smart Home Solution

Project showcase by Akash Kumar

  • 21 respects

WaterPi: Houseplant Remote Watering and Monitoring System

Project tutorial by Demirhan Aydin

  • 128 respects

Android App-Based Home Automation System Using IOT

Project tutorial by Team Autoshack

  • 75 respects

Battery Charger Monitor

Project tutorial by David Kanceruk

  • 1 comment
  • 18 respects


Project tutorial by Jeff Gensler

  • 10 respects
Add projectSign up / Login