<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: JT</title>
    <description>The latest articles on DEV Community by JT (@houkasaurusrex).</description>
    <link>https://dev.to/houkasaurusrex</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F188710%2Fc428800a-e2fd-4a87-bf27-09c148fb848a.jpeg</url>
      <title>DEV Community: JT</title>
      <link>https://dev.to/houkasaurusrex</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/houkasaurusrex"/>
    <language>en</language>
    <item>
      <title>Making a Bastion Host</title>
      <dc:creator>JT</dc:creator>
      <pubDate>Tue, 30 Mar 2021 12:05:33 +0000</pubDate>
      <link>https://dev.to/houkasaurusrex/making-a-bastion-host-28n0</link>
      <guid>https://dev.to/houkasaurusrex/making-a-bastion-host-28n0</guid>
      <description>&lt;p&gt;If we follow good security practices and deploy our database in a private subnet, direct database access is closed off from the wider internet. This is great for security, but can be frustrating when we just need to run some quick SQL commands from our local machines, connect a server that's not in the same network, or download some data without creating an API for this singular purpose. Luckily, we can get around this without exposing our database to the public internet with a "bastion host".&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Bastion Host?
&lt;/h2&gt;

&lt;p&gt;In medieval architecture, a bastion is a protrusion from a fortification that provides the most strategic vantage point possible without introducing any weak points to the structure. So if we relate this to our problem, a bastion host in computer science will function similarly - providing a point of access into a "fortified" private network without adding to the surface area of cyber attacks of your infrastructure. In practical terms, we will be using AWS infrastructure throughout this article, but the principles apply to any system of any scale we may work with. In our case, let's say we have a PostgreSQL - configured RDS instance living in a private subnet of our VPC. We're proud of our foresight and sleep soundly knowing we've done more than most to ensure the security of our application. A few sprints later, our project manager asks us to collect some data on the past few months of data for a big client meeting and, since this impromptu meeting was unforeseen, there is not much time to collect it. Since we've deployed our database in a private subnet, we can't connect to it to run our SQL queries and there isn't enough time to update our application code to retrieve this data either, so we will need to create our bastion host to proxy our connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Create a Bastion Host
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://app.cloudcraft.co/view/7ec0535f-8eb2-4eec-83d4-b80277caf0c4?key=Z8wC-Wi74NIcMsXeWXZXZA"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hmVNuRc---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/jthouk/image/upload/v1617100372/bastion_architecture_diagram_c23dd37b6d.png" alt="bastion architecture diagram"&gt;&lt;/a&gt;&lt;/p&gt;
The architecture we will be creating



&lt;h3&gt;
  
  
  Deploying the Server
&lt;/h3&gt;

&lt;p&gt;We can deploy a simple EC2 instance (T2 Micro running Amazon Linux or Ubuntu will work fine) using the &lt;a href="https://aws.amazon.com/ec2/getting-started/"&gt;dashboard&lt;/a&gt; or &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-services-ec2-instances.html"&gt;command line&lt;/a&gt; making sure to launch it in the same VPC as our database, but in a &lt;em&gt;public&lt;/em&gt; subnet. This will be our bastion host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 run-instances &lt;span class="nt"&gt;--image-id&lt;/span&gt; ami-xxxxxxxx &lt;span class="nt"&gt;--count&lt;/span&gt; 1 &lt;span class="nt"&gt;--instance-type&lt;/span&gt; t2.micro &lt;span class="nt"&gt;--key-name&lt;/span&gt; MyKeyPair &lt;span class="nt"&gt;--security-group-ids&lt;/span&gt; sg-903004f8 &lt;span class="nt"&gt;--subnet-id&lt;/span&gt; subnet-6e7f829e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: replace the ids and credentials with your own information.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring the Firewall
&lt;/h3&gt;

&lt;p&gt;After the server is launched (or while creating it in the dashboard), adjust the security groups for both the bastion host and the database to allow your machine to establish an ssh connection to your bastion host on port 22 and tunnel your psql connection to your database from your bastion.&lt;/p&gt;

&lt;p&gt;The following section will describe how to configure our firewalls using the AWS CLI to ensure this information stays up to date with AWS's frequently changing landscape.&lt;/p&gt;

&lt;p&gt;We will be allowing only connections from our IP in this example, so let's first get that information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://checkip.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return our IP address (&lt;strong&gt;e.g. 203.0.113.57&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Next let's update our EC2 bastion host to accept incoming connections on port 22.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 authorize-security-group-ingress &lt;span class="nt"&gt;--group-name&lt;/span&gt; ec2-bastion-sg &lt;span class="nt"&gt;--protocol&lt;/span&gt; tcp &lt;span class="nt"&gt;--port&lt;/span&gt; 22 &lt;span class="nt"&gt;--cidr&lt;/span&gt; 203.0.113.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last we'll update the database security group to allow connections on port 5432 (postgres) from our bastion host.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 authorize-security-group-ingress &lt;span class="nt"&gt;--group-name&lt;/span&gt; rds-postgre-sg &lt;span class="nt"&gt;--protocol&lt;/span&gt; tcp &lt;span class="nt"&gt;--port&lt;/span&gt; 5432 &lt;span class="nt"&gt;---source-group&lt;/span&gt; ec2-bastion-sg 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*Note: we should replace &lt;code&gt;group-name&lt;/code&gt; with the security group ids of our bastion host and database instance respectively, and &lt;code&gt;cidr&lt;/code&gt; with our IP address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to a Bastion Host
&lt;/h2&gt;

&lt;p&gt;After the instance is launched and the firewalls are configured and tested (we should be able to ssh into our bastion host), we can create our connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Opening an SSH Tunnel
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"~/bastion_key.pem"&lt;/span&gt; &lt;span class="nt"&gt;-NL&lt;/span&gt; 8886:postgress.cpypigm0kth7.us-east-1.rds.amazonaws.com:5432 ec2-user@132.5.10.11 &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;8886 is the port on our local machine we will be using to open the connection; we can choose any available port we want here. The flag -N will set up the tunnel without creating a session with the bastion server, -L will forward the port, and -v will print the output logs to the console.&lt;/p&gt;

&lt;p&gt;Once the output shows the connection is established successfully, we should check the port to make sure our machine is listening to the port correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;lsof &lt;span class="nt"&gt;-iTCP&lt;/span&gt; &lt;span class="nt"&gt;-sTCP&lt;/span&gt;:LISTEN &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;--color&lt;/span&gt; 8886
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optionally, we can add a configuration to our ssh config file to keep the connection alive to avoid annoying session timeouts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOT&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;&amp;gt; ~/.ssh/config
Host *
    ServerAliveInterval 30
    ServerAliveCountMax 2
&lt;/span&gt;&lt;span class="no"&gt;EOT
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connecting to the Database
&lt;/h3&gt;

&lt;p&gt;Now we should be able to connect to our database! If using postgres, the command will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-h&lt;/span&gt; localhost &lt;span class="nt"&gt;-p&lt;/span&gt; 8886 &lt;span class="nt"&gt;-U&lt;/span&gt; &amp;lt;username&amp;gt; &lt;span class="nt"&gt;-W&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Now we can use this connection to download our information in time for the important client meeting as well as reuse this solution for any future problems we may face.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In summary, by creating an EC2 instance in our database VPC in a public subnet, configuring the firewalls, and establishing an ssh tunnel, we can interact with our database in a private subnet without introducing too much extra infrastructure or reducing the security of our application.&lt;/p&gt;

&lt;p&gt;If you would like alternative solutions for specific vendors, databases, or local machines, please leave a comment and I'll add it to the post!&lt;/p&gt;

</description>
      <category>cloudskills</category>
    </item>
    <item>
      <title>How to Track Indoor Air Pollution with a Raspberry Pi</title>
      <dc:creator>JT</dc:creator>
      <pubDate>Fri, 19 Feb 2021 06:14:59 +0000</pubDate>
      <link>https://dev.to/houkasaurusrex/how-to-track-indoor-air-pollution-with-a-raspberry-pi-3aeo</link>
      <guid>https://dev.to/houkasaurusrex/how-to-track-indoor-air-pollution-with-a-raspberry-pi-3aeo</guid>
      <description>&lt;p&gt;You ever wonder what's actually in the air you breathe every day? I've lived in Beijing for a few years now and I'm always running a couple of air purifiers in my home, but how well do they actually work? Sure some have built in sensors, but how accurate are they? Well with a little Python, a Raspberry Pi, and some moxie, we can find our own answers.&lt;/p&gt;

