Skip to content

Instantly share code, notes, and snippets.

@rdlauer
Created March 13, 2025 15:06
Show Gist options
  • Save rdlauer/8be390f39462117631d8e1b215b4d359 to your computer and use it in GitHub Desktop.
Save rdlauer/8be390f39462117631d8e1b215b4d359 to your computer and use it in GitHub Desktop.
Indoor Air Quality Monitor Sketch using Blues, Adafruit IO, and IFTTT
#include <Arduino.h>
#include <Notecard.h>
#include <SensirionI2cScd4x.h>
#include <Wire.h>
#include "Adafruit_SGP40.h"
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#define usbSerial Serial
#define PRODUCT_UID "your-product-uid"
#define NO_SCD40_ERROR 0
#define IS_USB 0
Notecard notecard;
SensirionI2cScd4x scd40;
Adafruit_SGP40 sgp;
Adafruit_7segment matrix = Adafruit_7segment();
unsigned long currentMillis, startMillis;
unsigned long sensorReadPeriod = 1000 * 60 * 1; // read sensors every [x] mins
float temp, humid, voltage;
uint16_t co2, sraw;
int32_t vocIndex;
static int16_t scd40Error;
static char scd40ErrorMessage[64];
void printError(const char *message)
{
if (IS_USB)
{
usbSerial.println(message);
}
else
{
matrix.print("ERR");
matrix.writeDisplay();
}
}
void setup()
{
delay(2500);
if (IS_USB)
{
usbSerial.begin(115200);
while (!usbSerial)
;
}
Wire.begin();
// #######################
// Notecard Initialization
// #######################
notecard.begin();
if (IS_USB)
{
notecard.setDebugOutputStream(usbSerial);
}
{
J *req = notecard.newRequest("hub.set");
if (req != NULL)
{
JAddStringToObject(req, "product", PRODUCT_UID);
JAddStringToObject(req, "mode", "continuous");
notecard.sendRequest(req);
}
}
{
J *req = notecard.newRequest("note.template");
if (req != NULL)
{
JAddStringToObject(req, "file", "sensors.qo");
JAddBoolToObject(req, "sync", true);
J *body = JAddObjectToObject(req, "body");
if (body)
{
JAddNumberToObject(body, "temp", 12.1);
JAddNumberToObject(body, "humid", 12.1);
JAddNumberToObject(body, "co2", 12);
JAddNumberToObject(body, "raw", 12);
JAddNumberToObject(body, "voc", 12);
JAddNumberToObject(body, "voltage", 12.1);
}
notecard.sendRequest(req);
}
}
// ####################
// SCD40 Initialization
// ####################
scd40.begin(Wire, SCD41_I2C_ADDR_62);
uint64_t scd40SerialNumber = 0;
// Ensure sensor is in clean state
scd40Error = scd40.wakeUp();
if (scd40Error != NO_SCD40_ERROR)
{
printError("Error trying to execute wakeUp(): ");
errorToString(scd40Error, scd40ErrorMessage, sizeof scd40ErrorMessage);
printError(scd40ErrorMessage);
}
scd40Error = scd40.stopPeriodicMeasurement();
if (scd40Error != NO_SCD40_ERROR)
{
printError("Error trying to execute stopPeriodicMeasurement(): ");
errorToString(scd40Error, scd40ErrorMessage, sizeof scd40ErrorMessage);
printError(scd40ErrorMessage);
}
scd40Error = scd40.reinit();
if (scd40Error != NO_SCD40_ERROR)
{
printError("Error trying to execute reinit(): ");
errorToString(scd40Error, scd40ErrorMessage, sizeof scd40ErrorMessage);
printError(scd40ErrorMessage);
}
// Read out information about the sensor
scd40Error = scd40.getSerialNumber(scd40SerialNumber);
if (scd40Error != NO_SCD40_ERROR)
{
printError("Error trying to execute getSerialNumber(): ");
errorToString(scd40Error, scd40ErrorMessage, sizeof scd40ErrorMessage);
printError(scd40ErrorMessage);
return;
}
// ####################
// SGP40 Initialization
// ####################
if (!sgp.begin())
{
printError("SGP40 sensor not found :(");
while (1)
;
}
// ######################
// 7 segment display Init
// ######################
matrix.begin(0x70);
matrix.setBrightness(15);
matrix.print(8888);
matrix.writeDisplay();
// ##########################################
// Warm up the SGP40 by executing 50 readings
// ##########################################
for (int i = 0; i < 50; i++)
{
sgp.measureVocIndex();
matrix.print(i + 1);
matrix.writeDisplay();
delay(250);
}
matrix.print(8888);
matrix.writeDisplay();
startMillis = millis();
}
void loop()
{
currentMillis = millis();
if (currentMillis - startMillis >= sensorReadPeriod)
{
temp = 0.0;
humid = 0.0;
voltage = 0.0;
co2 = 0;
sraw = 0;
vocIndex = 0;
// Wake the SCD40 sensor up from sleep mode
scd40Error = scd40.wakeUp();
if (scd40Error != NO_SCD40_ERROR)
{
printError("Error trying to execute wakeUp(): ");
errorToString(scd40Error, scd40ErrorMessage, sizeof scd40ErrorMessage);
printError(scd40ErrorMessage);
return;
}
// Ignore first measurement after wake up.
scd40Error = scd40.measureSingleShot();
if (scd40Error != NO_SCD40_ERROR)
{
printError("Error trying to execute measureSingleShot(): ");
errorToString(scd40Error, scd40ErrorMessage, sizeof scd40ErrorMessage);
printError(scd40ErrorMessage);
return;
}
// Perform single shot measurement and read data.
scd40Error = scd40.measureAndReadSingleShot(co2, temp, humid);
if (scd40Error != NO_SCD40_ERROR)
{
printError("Error trying to execute measureAndReadSingleShot(): ");
errorToString(scd40Error, scd40ErrorMessage, sizeof scd40ErrorMessage);
printError(scd40ErrorMessage);
return;
}
// Get VOC index from SGP40 sensor
sraw = sgp.measureRaw(temp, humid);
vocIndex = sgp.measureVocIndex(temp, humid);
// Get the power supply voltage from the Notecard
{
J *req = notecard.newRequest("card.voltage");
if (J *rsp = notecard.requestAndResponse(req))
{
voltage = JGetNumber(rsp, "value");
}
else
{
voltage = 0.0;
}
}
// print out VOC Index to display
matrix.print(vocIndex);
matrix.writeDisplay();
// Send data to Notehub and Adafruit.io!
{
J *req = notecard.newRequest("note.add");
if (req != NULL)
{
JAddStringToObject(req, "file", "sensors.qo");
JAddBoolToObject(req, "sync", true);
J *body = JAddObjectToObject(req, "body");
if (body)
{
JAddNumberToObject(body, "temp", temp);
JAddNumberToObject(body, "humid", humid);
JAddNumberToObject(body, "co2", co2);
JAddNumberToObject(body, "raw", sraw);
JAddNumberToObject(body, "voc", vocIndex);
JAddNumberToObject(body, "voltage", voltage);
}
notecard.sendRequest(req);
}
}
startMillis = currentMillis;
}
delay(20);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment