├── README.md ├── board ├── aqi_base.pro ├── pcb │ ├── aqi_base.kicad_pcb │ ├── aqi_base_pcb.pdf │ └── readme.md ├── readme.md └── schematic │ ├── aqi_base.pdf │ ├── aqi_base.sch │ └── readme.md ├── bom ├── bom.txt ├── sensorplayground_base.csv └── sensorplayground_bom.pdf ├── cloud └── readme.md ├── doc ├── AQ_Levels.pdf ├── air_quality_results.txt └── construction.txt └── src ├── arduino ├── examples │ ├── aqi │ │ ├── air_quality.ino │ │ ├── credentials.h │ │ └── readme.md │ └── readme.md ├── libraries │ ├── faux_io │ │ ├── faux_io.cpp │ │ ├── faux_io.h │ │ └── readme.md │ ├── readme.md │ └── tdw_timer │ │ ├── tdw_timer.cpp │ │ └── tdw_timer.h └── readme.md ├── python └── readme.md └── readme.md /README.md: -------------------------------------------------------------------------------- 1 | # sensor-playground 2 | 3 | Indoor Air-Quality Monitor / Sensor Playground Project 4 | 5 | Copyright Notice 6 | 7 | Copyright (c) 2022, tdwgithub 8 | All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions are met: 12 | 1. Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | INTRODUCTION 29 | 30 | The Sensor Playground was designed to be a fun, easy-to-build platform for you 31 | to experiment with software, sensors, displays, the IoT, etc. It grew out of 32 | a project I started after my wife suggested that some of the reasons for how 33 | we had been feeling might be related to our indoor air-quality. 34 | 35 | It has been intentionally structured to make is straightforward for anyone 36 | with modest soldering skills and a bit of experience with tools like Arduino, 37 | to be able to construct a project that suits their ideas. 38 | 39 | See: https://hackaday.io/project/182619-sensor-playground for more information. 40 | 41 | 42 | It takes a kind-of "landing pad" approach, where you can build up the board 43 | and plug-in different kinds of processor modules, sensor sub-modules, displays, 44 | etc. without the need to solder them directly to the board. The number and 45 | variety of things you can plug in is virtually entirely up to you. 46 | 47 | BUILDING YOUR OWN 48 | 49 | There aren't a lot of "required" components, short of perhaps the power supply 50 | section & the processor module and even that has been designed to give you a 51 | wide range of choices. 52 | 53 | For example if you don't have a need for an encoder you don't need to install 54 | it or its supporting pull-up resistors. Same holds for the PCB-mount buttons. As for 55 | the sensors, I designed the PCB so that the processor I2C bus, SPI bus and a UART 56 | are available via sockets or pins at a number of locations across the board. 57 | This lets you connect the devices you choose at the points that make sense to 58 | your particular application. While there are outlines for some of the sensors 59 | that I chose to use, these are by no means the ones you have to select. 60 | 61 | 3.3V, 5.0V & GND are made available on most connectors, so that it's easy to 62 | find a nearby power connection. 63 | 64 | The component mounting types are all through-hole, making it pretty simple to 65 | solder in what you want and there is a fair amount of space to get your soldering 66 | iron into the places you need it to go. 67 | 68 | SURFACE MOUNT COMPONENTS...! (relax, they're optional!) 69 | 70 | I did add one (optional) surface-mount component: A processor reset controller. 71 | 72 | If the processor module you choose doesn't already provide a power-on reset, or 73 | perhaps its unreliable, then you can elect to install this component. It's only 74 | three pins and should be relatively easy for those with basic soldering skills and 75 | a reasonably steady hand to install. The only other component needed for the reset 76 | circuit is a 100K, through-hole resistor. See? Not so hard after all! 77 | 78 | PROCESSORS 79 | 80 | I mentioned that you have flexibility in terms of your choice of processors. This 81 | is due to fact that the board has been designed to take one of two common processor 82 | module types: 83 | 84 | 1. The ESP32 "DevKit"-style modules, from Esprssif, Inc. 85 | 2. A host of "Feather" form-factor modules. 86 | 87 | The ESP32 modules are dual-core powerhouses, with WiFi, Bluetooth, large memory, etc. 88 | If you have a processor-intensive application, the ESP32 is a great choice. 89 | 90 | The "Feather" modules are a form-factor and pin-functionality specification created 91 | by AdaFruit. These modules are available with a wide range of processors, memory, 92 | IO, etc. and the pin-out is nearly identical across them all. To date, I've used 93 | ESP8266 Feather modules, ESP32 modules (same processor as the ESP32 DevKit, 94 | different form-factor), & ARM Cortex M0. Given the circuit design I've chosen, 95 | all of these work on the Sensor Playground board. 96 | 97 | I chose to focus on I2C, SPI and UART as the primary mechanism for connecting with the 98 | sensors, displays, and other IO. This allows for a wide range of devices to be 99 | connected, while limiting the amount of digital IO that would otherwise need to be 100 | provided across the PCB. There are a few GPIO pins available on one of 101 | the headers (the same pins I use for the encoder and its button, along with the PCB- 102 | mount push buttons). 103 | 104 | What if I want other IO than just SPI, I2C or UART? 105 | 106 | Glad you asked! If you elect to use a Feather form-factor processor module, you could 107 | be in luck! That specification also allows for the addition of "Feather Wing" expansion 108 | modules to be plugged onto the top of Feather processor modules. You can find a 109 | pretty wide range of IO option boards that should satisfy a pretty wide range of requirements. 110 | 111 | Alternatively, you could build something that uses SPI or I2C to drive an ADC or DAC. That 112 | would provide a nice means for gaining analog I/O and would allow the addition of 113 | non bus-based sensors. I'm considering designing an add-on to do just that. 114 | 115 | How do I build this thing? 116 | 117 | I'll be adding some construction guides soon. These will give you hints about how to go 118 | about building the version of the Sensor Playground that best suits your application 119 | needs. 120 | 121 | To find the construction guides, along with tips, etc., look in the "doc" folder at the 122 | top level of this project. 123 | -------------------------------------------------------------------------------- /board/aqi_base.pro: -------------------------------------------------------------------------------- 1 | update=22/05/2015 07:44:53 2 | version=1 3 | last_client=kicad 4 | [general] 5 | version=1 6 | RootSch= 7 | BoardNm= 8 | [pcbnew] 9 | version=1 10 | LastNetListRead= 11 | UseCmpFile=1 12 | PadDrill=0.600000000000 13 | PadDrillOvalY=0.600000000000 14 | PadSizeH=1.500000000000 15 | PadSizeV=1.500000000000 16 | PcbTextSizeV=1.500000000000 17 | PcbTextSizeH=1.500000000000 18 | PcbTextThickness=0.300000000000 19 | ModuleTextSizeV=1.000000000000 20 | ModuleTextSizeH=1.000000000000 21 | ModuleTextSizeThickness=0.150000000000 22 | SolderMaskClearance=0.000000000000 23 | SolderMaskMinWidth=0.000000000000 24 | DrawSegmentWidth=0.200000000000 25 | BoardOutlineThickness=0.100000000000 26 | ModuleOutlineThickness=0.150000000000 27 | [cvpcb] 28 | version=1 29 | NetIExt=net 30 | [eeschema] 31 | version=1 32 | LibDir= 33 | [eeschema/libraries] 34 | -------------------------------------------------------------------------------- /board/pcb/aqi_base_pcb.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdwgithub/sensor-playground/380ca553df2cdc2fc384cbc9a52f78ab11ad0f07/board/pcb/aqi_base_pcb.pdf -------------------------------------------------------------------------------- /board/pcb/readme.md: -------------------------------------------------------------------------------- 1 | KiCad PCB layout file 2 | 3 | Copyright Notice 4 | 5 | Copyright (c) 2022, tdwgithub All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 8 | 9 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /board/readme.md: -------------------------------------------------------------------------------- 1 | 2 | Board Files 3 | 4 | Copyright Notice 5 | 6 | Copyright (c) 2022, tdwgithub 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | INTRODUCTION 28 | 29 | The board schematic and PCB layout were created using KiCad V5.1.x. 30 | 31 | Since the project design targets being a "landing pad" for various add-ons 32 | like sensor boards, displays, etc., the schematic doesn't call out or 33 | include any specific sensor, etc. This implies that the bill of materials 34 | doesn't specify them either. Rather, the schematic calls out the contact 35 | points that I've made available (via 0.0" header pins or sockets). With 36 | regard to the BOM, I will add elements to the BOM for the sensors and 37 | displays that I chose for my own application(s). 38 | 39 | PCB 40 | 41 | The PCB has been designed as a 2-layer board, using through-hole components 42 | wherever possible (there are only a few potential exceptions). The bottom of 43 | the board floods the unused space with a ground plane, to help avoid noise or 44 | ground-path issues. If you choose to modify the PCB layout, I recommend that 45 | you remove the ground plane flood first, do you mods, then re-flood. Also 46 | remember that there are multiple connection points to that plane and so you want 47 | to be sure those connections are maintained to it. If you intend to remove the 48 | ground flood, you must re-create the GND connections that originally used it. 49 | -------------------------------------------------------------------------------- /board/schematic/aqi_base.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdwgithub/sensor-playground/380ca553df2cdc2fc384cbc9a52f78ab11ad0f07/board/schematic/aqi_base.pdf -------------------------------------------------------------------------------- /board/schematic/aqi_base.sch: -------------------------------------------------------------------------------- 1 | EESchema Schematic File Version 4 2 | EELAYER 30 0 3 | EELAYER END 4 | $Descr A4 11693 8268 5 | encoding utf-8 6 | Sheet 1 1 7 | Title "" 8 | Date "" 9 | Rev "" 10 | Comp "" 11 | Comment1 "" 12 | Comment2 "" 13 | Comment3 "" 14 | Comment4 "" 15 | $EndDescr 16 | $Comp 17 | L ESP32-DEVKITC-32D:ESP32-DEVKITC-32D U1 18 | U 1 1 6142636E 19 | P 5050 6200 20 | F 0 "U1" H 5050 7367 50 0000 C CNN 21 | F 1 "ESP32-DEVKITC-32D" H 5050 7276 50 0000 C CNN 22 | F 2 "MODULE_ESP32-DEVKITC-32D" H 5050 6200 50 0001 L BNN 23 | F 3 "" H 5050 6200 50 0001 L BNN 24 | F 4 "4" H 5050 6200 50 0001 L BNN "PARTREV" 25 | F 5 "Espressif Systems" H 5050 6200 50 0001 L BNN "MANUFACTURER" 26 | 1 5050 6200 27 | 1 0 0 -1 28 | $EndComp 29 | $Comp 30 | L Regulator_Linear:LM1117-3.3 U2 31 | U 1 1 6145294E 32 | P 2400 1100 33 | F 0 "U2" H 2400 1342 50 0000 C CNN 34 | F 1 "LM1117-3.3" H 2400 1251 50 0000 C CNN 35 | F 2 "Package_TO_SOT_THT:TO-220-3_Horizontal_TabDown" H 2400 1100 50 0001 C CNN 36 | F 3 "http://www.ti.com/lit/ds/symlink/lm1117.pdf" H 2400 1100 50 0001 C CNN 37 | 1 2400 1100 38 | 1 0 0 -1 39 | $EndComp 40 | $Comp 41 | L 2821:2821 U3 42 | U 1 1 6150488C 43 | P 2050 6100 44 | F 0 "U3" H 2000 7275 50 0000 C CNN 45 | F 1 "2821" H 2050 6100 50 0001 L BNN 46 | F 2 "XCVR_2821" H 2050 6100 50 0001 L BNN 47 | F 3 "" H 2050 6100 50 0001 L BNN 48 | F 4 "Adafruit" H 2050 6100 50 0001 L BNN "MANUFACTURER" 49 | 1 2050 6100 50 | 1 0 0 -1 51 | $EndComp 52 | Wire Wire Line 53 | 2750 5300 3000 5300 54 | Wire Wire Line 55 | 5850 5500 6150 5500 56 | Wire Wire Line 57 | 2750 5400 3000 5400 58 | Wire Wire Line 59 | 5850 5800 6150 5800 60 | Wire Wire Line 61 | 2750 7100 3000 7100 62 | Wire Wire Line 63 | 5850 5300 6150 5300 64 | Text GLabel 6150 5500 2 50 Output ~ 0 65 | SCL 66 | Text GLabel 6150 5800 2 50 BiDi ~ 0 67 | SDA 68 | Text GLabel 3000 5300 2 50 Input ~ 0 69 | SCL 70 | Text GLabel 3000 5400 2 50 BiDi ~ 0 71 | SDA 72 | Wire Wire Line 73 | 5850 5400 6150 5400 74 | Text GLabel 6150 5400 2 50 Output ~ 0 75 | MOSI 76 | Wire Wire Line 77 | 2750 6700 3000 6700 78 | Text GLabel 3000 6700 2 50 Output ~ 0 79 | MOSI 80 | Wire Wire Line 81 | 2750 6800 3000 6800 82 | Text GLabel 3000 6800 2 50 Input ~ 0 83 | MISO 84 | Wire Wire Line 85 | 5850 6000 6150 6000 86 | Text GLabel 6150 6000 2 50 Input ~ 0 87 | MISO 88 | Wire Wire Line 89 | 2750 6600 3000 6600 90 | Text GLabel 3000 6600 2 50 Output ~ 0 91 | SCK 92 | Wire Wire Line 93 | 5850 6100 6150 6100 94 | Text GLabel 6150 6100 2 50 Output ~ 0 95 | SCK 96 | Wire Wire Line 97 | 1250 6600 1050 6600 98 | Text GLabel 1050 6600 0 50 Output ~ 0 99 | TXD 100 | Wire Wire Line 101 | 1250 6700 1050 6700 102 | Text GLabel 1050 6700 0 50 Input ~ 0 103 | RXD 104 | Text GLabel 3000 7100 2 50 UnSpc ~ 0 105 | GND 106 | Text GLabel 6150 5300 2 50 UnSpc ~ 0 107 | GND 108 | Wire Wire Line 109 | 2750 6300 3000 6300 110 | Text GLabel 3000 6300 2 50 Output ~ 0 111 | SPI_SEL1 112 | Wire Wire Line 113 | 4250 5800 4000 5800 114 | Text GLabel 4000 5800 0 50 Output ~ 0 115 | SPI_SEL1 116 | Text GLabel 4000 6600 0 50 UnSpc ~ 0 117 | GND 118 | Wire Wire Line 119 | 5850 5900 6150 5900 120 | Text GLabel 6150 5900 2 50 UnSpc ~ 0 121 | GND 122 | Wire Wire Line 123 | 2750 6200 3000 6200 124 | Text GLabel 3000 6200 2 50 Output ~ 0 125 | SPI_SEL2 126 | Wire Wire Line 127 | 4250 5700 4000 5700 128 | Text GLabel 4000 5700 0 50 Output ~ 0 129 | SPI_SEL2 130 | $Comp 131 | L Connector:Conn_01x03_Male PJ2 132 | U 1 1 615586C8 133 | P 1500 2150 134 | F 0 "PJ2" V 1350 2050 50 0000 L CNN 135 | F 1 "Conn_01x03_Male" V 1653 2294 50 0001 L CNN 136 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical" H 1500 2150 50 0001 C CNN 137 | F 3 "~" H 1500 2150 50 0001 C CNN 138 | 1 1500 2150 139 | 0 1 1 0 140 | $EndComp 141 | Wire Wire Line 142 | 1400 2350 1400 2500 143 | Text GLabel 1400 2500 0 50 UnSpc ~ 0 144 | 3.3V 145 | Wire Wire Line 146 | 1600 2350 1600 2500 147 | Text GLabel 1600 2500 2 50 UnSpc ~ 0 148 | 5V 149 | Wire Wire Line 150 | 1500 2350 1500 2600 151 | Text GLabel 1500 2600 3 50 UnSpc ~ 0 152 | GND 153 | $Comp 154 | L Connector:Conn_01x05_Male SJ1 155 | U 1 1 615617D3 156 | P 2550 2150 157 | F 0 "SJ1" V 2400 2100 50 0000 L CNN 158 | F 1 "Conn_01x05_Male" V 2703 2394 50 0001 L CNN 159 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Vertical" H 2550 2150 50 0001 C CNN 160 | F 3 "~" H 2550 2150 50 0001 C CNN 161 | 1 2550 2150 162 | 0 1 1 0 163 | $EndComp 164 | Wire Wire Line 165 | 2350 2350 2350 2450 166 | Wire Wire Line 167 | 2450 2350 2450 2450 168 | Wire Wire Line 169 | 2550 2350 2550 2450 170 | Wire Wire Line 171 | 2650 2350 2650 2450 172 | Wire Wire Line 173 | 2750 2350 2750 2450 174 | Text GLabel 2350 2450 3 50 Input ~ 0 175 | SCK 176 | Text GLabel 2450 2450 3 50 Input ~ 0 177 | MOSI 178 | Text GLabel 2550 2450 3 50 Output ~ 0 179 | MISO 180 | Text GLabel 2650 2450 3 50 Input ~ 0 181 | SPI_SEL1 182 | Text GLabel 2750 2450 3 50 Input ~ 0 183 | SPI_SEL2 184 | $Comp 185 | L Connector:Conn_01x05_Male SJ2 186 | U 1 1 61569283 187 | P 3300 2150 188 | F 0 "SJ2" V 3150 2100 50 0000 L CNN 189 | F 1 "Conn_01x05_Male" V 3453 2394 50 0001 L CNN 190 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Vertical" H 3300 2150 50 0001 C CNN 191 | F 3 "~" H 3300 2150 50 0001 C CNN 192 | 1 3300 2150 193 | 0 1 1 0 194 | $EndComp 195 | Wire Wire Line 196 | 3100 2350 3100 2450 197 | Wire Wire Line 198 | 3200 2350 3200 2450 199 | Wire Wire Line 200 | 3300 2350 3300 2450 201 | Wire Wire Line 202 | 3400 2350 3400 2450 203 | Wire Wire Line 204 | 3500 2350 3500 2450 205 | Text GLabel 3100 2450 3 50 Input ~ 0 206 | SCK 207 | Text GLabel 3200 2450 3 50 Input ~ 0 208 | MOSI 209 | Text GLabel 3300 2450 3 50 Output ~ 0 210 | MISO 211 | Text GLabel 3400 2450 3 50 Input ~ 0 212 | SPI_SEL1 213 | Text GLabel 3500 2450 3 50 Input ~ 0 214 | SPI_SEL2 215 | $Comp 216 | L Connector:Conn_01x05_Male USB_Power1 217 | U 1 1 61540DBD 218 | P 700 1250 219 | F 0 "USB_Power1" H 800 1600 50 0000 C CNN 220 | F 1 "Conn_01x05_Male" H 808 1540 50 0001 C CNN 221 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Vertical" H 700 1250 50 0001 C CNN 222 | F 3 "~" H 700 1250 50 0001 C CNN 223 | 1 700 1250 224 | 1 0 0 1 225 | $EndComp 226 | Wire Wire Line 227 | 1000 1100 1000 1050 228 | Wire Wire Line 229 | 1000 1400 1000 1450 230 | $Comp 231 | L Device:CP1 C1 232 | U 1 1 6154FC15 233 | P 1900 1250 234 | F 0 "C1" H 2015 1296 50 0000 L CNN 235 | F 1 "33uF" H 2015 1205 50 0000 L CNN 236 | F 2 "Capacitor_THT:CP_Radial_Tantal_D4.5mm_P5.00mm" H 1900 1250 50 0001 C CNN 237 | F 3 "~" H 1900 1250 50 0001 C CNN 238 | 1 1900 1250 239 | 1 0 0 -1 240 | $EndComp 241 | Connection ~ 1900 1100 242 | $Comp 243 | L power:GND #PWR0101 244 | U 1 1 61550DBF 245 | P 2400 1400 246 | F 0 "#PWR0101" H 2400 1150 50 0001 C CNN 247 | F 1 "GND" H 2405 1227 50 0000 C CNN 248 | F 2 "" H 2400 1400 50 0001 C CNN 249 | F 3 "" H 2400 1400 50 0001 C CNN 250 | 1 2400 1400 251 | 1 0 0 -1 252 | $EndComp 253 | Text GLabel 3150 1100 2 50 Output ~ 0 254 | 3.3V 255 | $Comp 256 | L Device:CP1 C2 257 | U 1 1 615532CC 258 | P 2900 1250 259 | F 0 "C2" H 3015 1296 50 0000 L CNN 260 | F 1 "33uF" H 3015 1205 50 0000 L CNN 261 | F 2 "Capacitor_THT:CP_Radial_Tantal_D4.5mm_P5.00mm" H 2900 1250 50 0001 C CNN 262 | F 3 "~" H 2900 1250 50 0001 C CNN 263 | 1 2900 1250 264 | 1 0 0 -1 265 | $EndComp 266 | Text GLabel 3150 1400 2 50 UnSpc ~ 0 267 | GND 268 | $Comp 269 | L Device:LED LED1 270 | U 1 1 61562C28 271 | P 4550 1100 272 | F 0 "LED1" H 4543 937 50 0000 C CNN 273 | F 1 "LED" H 4543 936 50 0001 C CNN 274 | F 2 "LED_THT:LED_D5.0mm" H 4550 1100 50 0001 C CNN 275 | F 3 "~" H 4550 1100 50 0001 C CNN 276 | 1 4550 1100 277 | -1 0 0 1 278 | $EndComp 279 | Wire Wire Line 280 | 4400 1100 4150 1100 281 | Text GLabel 4150 1100 0 50 Input ~ 0 282 | 3.3V 283 | $Comp 284 | L Device:R_US R1 285 | U 1 1 6156D9B2 286 | P 4700 1250 287 | F 0 "R1" H 4768 1296 50 0000 L CNN 288 | F 1 "10k" H 4768 1205 50 0000 L CNN 289 | F 2 "Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal" V 4740 1240 50 0001 C CNN 290 | F 3 "~" H 4700 1250 50 0001 C CNN 291 | 1 4700 1250 292 | 1 0 0 -1 293 | $EndComp 294 | $Comp 295 | L power:GND #PWR0102 296 | U 1 1 6156FE07 297 | P 4700 1400 298 | F 0 "#PWR0102" H 4700 1150 50 0001 C CNN 299 | F 1 "GND" H 4705 1227 50 0000 C CNN 300 | F 2 "" H 4700 1400 50 0001 C CNN 301 | F 3 "" H 4700 1400 50 0001 C CNN 302 | 1 4700 1400 303 | 1 0 0 -1 304 | $EndComp 305 | $Comp 306 | L Device:C C3 307 | U 1 1 61583B35 308 | P 8350 1250 309 | F 0 "C3" H 8250 1350 50 0000 L CNN 310 | F 1 ".01uF" H 8250 1450 50 0000 L CNN 311 | F 2 "Capacitor_THT:C_Disc_D5.0mm_W2.5mm_P5.00mm" H 8388 1100 50 0001 C CNN 312 | F 3 "~" H 8350 1250 50 0001 C CNN 313 | 1 8350 1250 314 | 1 0 0 -1 315 | $EndComp 316 | $Comp 317 | L Device:C C5 318 | U 1 1 61585B7A 319 | P 8950 1250 320 | F 0 "C5" H 8850 1350 50 0000 L CNN 321 | F 1 ".01uF" H 8850 1450 50 0000 L CNN 322 | F 2 "Capacitor_THT:C_Disc_D5.0mm_W2.5mm_P5.00mm" H 8988 1100 50 0001 C CNN 323 | F 3 "~" H 8950 1250 50 0001 C CNN 324 | 1 8950 1250 325 | 1 0 0 -1 326 | $EndComp 327 | $Comp 328 | L Device:C C6 329 | U 1 1 615866E8 330 | P 9250 1250 331 | F 0 "C6" H 9150 1350 50 0000 L CNN 332 | F 1 ".01uF" H 9150 1450 50 0000 L CNN 333 | F 2 "Capacitor_THT:C_Disc_D5.0mm_W2.5mm_P5.00mm" H 9288 1100 50 0001 C CNN 334 | F 3 "~" H 9250 1250 50 0001 C CNN 335 | 1 9250 1250 336 | 1 0 0 -1 337 | $EndComp 338 | $Comp 339 | L Device:C C7 340 | U 1 1 61587341 341 | P 9550 1250 342 | F 0 "C7" H 9450 1350 50 0000 L CNN 343 | F 1 ".01uF" H 9450 1450 50 0000 L CNN 344 | F 2 "Capacitor_THT:C_Disc_D5.0mm_W2.5mm_P5.00mm" H 9588 1100 50 0001 C CNN 345 | F 3 "~" H 9550 1250 50 0001 C CNN 346 | 1 9550 1250 347 | 1 0 0 -1 348 | $EndComp 349 | $Comp 350 | L Device:C C8 351 | U 1 1 61588003 352 | P 9850 1250 353 | F 0 "C8" H 9750 1350 50 0000 L CNN 354 | F 1 ".01uF" H 9700 1450 50 0000 L CNN 355 | F 2 "Capacitor_THT:C_Disc_D5.0mm_W2.5mm_P5.00mm" H 9888 1100 50 0001 C CNN 356 | F 3 "~" H 9850 1250 50 0001 C CNN 357 | 1 9850 1250 358 | 1 0 0 -1 359 | $EndComp 360 | Wire Wire Line 361 | 9850 1100 9550 1100 362 | Wire Wire Line 363 | 9550 1100 9250 1100 364 | Connection ~ 9550 1100 365 | Wire Wire Line 366 | 9250 1100 8950 1100 367 | Connection ~ 9250 1100 368 | Connection ~ 8950 1100 369 | Wire Wire Line 370 | 8350 1100 8050 1100 371 | Connection ~ 8350 1100 372 | Wire Wire Line 373 | 9850 1400 9550 1400 374 | Wire Wire Line 375 | 9550 1400 9250 1400 376 | Connection ~ 9550 1400 377 | Wire Wire Line 378 | 9250 1400 8950 1400 379 | Connection ~ 9250 1400 380 | Connection ~ 8950 1400 381 | Wire Wire Line 382 | 8350 1400 8050 1400 383 | Connection ~ 8350 1400 384 | $Comp 385 | L power:GND #PWR0103 386 | U 1 1 6159B646 387 | P 8050 1400 388 | F 0 "#PWR0103" H 8050 1150 50 0001 C CNN 389 | F 1 "GND" H 8055 1227 50 0000 C CNN 390 | F 2 "" H 8050 1400 50 0001 C CNN 391 | F 3 "" H 8050 1400 50 0001 C CNN 392 | 1 8050 1400 393 | 1 0 0 -1 394 | $EndComp 395 | Text GLabel 8050 1100 1 50 UnSpc ~ 0 396 | 3.3V 397 | $Comp 398 | L power:GND #PWR0104 399 | U 1 1 615C52A0 400 | P 10450 1400 401 | F 0 "#PWR0104" H 10450 1150 50 0001 C CNN 402 | F 1 "GND" H 10455 1227 50 0000 C CNN 403 | F 2 "" H 10450 1400 50 0001 C CNN 404 | F 3 "" H 10450 1400 50 0001 C CNN 405 | 1 10450 1400 406 | 1 0 0 -1 407 | $EndComp 408 | Text GLabel 10450 1100 1 50 UnSpc ~ 0 409 | 5V 410 | Wire Wire Line 411 | 1900 1100 1900 900 412 | Text GLabel 1900 900 1 50 UnSpc ~ 0 413 | 5V 414 | Text Notes 10900 900 2 50 ~ 0 415 | 5V Bypass Caps 416 | Text Notes 9300 900 2 50 ~ 0 417 | 3.3V Bypass Caps 418 | $Comp 419 | L Connector:Conn_01x08_Male AF-PM25x1 420 | U 1 1 615E168D 421 | P 8650 2150 422 | F 0 "AF-PM25x1" V 8500 2100 50 0000 C CNN 423 | F 1 "NC" V 8576 2078 50 0001 C CNN 424 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x08_P2.54mm_Vertical" H 8650 2150 50 0001 C CNN 425 | F 3 "~" H 8650 2150 50 0001 C CNN 426 | 1 8650 2150 427 | 0 1 1 0 428 | $EndComp 429 | Wire Wire Line 430 | 8950 2350 8950 2450 431 | Text GLabel 8950 2450 3 50 UnSpc ~ 0 432 | 5V 433 | Wire Wire Line 434 | 8850 2350 8850 2450 435 | Text GLabel 8850 2450 3 50 UnSpc ~ 0 436 | GND 437 | Wire Wire Line 438 | 8650 2350 8650 2450 439 | Wire Wire Line 440 | 8550 2350 8550 2450 441 | Text Notes 8250 2350 3 50 ~ 0 442 | NC 443 | Text Notes 8750 2350 3 50 ~ 0 444 | NC 445 | Text Notes 8350 2350 3 50 ~ 0 446 | NC 447 | $Comp 448 | L Connector:Conn_01x05_Male AF-MTU21D1 449 | U 1 1 615FA3F3 450 | P 5300 2150 451 | F 0 "AF-MTU21D1" V 5200 1950 50 0000 L CNN 452 | F 1 "Conn_01x05_Male" V 5453 2394 50 0001 L CNN 453 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Vertical" H 5300 2150 50 0001 C CNN 454 | F 3 "~" H 5300 2150 50 0001 C CNN 455 | 1 5300 2150 456 | 0 1 1 0 457 | $EndComp 458 | Wire Wire Line 459 | 5100 2350 5100 2450 460 | Text GLabel 5100 2450 3 50 Input ~ 0 461 | 3.3V 462 | Wire Wire Line 463 | 5300 2350 5300 2450 464 | Text GLabel 5300 2450 3 50 UnSpc ~ 0 465 | GND 466 | Wire Wire Line 467 | 5400 2350 5400 2450 468 | Text GLabel 5400 2450 3 50 BiDi ~ 0 469 | SDA 470 | Wire Wire Line 471 | 5500 2350 5500 2450 472 | Text GLabel 5500 2450 3 50 Input ~ 0 473 | SCL 474 | Text Notes 5150 2450 0 50 ~ 0 475 | NC 476 | $Comp 477 | L Connector:Conn_01x04_Male SF-SCD4x1 478 | U 1 1 6160F9C0 479 | P 6400 2150 480 | F 0 "SF-SCD4x1" V 6250 1850 50 0000 L CNN 481 | F 1 "Conn_01x04_Male" V 6553 2294 50 0001 L CNN 482 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical" H 6400 2150 50 0001 C CNN 483 | F 3 "~" H 6400 2150 50 0001 C CNN 484 | 1 6400 2150 485 | 0 1 1 0 486 | $EndComp 487 | Wire Wire Line 488 | 6500 2350 6500 2450 489 | Wire Wire Line 490 | 6400 2350 6400 2450 491 | Wire Wire Line 492 | 6300 2350 6300 2450 493 | Wire Wire Line 494 | 6200 2350 6200 2450 495 | $Comp 496 | L Connector:Conn_01x04_Male SF-SGPx1 497 | U 1 1 6161DABC 498 | P 7450 2150 499 | F 0 "SF-SGPx1" V 7300 1900 50 0000 L CNN 500 | F 1 "Conn_01x04_Male" V 7603 2294 50 0001 L CNN 501 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical" H 7450 2150 50 0001 C CNN 502 | F 3 "~" H 7450 2150 50 0001 C CNN 503 | 1 7450 2150 504 | 0 1 1 0 505 | $EndComp 506 | Wire Wire Line 507 | 7550 2350 7550 2450 508 | Wire Wire Line 509 | 7450 2350 7450 2450 510 | Wire Wire Line 511 | 7350 2350 7350 2450 512 | Wire Wire Line 513 | 7250 2350 7250 2450 514 | $Comp 515 | L Switch:SW_Push_Dual_x2 PB1 516 | U 1 1 6166E8FB 517 | P 7400 1450 518 | F 0 "PB1" H 7400 1643 50 0000 C CNN 519 | F 1 "SW_Push_Dual_x2" H 7400 1644 50 0001 C CNN 520 | F 2 "Button_Switch_THT:SW_PUSH_6mm" H 7400 1650 50 0001 C CNN 521 | F 3 "~" H 7400 1650 50 0001 C CNN 522 | 1 7400 1450 523 | 1 0 0 -1 524 | $EndComp 525 | $Comp 526 | L Device:R_US RB1 527 | U 1 1 6167114B 528 | P 7200 1300 529 | F 0 "RB1" H 7000 1400 50 0000 L CNN 530 | F 1 "10k" H 7000 1300 50 0000 L CNN 531 | F 2 "Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal" V 7240 1290 50 0001 C CNN 532 | F 3 "~" H 7200 1300 50 0001 C CNN 533 | 1 7200 1300 534 | 1 0 0 -1 535 | $EndComp 536 | $Comp 537 | L power:GND #PWR0105 538 | U 1 1 61672F1F 539 | P 7600 1450 540 | F 0 "#PWR0105" H 7600 1200 50 0001 C CNN 541 | F 1 "GND" H 7605 1277 50 0000 C CNN 542 | F 2 "" H 7600 1450 50 0001 C CNN 543 | F 3 "" H 7600 1450 50 0001 C CNN 544 | 1 7600 1450 545 | 1 0 0 -1 546 | $EndComp 547 | Wire Wire Line 548 | 7200 1150 7200 1050 549 | Text GLabel 7200 1050 1 50 UnSpc ~ 0 550 | 3.3V 551 | Wire Wire Line 552 | 7200 1450 6850 1450 553 | Connection ~ 7200 1450 554 | Text GLabel 6850 1450 0 50 Output ~ 0 555 | PB1 556 | Wire Wire Line 557 | 5850 6600 6150 6600 558 | Text GLabel 6150 6600 2 50 Input ~ 0 559 | PB1 560 | Wire Wire Line 561 | 2750 5700 3000 5700 562 | Text GLabel 3000 5700 2 50 Input ~ 0 563 | PB1 564 | $Comp 565 | L Switch:SW_Push_Dual_x2 PB2 566 | U 1 1 616896D5 567 | P 6000 1450 568 | F 0 "PB2" H 6000 1643 50 0000 C CNN 569 | F 1 "SW_Push_Dual_x2" H 6000 1644 50 0001 C CNN 570 | F 2 "Button_Switch_THT:SW_PUSH_6mm" H 6000 1650 50 0001 C CNN 571 | F 3 "~" H 6000 1650 50 0001 C CNN 572 | 1 6000 1450 573 | 1 0 0 -1 574 | $EndComp 575 | $Comp 576 | L Device:R_US RB2 577 | U 1 1 616896DB 578 | P 5800 1300 579 | F 0 "RB2" H 5600 1400 50 0000 L CNN 580 | F 1 "10k" H 5600 1300 50 0000 L CNN 581 | F 2 "Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal" V 5840 1290 50 0001 C CNN 582 | F 3 "~" H 5800 1300 50 0001 C CNN 583 | 1 5800 1300 584 | 1 0 0 -1 585 | $EndComp 586 | $Comp 587 | L power:GND #PWR0106 588 | U 1 1 616896E1 589 | P 6200 1450 590 | F 0 "#PWR0106" H 6200 1200 50 0001 C CNN 591 | F 1 "GND" H 6205 1277 50 0000 C CNN 592 | F 2 "" H 6200 1450 50 0001 C CNN 593 | F 3 "" H 6200 1450 50 0001 C CNN 594 | 1 6200 1450 595 | 1 0 0 -1 596 | $EndComp 597 | Wire Wire Line 598 | 5800 1150 5800 1050 599 | Text GLabel 5800 1050 1 50 UnSpc ~ 0 600 | 3.3V 601 | Wire Wire Line 602 | 5800 1450 5450 1450 603 | Connection ~ 5800 1450 604 | Text GLabel 5450 1450 0 50 Output ~ 0 605 | PB2 606 | Wire Wire Line 607 | 2750 5800 3000 5800 608 | Text GLabel 3000 5800 2 50 Input ~ 0 609 | PB2 610 | Wire Wire Line 611 | 5850 6700 6150 6700 612 | Text GLabel 6150 6700 2 50 Input ~ 0 613 | PB2 614 | $Comp 615 | L Connector:Conn_01x04_Male GPIO_HDR1 616 | U 1 1 616ED092 617 | P 4250 2100 618 | F 0 "GPIO_HDR1" V 4150 1850 50 0000 L CNN 619 | F 1 "Conn_01x04_Male" V 4403 2344 50 0001 L CNN 620 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical" H 4250 2100 50 0001 C CNN 621 | F 3 "~" H 4250 2100 50 0001 C CNN 622 | 1 4250 2100 623 | 0 1 1 0 624 | $EndComp 625 | Wire Wire Line 626 | 4050 2300 4050 2400 627 | Wire Wire Line 628 | 4150 2300 4150 2400 629 | Wire Wire Line 630 | 2750 5900 3000 5900 631 | Text GLabel 3000 5900 2 50 BiDi ~ 0 632 | GPIO1 633 | Wire Wire Line 634 | 2750 6000 3000 6000 635 | Text GLabel 3000 6000 2 50 BiDi ~ 0 636 | GPIO2 637 | Wire Wire Line 638 | 2750 6100 3000 6100 639 | Text GLabel 3000 6100 2 50 BiDi ~ 0 640 | GPIO3 641 | Wire Wire Line 642 | 5850 6500 6150 6500 643 | Text GLabel 6150 6500 2 50 BiDi ~ 0 644 | GPIO1 645 | Wire Wire Line 646 | 5850 6200 6150 6200 647 | Text GLabel 6150 6200 2 50 BiDi ~ 0 648 | GPIO2 649 | Wire Wire Line 650 | 5850 6400 6150 6400 651 | Wire Wire Line 652 | 4350 2300 4350 2400 653 | Wire Wire Line 654 | 4250 2300 4250 2400 655 | Text Notes 5050 1950 0 50 ~ 0 656 | Temp/Humidity 657 | Text Notes 6100 1950 0 50 ~ 0 658 | CO2 Sensor 659 | Text Notes 7150 1950 0 50 ~ 0 660 | TVOC Sensor 661 | Text Notes 8350 1950 0 50 ~ 0 662 | PM2.5 Sensor 663 | Text Notes 1100 1950 0 50 ~ 0 664 | Power Supply Access 665 | Text Notes 2350 1950 0 50 ~ 0 666 | SPI Communications Access 667 | Text Notes 3950 1950 0 50 ~ 0 668 | GPIO Access 669 | $Comp 670 | L Device:D D1 671 | U 1 1 615EB375 672 | P 1700 1100 673 | F 0 "D1" H 1700 1000 50 0000 C CNN 674 | F 1 "1N4007" H 1750 1200 50 0000 C CNN 675 | F 2 "Diode_THT:D_DO-41_SOD81_P10.16mm_Horizontal" H 1700 1100 50 0001 C CNN 676 | F 3 "~" H 1700 1100 50 0001 C CNN 677 | 1 1700 1100 678 | -1 0 0 1 679 | $EndComp 680 | Wire Wire Line 681 | 1850 1100 1900 1100 682 | $Comp 683 | L Connector:Conn_01x08_Male AF-OLED091 684 | U 1 1 6161131E 685 | P 10050 2150 686 | F 0 "AF-OLED091" V 9900 2100 50 0000 C CNN 687 | F 1 "Conn_01x08_Male" V 9976 2078 50 0001 C CNN 688 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x08_P2.54mm_Vertical" H 10050 2150 50 0001 C CNN 689 | F 3 "~" H 10050 2150 50 0001 C CNN 690 | 1 10050 2150 691 | 0 1 1 0 692 | $EndComp 693 | $Comp 694 | L power:GND #PWR0107 695 | U 1 1 6161B43C 696 | P 9650 2350 697 | F 0 "#PWR0107" H 9650 2100 50 0001 C CNN 698 | F 1 "GND" H 9655 2177 50 0000 C CNN 699 | F 2 "" H 9650 2350 50 0001 C CNN 700 | F 3 "" H 9650 2350 50 0001 C CNN 701 | 1 9650 2350 702 | 1 0 0 -1 703 | $EndComp 704 | Wire Wire Line 705 | 9750 2350 9750 2450 706 | Text GLabel 9750 2450 3 50 UnSpc ~ 0 707 | 3.3V 708 | Wire Wire Line 709 | 10250 2350 10250 2450 710 | Text GLabel 10250 2450 3 50 Input ~ 0 711 | SCL 712 | Wire Wire Line 713 | 10350 2350 10350 2450 714 | Text GLabel 10350 2450 3 50 BiDi ~ 0 715 | SDA 716 | Text Notes 9750 1950 0 50 ~ 0 717 | Adafruit OLED 718 | $Comp 719 | L Device:Rotary_Encoder_Switch ENC1 720 | U 1 1 6169C187 721 | P 9000 5050 722 | F 0 "ENC1" H 9000 5325 50 0000 C CNN 723 | F 1 "Rotary_Encoder_Switch" H 9000 5326 50 0001 C CNN 724 | F 2 "Rotary_Encoder:RotaryEncoder_Alps_EC11E-Switch_Vertical_H20mm" H 8850 5210 50 0001 C CNN 725 | F 3 "~" H 9000 5310 50 0001 C CNN 726 | 1 9000 5050 727 | 1 0 0 -1 728 | $EndComp 729 | $Comp 730 | L Device:R_US ER1 731 | U 1 1 616A6A51 732 | P 9300 4800 733 | F 0 "ER1" H 9350 4900 50 0000 L CNN 734 | F 1 "10k" H 9350 4800 50 0000 L CNN 735 | F 2 "Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal" V 9340 4790 50 0001 C CNN 736 | F 3 "~" H 9300 4800 50 0001 C CNN 737 | 1 9300 4800 738 | 1 0 0 -1 739 | $EndComp 740 | Wire Wire Line 741 | 9300 4650 9300 4550 742 | Text GLabel 9300 4550 1 50 BiDi ~ 0 743 | 3.3V 744 | Wire Wire Line 745 | 9300 5150 9300 5300 746 | $Comp 747 | L power:GND #PWR0108 748 | U 1 1 616B8124 749 | P 9300 5300 750 | F 0 "#PWR0108" H 9300 5050 50 0001 C CNN 751 | F 1 "GND" H 9305 5127 50 0000 C CNN 752 | F 2 "" H 9300 5300 50 0001 C CNN 753 | F 3 "" H 9300 5300 50 0001 C CNN 754 | 1 9300 5300 755 | 1 0 0 -1 756 | $EndComp 757 | Wire Wire Line 758 | 9300 4950 9600 4950 759 | Connection ~ 9300 4950 760 | Text GLabel 9600 4950 2 50 Output ~ 0 761 | GPIO3 762 | Wire Wire Line 763 | 8700 4950 8650 4950 764 | Text GLabel 8350 4950 0 50 Output ~ 0 765 | GPIO1 766 | Wire Wire Line 767 | 8700 5150 8650 5150 768 | Text GLabel 8350 5150 0 50 Output ~ 0 769 | GPIO2 770 | Wire Wire Line 771 | 8700 5050 7950 5050 772 | Wire Wire Line 773 | 7950 5050 7950 5100 774 | $Comp 775 | L power:GND #PWR0109 776 | U 1 1 616FA6FB 777 | P 7950 5100 778 | F 0 "#PWR0109" H 7950 4850 50 0001 C CNN 779 | F 1 "GND" H 7955 4927 50 0000 C CNN 780 | F 2 "" H 7950 5100 50 0001 C CNN 781 | F 3 "" H 7950 5100 50 0001 C CNN 782 | 1 7950 5100 783 | 1 0 0 -1 784 | $EndComp 785 | Text GLabel 4350 2400 3 50 BiDi ~ 0 786 | GPIO1 787 | Text GLabel 4250 2400 3 50 BiDi ~ 0 788 | GPIO2 789 | Text GLabel 4150 2400 3 50 BiDi ~ 0 790 | GPIO3 791 | $Comp 792 | L power:GND #PWR0110 793 | U 1 1 61740589 794 | P 4050 2400 795 | F 0 "#PWR0110" H 4050 2150 50 0001 C CNN 796 | F 1 "GND" H 4055 2227 50 0000 C CNN 797 | F 2 "" H 4050 2400 50 0001 C CNN 798 | F 3 "" H 4050 2400 50 0001 C CNN 799 | 1 4050 2400 800 | 1 0 0 -1 801 | $EndComp 802 | $Comp 803 | L Device:R_US ER2 804 | U 1 1 616B7AA7 805 | P 8650 4800 806 | F 0 "ER2" H 8450 4900 50 0000 L CNN 807 | F 1 "10k" H 8450 4800 50 0000 L CNN 808 | F 2 "Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal" V 8690 4790 50 0001 C CNN 809 | F 3 "~" H 8650 4800 50 0001 C CNN 810 | 1 8650 4800 811 | 1 0 0 -1 812 | $EndComp 813 | Connection ~ 8650 4950 814 | Wire Wire Line 815 | 8650 4950 8350 4950 816 | Wire Wire Line 817 | 8650 4650 8650 4550 818 | Text GLabel 8650 4550 1 50 BiDi ~ 0 819 | 3.3V 820 | $Comp 821 | L Device:R_US ER3 822 | U 1 1 616C2ABD 823 | P 8650 5300 824 | F 0 "ER3" H 8450 5350 50 0000 L CNN 825 | F 1 "10k" H 8450 5250 50 0000 L CNN 826 | F 2 "Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal" V 8690 5290 50 0001 C CNN 827 | F 3 "~" H 8650 5300 50 0001 C CNN 828 | 1 8650 5300 829 | 1 0 0 -1 830 | $EndComp 831 | Connection ~ 8650 5150 832 | Wire Wire Line 833 | 8650 5150 8350 5150 834 | Wire Wire Line 835 | 8650 5450 8650 5550 836 | Text GLabel 8650 5550 3 50 BiDi ~ 0 837 | 3.3V 838 | Wire Wire Line 839 | 10450 1100 10650 1100 840 | Wire Wire Line 841 | 10450 1400 10650 1400 842 | $Comp 843 | L Device:C C9 844 | U 1 1 617314E4 845 | P 10650 1250 846 | F 0 "C9" H 10500 1350 50 0000 L CNN 847 | F 1 ".01uF" H 10550 1450 50 0000 L CNN 848 | F 2 "Capacitor_THT:C_Disc_D5.0mm_W2.5mm_P5.00mm" H 10688 1100 50 0001 C CNN 849 | F 3 "~" H 10650 1250 50 0001 C CNN 850 | 1 10650 1250 851 | 1 0 0 -1 852 | $EndComp 853 | Wire Wire Line 854 | 8350 1100 8650 1100 855 | Wire Wire Line 856 | 8350 1400 8650 1400 857 | NoConn ~ 4250 5500 858 | NoConn ~ 4250 5600 859 | NoConn ~ 4250 5900 860 | NoConn ~ 4250 6000 861 | NoConn ~ 4250 6100 862 | NoConn ~ 4250 6200 863 | NoConn ~ 4250 6300 864 | NoConn ~ 4250 6500 865 | NoConn ~ 4250 6700 866 | NoConn ~ 4250 6800 867 | NoConn ~ 4250 6900 868 | NoConn ~ 4250 7000 869 | NoConn ~ 5850 7100 870 | NoConn ~ 5850 7000 871 | NoConn ~ 5850 6900 872 | NoConn ~ 1250 5400 873 | NoConn ~ 1250 5500 874 | NoConn ~ 1250 5700 875 | NoConn ~ 1250 6900 876 | NoConn ~ 4250 6400 877 | NoConn ~ 900 1150 878 | NoConn ~ 900 1250 879 | NoConn ~ 900 1350 880 | NoConn ~ 5200 2350 881 | NoConn ~ 8350 2350 882 | NoConn ~ 8250 2350 883 | NoConn ~ 8750 2350 884 | NoConn ~ 9850 2350 885 | Wire Wire Line 886 | 4000 6600 4250 6600 887 | Wire Wire Line 888 | 2100 1100 1900 1100 889 | NoConn ~ 9950 2350 890 | NoConn ~ 10050 2350 891 | NoConn ~ 10150 2350 892 | NoConn ~ 8450 2350 893 | $Comp 894 | L Device:C C4 895 | U 1 1 6172C9EC 896 | P 8650 1250 897 | F 0 "C4" H 8550 1350 50 0000 L CNN 898 | F 1 ".01uF" H 8550 1450 50 0000 L CNN 899 | F 2 "Capacitor_THT:C_Disc_D5.0mm_W2.5mm_P5.00mm" H 8688 1100 50 0001 C CNN 900 | F 3 "~" H 8650 1250 50 0001 C CNN 901 | 1 8650 1250 902 | 1 0 0 -1 903 | $EndComp 904 | Connection ~ 8650 1100 905 | Wire Wire Line 906 | 8650 1100 8950 1100 907 | Connection ~ 8650 1400 908 | Wire Wire Line 909 | 8650 1400 8950 1400 910 | Text GLabel 7550 2450 3 50 Input ~ 0 911 | SCL 912 | Text GLabel 7450 2450 3 50 BiDi ~ 0 913 | SDA 914 | Text GLabel 7350 2450 3 50 Input ~ 0 915 | 3.3V 916 | Text GLabel 7250 2450 3 50 UnSpc ~ 0 917 | GND 918 | Text GLabel 6500 2450 3 50 Input ~ 0 919 | SCL 920 | Text GLabel 6400 2450 3 50 BiDi ~ 0 921 | SDA 922 | Text GLabel 6300 2450 3 50 Input ~ 0 923 | 3.3V 924 | Text GLabel 6200 2450 3 50 UnSpc ~ 0 925 | GND 926 | Wire Wire Line 927 | 4250 7100 4000 7100 928 | Text GLabel 4000 7100 0 50 Input ~ 0 929 | 5V 930 | Wire Wire Line 931 | 1250 7000 1050 7000 932 | Text GLabel 1050 7000 0 50 Input ~ 0 933 | 5V 934 | NoConn ~ 2750 5100 935 | NoConn ~ 4250 5300 936 | Wire Wire Line 937 | 900 1050 1000 1050 938 | Wire Wire Line 939 | 900 1450 1000 1450 940 | Text Notes 8650 4200 0 50 ~ 0 941 | Rotary Encoder 942 | Wire Wire Line 943 | 5850 6800 6150 6800 944 | Text GLabel 6150 6800 2 50 Input ~ 0 945 | GPIO3 946 | Wire Wire Line 947 | 5850 6300 6150 6300 948 | Text GLabel 6150 6300 2 50 Output ~ 0 949 | TXD 950 | Text GLabel 6150 6400 2 50 Input ~ 0 951 | RXD 952 | NoConn ~ 5850 5600 953 | NoConn ~ 5850 5700 954 | $Comp 955 | L Device:Fuse F1 956 | U 1 1 619A9D67 957 | P 1300 1100 958 | F 0 "F1" H 1360 1146 50 0000 L CNN 959 | F 1 "Fuse" H 1360 1055 50 0000 L CNN 960 | F 2 "Fuse:Fuse_Littelfuse-LVR100" V 1230 1100 50 0001 C CNN 961 | F 3 "~" H 1300 1100 50 0001 C CNN 962 | 1 1300 1100 963 | 0 -1 -1 0 964 | $EndComp 965 | Wire Wire Line 966 | 2900 1100 3150 1100 967 | Connection ~ 2900 1400 968 | Wire Wire Line 969 | 2900 1400 3150 1400 970 | Connection ~ 2400 1400 971 | Wire Wire Line 972 | 2400 1400 2900 1400 973 | Wire Wire Line 974 | 2700 1100 2900 1100 975 | Connection ~ 2900 1100 976 | Wire Wire Line 977 | 1900 1400 2400 1400 978 | Connection ~ 1900 1400 979 | Wire Wire Line 980 | 1000 1400 1900 1400 981 | Wire Wire Line 982 | 1000 1100 1150 1100 983 | Wire Wire Line 984 | 1450 1100 1550 1100 985 | Text GLabel 8650 2450 3 50 Input ~ 0 986 | TXD 987 | Text GLabel 8550 2450 3 50 Output ~ 0 988 | RXD 989 | Text Notes 8600 2150 0 50 Italic 0 990 | RX 991 | Text Notes 8500 2150 0 50 Italic 0 992 | TX 993 | Wire Wire Line 994 | 1250 5200 1100 5200 995 | Text GLabel 1100 5200 0 50 Input ~ 0 996 | RESET 997 | Wire Wire Line 998 | 4250 5400 4100 5400 999 | Text GLabel 4100 5400 0 50 Input ~ 0 1000 | RESET 1001 | $Comp 1002 | L Connector:Conn_01x02_Male PP1 1003 | U 1 1 61D23E00 1004 | P 7250 3150 1005 | F 0 "PP1" H 7358 3239 50 0000 C CNN 1006 | F 1 "Conn_01x02_Male" H 7358 3240 50 0001 C CNN 1007 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical" H 7250 3150 50 0001 C CNN 1008 | F 3 "~" H 7250 3150 50 0001 C CNN 1009 | 1 7250 3150 1010 | 1 0 0 -1 1011 | $EndComp 1012 | Wire Wire Line 1013 | 7450 3150 7600 3150 1014 | Wire Wire Line 1015 | 7450 3250 7600 3250 1016 | Text GLabel 7600 3150 2 50 UnSpc ~ 0 1017 | 5V 1018 | Text GLabel 7600 3250 2 50 UnSpc ~ 0 1019 | GND 1020 | $Comp 1021 | L Connector:Conn_01x02_Male PP2 1022 | U 1 1 61D3EB09 1023 | P 7900 3150 1024 | F 0 "PP2" H 8008 3239 50 0000 C CNN 1025 | F 1 "Conn_01x02_Male" H 8008 3240 50 0001 C CNN 1026 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical" H 7900 3150 50 0001 C CNN 1027 | F 3 "~" H 7900 3150 50 0001 C CNN 1028 | 1 7900 3150 1029 | 1 0 0 -1 1030 | $EndComp 1031 | Wire Wire Line 1032 | 8100 3150 8250 3150 1033 | Wire Wire Line 1034 | 8100 3250 8250 3250 1035 | Text GLabel 8250 3150 2 50 UnSpc ~ 0 1036 | 5V 1037 | Text GLabel 8250 3250 2 50 UnSpc ~ 0 1038 | GND 1039 | $Comp 1040 | L Connector:Conn_01x02_Male PP3 1041 | U 1 1 61D444E5 1042 | P 8500 3150 1043 | F 0 "PP3" H 8608 3239 50 0000 C CNN 1044 | F 1 "Conn_01x02_Male" H 8608 3240 50 0001 C CNN 1045 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical" H 8500 3150 50 0001 C CNN 1046 | F 3 "~" H 8500 3150 50 0001 C CNN 1047 | 1 8500 3150 1048 | 1 0 0 -1 1049 | $EndComp 1050 | Wire Wire Line 1051 | 8700 3150 8850 3150 1052 | Wire Wire Line 1053 | 8700 3250 8850 3250 1054 | Text GLabel 8850 3150 2 50 UnSpc ~ 0 1055 | 5V 1056 | Text GLabel 8850 3250 2 50 UnSpc ~ 0 1057 | GND 1058 | $Comp 1059 | L Connector:Conn_01x02_Male PP4 1060 | U 1 1 61D4ACBF 1061 | P 9150 3150 1062 | F 0 "PP4" H 9258 3239 50 0000 C CNN 1063 | F 1 "Conn_01x02_Male" H 9258 3240 50 0001 C CNN 1064 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical" H 9150 3150 50 0001 C CNN 1065 | F 3 "~" H 9150 3150 50 0001 C CNN 1066 | 1 9150 3150 1067 | 1 0 0 -1 1068 | $EndComp 1069 | Wire Wire Line 1070 | 9350 3150 9500 3150 1071 | Wire Wire Line 1072 | 9350 3250 9500 3250 1073 | Text GLabel 9500 3150 2 50 UnSpc ~ 0 1074 | 5V 1075 | Text GLabel 9500 3250 2 50 UnSpc ~ 0 1076 | GND 1077 | Text Notes 8150 2900 0 50 ~ 0 1078 | 5V Power Access 1079 | $Comp 1080 | L Connector:Conn_01x05_Male PP5 1081 | U 1 1 61E25A31 1082 | P 9850 3200 1083 | F 0 "PP5" H 9958 3489 50 0000 C CNN 1084 | F 1 "Conn_01x05_Male" H 9958 3490 50 0001 C CNN 1085 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x05_P2.54mm_Vertical" H 9850 3200 50 0001 C CNN 1086 | F 3 "~" H 9850 3200 50 0001 C CNN 1087 | 1 9850 3200 1088 | 1 0 0 -1 1089 | $EndComp 1090 | Wire Wire Line 1091 | 10050 3000 10250 3000 1092 | Wire Wire Line 1093 | 10050 3400 10250 3400 1094 | Wire Wire Line 1095 | 10050 3200 10250 3200 1096 | Text GLabel 10250 3000 2 50 UnSpc ~ 0 1097 | 5V 1098 | Text GLabel 10250 3200 2 50 UnSpc ~ 0 1099 | GND 1100 | Text GLabel 10250 3400 2 50 UnSpc ~ 0 1101 | 3.3V 1102 | NoConn ~ 10050 3100 1103 | NoConn ~ 10050 3300 1104 | $Comp 1105 | L 2022-01-17_22-48-12:MIC803-29D3VC3-TR U5 1106 | U 1 1 61F011FE 1107 | P 2800 3750 1108 | F 0 "U5" H 4128 3603 60 0000 L CNN 1109 | F 1 "MIC803-29D3VC3-TR" H 4128 3497 60 0000 L CNN 1110 | F 2 "Package_TO_SOT_SMD:SOT-23" H 3600 4090 60 0001 C CNN 1111 | F 3 "" H 2800 3750 60 0000 C CNN 1112 | 1 2800 3750 1113 | 1 0 0 -1 1114 | $EndComp 1115 | Wire Wire Line 1116 | 2800 3750 2400 3750 1117 | Text GLabel 2400 3750 0 50 Output ~ 0 1118 | RESET 1119 | Wire Wire Line 1120 | 2800 4050 2400 4050 1121 | Text GLabel 2400 4050 0 50 UnSpc ~ 0 1122 | 3.3V 1123 | $Comp 1124 | L power:GND #PWR0112 1125 | U 1 1 61F21B78 1126 | P 2800 4250 1127 | F 0 "#PWR0112" H 2800 4000 50 0001 C CNN 1128 | F 1 "GND" H 2805 4077 50 0000 C CNN 1129 | F 2 "" H 2800 4250 50 0001 C CNN 1130 | F 3 "" H 2800 4250 50 0001 C CNN 1131 | 1 2800 4250 1132 | 1 0 0 -1 1133 | $EndComp 1134 | $Comp 1135 | L Device:R_US RR1 1136 | U 1 1 61F29DD3 1137 | P 2800 3900 1138 | F 0 "RR1" H 2868 3900 50 0000 L CNN 1139 | F 1 "R_US" H 2868 3855 50 0001 L CNN 1140 | F 2 "Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal" V 2840 3890 50 0001 C CNN 1141 | F 3 "~" H 2800 3900 50 0001 C CNN 1142 | 1 2800 3900 1143 | 1 0 0 -1 1144 | $EndComp 1145 | Connection ~ 2800 3750 1146 | Connection ~ 2800 4050 1147 | Text Notes 3350 3350 0 50 ~ 0 1148 | Reset Control 1149 | Text Notes 7350 7500 0 50 ~ 0 1150 | Sensor Playground(tm). Copyright (c) 2022 by TDW 1151 | $EndSCHEMATC 1152 | -------------------------------------------------------------------------------- /board/schematic/readme.md: -------------------------------------------------------------------------------- 1 | KiCad schematic for the Sensor Playground 2 | 3 | Copyright Notice 4 | 5 | Copyright (c) 2022, tdwgithub All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 8 | 9 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | 12 | 13 | This folder contains the Sensor Playground schematic in both KiCad and PDF formats. 14 | -------------------------------------------------------------------------------- /bom/bom.txt: -------------------------------------------------------------------------------- 1 | Sensor Playground BOM 2 | -------------------------------------------------------------------------------- /bom/sensorplayground_base.csv: -------------------------------------------------------------------------------- 1 | "Id";"Designator";"Package";"Quantity";"Designation";"Supplier and ref"; 2 | 1;"U3";"XCVR_2821";1;"2821";;; 3 | 2;"U1";"MODULE_ESP32-DEVKITC-32D";1;"ESP32-DEVKITC-32D";;; 4 | 3;"U5";"SOT-23";1;"MIC803-29D3VC3-TR";;; 5 | 4;"RR1";"R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal";1;"R_US";;; 6 | 5;"REF**,REF**,REF**,REF**,REF**,REF**,REF**,REF**,REF**,REF**,REF**,REF**,REF**,REF**,REF**,REF**,REF**";"MountingHole_3.2mm_M3_DIN965";17;"MountingHole_3.2mm_M3_DIN965";;; 7 | 6;"PP5,AF-MTU21D1,SJ2,SJ1,USB_Power1";"PinHeader_1x05_P2.54mm_Vertical";5;"Conn_01x05_Male";;; 8 | 7;"PP4,PP3,PP2,PP1";"PinHeader_1x02_P2.54mm_Vertical";4;"Conn_01x02_Male";;; 9 | 8;"F1";"Fuse_Littelfuse-LVR100";1;"Fuse";;; 10 | 9;"REF**,REF**,REF**,REF**,REF**,REF**,REF**,REF**";"MountingHole_3.2mm_M3_ISO7380_Pad";8;"MountingHole_3.2mm_M3_ISO7380_Pad";;; 11 | 10;"REF**,REF**,REF**,REF**";"MountingHole_2.2mm_M2";4;"MountingHole_2.2mm_M2";;; 12 | 11;"C4,C9,C8,C7,C6,C5,C3";"C_Disc_D5.0mm_W2.5mm_P5.00mm";7;".01uF";;; 13 | 12;"ER3,ER2,ER1,RB2,RB1,R1";"R_Axial_DIN0207_L6.3mm_D2.5mm_P10.16mm_Horizontal";6;"10k";;; 14 | 13;"GPIO_HDR1,SF-SGPx1,SF-SCD4x1";"PinHeader_1x04_P2.54mm_Vertical";3;"Conn_01x04_Male";;; 15 | 14;"ENC1";"RotaryEncoder_Alps_EC11E-Switch_Vertical_H20mm";1;"Rotary_Encoder_Switch";;; 16 | 15;"D1";"D_DO-41_SOD81_P10.16mm_Horizontal";1;"1N4007";;; 17 | 16;"AF-OLED091";"PinHeader_1x08_P2.54mm_Vertical";1;"Conn_01x08_Male";;; 18 | 17;"PB2,PB1";"SW_PUSH_6mm";2;"SW_Push_Dual_x2";;; 19 | 18;"AF-PM25x1";"PinHeader_1x08_P2.54mm_Vertical";1;"NC";;; 20 | 19;"LED1";"LED_D5.0mm";1;"LED";;; 21 | 20;"PJ2";"PinHeader_1x03_P2.54mm_Vertical";1;"Conn_01x03_Male";;; 22 | 21;"C2,C1";"CP_Radial_Tantal_D4.5mm_P5.00mm";2;"33uF";;; 23 | 22;"U2";"TO-220-3_Horizontal_TabDown";1;"LM1117-3.3";;; 24 | -------------------------------------------------------------------------------- /bom/sensorplayground_bom.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdwgithub/sensor-playground/380ca553df2cdc2fc384cbc9a52f78ab11ad0f07/bom/sensorplayground_bom.pdf -------------------------------------------------------------------------------- /cloud/readme.md: -------------------------------------------------------------------------------- 1 | This folder contains hints, notes & other IoT Cloud documents. 2 | -------------------------------------------------------------------------------- /doc/AQ_Levels.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tdwgithub/sensor-playground/380ca553df2cdc2fc384cbc9a52f78ab11ad0f07/doc/AQ_Levels.pdf -------------------------------------------------------------------------------- /doc/air_quality_results.txt: -------------------------------------------------------------------------------- 1 | 2 | Indoor Air Quality: Some Results 3 | 4 | As I've mentioned in other places, the reason I started this project was 5 | due the fact that I was waking up at night feeling slightly ill, light- 6 | headed, etc. I noticed this even more during a period where I was sick; 7 | something just seemed "off". During this time, my wife made the comment 8 | that she had often wondered about the quality of the air in our house. 9 | Given her comment and what I was experiencing, I started into this project. 10 | 11 | What did I find? 12 | 13 | The first iteration (while still in breadboard form) included CO2 & VOC 14 | sensors. I had already connected it to Adafruit.IO and done a simple 15 | dashboard that let me watch the current sensor data, along with some 16 | historical data. When I woke up at night, I would grab my phone and take 17 | a look. I did this over several nights, since I was waking up frequently 18 | anyway. What I found during those nightly looks was that our CO2 levels 19 | inside were nearly 2000ppm. Since I had been reading about indoor air 20 | quality, I knew that was too high, by a good bit in fact. 21 | 22 | Indoor CO2 levels should ideally stay below 1000ppm. While this isn't a 23 | dangerous level, at or above this level, many people start to feel kind 24 | of dozy, sleepy, light-headed, etc. and these effects intensify as the 25 | levels increase. In the recent years there has been an effort in 26 | public schools to monitor CO2 levels and based on the findings, modify 27 | classroom behaviors in order to control the levels. CO2 levels are 28 | very often tied to the efficiency of the building's air-handling 29 | systems (HVAC) and as you would expect, the more people there are in 30 | a room, the more CO2 is being dumped into that room through simple 31 | respiration. If the air-handling system can't exchange stale indoor 32 | air with fresh air quickly enough, CO2 levels can easily rise above 33 | recommended levels. The obvious (but not necessarily easy) thing to do 34 | is to increase the rate of air exchanges in the space you want to control. 35 | In the case of public schools, doing things like opening windows and doors 36 | seems to be the main recommendation. If the problems are significant, 37 | work on the HVAC system can be necessary. 38 | 39 | See: "AQ_Levels.pdf" in this project folder for some interesting data about 40 | CO2 & CO levels and their effects. 41 | 42 | In my home, I first needed to determine what actions I could take that 43 | would materially impact the CO2 levels. During one of my middle-of-the- 44 | night "wake and check the air quality" sessions, it occured to me to try 45 | to turn on the blower on our air-conditioner. Since we have a Nest 46 | Thermostat and it has the ability to turn the blower on separately from 47 | whether or not it is heating or cooling at the time, I just turned it on 48 | and started watching the CO2 levels. The levels when I started were 49 | around 1800ppm and within about a half hour, they were just below 1000ppm. 50 | As I was trolling around the Nest app, I found that it can schedule the 51 | blower to run for some number of minutes within a time of day range. So, 52 | I started to use that, initially setting time of the blower to be running 53 | at 1/2 hour, each hour of the day. That was intentionally aggressive 54 | since I wasn't sure what would keep levels where I wanted them. Since 55 | that time, I've been able to work out that running the blower for 56 | about 15 minutes each hour keeps the worst-case CO2 levels very near 57 | 1000ppm. It peaks just prior the blower staring, then drops below 58 | 1000ppm and cycles like that. 59 | 60 | Why does my house, with only 2 of us living here, allow CO2 levels to 61 | get so high? There's no single answer to that question, though there 62 | are a few things to be aware of: 63 | 64 | - Most homes are not built with specific facilities for controlling a 65 | mix of outdoor & indoor air. This was especiially true when the 66 | energy efficiency of houses wasn't considered an issue. As a result, 67 | outdoor air coming into the house was the result of mechanical air 68 | movement inside the house, drawing outdoor air in throught the 69 | numerous leaks the home presented. Being air-tight just wasn't a 70 | consideration for older homes. The result of this is that a large 71 | percentage of air that circulates in older homes (in particular) has 72 | been recirculated, with a relatively small percentage being sourced 73 | from outdoors. 74 | 75 | - Our house was built around 1941-1942, with no notion of fresh-air 76 | intake in its HVAC design and when we moved in, was quite "leaky". 77 | We had older windows & doors and the insulation wasn't the best. 78 | Over time though, we've replaced all the doors, all the windows and 79 | have added more insulation. The result is that the house has become 80 | more sealed.,,reducing its ability to bring fresh air into the space. 81 | 82 | - We also don't keep houseplants. It turns out that having plants around 83 | the house helps to reduce interior CO2. 84 | 85 | What can you do to improve CO2 levels? 86 | 87 | As I mentioned above, I was able to significantly impact the overall CO2 88 | levels in our home by simply scheduling our HVAC blower to run periodically. 89 | I realize that's not possible for everyone, so here are a few tips I learned 90 | while engaged in this project: 91 | 92 | 1. When you can, open some windows or doors. 93 | 2. Get some houseplants that are known to be good CO2 consumers: 94 | - Bird's Nest Fern 95 | - Snake Plant 96 | - Succulents & Cacti 97 | - Prayer Plants 98 | - See: https://balconygardenweb.com/most-effective-co2-absorbing-houseplants-proven-by-science/ 99 | for some further suggestions. 100 | 3. Turn on your bathroom vent fans. (It works, trust me.) 101 | 4. Add a fresh-air intake to your HVAC system. 102 | 103 | What about other indoor air-quality readings? 104 | 105 | In a typical home, Volatile Organic Compounds (VOC's) aren't usually too much 106 | of a problem. That said, individuals do react differently to different sources 107 | of VOC's. Think of things such as new carpeting. You know that smell that 108 | permeates the house when you install new carpeting? It's the result of VOC's. 109 | New curtains? VOC's. Aerosols? VOC's. The sensors I've used can provide you 110 | with an aggregate concentation level, but not levels of specific compounds. 111 | Nevertheless, you can see the effects of these (and other) sources of VOC's and 112 | using that information, you can take steps to reduce them. 113 | 114 | Another important air-quality metric for many is particulate-matter. These 115 | are pollutants that are suspended in the air and they can cause various 116 | symptoms in different individuals. Pollen, dust, smoke, etc. are all 117 | particulate-matter sources. By tracking the concentrations of these, you can 118 | again work to remove the sources to reduce their presence in your home. If 119 | you use a PM2.5-type sensor, you should be able to see a correlation between 120 | the outdoor air quality and your indoor conditions. I regularly check the 121 | US government's airnow.gov site. If you provide it with your zipcode, it will 122 | produce a display that shows the reported air-quality near your residence. In 123 | my own case, when airnow.gov reports a lower air quality value, my PM2.5 124 | sensor typically reports an increased concentration. 125 | 126 | Note that one of the ways to improve CO2 that I mentioned can also have a 127 | negative impact on PM2.5, as opening windows & doors will tend to bring 128 | more particulate matter into your home. If you build the Sensor Playground 129 | and have both CO2 & PM2.5 sensors on it, you should be able to see how one 130 | impacts the other. Your mileage may vary. 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /doc/construction.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | Construction notes. 4 | 5 | 6 | Copyright Notice 7 | 8 | Copyright (c) 2022, tdwgithub 9 | All rights reserved. 10 | 11 | Redistribution and use in source and binary forms, with or without 12 | modification, are permitted provided that the following conditions are met: 13 | 1. Redistributions of source code must retain the above copyright notice, this 14 | list of conditions and the following disclaimer. 15 | 2. Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | 30 | 31 | Introducion 32 | 33 | The minimum configuration you need for the Sensor Playground includes only: 34 | 35 | 1. The power-supply section. 36 | 2. The processor module section. 37 | 38 | Everything else is your choice. However, this document outlines some of the 39 | steps you'll need to take to pull off a successful build. 40 | 41 | 42 | Power Supply 43 | 44 | For this section, you will need the following components: 45 | 46 | - D1 (1N4007 Diode). 47 | - C1 (33uF Electrolytic Capacitor) 48 | - U2 (LM1117-3.3) Three-terminal regulator 49 | - C2 (33uF Electrolytic Capacitor) 50 | - F1 (LittelFuse LVR100) (Optional) 51 | - R1 (1K-ohm, 14/watt) Resistor. 52 | - LED1 (Standard 5.0mm through-hole) LED 53 | - USB_Power1 (DIYMall Micro-USB Power Adapter: https://www.amazon.com/gp/product/B07B5ZDLJY/ref=ppx_yo_dt_b_asin_title_o05_s00?ie=UTF8&psc=1) 54 | - PJ2 (3-pin male header, optional) 55 | 56 | 1. You'll first need to solder the 5-pin header to the micro-USB power adapter. 57 | This header is included with the package. Make sure to solder the side of 58 | the header that has the *shorter* pins into the power adapter board. 59 | 60 | 2. Insert the longer pins of the newly-built micro-USB power supply adapter into 61 | the Sensor Playground and solder. 62 | 63 | 3. Install D1 and solder. Note the orientation. The PCB shows a '+' & '-' symbol on 64 | either end of the outline. Make sure to install the end of the diode that has the 65 | stripe into the '-' side. 66 | 67 | 4. If you want to use a fuse, insert that into the board where "Fuse" has been 68 | labeled. If you decide you don't need the fuse, you can use a piece of solid-core 69 | wire in its place. Make sure the gauge is reasonable to avoid high resistance 70 | and potential heating/melting. 71 | 72 | 5. Insert C1 & C2 and solder. Note that the footprints on the PCB are marked with 73 | '+' & '-' symbols. When inserting the capacitors, make sure you orient them to 74 | the correct polarity! Solder. 75 | 76 | 6. Insert the LED and solder. Again, this is polarity-dependent. Typical through-hole 77 | LED's have one side that has been flattened. That side is typically the negative. 78 | 79 | 7. Insert and solder R1 (1K Ohm, 1/4 watt through-hole). 80 | 81 | 8. If you want to have a convenient place to verify the 5V, 3.3V & GND of the PCB, you 82 | can install PJ2. This is a simple 3-pin 0.1" make header. Insert the side with the 83 | short pins and solder. 84 | 85 | 86 | Processor 87 | 88 | Generally speaking, in order to maintain as much flexibility as possible, I recommend 89 | that you use female sockets for the processor module(s). This allows you to remove the 90 | modules if needed (and there occasions where that is indeed necessary). Depending on 91 | the level of flexibility you need, you can choose to populate the sockets for both the 92 | ESP32 DevKit modules and the Feather modules, or you can elect to populate only the 93 | sockets for the style you'll use. 94 | 95 | For either the ESP32 DevKit or the Feather modules, you'll need to install: 96 | 97 | - C3 (0.1uF bypass capacitor) 98 | - C4 (0.1uF bypass capacitor) 99 | 100 | For the ESP32 DevKit modules, you will need: 101 | 102 | - (2x) 0.1", 19-pin female socket header (DigiKey PN: ???) 103 | 104 | Insert the sockets into each of the *outer* set of holes for U1 (you'll see labels at the 105 | bottom of the PCB with the text "ESP32" and just above that "Feather"). Solder each socket 106 | header. 107 | 108 | For Feather form-factor modules, you need: 109 | 110 | - (1x) 0.1", 16-pin female socket header (DigiKey PN: ???) 111 | - (1x) 0.1", 12-pin female socket header (DigiKey PN: ???) 112 | 113 | Insert and solder the 12-pin female socket header into the *inner* set of holes on the left 114 | side of the U1 footprint. Next, insert and solder the 16-pin female socket heaer into the 115 | *inner* set of holes on the right side of the U1 footprint. 116 | 117 | As far was what I would describe as "required" components, that's about it. Of course, 118 | if you want to *do* something with the board, you'll probably want to add a bit more to 119 | it... 120 | 121 | Encoder/Buttons 122 | 123 | The rotary encoder and buttons provide a means for user input. With these and a display, you 124 | can createa pretty rich user experience. The encoder itself has a button, so with the two 125 | board-mount buttons, the encoder button and the encoder itself, you have a means to rotate 126 | between options in a hierarchical menu system. 127 | 128 | I'll provide example code to show how this can be accomplished. If you do choose to install 129 | the rotary encoder and buttons, here's what you need: 130 | 131 | - (1x) ENC1 (Alps EC11E) Rotary Encoder 132 | - (2x) PB1 & PB2 (6mm, PCB-mount pushbutton) 133 | - (3x) ER1, ER2 & ER3 (10k ohm, 1/4-watt resistor) 134 | - (2x) RB1 & RB2 (10k ohm, 1/4-watt resistor) 135 | - (1x) 0.1" 4-pin make header. (optional) 136 | 137 | You should probably install the puull-up resistors (ER1, ER2, ER3, RB1 & RB2) first, as these 138 | are the lowest profile components of this section. 139 | 140 | Once you've installed the oull-ups, install the push buttons. These have 2 pins on either side 141 | and it doesn't matter which way you choose to install them. Insert and install both. 142 | 143 | Finally, install the encoder. It has 3 pins on one side and 2 on the other. Orient the encoder 144 | accordingly, then solder it in place. 145 | 146 | If you have interest in accessing the IO lines that the encoder and buttons use, you can install 147 | the 4-pin male header (see the list above). Insert the side with the shorter pins into the PCB 148 | and solder. 149 | 150 | Of course, using the encoder and buttons probably makes most sense when used in conjunction with 151 | a display. That said, you don't have to. As an example, you could choose to only install one 152 | or both of the PCB-mount buttons and use them to control your code *without* the need for a 153 | display. Similarly, you could write your code to make use of the encoder, and not have a 154 | display. It's totally up to you. What was in my mind during the design was the combine the 155 | display, buttons and encoder into a nice little menu-driven system. To each his own... :-) 156 | 157 | Everything Else 158 | 159 | Before we get to the specifics, I want to make a few other points about how you can use things 160 | on this board. 161 | 162 | When it comes to all the connection points for the I2C/SPI/UART and Power, you get to choose 163 | whether you want to use: 164 | 165 | - Solder your stuff directly, using the holes at those footprints. 166 | - Use male, 0.1" header pins 167 | - Use female, 0.1" header sockets. 168 | - Something else (Not sure what, but you can if you'd like) 169 | 170 | Those connection points are yours to use as you like. Same goes for the mounting holes that 171 | are scattered across the top 2/3 of the board. 172 | 173 | I designed the PCB to place the display(s) that I use in the upper-right of the PCB, but once 174 | again, you don't need to use the connector up there (AF-OLED091) for that purpose. 175 | 176 | I'm guessing you've realized by now that you can pretty much do what you choose on this board, 177 | and get something running that you want. Sorry to keep pounding on that point. 178 | 179 | Ok, *now* Everything Else 180 | 181 | From here on, what you choose to populate is even more specific to your application requirements. 182 | In the center of the PCB, you'll find three footprints that provide access to the I2C: 183 | (SF-SCD4x1, SF-SGPx1, & AF-MTU21D1). The center footprint (AF-MTU21D1) is pinned-out slightly 184 | differently than the outer 2. This was to allow me to plug in a few different sensor boards, 185 | whose connectors werent' pinned the same way. However since you can set things up as you need to, 186 | this difference gives you a bit of added flexibilty when configuring your application. In any case, 187 | the I2C lines, power and ground are common to each. 188 | 189 | More toward the top of the PCB, you'll find another set of three 0.1" pin footprints (from left to 190 | right: AF-PM25x1, SJ1 & SJ2). SJ1 & Sj2 provide access to the SPI lines of the processor, with 2 191 | different "enable" lines (no power available here, but just across from them are the I2C connectors 192 | that have 3.3V & GND). AFPM25x1 brings out a UART (Tx/Rx only), along with +5V & GND. 193 | 194 | Inside the large rectangular outline at the top left of the PCB is another 5-pin footprint (PP5). 195 | This simply provides another connection point for the +5V, +3.3V & GND buses. 196 | 197 | 198 | (INCOMPLETE) 199 | -------------------------------------------------------------------------------- /src/arduino/examples/aqi/air_quality.ino: -------------------------------------------------------------------------------- 1 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 2 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 3 | /* 4 | Copyright (c) 2022, tdwgithub 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 29 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 30 | /* 31 | ** Air-Quality Monitor Demonstration Code. 32 | ** Copyright 2021,2022 by TDW 33 | ** 34 | ** Supports various processor modules, with and without 35 | ** WiFi/Adaftuit IO support. 36 | ** 37 | ** Relies heavily on I2C devices/sensors. 38 | ** 39 | ** Example code only. You mileage may vary. 40 | */ 41 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 42 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 43 | 44 | #include "credentials.h" 45 | 46 | /* 47 | * Switches for enabling/disabling various features. 48 | */ 49 | #define USE_ADAFRUIT_IO (1) 50 | #define USE_PM25_SENSOR (1) 51 | #define USE_DISPLAY (1) 52 | #define USE_SERIAL (0) 53 | #define USE_PUSHOVER (0) 54 | 55 | /* 56 | * What processor module are we using? Define 57 | * "CPU" to one of these (or add definitions for 58 | * cpu's you use. 59 | */ 60 | #define ESP8266 (1) 61 | #define ESP32S2 (2) 62 | #define ESP32S1 (3) 63 | #define CORTEX_M0 (4) 64 | #define CORTEX_M1 (5) 65 | #define CORECT_M4 (6) 66 | 67 | // 68 | // Which CPU are we using from the above list? 69 | // 70 | #define CPU (ESP8266) 71 | 72 | // 73 | // If using the CO2 sensor, use altitude adjustment 74 | // 75 | #if (LOCATION==HOME) 76 | #define LOCATION_ALTITUDE ((uint16_t)365) 77 | #elif (LOCATION==MOMDAD) 78 | #define LOCATION_ALTITUDE ((uint16_t)879) 79 | #endif 80 | 81 | // Header Includes 82 | #include "SparkFun_SCD4x_Arduino_Library.h" 83 | #include "SparkFun_SGP40_Arduino_Library.h" 84 | #include "Adafruit_PM25AQI.h" 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | 91 | #if (USE_ADAFRUIT_IO==1) 92 | #include 93 | #include 94 | #include 95 | #include 96 | #include 97 | #include 98 | #include 99 | #include 100 | #include 101 | #else 102 | // 103 | // Spoofs the Adafruit.IO objects for controllers that don't 104 | // have WiFi or another way to reach the Internet. 105 | // 106 | #include "faux_io.h" 107 | #endif 108 | 109 | #if (CPU==ESP8266) 110 | //#include 111 | #endif 112 | 113 | // 114 | // If USE_SERIAL is defined for a processor that only has a single 115 | // UART, we undef the macro, so that that UART can be used for 116 | // things like the PM2.5 sensor. If your module has another UART, 117 | // you can allow it to be used for console serial 118 | // 119 | #if ( (CPU==ESP8266) || (CPU==ESP32S2) ) 120 | #if (USE_PM25_SENSOR==1) 121 | #ifdef USE_SERIAL 122 | #undef USE_SERIAL 123 | #endif 124 | #define USE_SERIAL (0) 125 | #else 126 | #ifdef USE_SERIAL 127 | #undef USE_SERIAL 128 | #endif 129 | #define USE_SERIAL (1) 130 | #endif 131 | #else 132 | // OTHER CPU CONFIGURATION... 133 | #endif 134 | 135 | // 136 | // Display Configurations 137 | // 138 | #define SCREEN_WIDTH 128 // OLED display width, in pixels 139 | #define SCREEN_HEIGHT 64 // OLED display height, in pixels 140 | 141 | // 142 | // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) 143 | // The pins for I2C are defined by the Wire-library. 144 | // On an arduino UNO: A4(SDA), A5(SCL) 145 | // On an arduino MEGA 2560: 20(SDA), 21(SCL) 146 | // On an arduino LEONARDO: 2(SDA), 3(SCL), ... 147 | // 148 | #define OLED_RESET -1 //4 // Reset pin # (or -1 if sharing Arduino reset pin) 149 | #define SCREEN_ADDRESS 0x3D ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 150 | #if (USE_DISPLAY==1) 151 | Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); 152 | #endif 153 | 154 | // 155 | // WiFi Configuration 156 | // 157 | const char *ssid = NET_SSID; 158 | const char *password = NET_PW; 159 | 160 | // 161 | // PUSHOVER DEFINITIONS 162 | // 163 | #if (USE_PUSHOVER==1) 164 | // Token & Key for my account 165 | String apitoken = PUSHOVER_API_TOKEN; 166 | String userkey = PUSHOVER_USER; 167 | 168 | // 169 | // Wifi connection object for PUSHOVER 170 | // 171 | WiFiClient pushoverClient; 172 | const char pushoversite[] = "api.pushover.net"; 173 | char pushoverMsg[128]; 174 | WiFiClient client; 175 | int length; 176 | #endif 177 | 178 | // 179 | // ADAFRUIT.IO Feed Names 180 | // 181 | #define AIO_FEED_CO2 ("CO2") 182 | #define AIO_FEED_VOC ("VOC") 183 | #define AIO_FEED_PM25 ("PM2.5") 184 | #define AIO_FEED_TEMPERATURE ("House Temperature") 185 | #define AIO_FEED_HUMIDITY ("House Humidity") 186 | 187 | // 188 | // Primary Adafruit object 189 | // 190 | AdafruitIO_WiFi io(AIO_USERNAME, AIO_KEY, NET_SSID, NET_PW); 191 | 192 | // 193 | // Create the Adafruit.IO feed objects, using the names defined 194 | // above. 195 | // 196 | AdafruitIO_Feed *temperature = io.feed(AIO_FEED_TEMPERATURE); 197 | AdafruitIO_Feed *humidity = io.feed(AIO_FEED_HUMIDITY); 198 | AdafruitIO_Feed *co2 = io.feed(AIO_FEED_CO2); 199 | AdafruitIO_Feed *voc = io.feed(AIO_FEED_VOC); 200 | AdafruitIO_Feed *pm25 = io.feed(AIO_FEED_PM25); 201 | 202 | // 203 | // Asynchronous timer management struct 204 | // 205 | typedef struct 206 | { 207 | uint8_t running; 208 | uint32_t capturedTicks; 209 | }TM_TIMER_STRUCT; 210 | 211 | // 212 | // TIMER Functions Prototypes 213 | // 214 | static int TIMER_start(TM_TIMER_STRUCT *timer); 215 | static int TIMER_finished(TM_TIMER_STRUCT *timer, unsigned long timeoutMS); 216 | static int TIMER_cancel(TM_TIMER_STRUCT *timer); 217 | static int TIMER_running(TM_TIMER_STRUCT *timer); 218 | static float CtoF(float degreesCelsius); 219 | 220 | // Prototype for pushover notification method 221 | byte pushover(String pushovermessage); 222 | 223 | // Adafruit.io update timer 224 | static TM_TIMER_STRUCT adafruitUpdateTimer; 225 | #define ADAFRUIT_UPDATE_TIMEOUT_MS (10000) 226 | 227 | // CO2 sensor read timer 228 | static TM_TIMER_STRUCT co2UpdateTimer; 229 | #define CO2_UPDATE_TIMEOUT_MS (5000) 230 | 231 | // VOC sensor read timer 232 | static TM_TIMER_STRUCT vocUpdateTimer; 233 | #define VOC_UPDATE_TIMEOUT_MS (1000) 234 | 235 | // OLED display update timer 236 | static TM_TIMER_STRUCT oledUpdateTimer; 237 | static TM_TIMER_STRUCT displayShiftTimer; 238 | #define OLED_DISPLAY_SHIFT_TIMEOUT_MS (5000) 239 | #define OLED_UPDATE_TIMEOUT_MS (250) 240 | #define CLEAR_DISPLAY_TIMEOUT_MS (2500) 241 | 242 | // PM 2.5 sensor update timer 243 | static TM_TIMER_STRUCT pmUpdateTimer; 244 | #define PM_UPDATE_TIMEOUT_MS (1000) 245 | 246 | // Other methods 247 | static void _startPMSensor(void); 248 | static void _startVOCSensor(void); 249 | static void _startCO2Sensor(void); 250 | static void _sendStartupPushover(void); 251 | static void _startAdafruitIO(void); 252 | static void _startDisplay(void); 253 | static void _bannerToDisplay(void); 254 | 255 | // 256 | // Sensor objects 257 | // 258 | SCD4x mySensor; 259 | SGP40 vocSensor; 260 | int displayState; 261 | 262 | #if (USE_PM25_SENSOR==1) 263 | #if ( (CPU==ESP8266) || (CPU==ESP32S2) ) 264 | #define pmSerial Serial 265 | #else 266 | HardwareSerial pmSerial(1); 267 | #endif 268 | Adafruit_PM25AQI aqiSensor = Adafruit_PM25AQI(); 269 | #endif 270 | 271 | // Which IO pins are the two discrete buttons connected? 272 | #if (CPU==ESP32S2) 273 | #define LEFT_BUTTON_PIN (9) 274 | #define RIGHT_BUTTON_PIN (5) 275 | #else 276 | #define LEFT_BUTTON_PIN (0) 277 | #define RIGHT_BUTTON_PIN (2) 278 | #endif 279 | 280 | // Which IO pins are the encoder inputs connected? 281 | #if ( (CPU==ESP8266) || (CPU==ESP32S2) ) 282 | #define ENCODER_UP_PIN (12) 283 | #define ENCODER_DN_PIN (13) 284 | #define ENCODER_BTN_PIN (14) 285 | #else 286 | #define ENCODER_UP_PIN (4) 287 | #define ENCODER_DN_PIN (5) 288 | #define ENCODER_BTN_PIN (15) 289 | #endif 290 | 291 | /* 292 | * USE THESE MACROS TO CONTROL PRINTING TO THE CONSOLE! 293 | */ 294 | #if (USE_SERIAL==1) 295 | #define PRINT Serial.print 296 | #define PRINTLN Serial.println 297 | #define FLUSH Serial.flush 298 | #else 299 | #define PRINT(x) 300 | #define PRINTLN(x) 301 | #define FLUSH() 302 | #endif 303 | 304 | // 305 | // Flag controls whether or not to update the display 306 | // 307 | static int displayOK = 1; 308 | 309 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 310 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 311 | // APPLICATION CODE 312 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 313 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 314 | 315 | /*! \fn 316 | ** 317 | ** FUNCTION: setup 318 | ** 319 | ** DESCRIPTION: Arduino initialization method. Do any 320 | ** set up here that needs to be done prior to 321 | ** entering the main loop function of the application. 322 | ** 323 | ** PARAMETERS: 324 | ** 325 | ** RETURNS: 326 | ** 327 | ** COMMENTS: 328 | ** 329 | */ 330 | void setup() 331 | { 332 | displayState = 0; 333 | 334 | // Get I2C configured 335 | Wire.begin(); 336 | 337 | #if (USE_SERIAL==1) 338 | Serial.begin(9600); 339 | PRINTLN(F("Home Air-Quality Monitor.")); 340 | #endif 341 | 342 | // 343 | // Set up GPIO for buttons and encoder. 344 | // 345 | pinMode(LEFT_BUTTON_PIN, INPUT); 346 | pinMode(RIGHT_BUTTON_PIN, INPUT); 347 | pinMode(ENCODER_UP_PIN, INPUT); 348 | pinMode(ENCODER_DN_PIN, INPUT); 349 | pinMode(ENCODER_BTN_PIN,INPUT); 350 | 351 | // 352 | // Set up the display 353 | // 354 | _startDisplay(); 355 | 356 | // 357 | // Connect to Adafruit.io 358 | // 359 | _startAdafruitIO(); 360 | 361 | // 362 | // Send startup message via PUSHOVER 363 | // 364 | _sendStartupPushover(); 365 | 366 | // 367 | // Set up the second UART & PM 2.5 sensor 368 | // 369 | _startPMSensor(); 370 | 371 | // 372 | // Start up the CO2 sensor 373 | // 374 | _startCO2Sensor(); 375 | 376 | // 377 | // Start up the VOC sensor 378 | // 379 | _startVOCSensor(); 380 | 381 | // 382 | // Final messages (on Serial and display) 383 | // 384 | PRINTLN(""); 385 | PRINTLN("Starting sensor loop..."); 386 | FLUSH(); 387 | 388 | _bannerToDisplay(); 389 | } 390 | 391 | /*! \fn 392 | ** 393 | ** FUNCTION: loop 394 | ** 395 | ** DESCRIPTION: This method is called by the Arduino framework, as the primary application 396 | ** loop. Your goal is to do your work here and exit the function. If you 397 | ** do anything that takes significant time, you'll need to put "yield()" calls 398 | ** to keep the watchdog from resetting the device. 399 | ** 400 | ** PARAMETERS: 401 | ** 402 | ** RETURNS: 403 | ** 404 | ** COMMENTS: 405 | ** 406 | */ 407 | void loop() 408 | { 409 | static float temp; 410 | static float myHumidity; 411 | static uint16_t CO2; 412 | static uint32_t vocIndex; 413 | #if (USE_PM25_SENSOR==1) 414 | static PM25_AQI_Data data; 415 | #endif 416 | 417 | // If LEFT button pressed, display is enabled 418 | if (digitalRead(LEFT_BUTTON_PIN) == LOW) 419 | { 420 | displayOK = 1; 421 | TIMER_start(&oledUpdateTimer); 422 | } 423 | 424 | // If right button is pressed, display is disabled 425 | if (digitalRead(RIGHT_BUTTON_PIN) == LOW) 426 | { 427 | displayOK = 0; 428 | displayState = 0; 429 | display.clearDisplay(); 430 | display.display(); 431 | } 432 | 433 | // Keep Adafruit IO refreshed 434 | io.run(); 435 | 436 | // Time to read the PM2.5 Sensor 437 | if (TIMER_finished(&pmUpdateTimer, PM_UPDATE_TIMEOUT_MS)) 438 | { 439 | #if (USE_PM25_SENSOR==1) 440 | if (aqiSensor.read(&data)) 441 | { 442 | // Print concentations to the console. 443 | PRINTLN("PM2.5 Readings:"); 444 | PRINTLN(); 445 | PRINTLN(F("---------------------------------------")); 446 | PRINTLN(F("Concentration Units (standard)")); 447 | PRINTLN(F("---------------------------------------")); 448 | PRINTLN(F("PM 1.0: ")); PRINT(data.pm10_standard); 449 | PRINT(F("\t\tPM 2.5: ")); PRINT(data.pm25_standard); 450 | PRINT(F("\t\tPM 10: ")); PRINTLN(data.pm100_standard); 451 | PRINTLN(F("Concentration Units (environmental)")); 452 | PRINTLN(F("---------------------------------------")); 453 | PRINT(F("PM 1.0: ")); PRINT(data.pm10_env); 454 | PRINT(F("\t\tPM 2.5: ")); PRINT(data.pm25_env); 455 | PRINT(F("\t\tPM 10: ")); PRINT(data.pm100_env); 456 | PRINTLN(F("---------------------------------------")); 457 | PRINT(F("Particles > 0.3um / 0.1L air:")); PRINTLN(data.particles_03um); 458 | PRINT(F("Particles > 0.5um / 0.1L air:")); PRINTLN(data.particles_05um); 459 | PRINT(F("Particles > 1.0um / 0.1L air:")); PRINTLN(data.particles_10um); 460 | PRINT(F("Particles > 2.5um / 0.1L air:")); PRINTLN(data.particles_25um); 461 | PRINT(F("Particles > 5.0um / 0.1L air:")); PRINTLN(data.particles_50um); 462 | PRINT(F("Particles > 10 um / 0.1L air:")); PRINTLN(data.particles_100um); 463 | PRINTLN(F("---------------------------------------")); 464 | 465 | // Do any math that we want for reporting to Adafruit IO 466 | } 467 | else 468 | { 469 | PRINTLN("Could not read from PM2.5 sensor!"); 470 | } 471 | #endif 472 | 473 | // Restart the read timer 474 | TIMER_start(&pmUpdateTimer); 475 | } 476 | 477 | // Time to read the VOCs? 478 | if (TIMER_finished(&vocUpdateTimer, VOC_UPDATE_TIMEOUT_MS)) 479 | { 480 | vocIndex = vocSensor.getVOCindex(); 481 | TIMER_start(&vocUpdateTimer); 482 | } 483 | 484 | // Time to read the CO2? 485 | if (TIMER_finished(&co2UpdateTimer, CO2_UPDATE_TIMEOUT_MS)) 486 | { 487 | if (mySensor.readMeasurement()) // readMeasurement will return true when fresh data is available 488 | { 489 | CO2 = mySensor.getCO2(); 490 | myHumidity = mySensor.getHumidity(); 491 | temp = mySensor.getTemperature(); 492 | } 493 | 494 | TIMER_start(&co2UpdateTimer); 495 | } 496 | 497 | #if (USE_DISPLAY==1) 498 | 499 | // 500 | // If the display was disabled by the pushbutton, 501 | // we continuously reset the display timer, so that 502 | // it will NOT display anything. 503 | // 504 | if (!displayOK) 505 | TIMER_start(&oledUpdateTimer); 506 | 507 | // 508 | // Time to update the OLED? Since we can't display everyting 509 | // at one time on the OLED, we break it up into a couple of 510 | // "screens", with different data on each, including a blank 511 | // screen to help extend the OLED lifetime a little. 512 | // 513 | if (TIMER_finished(&oledUpdateTimer, OLED_UPDATE_TIMEOUT_MS)) 514 | { 515 | display.setTextWrap(false); 516 | display.clearDisplay(); 517 | 518 | switch (displayState) 519 | { 520 | // 521 | // Display CO2, PM2.5 & VOC's on the OLED 522 | // 523 | case 0: 524 | { 525 | display.setCursor(0,0); 526 | display.print("CO2: "); 527 | display.print(CO2); 528 | 529 | display.setCursor(0,18); 530 | display.print("VOC: "); 531 | display.print(vocIndex); 532 | 533 | #if (USE_PM25_SENSOR==1) 534 | display.setCursor(0,36); 535 | display.print(" PM: "); 536 | display.print(data.pm25_standard); 537 | #endif 538 | 539 | display.display(); 540 | 541 | if (TIMER_finished(&displayShiftTimer, OLED_DISPLAY_SHIFT_TIMEOUT_MS)) 542 | { 543 | displayState = 1; 544 | TIMER_start(&displayShiftTimer); 545 | } 546 | } 547 | break; 548 | 549 | // 550 | // Display TEMP and HUMIDITY on OLED 551 | // 552 | case 1: 553 | { 554 | display.setCursor(0,0); 555 | display.print("TMP: "); 556 | display.print(CtoF(temp)); 557 | 558 | display.setCursor(0,18); 559 | display.print("HUM: "); 560 | display.print(myHumidity); 561 | 562 | display.display(); 563 | 564 | // 565 | // We want to blank the display periodically, since this is an OLED. 566 | // and OLED's don't last forever. So we blank it every sof often for 567 | // a bit, then move back to the top 568 | // 569 | if (TIMER_finished(&displayShiftTimer, OLED_DISPLAY_SHIFT_TIMEOUT_MS)) 570 | { 571 | displayState = 2; 572 | TIMER_start(&displayShiftTimer); 573 | display.clearDisplay(); 574 | display.display(); 575 | } 576 | } 577 | break; 578 | 579 | // 580 | // Blanking period expired? Set up to display 581 | // state zero (CO2, PM2.5 & VOC's) 582 | // 583 | case 2: 584 | { 585 | if (TIMER_finished(&displayShiftTimer, CLEAR_DISPLAY_TIMEOUT_MS)) 586 | { 587 | displayState = 0; 588 | TIMER_start(&displayShiftTimer); 589 | } 590 | } 591 | break; 592 | } 593 | 594 | TIMER_start(&oledUpdateTimer); 595 | } 596 | #endif 597 | 598 | // 599 | // Time to update Cloud? 600 | // 601 | if (TIMER_finished(&adafruitUpdateTimer, ADAFRUIT_UPDATE_TIMEOUT_MS)) 602 | { 603 | char msg[128]; 604 | PRINTLN(); 605 | 606 | PRINT("VOC Index: "); 607 | PRINT(vocIndex); 608 | PRINTLN(); 609 | 610 | // Serial monitor display 611 | PRINT(F("CO2(ppm):")); 612 | PRINT(CO2); 613 | 614 | sprintf(msg, "\tTemperature(F): %f", CtoF(temp)); 615 | PRINT(msg); 616 | 617 | sprintf(msg, "\tHumidity(%RH): %f", myHumidity); 618 | PRINT(msg); 619 | 620 | PRINTLN(""); 621 | PRINTLN("Updating AdafruitIO..."); 622 | 623 | FLUSH(); 624 | 625 | temperature->save(CtoF(temp)); 626 | humidity->save(myHumidity); 627 | 628 | #if (USE_PM25_SENSOR==1) 629 | pm25->save(data.pm25_standard); 630 | #endif 631 | co2->save(CO2); 632 | voc->save(vocIndex); 633 | 634 | TIMER_start(&adafruitUpdateTimer); 635 | } 636 | 637 | yield(); 638 | } 639 | 640 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 641 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 642 | // STARTUP/INITIALIZATION FUNCTIONS 643 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 644 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 645 | 646 | /*! \fn 647 | ** 648 | ** FUNCTION: _startPMSensor 649 | ** 650 | ** DESCRIPTION: Get the PM2.5 sensor set up and ready to use. 651 | ** 652 | ** PARAMETERS: 653 | ** 654 | ** RETURNS: 655 | ** 656 | ** COMMENTS: 657 | ** 658 | */ 659 | static void _startPMSensor(void) 660 | { 661 | #if (USE_PM25_SENSOR==1) 662 | #if ( (CPU==ESP8266) || (CPU==ESP32S2) ) 663 | pmSerial.begin(9600); 664 | #else 665 | pmSerial.begin(9600, SERIAL_8N1, 16, 17); 666 | #endif 667 | delay(1000); 668 | 669 | if (!aqiSensor.begin_UART(&pmSerial)) 670 | { 671 | PRINTLN("Could not find PM 2.5 sensor!"); 672 | while (1) {yield(); delay(1000);} 673 | } 674 | #endif 675 | TIMER_start(&pmUpdateTimer); 676 | } 677 | 678 | /*! \fn 679 | ** 680 | ** FUNCTION: _startCO2Sensor 681 | ** 682 | ** DESCRIPTION: Configure the code to use the CO2 sensor. 683 | ** 684 | ** PARAMETERS: 685 | ** 686 | ** RETURNS: 687 | ** 688 | ** COMMENTS: 689 | ** 690 | */ 691 | static void _startCO2Sensor(void) 692 | { 693 | // 694 | // ".begin: will start periodic measurements for us (see the later examples for details on how to override this) 695 | // 696 | if (mySensor.begin() == false) 697 | { 698 | PRINTLN(F("Sensor not detected. Please check wiring. Freezing...")); 699 | while (1){PRINTLN("CO2 sensor startup failure!"); delay(1000); yield();} 700 | } 701 | else 702 | { 703 | uint16_t altitude = LOCATION_ALTITUDE; 704 | 705 | //mySensor.enableDebugging(); // Uncomment this line to get helpful debug messages on Serial 706 | 707 | mySensor.stopPeriodicMeasurement(); 708 | delay(500); 709 | PRINTLN(""); 710 | PRINT("Verifying sensor altitude..."); 711 | if (mySensor.getSensorAltitude() != altitude) 712 | { 713 | delay(500); 714 | PRINTLN(""); 715 | PRINT("Setting sensor altitude to: " + String(altitude)+"..."); 716 | if (mySensor.setSensorAltitude(altitude)==true) 717 | { 718 | PRINT("Success! Saving settings..."); 719 | delay(500); 720 | if (mySensor.persistSettings()) 721 | { 722 | PRINT("Success!"); 723 | PRINTLN(""); 724 | PRINT("Verifying settings..."); 725 | delay(500); 726 | 727 | if (mySensor.getSensorAltitude() == altitude) 728 | { 729 | delay(500); 730 | PRINT("Success!"); 731 | mySensor.startPeriodicMeasurement(); 732 | } 733 | } 734 | else 735 | PRINT("Failed!"); 736 | } 737 | else 738 | PRINT("Failed!"); 739 | } 740 | else 741 | { 742 | PRINT("Altitude is correct at: " + String(LOCATION_ALTITUDE)); 743 | mySensor.startPeriodicMeasurement(); 744 | } 745 | } 746 | 747 | TIMER_start(&co2UpdateTimer); 748 | } 749 | 750 | /*! \fn 751 | ** 752 | ** FUNCTION: _startVOCSensor 753 | ** 754 | ** DESCRIPTION: Configure the VOC sensor. 755 | ** 756 | ** PARAMETERS: 757 | ** 758 | ** RETURNS: 759 | ** 760 | ** COMMENTS: 761 | ** 762 | */ 763 | static void _startVOCSensor(void) 764 | { 765 | if (vocSensor.begin()==false) 766 | { 767 | PRINTLN(""); 768 | PRINTLN("VOC sensor startup failure!"); 769 | while (1){yield();delay(1000);}; 770 | } 771 | 772 | TIMER_start(&vocUpdateTimer); 773 | delay(1100); 774 | } 775 | 776 | /*! \fn 777 | ** 778 | ** FUNCTION: _sendStartupPushover 779 | ** 780 | ** DESCRIPTION: Sends a start-up message to Pushover 781 | ** 782 | ** PARAMETERS: 783 | ** 784 | ** RETURNS: 785 | ** 786 | ** COMMENTS: 787 | ** 788 | */ 789 | static void _sendStartupPushover(void) 790 | { 791 | #if (USE_PUSHOVER==1) 792 | PRINTLN(""); 793 | PRINT("Sending startup notification..."); 794 | pushover("Air-quality monitor startup.\r\nCopyright (c) 2021 TDW"); 795 | PRINT("Complete!"); 796 | PRINTLN(""); 797 | delay(1000); 798 | #endif 799 | } 800 | 801 | /*! \fn 802 | ** 803 | ** FUNCTION: _startAdafruitIO 804 | ** 805 | ** DESCRIPTION: Connect to Adafruit.IO 806 | ** 807 | ** PARAMETERS: 808 | ** 809 | ** RETURNS: 810 | ** 811 | ** COMMENTS: 812 | ** 813 | */ 814 | static void _startAdafruitIO(void) 815 | { 816 | #if (USE_ADAFRUIT_IO==1) 817 | char msg[64]; 818 | 819 | sprintf(msg, "%s...", ssid); 820 | display.setTextSize(1); // Normal 1:1 pixel scale 821 | display.setTextColor(SSD1306_WHITE); // Draw white text 822 | display.clearDisplay(); 823 | display.setCursor(0,0); 824 | display.print(msg); 825 | display.display(); 826 | 827 | #if ( (CPU==ESP8266) || (CPU==ESP32S2) ) 828 | PRINTLN(""); 829 | PRINT("Connecting to WiFi"); 830 | 831 | WiFi.begin(ssid, password); 832 | while (WiFi.status() != WL_CONNECTED) 833 | { 834 | delay(500); 835 | PRINT("!"); 836 | yield(); 837 | } 838 | #endif 839 | 840 | PRINTLN(""); 841 | PRINT("Connecting to Adafruit.IO"); 842 | io.connect(); 843 | while(io.status() < AIO_CONNECTED) 844 | { 845 | PRINT("."); 846 | delay(500); 847 | yield(); 848 | } 849 | PRINT("Connected!"); 850 | PRINTLN(""); 851 | delay(2000); 852 | yield(); 853 | #endif 854 | // Kick off the Adafruit update timer 855 | TIMER_start(&adafruitUpdateTimer); 856 | } 857 | 858 | /*! \fn 859 | ** 860 | ** FUNCTION: _startDisplay 861 | ** 862 | ** DESCRIPTION: Configure the display 863 | ** 864 | ** PARAMETERS: 865 | ** 866 | ** RETURNS: 867 | ** 868 | ** COMMENTS: 869 | ** 870 | */ 871 | static void _startDisplay(void) 872 | { 873 | #if (USE_DISPLAY==1) 874 | // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally 875 | if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) 876 | { 877 | PRINTLN(F("SSD1306 allocation failed")); 878 | for(;;); // Don't proceed, loop forever 879 | } 880 | 881 | display.setTextWrap(false); 882 | 883 | // Show initial display buffer contents on the screen -- 884 | // the library initializes this with an Adafruit splash screen. 885 | display.display(); 886 | delay(2000); // Pause for 2 seconds 887 | display.clearDisplay(); 888 | display.display(); 889 | 890 | display.setTextSize(1); // Normal 1:1 pixel scale 891 | display.setTextColor(SSD1306_WHITE); // Draw white text 892 | display.setCursor(0,0); 893 | display.print("AQ monitor startup..."); 894 | display.display(); 895 | 896 | TIMER_start(&displayShiftTimer); 897 | TIMER_start(&oledUpdateTimer); 898 | delay(2000); 899 | #endif 900 | } 901 | 902 | /*! \fn 903 | ** 904 | ** FUNCTION: _bannerToDisplay 905 | ** 906 | ** DESCRIPTION: Displays a simple start-up banner on the display. 907 | ** 908 | ** PARAMETERS: 909 | ** 910 | ** RETURNS: 911 | ** 912 | ** COMMENTS: 913 | ** 914 | */ 915 | static void _bannerToDisplay(void) 916 | { 917 | #if (USE_DISPLAY==1) 918 | display.clearDisplay(); 919 | display.setCursor(0,0); 920 | display.print("Startup OK."); 921 | display.display(); 922 | delay(1000); 923 | display.setTextSize(2); 924 | #endif 925 | } 926 | 927 | /*! \fn 928 | ** 929 | ** FUNCTION: pushover 930 | ** 931 | ** DESCRIPTION: Given a string to send, this sends it to Pushover for notification. 932 | ** 933 | ** PARAMETERS: 934 | ** 935 | ** RETURNS: 936 | ** 937 | ** COMMENTS: 938 | ** 939 | */ 940 | byte pushover(String pushovermessage) 941 | { 942 | #if (USE_PUSHOVER==1) 943 | length = 81 + pushovermessage.length(); 944 | 945 | PRINTLN(""); 946 | PRINT("Connecting to PUSHOVER..."); 947 | if (pushoverClient.connect(pushoversite,80)) 948 | { 949 | PRINT("Success!"); 950 | 951 | pushoverClient.println("POST /1/messages.json HTTP/1.1"); 952 | pushoverClient.println("Host: api.pushover.net"); 953 | pushoverClient.println("Connection: close\r\nContent-Type: application/x-www-form-urlencoded"); 954 | pushoverClient.print("Content-Length: "); 955 | pushoverClient.print(length); 956 | pushoverClient.println("\r\n");; 957 | pushoverClient.print("token="); 958 | pushoverClient.print(apitoken); 959 | pushoverClient.print("&user="); 960 | pushoverClient.print(userkey); 961 | pushoverClient.print("&message="); 962 | pushoverClient.print(pushovermessage); 963 | 964 | if (pushoverClient.connected()) 965 | { 966 | // Flush the response 967 | while (pushoverClient.available()) 968 | { 969 | char ch = pushoverClient.read(); 970 | yield(); 971 | } 972 | 973 | yield(); 974 | } 975 | 976 | PRINTLN(""); 977 | PRINTLN("Pushover message sent!"); 978 | 979 | if (pushoverClient.connected()) 980 | pushoverClient.stop(); 981 | } 982 | else 983 | { 984 | if (pushoverClient.connected()) 985 | pushoverClient.stop(); 986 | PRINT("Failed!"); 987 | } 988 | 989 | yield(); 990 | #endif 991 | 992 | return (0x00); 993 | } 994 | 995 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 996 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 997 | // UTILITY FUNCTIONS 998 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 999 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1000 | 1001 | /*! \fn 1002 | ** 1003 | ** FUNCTION: TIMER_start 1004 | ** 1005 | ** DESCRIPTION: Captures the system ticks when called. Used 1006 | ** to compare later to see if the timer has expired. 1007 | ** 1008 | ** PARAMETERS: 1009 | ** 1010 | ** RETURNS: 1011 | ** 1012 | ** COMMENTS: 1013 | ** 1014 | */ 1015 | static int TIMER_start(TM_TIMER_STRUCT *timer) 1016 | { 1017 | if (timer != NULL) 1018 | { 1019 | timer->running = 1; 1020 | timer->capturedTicks = millis(); 1021 | return (1); 1022 | } 1023 | 1024 | return (0); 1025 | } 1026 | 1027 | /*! \fn 1028 | ** 1029 | ** FUNCTION: TIMER_finished 1030 | ** 1031 | ** DESCRIPTION: When called, compares the captured system ticks 1032 | ** plus the expected period, with the current system 1033 | ** ticks. If exceeded, time has expired. 1034 | ** 1035 | ** PARAMETERS: 1036 | ** 1037 | ** RETURNS: 1038 | ** 1039 | ** COMMENTS: 1040 | ** 1041 | */ 1042 | static int TIMER_finished(TM_TIMER_STRUCT *timer, unsigned long timeoutMS) 1043 | { 1044 | if ( (timer) && (timeoutMS) && (timer->running) ) 1045 | { 1046 | uint32_t current = millis(); 1047 | 1048 | if ( (timer->running) && ((current - timer->capturedTicks) >= timeoutMS) ) 1049 | { 1050 | timer->running = 0; 1051 | return (1); 1052 | } 1053 | } 1054 | 1055 | return (0); 1056 | } 1057 | 1058 | /*! \fn 1059 | ** 1060 | ** FUNCTION: TIMER_cancel 1061 | ** 1062 | ** DESCRIPTION: Clears the "timer running" flag of the given timer 1063 | ** object. 1064 | ** 1065 | ** PARAMETERS: 1066 | ** 1067 | ** RETURNS: 1068 | ** 1069 | ** COMMENTS: 1070 | ** 1071 | */ 1072 | static int TIMER_cancel(TM_TIMER_STRUCT *timer) 1073 | { 1074 | if (timer) 1075 | { 1076 | timer->running = 0; 1077 | return (1); 1078 | } 1079 | 1080 | return (0); 1081 | } 1082 | 1083 | /*! \fn 1084 | ** 1085 | ** FUNCTION: TIMER_running 1086 | ** 1087 | ** DESCRIPTION: Checks the "timer running" flag so callers can decide if 1088 | ** it thinks its running. 1089 | ** 1090 | ** PARAMETERS: 1091 | ** 1092 | ** RETURNS: 1093 | ** 1094 | ** COMMENTS: 1095 | ** 1096 | */ 1097 | static int TIMER_running(TM_TIMER_STRUCT *timer) 1098 | { 1099 | return (int)(timer->running != 0); 1100 | } 1101 | 1102 | /*! \fn 1103 | ** 1104 | ** FUNCTION: CtoF 1105 | ** 1106 | ** DESCRIPTION: Utility to convert from degrees C to degrees F. 1107 | ** 1108 | ** PARAMETERS: 1109 | ** 1110 | ** RETURNS: 1111 | ** 1112 | ** COMMENTS: 1113 | ** 1114 | */ 1115 | static float CtoF(float degreesCelsius) 1116 | { 1117 | return ( (degreesCelsius*1.8) + 32.0 ); 1118 | } 1119 | -------------------------------------------------------------------------------- /src/arduino/examples/aqi/credentials.h: -------------------------------------------------------------------------------- 1 | #ifndef __CREDENTIALS_H_INCLUDED 2 | #define __CREDENTIALS_H_INCLUDED 3 | 4 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 6 | // WHERE IS YOUR BOARD LOCATED? 7 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 8 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 9 | #define HOME (1) 10 | #define MOMDAD (2) 11 | #define LOCATION (HOME) 12 | 13 | 14 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 15 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 | // WiFi Credentials 17 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 18 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 19 | #if (LOCATION==HOME) 20 | #define NET_SSID ("YOUR WIFI SSID") 21 | #define NET_PW ("YOUR WIFI PASSWORD") 22 | #elif (LOCATION==MOMDAD) 23 | #define NET_SSID ("YOUR WIFI SSID") 24 | #define NET_PW ("YOUR WIFIPASSWORD") 25 | #endif 26 | 27 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 28 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 29 | // If you use PUSHOVER for push notifications, edit your credentials 30 | // here. 31 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 32 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 33 | #define PUSHOVER_USER ("YOUR PUSHOVER USER KEY") 34 | #define PUSHOVER_API_TOKEN ("YOUR PUSHOVER API TOKEN") 35 | 36 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 37 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 38 | // ADAFRUIT.IO CREDENTIALS 39 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 40 | //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 41 | #define AIO_USERNAME ("YOUR ADAFRUIT.IO USER NAME") 42 | #define AIO_KEY ("YOUR ADAFRUIT.IO API KEY") 43 | 44 | 45 | #endif // __CREDENTIALS_H_INCLUDED 46 | -------------------------------------------------------------------------------- /src/arduino/examples/aqi/readme.md: -------------------------------------------------------------------------------- 1 | Indoor Air-Quality Example Sketch 2 | 3 | Copyright (c) 2022, tdwgithub 4 | All rights reserved. 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 13 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 16 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 19 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 20 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 21 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | 23 | Introduction 24 | 25 | This is the sketch that was the basis of the whole project. It demonstrates how 26 | to use the sensors that I chose and implements a decent IoT-connected indoor 27 | air-quality monitoring system. 28 | 29 | I've made use of some ugly compile-time switches to allow this to be used on 30 | a number of different processor modules, and I'll describe some of that in 31 | detail here. I'll discuss how this all fits together along with some 32 | suggestions for what you can do next. 33 | 34 | Sketch Details 35 | 36 | (INCOMPLETE) 37 | -------------------------------------------------------------------------------- /src/arduino/examples/readme.md: -------------------------------------------------------------------------------- 1 | A collection of Arduino-environment examples. 2 | 3 | Copyright Notice 4 | 5 | Copyright (c) 2022, tdwgithub 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/arduino/libraries/faux_io/faux_io.cpp: -------------------------------------------------------------------------------- 1 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 2 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 3 | /* 4 | Copyright (c) 2022, tdwgithub 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 29 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 30 | /* 31 | ** faux_io.cpp - Non-functional emulation of Adafruit.io client library. 32 | ** Copyright 2021, 2022 by TDW 33 | ** 34 | ** This provides a non-functional implementation of the Adafruit.IO client object. It allows 35 | ** the example air-quality software to act as though it has a connection to AIO, for devices 36 | ** that don't have a network connection, or for cases where there is no AIO account. 37 | */ 38 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 39 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 40 | #include "Arduino.h" 41 | #include "faux_io.h" 42 | 43 | 44 | /*! \fn 45 | ** 46 | ** FUNCTION: AdafruitIO_Feed 47 | ** 48 | ** DESCRIPTION: Feed object dummy implementation. 49 | ** 50 | ** PARAMETERS: 51 | ** 52 | ** RETURNS: 53 | ** 54 | ** COMMENTS: 55 | ** 56 | */ 57 | AdafruitIO_Feed::AdafruitIO_Feed(String feedName) 58 | { 59 | this->feedName = feedName; 60 | } 61 | 62 | /*! \fn 63 | ** 64 | ** FUNCTION: saveParent 65 | ** 66 | ** DESCRIPTION: 67 | ** 68 | ** PARAMETERS: 69 | ** 70 | ** RETURNS: 71 | ** 72 | ** COMMENTS: 73 | ** 74 | */ 75 | void AdafruitIO_Feed::saveParent(void *parent) 76 | { 77 | return; 78 | } 79 | 80 | /*! \fn 81 | ** 82 | ** FUNCTION: save 83 | ** 84 | ** DESCRIPTION: 85 | ** 86 | ** PARAMETERS: 87 | ** 88 | ** RETURNS: 89 | ** 90 | ** COMMENTS: 91 | ** 92 | */ 93 | void AdafruitIO_Feed::save(float value) 94 | { 95 | return; 96 | } 97 | 98 | /*! \fn 99 | ** 100 | ** FUNCTION: save 101 | ** 102 | ** DESCRIPTION: 103 | ** 104 | ** PARAMETERS: 105 | ** 106 | ** RETURNS: 107 | ** 108 | ** COMMENTS: 109 | ** 110 | */ 111 | void AdafruitIO_Feed::save(int value) 112 | { 113 | return; 114 | } 115 | 116 | /*! \fn 117 | ** 118 | ** FUNCTION: save 119 | ** 120 | ** DESCRIPTION: 121 | ** 122 | ** PARAMETERS: 123 | ** 124 | ** RETURNS: 125 | ** 126 | ** COMMENTS: 127 | ** 128 | */ 129 | void AdafruitIO_Feed::save(byte value) 130 | { 131 | return; 132 | } 133 | 134 | /*! \fn 135 | ** 136 | ** FUNCTION: save 137 | ** 138 | ** DESCRIPTION: 139 | ** 140 | ** PARAMETERS: 141 | ** 142 | ** RETURNS: 143 | ** 144 | ** COMMENTS: 145 | ** 146 | */ 147 | void AdafruitIO_Feed::save(uint16_t value) 148 | { 149 | return; 150 | } 151 | 152 | /*! \fn 153 | ** 154 | ** FUNCTION: 155 | ** 156 | ** DESCRIPTION: 157 | ** 158 | ** PARAMETERS: 159 | ** 160 | ** RETURNS: 161 | ** 162 | ** COMMENTS: 163 | ** 164 | */ 165 | void AdafruitIO_Feed::save(uint32_t value) 166 | { 167 | return; 168 | } 169 | 170 | 171 | //############################################################### 172 | 173 | /*! \fn 174 | ** 175 | ** FUNCTION: AdafruitIO_WiFi 176 | ** 177 | ** DESCRIPTION: Dummy AIO object constuctor. 178 | ** 179 | ** PARAMETERS: 180 | ** 181 | ** RETURNS: 182 | ** 183 | ** COMMENTS: 184 | ** 185 | */ 186 | AdafruitIO_WiFi::AdafruitIO_WiFi(String userName, String io_key, String ssid, String pw) 187 | { 188 | return; 189 | } 190 | 191 | /*! \fn 192 | ** 193 | ** FUNCTION: feed 194 | ** 195 | ** DESCRIPTION: Dummy feed method 196 | ** 197 | ** PARAMETERS: 198 | ** 199 | ** RETURNS: 200 | ** 201 | ** COMMENTS: 202 | ** 203 | */ 204 | AdafruitIO_Feed *AdafruitIO_WiFi::feed(String feedName) 205 | { 206 | AdafruitIO_Feed *ret = new AdafruitIO_Feed(feedName); 207 | if (ret) 208 | ret->saveParent((void *)this); 209 | 210 | return (ret); 211 | } 212 | 213 | /*! \fn 214 | ** 215 | ** FUNCTION: run 216 | ** 217 | ** DESCRIPTION: Dummy AIO run method 218 | ** 219 | ** PARAMETERS: 220 | ** 221 | ** RETURNS: 222 | ** 223 | ** COMMENTS: 224 | ** 225 | */ 226 | void AdafruitIO_WiFi::run(void) 227 | { 228 | return; 229 | } 230 | 231 | /*! \fn 232 | ** 233 | ** FUNCTION: connect 234 | ** 235 | ** DESCRIPTION: Dummy ADIO connect method 236 | ** 237 | ** PARAMETERS: 238 | ** 239 | ** RETURNS: 240 | ** 241 | ** COMMENTS: 242 | ** 243 | */ 244 | int AdafruitIO_WiFi::connect(void) 245 | { 246 | return (1); 247 | } 248 | 249 | /*! \fn 250 | ** 251 | ** FUNCTION: status 252 | ** 253 | ** DESCRIPTION: Dummy AIO status method 254 | ** 255 | ** PARAMETERS: 256 | ** 257 | ** RETURNS: 258 | ** 259 | ** COMMENTS: 260 | ** 261 | */ 262 | ADAFRUIT_CONNECT_STATUS AdafruitIO_WiFi::status(void) 263 | { 264 | return AIO_CONNECTED; 265 | } 266 | 267 | //############################################################### 268 | -------------------------------------------------------------------------------- /src/arduino/libraries/faux_io/faux_io.h: -------------------------------------------------------------------------------- 1 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 2 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 3 | /* 4 | Copyright (c) 2022, tdwgithub 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 29 | //$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 30 | #ifndef __FAUX_IO_H_INCLUDED 31 | #define __FAUX_IO_H_INCLUDED 32 | 33 | #include "Arduino.h" 34 | 35 | class AdafruitIO_Feed 36 | { 37 | public: 38 | AdafruitIO_Feed(String feedName); 39 | void saveParent(void *parent); 40 | void save(float value); 41 | void save(int value); 42 | void save(byte value); 43 | void save(uint16_t value); 44 | void save(uint32_t value); 45 | 46 | private: 47 | void *parent; 48 | String feedName; 49 | }; 50 | 51 | typedef enum 52 | { 53 | AIO_CONNECTED, 54 | AIO_NOT_CONNECTED, 55 | AIO_CONNECTION_ERROR 56 | }ADAFRUIT_CONNECT_STATUS; 57 | 58 | class AdafruitIO_WiFi 59 | { 60 | public: 61 | AdafruitIO_WiFi(String userName, String io_key, String ssid, String pw); 62 | AdafruitIO_Feed *feed(String feedName); 63 | void run(void); 64 | int connect(void); 65 | ADAFRUIT_CONNECT_STATUS status(void); 66 | }; 67 | 68 | 69 | 70 | #endif // FAUX_IO_H_INCLUDED 71 | -------------------------------------------------------------------------------- /src/arduino/libraries/faux_io/readme.md: -------------------------------------------------------------------------------- 1 | 2 | Non-functional "dummy" Adafruit.io library 3 | 4 | Copyright Notice 5 | 6 | Copyright (c) 2022, tdwgithub 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /src/arduino/libraries/readme.md: -------------------------------------------------------------------------------- 1 | This folder contains various libraries that I've written to support the Arduino examples. 2 | 3 | Copyright Notice 4 | 5 | Copyright (c) 2022, tdwgithub 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/arduino/libraries/tdw_timer/tdw_timer.cpp: -------------------------------------------------------------------------------- 1 | /*! \file 2 | ** 3 | ** MODULE: tdw_timer.cpp 4 | ** 5 | ** DESCRIPTION: Arduino library for asynchronous timers. 6 | ** 7 | ** Copyright 2021-2022 by Forty|W, Inc. All rights reserved. 8 | ** 9 | ** REVISION HISTORY: 10 | ** 11 | */ 12 | 13 | #include 14 | #include 15 | #include "tdw_timer.h" 16 | 17 | /*! \fn 18 | ** 19 | ** FUNCTION: No-parameter constructor. 20 | ** 21 | ** DESCRIPTION: Assumes the user will supply timeout 22 | ** to the "finished()" method. 23 | ** 24 | ** PARAMETERS: 25 | ** 26 | ** RETURNS: 27 | ** 28 | ** COMMENTS: 29 | ** 30 | */ 31 | AsyncTimer::AsyncTimer() 32 | { 33 | is_running = false; 34 | timeoutMS = 0; 35 | capture = 0; 36 | } 37 | 38 | /*! \fn 39 | ** 40 | ** FUNCTION: No-parameter destructor. 41 | ** 42 | ** DESCRIPTION: 43 | ** 44 | ** PARAMETERS: 45 | ** 46 | ** RETURNS: 47 | ** 48 | ** COMMENTS: 49 | ** 50 | */ 51 | AsyncTimer::~AsyncTimer() 52 | { 53 | is_running = false; 54 | timeoutMS = 0; 55 | capture = 0; 56 | } 57 | 58 | /*! \fn 59 | ** 60 | ** FUNCTION: Timeout-parameter provided constructor. 61 | ** 62 | ** DESCRIPTION: 63 | ** 64 | ** PARAMETERS: 65 | ** 66 | ** RETURNS: 67 | ** 68 | ** COMMENTS: 69 | ** 70 | */ 71 | AsyncTimer::AsyncTimer(uint32_t _timeoutMS) 72 | { 73 | capture = millis(); 74 | is_running = true; 75 | this->timeoutMS = _timeoutMS; 76 | } 77 | 78 | /*! \fn 79 | ** 80 | ** FUNCTION: start 81 | ** 82 | ** DESCRIPTION: No arguments. Implies that the "finished()" method will be 83 | ** called with the timeout. 84 | ** 85 | ** PARAMETERS: 86 | ** 87 | ** RETURNS: 88 | ** 89 | ** COMMENTS: 90 | ** 91 | */ 92 | void AsyncTimer::start() 93 | { 94 | capture = millis(); 95 | is_running = true; 96 | timeoutMS = 0; 97 | } 98 | 99 | /*! \fn 100 | ** 101 | ** FUNCTION: start 102 | ** 103 | ** DESCRIPTION: A timeout value must be provided. Implies the "finished()" method 104 | ** will be called without any argument. 105 | ** 106 | ** PARAMETERS: 107 | ** 108 | ** RETURNS: 109 | ** 110 | ** COMMENTS: 111 | ** 112 | */ 113 | void AsyncTimer::start(uint32_t _timeoutMS) 114 | { 115 | this->timeoutMS = _timeoutMS; 116 | this->capture = millis(); 117 | this->is_running = true; 118 | } 119 | 120 | /*! \fn 121 | ** 122 | ** FUNCTION: finished 123 | ** 124 | ** DESCRIPTION: Assumes that "start()" was called with a timeout value. 125 | ** 126 | ** PARAMETERS: 127 | ** 128 | ** RETURNS: 129 | ** 130 | ** COMMENTS: 131 | ** 132 | */ 133 | bool AsyncTimer::finished() 134 | { 135 | if (this->is_running) 136 | { 137 | if (this->timeoutMS > 0) 138 | { 139 | return this->_finished(this->timeoutMS); 140 | } 141 | } 142 | 143 | return (true); 144 | } 145 | 146 | /*! \fn 147 | ** 148 | ** FUNCTION: finished 149 | ** 150 | ** DESCRIPTION: Checks to see if the timer has expired, using the argument. 151 | ** 152 | ** PARAMETERS: 153 | ** 154 | ** RETURNS: 155 | ** 156 | ** COMMENTS: 157 | ** 158 | */ 159 | bool AsyncTimer::finished(uint32_t _timeoutMS) 160 | { 161 | if (_timeoutMS > 0) 162 | { 163 | if (this->is_running) 164 | { 165 | return (this->_finished(_timeoutMS)); 166 | } 167 | } 168 | 169 | return (false); 170 | } 171 | 172 | /*! \fn 173 | ** 174 | ** FUNCTION: running 175 | ** 176 | ** DESCRIPTION: Returns whether or not the timer is running. 177 | ** 178 | ** PARAMETERS: 179 | ** 180 | ** RETURNS: 181 | ** 182 | ** COMMENTS: 183 | ** 184 | */ 185 | bool AsyncTimer::running() 186 | { 187 | return (this->is_running); 188 | } 189 | 190 | /*! \fn 191 | ** 192 | ** FUNCTION: cancel 193 | ** 194 | ** DESCRIPTION: Sets the object to be NOT running. 195 | ** 196 | ** PARAMETERS: 197 | ** 198 | ** RETURNS: 199 | ** 200 | ** COMMENTS: 201 | ** 202 | */ 203 | void AsyncTimer::cancel() 204 | { 205 | this->is_running = false; 206 | this->capture = 0; 207 | this->timeoutMS = 0; 208 | } 209 | 210 | // INTERNAL 211 | 212 | /*! \fn 213 | ** 214 | ** FUNCTION: _finished 215 | ** 216 | ** DESCRIPTION: Internal method that checks the timeout value versus the 217 | ** captured ticks. If expired, the "is_running" flag is 218 | ** reset and TRUE is returned. 219 | ** 220 | ** PARAMETERS: 221 | ** 222 | ** RETURNS: 223 | ** 224 | ** COMMENTS: 225 | ** 226 | */ 227 | bool AsyncTimer::_finished(uint32_t _timeoutMS) 228 | { 229 | if (_timeoutMS > 0) 230 | { 231 | uint32_t current = millis(); 232 | 233 | if ( (current-this->capture) >= _timeoutMS ) 234 | { 235 | this->is_running = false; 236 | return (true); 237 | } 238 | } 239 | 240 | return (false); 241 | } 242 | -------------------------------------------------------------------------------- /src/arduino/libraries/tdw_timer/tdw_timer.h: -------------------------------------------------------------------------------- 1 | /*! \file 2 | ** 3 | ** MODULE: tdw_timer.h 4 | ** 5 | ** DESCRIPTION: My simple async timer class. 6 | ** 7 | ** Copyright 2021-2022 by Forty|W, Inc. All rights reserved. 8 | ** 9 | ** REVISION HISTORY: 10 | ** 11 | */ 12 | 13 | #ifndef __TDW_TIMER_H_INCLUDED 14 | #define __TDW_TIMER_H_INCLUDED 15 | 16 | #include "Arduino.h" 17 | #include 18 | #include 19 | 20 | class AsyncTimer 21 | { 22 | public: 23 | AsyncTimer(uint32_t _timeoutMS); 24 | AsyncTimer(); 25 | ~AsyncTimer(); 26 | void start(); 27 | void start(uint32_t _timeoutMS); 28 | bool finished(uint32_t _timeoutMS); 29 | bool finished(); 30 | bool running(); 31 | void cancel(); 32 | 33 | private: 34 | bool is_running; 35 | uint32_t timeoutMS; 36 | uint32_t capture; 37 | 38 | bool _finished(uint32_t _timeoutMS); 39 | }; 40 | 41 | 42 | 43 | #endif // __TDW_TIMER_H_INCLUDED 44 | -------------------------------------------------------------------------------- /src/arduino/readme.md: -------------------------------------------------------------------------------- 1 | Example Arduino sketches. 2 | 3 | Copyright Notice 4 | 5 | Copyright (c) 2022, tdwgithub 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | Introduction 27 | 28 | The samples here demonstrate various features of the Sensor Playground. They 29 | aren't written as "final" applications, per se. You'll note that I've tried 30 | to keep the code fairly straightforward, not doing much of the Computer-Sciency 31 | stuff I'm typically tempted to do. The idea is to understand how to use the 32 | features of the board, not to offer an off-the-rack application solution. 33 | 34 | Arduino Libraries 35 | 36 | I didn't provide the various 3rd-party libraries that I use in my sketches. 37 | If I were to do that, I'd probably be violating a ton of copyrights, plus I'd 38 | just end up duplicating the instructions for their use that the authors have 39 | already provided. 40 | 41 | I do provide the source and libraries that *I'm* personally responsible for. 42 | If you're not familiar with how to create your own libraries and how to use them 43 | I'd suggest taking a look here: https://roboticsbackend.com/arduino-create-library/ 44 | and at other sources on the InterWebs. 45 | 46 | I've separated the Arduino example sketches from the support libraries I've written. 47 | If you want to use the examples, you'll need to install those libraries into your 48 | copy of the Arduino environment. 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/python/readme.md: -------------------------------------------------------------------------------- 1 | 2 | Contains microPython and CircuitPython examples. 3 | 4 | Copyright Notice 5 | 6 | Copyright (c) 2022, tdwgithub 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /src/readme.md: -------------------------------------------------------------------------------- 1 | Source code examples for Sensor Playground, including air-quality examples, etc. 2 | 3 | Copyright Notice 4 | 5 | Copyright (c) 2022, tdwgithub 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | --------------------------------------------------------------------------------