&lt;p&gt;This will focus primarily on measuring &lt;a href="https://www.iqair.com/blog/air-quality/pm2-5"&gt;PM2.5&lt;/a&gt; and &lt;a href="https://www.iqair.com/us/blog/air-quality/pm10"&gt;PM10&lt;/a&gt; in the air and converting the values to an Air Quality Index (&lt;a href="https://en.wikipedia.org/wiki/Air_quality_index"&gt;AQI&lt;/a&gt;). For measuring other harmful chemicals like Nitrogen Dioxide (NO2) or Carbon Monoxide (CO), see the "Adding Sensors" section at the end of the article&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.raspberrypi.org/products/"&gt;Raspberry Pi&lt;/a&gt; Configured, &lt;a href="https://www.raspberrypi.org/documentation/configuration/wireless/headless.md"&gt;Headless&lt;/a&gt; (I used a 3b, but anything with a USB port should do just fine, though built in wifi is recommended for portability) (~$35)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/SDS011-Quality-Detection-Conditioning-Monitor/dp/B07FSDMRR5"&gt;SDS011 PM Sensor&lt;/a&gt; (~$15)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://io.adafruit.com/"&gt;Adafruit IO&lt;/a&gt; Account (free)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting up Adafruit IO
&lt;/h3&gt;

&lt;p&gt;Create an account on &lt;a href="https://io.adafruit.com/"&gt;Adafruit IO&lt;/a&gt;. This is a great site to collect data streams and display them on custom dashboards. After you've created an account, let's create a couple of feeds. We'll need three: a pm2.5, a pm10, and a log feed (I named mine "beijing-twofive", "beijing-ten", and "logs" respectively). After that, you can either create a dashboard now or later and play around with how you want to display the data. This is how I have mine set up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fXiniTVM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/jthouk/image/upload/v1605250759/x4j2mf7ta1mdpdxpk6cy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fXiniTVM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/jthouk/image/upload/v1605250759/x4j2mf7ta1mdpdxpk6cy.jpg" alt="Adafruit IO Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure your Raspberry Pi
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.raspberrypi.org/documentation/configuration/wireless/headless.md"&gt;Configure&lt;/a&gt; your Raspberry Pi with the installers on the product website. Once configured with either Raspbian or a Linux distribution of your choice (this project should be compatible with most distributions, but it's only been tested on Debian, Raspbian, and MacOS), install the following dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-programming-environment-on-debian-10"&gt;Python3&lt;/a&gt; (you can skip the virtual env setup for our purposes)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-git-on-debian-10"&gt;Git&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Download the Code and Install Dependencies
&lt;/h3&gt;

&lt;p&gt;SSH into your Raspberry Pi (or login if GUI is installed and open your terminal) and download the repo in your home directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone git@github.com:HoukasaurusRex/RaspberryPi-AQIPi.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then install the python dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure ENV with Adafruit Keys and Feed Names
&lt;/h3&gt;

&lt;p&gt;Retrieve your AdafruitIO username and key from the dashboard and add them to you env.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'AIO_USERNAME="Hackerman"
AIO_KEY="aio_xXxXx"
CITY="beijing"
AIO_LOGS="logs"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both &lt;code&gt;CITY&lt;/code&gt; and &lt;code&gt;AIO_LOGS&lt;/code&gt; are feed names created in the AdafruitIO dashboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run the Code
&lt;/h3&gt;

&lt;p&gt;Now you can run the code. I like to use &lt;a href="https://www.howtogeek.com/662422/how-to-use-linuxs-screen-command/"&gt;screen&lt;/a&gt; to save my terminal process to be accessible later, but you can just run it in your main shell as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;screen &lt;span class="nt"&gt;-S&lt;/span&gt; aqipi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/RaspberryPi-AQIPi
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x run.sh
sh run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There won't be any output in your terminal, but you should be able to go to your AdafruitIO feeds and start seeing results!&lt;/p&gt;

&lt;h3&gt;
  
  
  Sensor Placement
&lt;/h3&gt;

&lt;p&gt;Standard advice for locating your sensor is that it should be outside and four metres above ground level. That’s good advice for general environmental monitoring; however, we’re not necessarily interested in general environmental monitoring – we’re interested in knowing what we’re breathing in.&lt;/p&gt;

