[{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003e\u003cspan\u003eIf you're working an office job, you're...in an office. Whether that's in your home or not, I'm going to hazard a guess that it's also indoors - and being indoors for an extended period of time without well-ventilated spaces can legitimately lead to low air quality (and in theory health issues).\u003c/span\u003e\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/674/original/indoor-air-quality.jpg?1741963693","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003cp\u003eNow, I'm not hear to spread fear and make you all think you're dying a slow death by breathing in your co-worker's exhalations. However, I am here to show off an \u003cstrong\u003eeasy way to build a cloud-connected indoor air quality system\u003c/strong\u003e with:\u003c/p\u003e\n\n\u003col\u003e\n\u003cli\u003eA variety of Adafruit air quality sensors.\u003c/li\u003e\n\u003cli\u003eA \u003ca href=\"https://shop.blues.com/products/notecard-cellular\"\u003eBlues Notecard\u003c/a\u003e to cloud-connect the project with LTE connectivity.\u003c/li\u003e\n\u003cli\u003e\n\u003ca href=\"https://io.adafruit.com/\"\u003eAdafruit IO\u003c/a\u003e to visualize air quality data and integrate with other services.\u003c/li\u003e\n\u003cli\u003eA \u003ca href=\"https://www.philips-hue.com/en-us/products/smart-light-strips\"\u003ePhilips Hue LED strip\u003c/a\u003e to provide real-time visuals for low air quality alerts.\u003c/li\u003e\n\u003c/ol\u003e\n","metadata":{"markdown":"Now, I'm not hear to spread fear and make you all think you're dying a slow death by breathing in your co-worker's exhalations. However, I am here to show off an **easy way to build a cloud-connected indoor air quality system** with:\n\n1. A variety of Adafruit air quality sensors.\n1. A [Blues Notecard](https://shop.blues.com/products/notecard-cellular) to cloud-connect the project with LTE connectivity.\n1. [Adafruit IO](https://io.adafruit.com/) to visualize air quality data and integrate with other services.\n1. A [Philips Hue LED strip](https://www.philips-hue.com/en-us/products/smart-light-strips) to provide real-time visuals for low air quality alerts."}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/675/original/project-with-lights.jpg?1741963741","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003ch2\u003eThe Hardware Components\u003c/h2\u003e\n\n\u003cp\u003eLet's start with the sourcing and assembly of our distinct hardware components.\u003c/p\u003e\n\n\u003ch3\u003eAir Quality Sensors from Adafruit\u003c/h3\u003e\n\n\u003cp\u003e\u003ca href=\"https://www.adafruit.com/\"\u003eAdafruit\u003c/a\u003e is my go-to source for components when prototyping a new project. So, inspired by TJ VanToll's project on \u003ca href=\"https://www.hackster.io/tjvantoll/monitoring-home-co2-levels-with-lorawan-0b53a3\"\u003eMonitoring Home CO2 Levels with LoRaWAN\u003c/a\u003e, I started with the \u003ca href=\"https://www.adafruit.com/product/5187\"\u003eAdafruit SCD40 sensor\u003c/a\u003e to measure temperature, humidity, and CO2 levels.\u003c/p\u003e\n","metadata":{"markdown":"## The Hardware Components\n\nLet's start with the sourcing and assembly of our distinct hardware components.\n\n### Air Quality Sensors from Adafruit\n\n[Adafruit](https://www.adafruit.com/) is my go-to source for components when prototyping a new project. So, inspired by TJ VanToll's project on [Monitoring Home CO2 Levels with LoRaWAN](https://www.hackster.io/tjvantoll/monitoring-home-co2-levels-with-lorawan-0b53a3), I started with the [Adafruit SCD40 sensor](https://www.adafruit.com/product/5187) to measure temperature, humidity, and CO2 levels."}},{"element_type":"product","content":"https://www.adafruit.com/product/5187","metadata":{}},{"element_type":"markdown","content":"\u003cp\u003eNext, I wanted to find an easy way to \u003cstrong\u003emeasure VOC levels\u003c/strong\u003e (volatile organic compounds). \u003ca href=\"https://www.adafruit.com/product/4829\"\u003eAdafruit's SGP40 gas sensor\u003c/a\u003e provides just that, with the supported library giving access to \"raw\" gas levels and an algorithm to output a VOC index.\u003c/p\u003e\n","metadata":{"markdown":"Next, I wanted to find an easy way to **measure VOC levels** (volatile organic compounds). [Adafruit's SGP40 gas sensor](https://www.adafruit.com/product/4829) provides just that, with the supported library giving access to \"raw\" gas levels and an algorithm to output a VOC index."}},{"element_type":"product","content":"https://www.adafruit.com/product/4829","metadata":{}},{"element_type":"markdown","content":"\u003cp\u003eAs an aside, I also used a \u003ca href=\"https://www.adafruit.com/product/1001\"\u003e7-segment display\u003c/a\u003e to show the most recently calculated VOC index, locally:\u003c/p\u003e\n","metadata":{"markdown":"As an aside, I also used a [7-segment display](https://www.adafruit.com/product/1001) to show the most recently calculated VOC index, locally:"}},{"element_type":"product","content":"https://www.adafruit.com/product/1001","metadata":{}},{"element_type":"markdown","content":"\u003ch3\u003eCloud Connectivity from Blues\u003c/h3\u003e\n\n\u003cp\u003eI knew I needed \u003cstrong\u003emore than just a local display of a single value\u003c/strong\u003e on that 7-segment display. I wanted to create a true cloud-connected system that let me use a cloud service like \u003cstrong\u003eAdafruit IO\u003c/strong\u003e to visualize data, send alerts, and interface with other \"smart home\" devices.\u003c/p\u003e\n\n\u003cp\u003eThat journey started and ended with \u003ca href=\"https://blues.com/\"\u003eBlues\u003c/a\u003e and the \u003ca href=\"https://shop.blues.com/products/notecard-cellular\"\u003eNotecard Cellular\u003c/a\u003e.\u003c/p\u003e\n","metadata":{"markdown":"### Cloud Connectivity from Blues\n\nI knew I needed **more than just a local display of a single value** on that 7-segment display. I wanted to create a true cloud-connected system that let me use a cloud service like **Adafruit IO** to visualize data, send alerts, and interface with other \"smart home\" devices.\n\nThat journey started and ended with [Blues](https://blues.com/) and the [Notecard Cellular](https://shop.blues.com/products/notecard-cellular)."}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/676/original/notecard.png?1741963859","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003cp\u003eWhile even saying the word \"cellular\" can be scary to some, Blues simplifies cellular by hiding that archaic AT command syntax behind a \u003ca href=\"https://dev.blues.io/api-reference/notecard-api/introduction/\"\u003erobust JSON-based API\u003c/a\u003e. Not to mention, there are no monthly SIM fees with Notecard - just a one-time charge for the hardware that \u003cstrong\u003eincludes 500MB of data and service in 140+ countries\u003c/strong\u003e.\u003c/p\u003e\n\n\u003ch3\u003eHost MCU and Carrier Board\u003c/h3\u003e\n\n\u003cp\u003eTo bring all the components together, I chose to use an STM32-based host MCU (the Feather-compatible \u003ca href=\"https://shop.blues.com/collections/feather-mcu/products/cygnet\"\u003eBlues Cygnet\u003c/a\u003e) and another Blues-provided component, the \u003ca href=\"https://shop.blues.com/products/notecarrier-f\"\u003eNotecarrier F\u003c/a\u003e. Notecarrier F provides a (physical) development platform on which you can mount a Notecard, a Feather-based host MCU, and wire up your sensors and other peripherals.\u003c/p\u003e\n","metadata":{"markdown":"While even saying the word \"cellular\" can be scary to some, Blues simplifies cellular by hiding that archaic AT command syntax behind a [robust JSON-based API](https://dev.blues.io/api-reference/notecard-api/introduction/). Not to mention, there are no monthly SIM fees with Notecard - just a one-time charge for the hardware that **includes 500MB of data and service in 140+ countries**.\n\n### Host MCU and Carrier Board\n\nTo bring all the components together, I chose to use an STM32-based host MCU (the Feather-compatible [Blues Cygnet](https://shop.blues.com/collections/feather-mcu/products/cygnet)) and another Blues-provided component, the [Notecarrier F](https://shop.blues.com/products/notecarrier-f). Notecarrier F provides a (physical) development platform on which you can mount a Notecard, a Feather-based host MCU, and wire up your sensors and other peripherals."}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/677/original/cygnet-carr-f.jpg?1741963881","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003ch3\u003eFinal Hardware Results\u003c/h3\u003e\n\n\u003cp\u003eWiring the 7-segment display was as simple as providing power and attaching ground, SDA, and SCL wires. Thanks to the convenience of STEMMA QT/Qwiic connectors, the other two Adafruit air quality sensors were attached via a few cables:\u003c/p\u003e\n","metadata":{"markdown":"### Final Hardware Results\n\nWiring the 7-segment display was as simple as providing power and attaching ground, SDA, and SCL wires. Thanks to the convenience of STEMMA QT/Qwiic connectors, the other two Adafruit air quality sensors were attached via a few cables:"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/678/original/final-project.jpg?1741963906","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003ch2\u003eWriting the Firmware\u003c/h2\u003e\n\n\u003cp\u003eI generally flip back and forth between writing Arduino/C and CircuitPython. For this project I chose Arduino/C - only because CircuitPython support for the Blues Cygnet is \u003cem\u003enot quite ready\u003c/em\u003e. However, either way, there is robust support for both languages provided by the Adafruit sensor libraries and \u003ca href=\"https://dev.blues.io/tools-and-sdks/firmware-libraries/python-library/\"\u003eBlues Python\u003c/a\u003e or \u003ca href=\"https://dev.blues.io/tools-and-sdks/firmware-libraries/arduino-library/\"\u003eBlues Arduino\u003c/a\u003e SDKs.\u003c/p\u003e\n\n\u003cp\u003eThe full source code is \u003ca href=\"https://gist.github.com/rdlauer/8be390f39462117631d8e1b215b4d359\"\u003eavailable here on GitHub\u003c/a\u003e. However, here are a few highlights:\u003c/p\u003e\n\n\u003ch3\u003eAir Quality Sensor Data\u003c/h3\u003e\n\n\u003cp\u003eAside from some necessary ceremony to initialize the air quality sensors, actually \npulling the values out is a matter of a few commands:\u003c/p\u003e\n\u003ccode class=\"\"\u003e\u003cpre\u003escd40.measureAndReadSingleShot(co2, temp, humid);\nsraw = sgp.measureRaw(temp, humid);\nvocIndex = sgp.measureVocIndex(temp, humid);\n\u003c/pre\u003e\u003c/code\u003e\n\u003ch3\u003eCoding Cellular Connectivity\u003c/h3\u003e\n\n\u003cp\u003eAgain, Blues makes it incredibly easy to cloud-connect this project. In fact, it's only two commands to send local sensor data to the cloud.\u003c/p\u003e\n\n\u003cp\u003eThe first destination for our data is the \u003ca href=\"https://notehub.io/\"\u003eBlues cloud service Notehub\u003c/a\u003e, which acts as a \u003cstrong\u003esecure cloud proxy for Notecard\u003c/strong\u003e and will eventually route collected sensor data to Adafruit IO.\u003c/p\u003e\n\n\u003col\u003e\n\u003cli\u003eUsing the \u003ccode class=\"inline\"\u003ehub.set\u003c/code\u003e API:\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003cp\u003eThe \u003ca href=\"https://dev.blues.io/api-reference/notecard-api/hub-requests/latest/#hub-set\"\u003ehub.set API\u003c/a\u003e tells the Notecard to which Notehub cloud project it should send data.\u003c/p\u003e\n\n\u003cp\u003eAfter setting up a (free) Notehub project, you're given a globally unique \u003ccode class=\"inline\"\u003eProductUID\u003c/code\u003e:\u003c/p\u003e\n","metadata":{"markdown":"## Writing the Firmware\n\nI generally flip back and forth between writing Arduino/C and CircuitPython. For this project I chose Arduino/C - only because CircuitPython support for the Blues Cygnet is *not quite ready*. However, either way, there is robust support for both languages provided by the Adafruit sensor libraries and [Blues Python](https://dev.blues.io/tools-and-sdks/firmware-libraries/python-library/) or [Blues Arduino](https://dev.blues.io/tools-and-sdks/firmware-libraries/arduino-library/) SDKs.\n\nThe full source code is [available here on GitHub](https://gist.github.com/rdlauer/8be390f39462117631d8e1b215b4d359). However, here are a few highlights:\n\n### Air Quality Sensor Data\n\nAside from some necessary ceremony to initialize the air quality sensors, actually \npulling the values out is a matter of a few commands:\n\n```\nscd40.measureAndReadSingleShot(co2, temp, humid);\nsraw = sgp.measureRaw(temp, humid);\nvocIndex = sgp.measureVocIndex(temp, humid);\n```\n\n### Coding Cellular Connectivity\n\nAgain, Blues makes it incredibly easy to cloud-connect this project. In fact, it's only two commands to send local sensor data to the cloud.\n\nThe first destination for our data is the [Blues cloud service Notehub](https://notehub.io/), which acts as a **secure cloud proxy for Notecard** and will eventually route collected sensor data to Adafruit IO.\n\n1. Using the `hub.set` API:\n\n   The [hub.set API](https://dev.blues.io/api-reference/notecard-api/hub-requests/latest/#hub-set) tells the Notecard to which Notehub cloud project it should send data.\n   \n   After setting up a (free) Notehub project, you're given a globally unique `ProductUID`:"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/679/original/notehub-project.png?1741963973","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003cp\u003eAnd the supporting code block:\u003c/p\u003e\n\u003ccode class=\"\"\u003e\u003cpre\u003e   {\n      J *req = notecard.newRequest(\"hub.set\");\n      if (req != NULL)\n      {\n         JAddStringToObject(req, \"product\", PRODUCT_UID);\n         JAddStringToObject(req, \"mode\", \"continuous\");\n         notecard.sendRequest(req);\n      }\n   }\n\u003c/pre\u003e\u003c/code\u003e\n\u003col\u003e\n\u003cli\u003eUsing the \u003ccode class=\"inline\"\u003enote.add\u003c/code\u003e API\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003cp\u003eEvery time we want to sync accumulated sensor data with the cloud, we call the \u003ca href=\"https://dev.blues.io/api-reference/notecard-api/note-requests/latest/#note-add\"\u003enote.add API\u003c/a\u003e. This creates an event (or a \u003ca href=\"https://dev.blues.io/api-reference/glossary/#note\"\u003eNote\u003c/a\u003e in Blues lingo) full of all our sensor data.\u003c/p\u003e\n\u003ccode class=\"\"\u003e\u003cpre\u003e   {\n      J *req = notecard.newRequest(\"note.add\");\n      if (req != NULL)\n      {\n        JAddStringToObject(req, \"file\", \"sensors.qo\");\n        JAddBoolToObject(req, \"sync\", true);\n        J *body = JAddObjectToObject(req, \"body\");\n        if (body)\n        {\n          JAddNumberToObject(body, \"temp\", temp);\n          JAddNumberToObject(body, \"humid\", humid);\n          JAddNumberToObject(body, \"co2\", co2);\n          JAddNumberToObject(body, \"raw\", sraw);\n          JAddNumberToObject(body, \"voc\", vocIndex);\n          JAddNumberToObject(body, \"voltage\", voltage);\n        }\n        notecard.sendRequest(req);\n      }\n   }\n\u003c/pre\u003e\u003c/code\u003e\n\u003cp\u003eAnd the resulting data appears in the Notehub project like so:\u003c/p\u003e\n","metadata":{"markdown":"   And the supporting code block:\n\n   ```\n   {\n      J *req = notecard.newRequest(\"hub.set\");\n      if (req != NULL)\n      {\n         JAddStringToObject(req, \"product\", PRODUCT_UID);\n         JAddStringToObject(req, \"mode\", \"continuous\");\n         notecard.sendRequest(req);\n      }\n   }\n   ```\n\n1. Using the `note.add` API\n\n   Every time we want to sync accumulated sensor data with the cloud, we call the [note.add API](https://dev.blues.io/api-reference/notecard-api/note-requests/latest/#note-add). This creates an event (or a [Note](https://dev.blues.io/api-reference/glossary/#note) in Blues lingo) full of all our sensor data.\n\n   ```\n   {\n      J *req = notecard.newRequest(\"note.add\");\n      if (req != NULL)\n      {\n        JAddStringToObject(req, \"file\", \"sensors.qo\");\n        JAddBoolToObject(req, \"sync\", true);\n        J *body = JAddObjectToObject(req, \"body\");\n        if (body)\n        {\n          JAddNumberToObject(body, \"temp\", temp);\n          JAddNumberToObject(body, \"humid\", humid);\n          JAddNumberToObject(body, \"co2\", co2);\n          JAddNumberToObject(body, \"raw\", sraw);\n          JAddNumberToObject(body, \"voc\", vocIndex);\n          JAddNumberToObject(body, \"voltage\", voltage);\n        }\n        notecard.sendRequest(req);\n      }\n   }\n   ```\n\n   And the resulting data appears in the Notehub project like so:"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/680/original/notehub-event.png?1741964007","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003ch2\u003eCloud Connectivity\u003c/h2\u003e\n\n\u003cp\u003eAt this point we are \u003cstrong\u003egathering sensor data locally and sending it periodically to the cloud\u003c/strong\u003e\nover cellular.\u003c/p\u003e\n\n\u003cp\u003eCool!\u003c/p\u003e\n\n\u003cp\u003eHowever, now we need to \u003cem\u003edo something meaningful\u003c/em\u003e with this data, and that's where \u003ca href=\"https://io.adafruit.com/\"\u003eAdafruit IO\u003c/a\u003e comes into play. With a generous free tier, this cloud service provides a ridiculously easy way to \u003cstrong\u003evisualize data (dashboards), create alerts (actions), and even integrate with third-party services (power-ups)\u003c/strong\u003e.\u003c/p\u003e\n\n\u003ch3\u003eAdafruit IO Feeds\u003c/h3\u003e\n\n\u003cp\u003eAfter setting up an Adafruit IO account, head to the \u003cstrong\u003eFeeds\u003c/strong\u003e section to create one feed for each data element you want to use:\u003c/p\u003e\n","metadata":{"markdown":"## Cloud Connectivity\n\nAt this point we are **gathering sensor data locally and sending it periodically to the cloud**\nover cellular.\n\nCool!\n\nHowever, now we need to *do something meaningful* with this data, and that's where [Adafruit IO](https://io.adafruit.com/) comes into play. With a generous free tier, this cloud service provides a ridiculously easy way to **visualize data (dashboards), create alerts (actions), and even integrate with third-party services (power-ups)**.\n\n### Adafruit IO Feeds\n\nAfter setting up an Adafruit IO account, head to the **Feeds** section to create one feed for each data element you want to use:"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/681/original/all-feeds.png?1741964033","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003ch3\u003eBlues Notehub\u003c/h3\u003e\n\n\u003cp\u003eNext, we need to head \u003cem\u003eback to Notehub\u003c/em\u003e to create a \u003cstrong\u003eroute\u003c/strong\u003e which will tell Notehub how to deliver events to Adafruit IO!\u003c/p\u003e\n\n\u003cp\u003eFrom the \u003cstrong\u003eRoutes\u003c/strong\u003e menu option, choose \u003cstrong\u003eGeneral HTTP/HTTPS Request/Response\u003c/strong\u003e which lets us send data to the Adafruit IO HTTP APIs.\u003c/p\u003e\n","metadata":{"markdown":"### Blues Notehub\n\nNext, we need to head *back to Notehub* to create a **route** which will tell Notehub how to deliver events to Adafruit IO!\n\nFrom the **Routes** menu option, choose **General HTTP/HTTPS Request/Response** which lets us send data to the Adafruit IO HTTP APIs."}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/682/original/create-route.png?1741964056","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003cp\u003eTo set up the rest of the route:\u003c/p\u003e\n\n\u003col\u003e\n\u003cli\u003eThe \u003cstrong\u003eURL\u003c/strong\u003e will be: \u003ccode class=\"inline\"\u003ehttps://io.adafruit.com/api/v2/{username}/groups/{group}/data\u003c/code\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003cp\u003eWhere \u003ccode class=\"inline\"\u003e{username}\u003c/code\u003e is your Adafruit IO username and \u003ccode class=\"inline\"\u003e{group}\u003c/code\u003e is the name of the group that encapsulates your feeds. (This is probably \u003ccode class=\"inline\"\u003edefault\u003c/code\u003e.)\u003c/p\u003e\n\n\u003col\u003e\n\u003cli\u003eUnder \u003cstrong\u003eHTTP Headers\u003c/strong\u003e you'll have to add your Adafruit IO key (which is available back in your Adafruit IO account).\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003cp\u003e\u003ccode class=\"inline\"\u003eX-AIO-Key\u003c/code\u003e is the name and your Adafruit IO key is the value.\u003c/p\u003e\n\n\u003col\u003e\n\u003cli\u003eUnder \u003cstrong\u003eFilters\u003c/strong\u003e you'll want to send the sensor data associated with this project and ignore other session and health info that Notecard sends.\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003cp\u003eTherefore, under the \u003cstrong\u003eNotefiles\u003c/strong\u003e section, choose \u003cstrong\u003eSelected Notefiles\u003c/strong\u003e, and then \u003ccode class=\"inline\"\u003esensors.qo\u003c/code\u003e (which is the \u003ca href=\"https://gist.github.com/rdlauer/8be390f39462117631d8e1b215b4d359#file-main-cpp-L76\"\u003ename of the Notefile used in code\u003c/a\u003e).\u003c/p\u003e\n\n\u003col\u003e\n\u003cli\u003eLastly in the \u003cstrong\u003eData\u003c/strong\u003e section, we'll need to edit or \u003cem\u003etransform\u003c/em\u003e the JSON payload sent from the device before it's delivered to Adafruit IO. Every cloud service has a certain format of data they expect, and Adafruit IO is no different!\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003cp\u003eIn essence we need to create a set of key-value pairs, one for each data element we are sending. Something like...\u003c/p\u003e\n\u003ccode class=\"\"\u003e\u003cpre\u003e   {\n     \"key\":\"co2-feed\",\n     \"value\":body.co2\n   }\n\u003c/pre\u003e\u003c/code\u003e\n\u003cp\u003eHow is this accomplished? Through the magic of \u003ca href=\"https://jsonata.org/\"\u003eJSONata\u003c/a\u003e expressions! JSONata lets you transform JSON objects on the fly.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eTIP:\u003c/strong\u003e The \u003ca href=\"https://try.jsonata.org/\"\u003eJSONata Exerciser\u003c/a\u003e is a great way to test out JSONata expressions!\u003c/p\u003e\n\n\u003cp\u003eHere's the full JSONata expression I used, with an image of this expression in the aforementioned JSONata Exerciser:\u003c/p\u003e\n\u003ccode class=\"\"\u003e\u003cpre\u003e```\n{\n  \"feeds\":[\n     {\n           \"key\":\"bestlocation-feed\",\n           \"value\":best_location\n     },\n     {\n           \"key\":\"co2-feed\",\n           \"value\":body.co2\n     },\n     {\n           \"key\":\"humid-feed\",\n           \"value\":body.humid\n     },\n     {\n           \"key\":\"raw-feed\",\n           \"value\":body.raw\n     },\n     {\n           \"key\":\"temp-feed\",\n           \"value\":body.temp\n     },\n     {\n           \"key\":\"transport-feed\",\n           \"value\":transport\n     },\n     {\n           \"key\":\"voc-feed\",\n           \"value\":body.voc\n     },\n     {\n           \"key\":\"voltage-feed\",\n           \"value\":body.voltage/5*100\n     }\n  ],\n  \"created_at\":$fromMillis(when * 1000),\n  \"location\":{\n     \"lat\":best_lat,\n     \"lon\":best_lon\n  }\n}\n```\n\u003c/pre\u003e\u003c/code\u003e","metadata":{"markdown":"To set up the rest of the route:\n\n1. The **URL** will be: `https://io.adafruit.com/api/v2/{username}/groups/{group}/data`\n   \n   Where `{username}` is your Adafruit IO username and `{group}` is the name of the group that encapsulates your feeds. (This is probably `default`.)\n\n1. Under **HTTP Headers** you'll have to add your Adafruit IO key (which is available back in your Adafruit IO account).\n   \n   `X-AIO-Key` is the name and your Adafruit IO key is the value.\n\n1. Under **Filters** you'll want to send the sensor data associated with this project and ignore other session and health info that Notecard sends.\n   \n   Therefore, under the **Notefiles** section, choose **Selected Notefiles**, and then `sensors.qo` (which is the [name of the Notefile used in code](https://gist.github.com/rdlauer/8be390f39462117631d8e1b215b4d359#file-main-cpp-L76)).\n\n1. Lastly in the **Data** section, we'll need to edit or *transform* the JSON payload sent from the device before it's delivered to Adafruit IO. Every cloud service has a certain format of data they expect, and Adafruit IO is no different!\n\n   In essence we need to create a set of key-value pairs, one for each data element we are sending. Something like...\n\n   ```\n   {\n     \"key\":\"co2-feed\",\n     \"value\":body.co2\n   }\n   ```\n\n   How is this accomplished? Through the magic of [JSONata](https://jsonata.org/) expressions! JSONata lets you transform JSON objects on the fly.\n\n   **TIP:** The [JSONata Exerciser](https://try.jsonata.org/) is a great way to test out JSONata expressions!\n\n   Here's the full JSONata expression I used, with an image of this expression in the aforementioned JSONata Exerciser:\n\n    ```\n    {\n      \"feeds\":[\n         {\n               \"key\":\"bestlocation-feed\",\n               \"value\":best_location\n         },\n         {\n               \"key\":\"co2-feed\",\n               \"value\":body.co2\n         },\n         {\n               \"key\":\"humid-feed\",\n               \"value\":body.humid\n         },\n         {\n               \"key\":\"raw-feed\",\n               \"value\":body.raw\n         },\n         {\n               \"key\":\"temp-feed\",\n               \"value\":body.temp\n         },\n         {\n               \"key\":\"transport-feed\",\n               \"value\":transport\n         },\n         {\n               \"key\":\"voc-feed\",\n               \"value\":body.voc\n         },\n         {\n               \"key\":\"voltage-feed\",\n               \"value\":body.voltage/5*100\n         }\n      ],\n      \"created_at\":$fromMillis(when * 1000),\n      \"location\":{\n         \"lat\":best_lat,\n         \"lon\":best_lon\n      }\n    }\n    ```"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/683/original/jsonata-exerciser.png?1741964092","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003col\u003e\n\u003cli\u003eFinally, save the route (making sure it's enabled) and watch as the next event comes in. You should see a little green check under the status column in the \u003cstrong\u003eEvent\u003c/strong\u003e view, which tells you the event was successfully routed to Adafruit IO!\u003c/li\u003e\n\u003c/ol\u003e\n","metadata":{"markdown":"1. Finally, save the route (making sure it's enabled) and watch as the next event comes in. You should see a little green check under the status column in the **Event** view, which tells you the event was successfully routed to Adafruit IO!"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/684/original/notehub-event-routed.png?1741964117","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003ch3\u003eAdafruit IO Dashboard\u003c/h3\u003e\n\n\u003cp\u003eNow that we have data flowing into Adafruit IO, let's create a dashboard.\u003c/p\u003e\n","metadata":{"markdown":"### Adafruit IO Dashboard\n\nNow that we have data flowing into Adafruit IO, let's create a dashboard."}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/685/original/dashboard.png?1741964142","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003col\u003e\n\u003cli\u003e\u003cp\u003eHead to the \u003cstrong\u003eDashboards\u003c/strong\u003e menu option in Adafruit IO and create your first dashboard.\u003c/p\u003e\u003c/li\u003e\n\u003cli\u003e\u003cp\u003eUsing the intuitive UI provided, create one or more \u003cstrong\u003eblocks\u003c/strong\u003e for each \u003cstrong\u003efeed\u003c/strong\u003e. For example, using the VOC index data feed, I created a \u003cstrong\u003egauge\u003c/strong\u003e to show the most recent reading and a \u003cstrong\u003eline chart\u003c/strong\u003e to show historical readings:\u003c/p\u003e\u003c/li\u003e\n\u003c/ol\u003e\n","metadata":{"markdown":"1. Head to the **Dashboards** menu option in Adafruit IO and create your first dashboard.\n\n1. Using the intuitive UI provided, create one or more **blocks** for each **feed**. For example, using the VOC index data feed, I created a **gauge** to show the most recent reading and a **line chart** to show historical readings:"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/686/original/voc-gauge.png?1741964166","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003col\u003e\n\u003cli\u003eNow, \u003cstrong\u003erepeat the previous step\u003c/strong\u003e for every data element you want to visualize. It's that simple!\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003ch3\u003eAdafruit IO Actions\u003c/h3\u003e\n\n\u003cp\u003eNext up, I thought it would be handy to \u003cstrong\u003ereceive an email alert\u003c/strong\u003e every time my VOC index went above 100 (generally a point when air quality goes from good to not-so-good!).\u003c/p\u003e\n","metadata":{"markdown":"1. Now, **repeat the previous step** for every data element you want to visualize. It's that simple!\n\n### Adafruit IO Actions\n\nNext up, I thought it would be handy to **receive an email alert** every time my VOC index went above 100 (generally a point when air quality goes from good to not-so-good!)."}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/687/original/aqi.png?1741964189","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003col\u003e\n\u003cli\u003e\u003cp\u003eHead to the \u003cstrong\u003eActions\u003c/strong\u003e menu option in Adafruit IO.\u003c/p\u003e\u003c/li\u003e\n\u003cli\u003e\u003cp\u003eCreate a new action using either a form or the \"Blockly\" editor (which feels a bit like programming in \u003ca href=\"https://scratch.mit.edu/\"\u003eScratch\u003c/a\u003e!).\u003c/p\u003e\u003c/li\u003e\n\u003cli\u003e\u003cp\u003eHere's what my email alert looks like in the \u003cstrong\u003eBlockly editor\u003c/strong\u003e:\u003c/p\u003e\u003c/li\u003e\n\u003c/ol\u003e\n","metadata":{"markdown":"1. Head to the **Actions** menu option in Adafruit IO.\n\n1. Create a new action using either a form or the \"Blockly\" editor (which feels a bit like programming in [Scratch](https://scratch.mit.edu/)!).\n\n1. Here's what my email alert looks like in the **Blockly editor**:"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/688/original/blockly.png?1741964213","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003e\u003cspan\u003e4.\u003c/span\u003e\u003cspan\u003e And the alerts? Pretty slick:\u003c/span\u003e\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/689/original/aio-email.png?1741964255","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003ch3\u003eAdafruit IO Power-Ups\u003c/h3\u003e\n\n\u003cp\u003eNow it's time to get serious with \u003cstrong\u003eAdafruit IO Power-Ups\u003c/strong\u003e!\u003c/p\u003e\n\n\u003cp\u003eThese are robust integrations with third party services like IFTTT, Zapier, and SMS for messaging (among other services).\u003c/p\u003e\n\n\u003cp\u003ePersonally I thought it would be pretty neat to use \u003ca href=\"https://ifttt.com/\"\u003eIFTTT\u003c/a\u003e to integrate with my Philips Hue LED strip and provide some more dramatic feedback when my local air quality (the VOC index) goes wrong!\u003c/p\u003e\n\n\u003cp\u003eRemember this image from earlier?\u003c/p\u003e\n","metadata":{"markdown":"### Adafruit IO Power-Ups\n\nNow it's time to get serious with **Adafruit IO Power-Ups**!\n\nThese are robust integrations with third party services like IFTTT, Zapier, and SMS for messaging (among other services).\n\nPersonally I thought it would be pretty neat to use [IFTTT](https://ifttt.com/) to integrate with my Philips Hue LED strip and provide some more dramatic feedback when my local air quality (the VOC index) goes wrong!\n\nRemember this image from earlier?"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/690/original/hue-thumbs-up-down.jpg?1741964280","metadata":{"caption":""}},{"element_type":"markdown","content":"\u003cp\u003eLet's get this set up.\u003c/p\u003e\n\n\u003col\u003e\n\u003cli\u003e\u003cp\u003eChoose the \u003cstrong\u003eIFTTT\u003c/strong\u003e from the \u003cstrong\u003ePower-Ups\u003c/strong\u003e menu in Adafruit IO.\u003c/p\u003e\u003c/li\u003e\n\u003cli\u003e\u003cp\u003eLog in to your IFTTT account and \u003ca href=\"https://ifttt.com/create\"\u003ecreate an applet\u003c/a\u003e.\u003c/p\u003e\u003c/li\u003e\n\u003cli\u003e\u003cp\u003eThe first step of the applet is to monitor a monitor a feed from Adafruit IO. Search for \"Adafruit\" and complete the form as needed to set the color to green (e.g. if the VOC value is \u0026lt;= 100).\u003c/p\u003e\u003c/li\u003e\n\u003c/ol\u003e\n","metadata":{"markdown":"Let's get this set up.\n\n1. Choose the **IFTTT** from the **Power-Ups** menu in Adafruit IO.\n\n1. Log in to your IFTTT account and [create an applet](https://ifttt.com/create).\n\n1. The first step of the applet is to monitor a monitor a feed from Adafruit IO. Search for \"Adafruit\" and complete the form as needed to set the color to green (e.g. if the VOC value is \u0026lt;= 100)."}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/691/original/ifttt-voc.png?1741964303","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003e\u003cspan\u003e4.\u003c/span\u003e\u003cspan\u003e Next, in the \"then\" section, you'll want to connect to your Hue account and select the \"change color\" action for your HUE light.\u003c/span\u003e\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/692/original/ifttt-hue.png?1741964329","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003e\u003cspan\u003e5.\u003c/span\u003e\u003cspan\u003e The IFTTT applet should then be complete!\u003c/span\u003e\u003c/p\u003e\n      \n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/693/original/ifttt-green.png?1741964353","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n        \u003cp\u003e\u003cspan\u003e6.\u003c/span\u003e\u003cspan\u003e You'll want to repeat this process to set the red color, changing the \"if\" statement to trigger if the VOC value is \u0026gt; 100.\u003c/span\u003e\u003c/p\u003e\n      \n\n\n\n\n\n","metadata":{}},{"element_type":"markdown","content":"\u003cp\u003e\u003cstrong\u003eNOTE:\u003c/strong\u003e The free tier of IFTTT only polls your Adafruit IO feed once per hour. You can upgrade to their \"Pro\" tier to get more frequent polling.\u003c/p\u003e\n\n\u003ch2\u003eFinal Results\u003c/h2\u003e\n\n\u003cp\u003eAnd the results? Great success!\u003c/p\u003e\n\n\u003cp\u003eThe VOC levels in my office naturally range from about 90 to 130 throughout the day, but I hard-coded in some VOC values to quickly demonstrate the lights changing:\u003c/p\u003e\n","metadata":{"markdown":"**NOTE:** The free tier of IFTTT only polls your Adafruit IO feed once per hour. You can upgrade to their \"Pro\" tier to get more frequent polling.\n\n## Final Results\n\nAnd the results? Great success!\n\nThe VOC levels in my office naturally range from about 90 to 130 throughout the day, but I hard-coded in some VOC values to quickly demonstrate the lights changing:"}},{"element_type":"embed","content":"https://youtu.be/pYM0PNfwX8U"},{"element_type":"markdown","content":"\u003cp\u003e\u003cem\u003eBonus points if you can spot the cat!\u003c/em\u003e\u003c/p\u003e\n\n\u003cp\u003eSo I hope you've seen \u003cstrong\u003ehow easy it can be to build a truly cloud-connected product\u003c/strong\u003e that can:\u003c/p\u003e\n\n\u003col\u003e\n\u003cli\u003eMeasure indoor air quality.\u003c/li\u003e\n\u003cli\u003eSend that data to the cloud.\u003c/li\u003e\n\u003cli\u003eVisualize that sensor data on a dashboard.\u003c/li\u003e\n\u003cli\u003eGenerate email alerts when air quality goes south.\u003c/li\u003e\n\u003cli\u003eVisualize the results on a local smart home device.\u003c/li\u003e\n\u003c/ol\u003e\n\n\u003cp\u003eThanks to the simplicity (and power) of \u003cstrong\u003eBlues Notecard and Adafruit IO\u003c/strong\u003e this project took me only a few hours to piece together - and I hope you can do the same!\u003c/p\u003e\n\n\u003cp\u003eGet started with \u003ca href=\"https://io.adafruit.com/\"\u003eAdafruit IO here\u003c/a\u003e and get your own \u003ca href=\"https://shop.blues.com/products/blues-global-starter-kit\"\u003eBlues Starter Kit here\u003c/a\u003e. The starter kit includes an STM32 host, Notecarrier F, and a Notecard Cell+WiFi. Remember too, the full source code for this project is \u003ca href=\"https://gist.github.com/rdlauer/8be390f39462117631d8e1b215b4d359\"\u003eavailable here on GitHub\u003c/a\u003e!\u003c/p\u003e\n\n\u003cp\u003eHappy Hacking!\u003c/p\u003e\n","metadata":{"markdown":"*Bonus points if you can spot the cat!*\n\nSo I hope you've seen **how easy it can be to build a truly cloud-connected product** that can:\n\n1. Measure indoor air quality.\n1. Send that data to the cloud.\n1. Visualize that sensor data on a dashboard.\n1. Generate email alerts when air quality goes south.\n1. Visualize the results on a local smart home device.\n\nThanks to the simplicity (and power) of **Blues Notecard and Adafruit IO** this project took me only a few hours to piece together - and I hope you can do the same!\n\nGet started with [Adafruit IO here](https://io.adafruit.com/) and get your own [Blues Starter Kit here](https://shop.blues.com/products/blues-global-starter-kit). The starter kit includes an STM32 host, Notecarrier F, and a Notecard Cell+WiFi. Remember too, the full source code for this project is [available here on GitHub](https://gist.github.com/rdlauer/8be390f39462117631d8e1b215b4d359)!\n\nHappy Hacking!"}}]