<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Erin Dachtler]]></title><description><![CDATA[Technology and programming tutorials]]></description><link>https://blog.erindachtler.me/</link><generator>Ghost 0.7</generator><lastBuildDate>Mon, 22 Sep 2025 00:09:55 GMT</lastBuildDate><atom:link href="https://blog.erindachtler.me/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Tutorial - A wifi enabled washing machine]]></title><description><![CDATA[<p>It gets hot here during the summer. Real hot.</p>

<p>This sucks for us humans, but it's apparently great for mildew. Forgetting a load in the washing machine for even an hour or two can start to produce that telltale smell.</p>

<p>After getting sick of having to constantly re-wash forgotten clothes</p>]]></description><link>https://blog.erindachtler.me/tutorial-a-wifi-enabled-washing-machine/</link><guid isPermaLink="false">36d6b52f-a6e5-4e13-941f-19a2efe95ab3</guid><category><![CDATA[tutorial]]></category><category><![CDATA[arduino]]></category><category><![CDATA[esp8266]]></category><dc:creator><![CDATA[Erin Dachtler]]></dc:creator><pubDate>Thu, 08 Sep 2016 19:48:27 GMT</pubDate><content:encoded><![CDATA[<p>It gets hot here during the summer. Real hot.</p>

<p>This sucks for us humans, but it's apparently great for mildew. Forgetting a load in the washing machine for even an hour or two can start to produce that telltale smell.</p>

<p>After getting sick of having to constantly re-wash forgotten clothes I decided to do something about it. I set a timer for 55 minutes when starting the washer. Unfortunately it turns out I'm really good at ignoring timers, so instead I made a module that talks to the internet on my washer's behalf.</p>

<h2 id="partslist">Parts List</h2>

<p><strong>NodeMCU v3</strong>
<img src="https://blog.erindachtler.me/content/images/2016/09/nodemcu.jpg" alt="NodeMCU v3">
<a href="https://www.aliexpress.com/wholesale?SearchText=nodemcu+v3">Store Link</a></p>

<p>You can use any ESP8266-based board you want. I just used this because it was cheap.</p>

<p>Keep in mind that if you want to use this for any other projects, the Lolin (v3) board is slightly wider than the previous board versions which makes it too big to fit on NodeMCU shield boards and also too wide for a standard breadboard.</p>

<p><strong>SW-420 Vibration Sensor</strong>
<img src="https://blog.erindachtler.me/content/images/2016/09/kdWK7.jpg" alt="SW-420 Vibration Sensor">
<a href="https://www.aliexpress.com/wholesale?catId=0&amp;SearchText=sw-420">Store Link</a></p>

<p>The nice thing about the vibration sensor is that it has a voltage range of 3.3v to 5v, so it will work fine with our ESP8266 but we could also use it with an Arduino if we wanted.</p>

<p><strong>Wires</strong></p>

<p>There are only two components we need to connect so use whatever you want. You could solder jumper wires directly onto the board leads, or just use a breadboard. I used <a href="https://www.aliexpress.com/wholesale?SearchText=dupont+wires">Dupont wires</a> for mine.</p>

<h2 id="prep">Prep</h2>

<ol>
<li><p>Install the <a href="https://www.arduino.cc/en/Main/Software">Arduino IDE</a> if you don't already have it.</p></li>
<li><p>You will also need to install the CH340 (USB to Serial chip on the NodeMCU board) <a href="http://www.wemos.cc/downloads/">driver</a> if you are using the board I listed above.</p></li>
</ol>

<h2 id="assembly">Assembly</h2>

<ol>
<li>Connect a 3v pin on the NodeMCU board to the Vcc pin on the vibration sensor.  </li>
<li>Connect a ground pin on NodeMCU board to the ground pin on the sensor.  </li>
<li>Connect the digital output (DO) pin on the sensor to pin D1 on the board.</li>
</ol>

<p>Next, plug your board into your computer with a USB cable to give it power.</p>

<p>Now that it's powered up, you should be able to see the LED on the sensor module flicker when you move or shake it. If the sensor gets stuck in one position you may have to tap it a few times to get it working again.</p>

<p>The screw on the sensor module sets the sensitivity. Adjust it to the highest possible setting.</p>

<h2 id="software">Software</h2>

<p>The first thing you need is a way to get phone notifications from an HTTP request. If you already have a way to do this, skip this step and move on.</p>

<p>Go to <a href="https://ifttt.com/">IFTTT</a> and create an account. Then add <a href="https://ifttt.com/recipes/461992-washing-machine-done">this recipe</a> to your account, so when your Maker URL is called your phone will show a notification. You can get your URL by going to <a href="https://ifttt.com/maker">https://ifttt.com/maker</a> and clicking "How to Trigger Events".</p>

<p>Okay, now you should have a URL that looks something like this:</p>

<p><code>https://maker.ifttt.com/trigger/{event}/with/key/{yourkey}</code></p>

<p>Next we'll need the <a href="https://gist.github.com/download13/c7c34a04d9704808fb21f73116c2858a">Ardunio sketch</a>.</p>

<p>Once you've downloaded the sketch you'll need to replace the <code>WIFI_SSID</code>, <code>WIFI_KEY</code>, and <code>NOTIFY_URL</code> defines with your own values.</p>

<p>Oh I almost forgot! Here's a <a href="http://www.instructables.com/id/Programming-ESP8266-ESP-12E-NodeMCU-Using-Arduino-/">tutorial</a> on how to make the Arduino IDE work with NodeMCU boards.</p>

<p>Then just write the sketch to your board and pop it on the washing machine. I used a small plastic box with magets glued to it.</p>

<p>For power I just plugged a spare phone charger into the USB port on the board.</p>]]></content:encoded></item><item><title><![CDATA[Self-hosting a blog with Ghost and Docker]]></title><description><![CDATA[<p>This is the story of how I got this blog up and running, and hopefully it's helpful to other people looking to self-host Ghost (hey that rhymed).</p>

<p>After <a href="https://github.com/docker-library/ghost/issues/15">some trouble</a> getting the <a href="https://hub.docker.com/_/ghost/">official Docker image for Ghost</a> to work outside of my development box, I decided to make <a href="https://hub.docker.com/r/download13/ghost/">my own</a></p>]]></description><link>https://blog.erindachtler.me/self-hosting-a-blog-with-ghost-and-docker/</link><guid isPermaLink="false">b12e4ce0-f9b2-4c2e-b86a-99b6c957f6b1</guid><category><![CDATA[tutorial]]></category><category><![CDATA[docker]]></category><dc:creator><![CDATA[Erin Dachtler]]></dc:creator><pubDate>Wed, 04 May 2016 18:28:10 GMT</pubDate><content:encoded><![CDATA[<p>This is the story of how I got this blog up and running, and hopefully it's helpful to other people looking to self-host Ghost (hey that rhymed).</p>

<p>After <a href="https://github.com/docker-library/ghost/issues/15">some trouble</a> getting the <a href="https://hub.docker.com/_/ghost/">official Docker image for Ghost</a> to work outside of my development box, I decided to make <a href="https://hub.docker.com/r/download13/ghost/">my own image</a>.</p>

<p>Checking the <a href="https://github.com/TryGhost/Ghost">Ghost Github repository</a>, the documentation says:</p>

<blockquote>
  <ol>
  <li>Download the <a href="https://ghost.org/developers/">latest release</a> of Ghost</li>
  <li>Unzip in the location you want to install</li>
  <li>Fire up a terminal</li>
  <li><code>npm install --production</code></li>
  <li>Start Ghost!
  <ul><li>Local environment: <code>npm start</code></li>
  <li>On a server: <code>npm start --production</code></li></ul></li>
  <li><code>http://localhost:2368/ghost</code></li>
  </ol>
</blockquote>

<p>Converted into a Dockerfile, those instruction look something like the following.</p>

<h6 id="dockerfile">Dockerfile</h6>

<pre><code class="language-docker">FROM download13/node

WORKDIR /ghost  
COPY config.js /ghost/config.js  
RUN wget -O ghost.zip https://github.com/TryGhost/Ghost/releases/download/0.7.9/Ghost-0.7.9.zip \  
    &amp;&amp; unzip ghost.zip \
    &amp;&amp; rm ghost.zip \
    &amp;&amp; npm install --production \
    &amp;&amp; npm cache clean \
    &amp;&amp; rm -rf /tmp/npm*

CMD ["npm", "start", "--production"]
</code></pre>

<p>The <code>npm cache clean</code> and <code>rm -rf /tmp/npm*</code> bits are just cleanup to keep the image size down. The important part here is the <code>COPY config.js /ghost/config.js</code> line, which will bring in our custom config file.</p>

<p>Speaking of which...</p>

<h6 id="configjs">config.js</h6>

<pre><code class="language-javascript">module.exports = {  
    production: {
        url: process.env.BLOG_URL,
        mail: {},
        database: {
            client: 'sqlite3',
            connection: {
                filename: '/ghost/content/data/ghost.db'
            },
            debug: false
        },
        server: {
            host: '0.0.0.0',
            port: process.env.PORT || '80'
        }
    }
};
</code></pre>

<p>Two fields are set via environment variables. The <code>url</code> field (set by <code>BLOG_URL</code>) will be the base URL for your blog. It's required if you don't want a somewhat broken experience. <code>server.port</code> defaults to 80 which will be fine for most uses, but can be overridden by setting the <code>PORT</code> environment variable.</p>

<p>Let's test what we've got so far.</p>

<pre><code># docker build -t ghosttest .
...
Successfully built 767d2c6e0177  
</code></pre>

<p>Sweet! This might actually work. Now let's try running it.</p>

<p>Note that <code>192.168.99.100</code> is the IP of the Docker host VM on my computer. Replace it with <code>localhost</code> or the address of your Docker host if different.</p>

<pre><code># docker run -it -e "BLOG_URL=http://192.168.99.100" -p 80:80 ghosttest

&gt; ghost@0.7.9 start /ghost
&gt; node index

WARNING: Ghost is attempting to use a direct method to send email.  
It is recommended that you explicitly configure an email service.  
Help and documentation can be found at http://support.ghost.org/mail.

Migrations: Database initialisation required for version 004  
Migrations: Creating tables...  
Migrations: Creating table: posts  
Migrations: Creating table: users  
Migrations: Creating table: roles  
Migrations: Creating table: roles_users  
Migrations: Creating table: permissions  
Migrations: Creating table: permissions_users  
Migrations: Creating table: permissions_roles  
Migrations: Creating table: permissions_apps  
Migrations: Creating table: settings  
Migrations: Creating table: tags  
Migrations: Creating table: posts_tags  
Migrations: Creating table: apps  
Migrations: Creating table: app_settings  
Migrations: Creating table: app_fields  
Migrations: Creating table: clients  
Migrations: Creating table: client_trusted_domains  
Migrations: Creating table: accesstokens  
Migrations: Creating table: refreshtokens  
Migrations: Running fixture populations  
Migrations: Creating owner  
Migrations: Ensuring default settings  
Ghost is running in production...  
Your blog is now available on http://192.168.99.100  
Ctrl+C to shut down  
</code></pre>

<p>Navigating to <code>http://192.168.99.100</code> I see the following page.</p>

<p><img src="https://blog.erindachtler.me/content/images/2016/05/ghost_demo-1.png" alt="Ghost Blog Homepage"></p>

<p>Woohoo! Done! Well, not quite.</p>

<p>There's a little more to deploying it, but first I'm going to put what we have so far into an <a href="https://hub.docker.com/r/download13/ghost/">Docker Hub Automated Build</a> for the sake of convenience. Now whenever we want to use this image, we can just refer to it as <code>download13/ghost</code>.</p>

<p>If you want to deploy this on your personal server, you're probably going to want to keep the contents of your blog even if you have to re-create it's container.</p>

<p>There are two directories that need to persist across containers if you want your blog to work correctly.</p>

<p><code>/ghost/content/data</code> contains the sqlite database file which stores all your users/posts/tags/etc.</p>

<p><code>/ghost/content/images</code> is where all uploaded images are kept.</p>

<p>Let's add these volumes to our container.</p>

<pre><code># mkdir -p ~/ghost_data/{data,images}

# docker run -it -e "BLOG_URL=http://192.168.99.100" -p 80:80 -v ~/ghost_data/data:/ghost/content/data -v ~/ghost_data/images:/ghost/content/images download13/ghost
</code></pre>

<p>Of course running it from Docker Compose is a bit easier.</p>

<h6 id="dockercomposeyml">docker-compose.yml</h6>

<pre><code class="language-yaml">version: "2"  
services:  
    blog:
        image: download13/ghost
        restart: always
        volumes:
            - ~/ghost_data/data:/ghost/content/data
            - ~/ghost_data/images:/ghost/content/images
        environment:
            BLOG_URL: http://192.168.99.100
</code></pre>

<pre><code># mkdir -p ~/ghost_data/{data,images}

# docker-compose up -d

Creating ghosttest_blog_1  
</code></pre>

<p>And that's it! Now you've got a working Ghost blog!</p>]]></content:encoded></item><item><title><![CDATA[Welcome to my blog!]]></title><description><![CDATA[<p>This is where I'll put all the things trying to escape my head.</p>]]></description><link>https://blog.erindachtler.me/welcome-to-my-blog/</link><guid isPermaLink="false">46a02ad4-d678-4539-8719-49590ed707d3</guid><dc:creator><![CDATA[Erin Dachtler]]></dc:creator><pubDate>Tue, 03 May 2016 21:02:19 GMT</pubDate><content:encoded><![CDATA[<p>This is where I'll put all the things trying to escape my head.</p>]]></content:encoded></item></channel></rss>