&lt;p&gt;Choose a location where you spend most of your time or where you might be particularly interested in the general air quality (e.g. in the kitchen or garage) and place your sensor in a safe place where it won't be affected by excessive moisture or humidity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_data&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="n"&gt;pm_twofive_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="n"&gt;pm_ten_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="n"&gt;readings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

  &lt;span class="c1"&gt;# This will take 11 data samples and use the built in `statistics` module to upload a median value
&lt;/span&gt;  &lt;span class="c1"&gt;# to filter out excessive data spikes in the readings
&lt;/span&gt;  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;readings&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="n"&gt;datum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Convert the readings from bytes to ints and append to the array of data samples
&lt;/span&gt;    &lt;span class="n"&gt;pm_twofive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;byteorder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'little'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="n"&gt;pm_twofive_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pm_twofive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pm_ten&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;byteorder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'little'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="n"&gt;pm_ten_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pm_ten&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;readings&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="c1"&gt;# Take a little break ☕️
&lt;/span&gt;    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;# Calculate the AQI from ppm^2 using the US EPA API table detailed in the section below
&lt;/span&gt;  &lt;span class="c1"&gt;# then upload to your AdafruitIO feeds using the Adafruit_IO SDK
&lt;/span&gt;  &lt;span class="n"&gt;pm_twofive_aqi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calc_aqi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'pm_twofive'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pm_twofive_data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;send_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'twofive'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pm_twofive_aqi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;pm_ten_aqi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calc_aqi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'pm_ten'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pm_ten_data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;send_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'ten'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pm_ten_aqi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pm_twofive_aqi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pm_ten_aqi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Converting PPM^2 to AQI
&lt;/h3&gt;

&lt;p&gt;This is converting to the &lt;a href="https://en.wikipedia.org/wiki/Air_quality_index"&gt;US EPA AQI&lt;/a&gt;, in order to use a different standard, you might need to tweak the formula to fit your country's AQI model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oVa_tN94--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/jthouk/image/upload/v1605257657/snz4jmfdnkdugzmxubus.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oVa_tN94--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/jthouk/image/upload/v1605257657/snz4jmfdnkdugzmxubus.png" alt="US EPA AQI Formula: I = (I_high - I_low) / (C_high - C_low) * (C - C_low) + I_low"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Exponential Backoff
&lt;/h3&gt;

&lt;p&gt;This repo implements an exponential backoff policy to retry connections after an exponentially longer period to account for common network errors or connection issues with your sensor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exponential_backoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;First testing against the air quality outside, make sure it seems to match up with &lt;a href="https://www.iqair.com/air-quality-map"&gt;IQ Air's&lt;/a&gt; Air Quality Index. &lt;/p&gt;

&lt;p&gt;After a few weeks of running the sensor, it seems the sensors on my air purifiers would often underreport pm2.5 values by as much as 50% and were seldom correlated with each other. The Raspberry Pi on the other hand, filtering out for data spikes, seems to respond very reasonably to real world phenomena such as rising with the outdoor AQI and falling linearly when the purifiers are all left on high for a few hours. I have also learned how much barometric pressure differences will also increase the penetration of outdoor pollution to my home. Interestingly, poor kitchen ventilation also causes quite noticeable spikes in indoor air pollution!&lt;/p&gt;

&lt;p&gt;I've been using this sensor a few months now and have since felt much more empowered with managing the quality of the air I breathe each day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Sensors
&lt;/h2&gt;

&lt;p&gt;This project can be easily extended by adding additional sensors, such as an Ozone (O3), Carbon Monoxide (CO), Nitrogen Dioxide (NO2), or any other harmful air pollutants that might be more relevant to your area. If you do, please let me know and I'd like to compare your findings and update the AQIPi repository to extend the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgements
&lt;/h2&gt;

&lt;p&gt;Big thanks to Andrew Gregory from &lt;a href="https://www.raspberrypi.org/blog/monitor-air-quality-with-a-raspberry-pi/"&gt;raspberrypi.org&lt;/a&gt; on his work providing the inspiration for this project.&lt;/p&gt;

&lt;p&gt;You can view this article and more like it as well as sign up to my newsletter to get the latest posts on &lt;a href="https://jt.houk.space"&gt;https://jt.houk.space&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>linux</category>
      <category>datascience</category>
    </item>
  </channel>
</rss>
