<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Brian Carrigan</title>
    <description>The personal blog of Brian Carrigan, software and electronics engineer.</description>
    <link>http://bcarrigan.com/</link>
    <atom:link href="http://bcarrigan.com/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Sat, 17 Jun 2023 16:46:01 +0000</pubDate>
    <lastBuildDate>Sat, 17 Jun 2023 16:46:01 +0000</lastBuildDate>
    <generator>Jekyll v3.9.3</generator>
    
      <item>
        <title>Dreadbot</title>
        <description>&lt;p&gt;There is a wonderful Magic: The Gathering scene here in Durham, NC. Unfortunately, Magic often deserves the trope that it gets of being an expensive hobby. In order to enjoy the game on a budget, myself and a group of locals created a new format: Paper Dreadful. The format is simple: create a 60 card deck with a maximum of 4 of any card (other than basic lands) and the total cost must be $20 or less on that day. The same rule applies with the sideboard, except it must by $5 or less.&lt;/p&gt;

&lt;p&gt;The problem here is that Magic cards change in price all the time. What happens if I build a deck and then a card skyrockets in price?&lt;/p&gt;

&lt;h2 id=&quot;proposed-solution&quot;&gt;Proposed Solution&lt;/h2&gt;

&lt;p&gt;What our group essentially needs is a way to cement the price for a deck list in place so that we know that it was $20 at the time it was built. The initial way we did this was by taking a screenshot of the deck and post it in a discord. This was cumbersome though and it did not split the price out into mainboard and sideboard cards.&lt;/p&gt;

&lt;p&gt;Our proposed solution was to make a discord bot where you paste a link to your deck and it stamps it with an approval or rejection based on the price that day. Such a bot would need to interact with three services: the deck list provider, a pricing provider, and Discord.&lt;/p&gt;

&lt;p&gt;I have been playing around with Rust a lot recently and decided that this would be a good place to try it out on a larger project. If you want to follow along in the code, the source can be found at &lt;a href=&quot;https://github.com/Carrigan/dreadbot&quot;&gt;https://github.com/Carrigan/dreadbot&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;consuming-apis&quot;&gt;Consuming APIs&lt;/h2&gt;

&lt;p&gt;In order to build a price list for a given deck, we need to work with at least two resources: the deck lists on &lt;a href=&quot;https://mtggoldfish.com&quot;&gt;mtggoldfish.com&lt;/a&gt; and pricing info from &lt;a href=&quot;http://scryfall.com&quot;&gt;scryfall.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The MTGGoldfish connection is quite simple: all we are doing is downloading a text file formatted with one card per line and the quantity of that card. There are two parts of a deck, the main deck and the sideboard, and they are separated by an empty line. We consume this file by creating two data types: a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deck&lt;/code&gt; object that has many &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Card&lt;/code&gt; objects.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mainboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sideboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\r\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_goldfish_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sideboard_flag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sideboard&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mainboard&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sideboard_flag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that we have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deck&lt;/code&gt; parsed, our next goal is to pair this info with pricing info. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Card&lt;/code&gt; object is capable of holding pricing information, but at this point that information is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;. Our Scryfall API takes the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deck&lt;/code&gt; object and builds a query based on the cards in it. It then finds the cheapest version a given card and returns a vector of pricing information. This vector can be passed back to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deck&lt;/code&gt; object’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update_pricing(&amp;amp;mut self, scryfall_entries: Vec&amp;lt;PricingSource&amp;gt;)&lt;/code&gt; function to fill in pricing. We now have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deck&lt;/code&gt; object with a list of fully filled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Card&lt;/code&gt; objects.&lt;/p&gt;

&lt;h2 id=&quot;interacting-with-discord&quot;&gt;Interacting with Discord&lt;/h2&gt;

&lt;p&gt;Now that we have a way of fetching populated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deck&lt;/code&gt; objects, we need a way for users to work with them. There are three important things we need to do: verify that a new deck’s main deck is under $20 and that its sideboard is under $5, give pricing info about a deck to guide users to meet these limits, and verify that a list has not changed.&lt;/p&gt;

&lt;p&gt;Our group was already using Discord to do these things manually, so it seemed like a natural extension to provide discord bindings to do them. I found the &lt;a href=&quot;https://crates.io/crates/serenity&quot;&gt;Serenity&lt;/a&gt; crate which allows you to do this quite easily by implementing a trait with callbacks for message and connection events. For our handler, I started with a root level regex that defines whether the bot should respond to a message and then if so, attempts to see if the contents of the message inside the package are a valid command. If so, the bot performs the action; otherwise the bot will return a message telling the user what the available commands are.&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EventHandler&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Handler&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DREADBOT_PREFIX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;captures&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.captures&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remaining_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;captures&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dreadbot_verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remaining_message&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dreadbot_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remaining_message&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dreadbot_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remaining_message&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;c&quot;&gt;// Fallback to the help message&lt;/span&gt;
                &lt;span class=&quot;nf&quot;&gt;dreadbot_help&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ready&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{} is connected!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.user.name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All of the functions take an argument in the form of a MTGGoldfish link. The verify function will retrieve the deck and pricing and give the deck a stamp of approval/disapproval and also give it a hash based on its contents so that people cannot change the cards in the deck list after it received a stamp of approval.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/dreadbot_verify.png&quot; alt=&quot;Verify Function Example&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The info function retrieves the cards and pricing, but instead of applying any of our format’s logic to it, it simply returns the deck contents and how it priced the deck. This is useful for when you are at $20.05 and need to shave that remaining 5 cents.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/dreadbot_info.png&quot; alt=&quot;Info Function Example&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Finally, the hash function retrieves just the deck contents and runs our hash algorithm on them to verify that they are the same as when they were initially submitted.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/dreadbot_hash.png&quot; alt=&quot;Hash Function Example&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This bot has been in production for over a month now and our format is currently running its first league using the bot. I run the production version on a cheap server and it has had no outages or problems to date.&lt;/p&gt;

&lt;p&gt;This was my first medium sized project in Rust, and I really enjoyed working with it. I worked with C as an embedded engineer for 4 years and it amazes me that a language exists that can output machine code as efficient as that while having all the conveniences of modern languages. I loved being able to perform test-driven design in the same file that I created new functions in. Between this and Elixir, I have also fallen in love with pattern matching. I have been working on several smaller embedded projects in Rust and hope that I get to work with it more in the future!&lt;/p&gt;

&lt;p&gt;Project link: &lt;a href=&quot;https://github.com/Carrigan/dreadbot&quot;&gt;https://github.com/Carrigan/dreadbot&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 28 Oct 2019 12:00:00 +0000</pubDate>
        <link>http://bcarrigan.com/2019/10/28/dreadbot/</link>
        <guid isPermaLink="true">http://bcarrigan.com/2019/10/28/dreadbot/</guid>
        
        
      </item>
    
      <item>
        <title>Wunderscan</title>
        <description>&lt;p&gt;I didn’t actually plan this project, but rather it found me. I was at the Scrap Exchange, a local reuse center, when I found a very nice looking scanner that you might find at a grocery store kiosk. It had been ripped from its connectors and had unlabeled wires hanging from it; a bit of hardware gore if such a thing exists. It was labelled on sale for $6.25, and a quick search revealed that it originally retailed at over $500. I thought a great use of this would be to keep near our pantry so that when we are out of a spice or snack, we could just scan the item and it would get posted to our shopping list. I figured I would try to reverse engineer it, and if all else fails, scrap it for the laser and motor. This is a longer post that goes through the entire process, so strap in!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/scanner.jpg&quot; alt=&quot;Final Scanner Picture&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;reverse-engineering&quot;&gt;Reverse Engineering&lt;/h2&gt;

&lt;p&gt;I was able to locate the datasheet for the scanner and find that it uses an RS232 serial connection to send data, and that normally there would be a DB9 connector where the frayed ends were. Having no wiring diagram, I had to start by finding the power connections.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/reverse_1.jpg&quot; alt=&quot;Finding the Right Connections&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This part of reverse engineering is really more luck than anything. You run the risk of frying your part when you connect the power improperly. I set my power supply for 5V and tried the following things to find the power cables:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Using the BBROYGBVGW color scheme used for resistors to match wires to their DB9 pinouts. This did not work.&lt;/li&gt;
  &lt;li&gt;Start trying wires based on likely color codes; for instance black and brown for ground, and red or orange for power. This wound up working, though who knows if it was for any of the logic that I thought. It turned out that orange was the 5V line and brown was ground.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that the part was powered and working, all that was left was finding the RS232 transmit line. I used my oscilloscope to do this part and found it by scanning items and looking for the signal. It was not long before this wire was found and I was able to receive UPC codes. The scanner simply sends the ASCII representation of the UPC code followed by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\r\n&lt;/code&gt; characters. This would make parsing the messages easy.&lt;/p&gt;

&lt;h2 id=&quot;posting-to-wunderlist&quot;&gt;Posting to Wunderlist&lt;/h2&gt;

&lt;p&gt;My wife and I use Wunderlist for our grocery shopping, so the next step for this project was to figure out how to post the item information to a shared grocery list. This consists of two parts: converting the UPC number to an item name, such as “16 Oz Almonds” and then making a request to Wunderlist with this information.&lt;/p&gt;

&lt;h3 id=&quot;upc-lookup&quot;&gt;UPC Lookup&lt;/h3&gt;

&lt;p&gt;For UPC lookup, the &lt;a href=&quot;http://upcdatabase.org/&quot;&gt;UPC Database&lt;/a&gt; looked like the best choice since its free tier would work for a low lookup count and because you can add your own UPC labels for items they don’t have. The service is also very easy to use; simply sign up for an account, and then you can search using the following URL scheme:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET https://api.upcdatabase.org/product/:upc_code/:api_token
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will return a product or an error if none could be found.&lt;/p&gt;

&lt;h3 id=&quot;wunderlist-post&quot;&gt;Wunderlist Post&lt;/h3&gt;

&lt;p&gt;Next up was posting to Wunderlist. As with above, our first step is getting an API token. This can be done right in their developer web interface by creating a new application to use with Wunderlist and then creating an OAuth 2 token for your own account. Once you have an OAuth2 token and a client ID for your application, you can start making requests.&lt;/p&gt;

&lt;p&gt;In order to create an item, we first need to find the ID of the list that the item will be added to. We can do this using the following API point:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET https://a.wunderlist.com/api/v1/lists
  X-Access-Token: &amp;lt;your access token&amp;gt;
  X-Client-Id: &amp;lt;your client id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This returns a list of lists, at which point you can find the one you want by searching through the results looking for the desired title. Grab the ID of the desired list, and then posts can be made using:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;POST https://a.wunderlist.com/api/v1/tasks
  X-Access-Token: &amp;lt;your access token&amp;gt;
  X-Client-Id: &amp;lt;your client id&amp;gt;
  Body: {
	   &quot;list_id&quot;: &amp;lt;id from the last step&amp;gt;,
	   &quot;title&quot;: &quot;&amp;lt;title from UPC lookup&amp;gt;&quot;
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;feeding-our-pi&quot;&gt;Feeding our Pi&lt;/h2&gt;

&lt;p&gt;To tie this all together, we need a program that does the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Listens for UPC codes coming over the Serial connection.&lt;/li&gt;
  &lt;li&gt;Upon receipt of a UPC code, query the UPC database as shown above.&lt;/li&gt;
  &lt;li&gt;Once a product name (or error) is determined, post this to Wunderlist.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While we could have potentially used a Particle or other low-power embedded device, I wanted to try out Elixir’s &lt;a href=&quot;https://nerves-project.org/&quot;&gt;Nerves&lt;/a&gt; project, a way to write embedded software using Elixir. So, a Raspberry Pi Zero W was chosen as the compute module so that the scanner could be placed anywhere in the house and still connect.&lt;/p&gt;

&lt;h3 id=&quot;building-the-hardware&quot;&gt;Building the Hardware&lt;/h3&gt;

&lt;p&gt;The hardware for this project was pretty simple: we feed 5V in through a barrel connector to the scanner and Raspberry Pi, then connect the Raspberry Pi to an MAX3232 level converter for the serial line conversion. This needs to happen because RS232 uses different voltages than the Raspberry Pi for its serial communication; see the oscilloscope screenshot below for the 3.3V serial signal versus the RS232 signal:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/oscope_serial.png&quot; alt=&quot;RS232 and TTL Levels&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;writing-the-software&quot;&gt;Writing the Software&lt;/h3&gt;

&lt;p&gt;The software side and working with Nerves in general I think deserves its own post. For this article, lets do a high level overview of the project and code. First, the code can be found at the following link:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/carrigan/wunderscan&quot;&gt;Wunderscan Github Link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will talk about the code in three sections: the project level, the supporting code, and the main loop.&lt;/p&gt;

&lt;h4 id=&quot;project-level&quot;&gt;Project Level&lt;/h4&gt;

&lt;p&gt;The project level is where we import the required libraries, setup system variables, and tell Nerves how to build our software. Unlike working with the standard Raspberry Pi environment with Raspbian and Python/C/C++ where you flash an operating system on your Raspberry Pi and then copy your program files over, Nerves builds a root image that boots into your application. You can even set your project up to allow SSH firmware pushes, allowing you to compile everything on your PC and push a new copy of the entire root system over SSH to your device. This takes all of the manual Raspberry Pi configuration out of the picture; all of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo raspi-config&lt;/code&gt; commands, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wpa_supplicant.conf&lt;/code&gt; placement, and manually changing files to set up your pi is replaced by the Nerves configuration. This is wonderful for setting up a new Raspberry Pi to run your application.&lt;/p&gt;

&lt;p&gt;One of my other favorite things about working with Nerves has been how you can feed your secrets into the software at compile time using the config files. Consider the following, where we pass in our API keys using environmental variables on the computer that is compiling the code:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Elixir&quot;&gt;config :nerves_network, :default, wlan0: [
    ssid: System.get_env(&quot;NERVES_NETWORK_SSID&quot;),
    psk: System.get_env(&quot;NERVES_NETWORK_PSK&quot;),
    key_mgmt: String.to_atom(key_mgmt)
  ]

config :scanner,
  wunderlist_access_key: System.get_env(&quot;WUNDERLIST_ACCESS_KEY&quot;),
  wunderlist_client_id: System.get_env(&quot;WUNDERLIST_CLIENT_ID&quot;),
  upcdatabase_access_key: System.get_env(&quot;UPCDATABASE_ACCESS_KEY&quot;),
  list_name: &quot;Scanned Groceries&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In a normal Raspberry Pi project running Raspbian and your language of choice, you would likely need to manually SSH in and send the keys or manually type them in using the keyboard. With Nerves, simply compile them in and flash your image. Done.&lt;/p&gt;

&lt;h4 id=&quot;supporting-code&quot;&gt;Supporting Code&lt;/h4&gt;

&lt;p&gt;What I call the supporting code is simply some high level APIs written to abstract the UPC database queries and Wunderlist interactions. I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTTPoison&lt;/code&gt; to make the requests and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Poison&lt;/code&gt; to decode the JSON. As an example, the below code shows a request to find the ID of a list on Wunderlist given the title of the list.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Elixir&quot;&gt;def find_list_id(title) do
  &quot;https://a.wunderlist.com/api/v1/lists&quot;
  |&amp;gt; HTTPoison.get(auth_headers())
  |&amp;gt; process_response(title)
end

def process_response({:ok, %HTTPoison.Response{body: body}}, title) do
  Poison.decode!(body)
  |&amp;gt; Enum.find(%{}, fn list_item -&amp;gt; Map.get(list_item, &quot;title&quot;) == title end)
  |&amp;gt; Map.fetch(&quot;id&quot;)
end

def process_response(error, _), do: error

defp auth_headers() do
  [
    &quot;X-Access-Token&quot;: Application.get_env(:scanner, :wunderlist_access_key),
    &quot;X-Client-Id&quot;: Application.get_env(:scanner, :wunderlist_client_id)
  ]
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In Elixir, it is idiomatic to return a tuple or error code instead of raising errors. Thus, this API returns one of three ways:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{:ok, &amp;lt;id&amp;gt;}&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{:error, %HTTPoison.Error{}}&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:error&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This lets us match on the return in the calling code, making handling errors a natural part of program writing.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Elixir&quot;&gt;response = Application.get_env(:scanner, :list_name)
  |&amp;gt; Scanner.WunderlistApi.find_list_id()

case response do
  {:ok, id} -&amp;gt; id
  {:error, error} -&amp;gt;
    Logger.error error.reason
    nil
  :error -&amp;gt;
    Logger.error &quot;Key not found&quot;
    nil
end
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;main-loop&quot;&gt;Main Loop&lt;/h4&gt;

&lt;p&gt;Unlike a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;while&lt;/code&gt; loop or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; loop in other languages, Elixir and Erlang use processes which are supervised by the system for very high levels of system robustness. If a piece of python code crashes, the application will exit and you will likely need your operating system to restart it. If an Elixir process crashes, the supervisor will automatically restart the relevant processes without OS intervention. This makes the style of writing programs &lt;em&gt;very&lt;/em&gt; different, but its a style I’ve grown to really enjoy.&lt;/p&gt;

&lt;p&gt;For our main loop, we will be using Elixir’s &lt;a href=&quot;https://hexdocs.pm/elixir/GenServer.html&quot;&gt;GenServer&lt;/a&gt; behavior to implement the steps shown above in the “Feeding Our Pi” section. We start by initializing our serial port, HTTP client, and polling to see if the internet has connected yet.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Elixir&quot;&gt;def init(port) do
  #Start the NERVES port
  begin_serial(port)

  # Start httpoison
  HTTPoison.start()

  # Start polling for the ethernet being available
  send(self(), :ethernet_check)

  {:ok, %{ id: nil }}
end

defp begin_serial(port) do
  {:ok, pid} = Nerves.UART.start_link
  Nerves.UART.open(pid, port, speed: 9600, active: true)
  Nerves.UART.configure(pid, framing: {Nerves.UART.Framing.Line, separator: &quot;\r\n&quot;})

  pid
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Starting the serial port in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;active&lt;/code&gt; mode will cause it to send a message every time it receives a string. We can setup a callback on our GenServer that fires every time we receive data. This is where we can write the code to query the UPC code and post to Wunderlist.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-Elixir&quot;&gt;def handle_info({:nerves_uart, _pid, barcode}, %{id: id}) do
  # Check the barcode
  todo_title = case Scanner.UpcDatabaseApi.lookup(barcode) do
    {:ok, product_map} -&amp;gt; get_best_name(product_map, barcode)
    _ -&amp;gt; &quot;Barcode error: #{barcode}&quot;
  end

  # Post the item
  Scanner.WunderlistApi.create_todo(id, todo_title)
  Logger.info &quot;Got info: #{barcode}. Posted item: #{todo_title}&quot;

  # Keep the state constant
  {:noreply, %{ id: id }}
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As can be seen here, if there are any problems during the UPC retrieval, we simply post a “Barcode error: &lt;upc&gt;&quot; message instead of the product name.&lt;/upc&gt;&lt;/p&gt;

&lt;h2 id=&quot;project-enclosure&quot;&gt;Project Enclosure&lt;/h2&gt;

&lt;p&gt;While I’ve printed some simple 3D designs before, this was my first one with features that held multiple parts together in an assembly. I used Fusion 360 for my modeling, and began by modeling all of the individual components or finding the common ones like the Raspberry Pi online. From there, I moved the components around until they had ample space to be wired and then figured out how to secure them in place. The Raspberry Pi is screwed into some posts using heat-set inserts.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/3d_slice_3.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;/img/scanner_1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The power connector has a very small clearance and will simply be glued in place:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/3d_slice_4.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;/img/scanner_3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The scanner itself sits on an inner wall and is held in place by the outer wall. It is high enough up that the cavity beneath it can easily fit all of the wires.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/3d_slice_1.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;/img/scanner_2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The whole thing includes some fillets and cutouts to neatly display the scanner:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/3d_slice_2.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;/img/scanner_4.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Not having a 3D Printer myself, I went on &lt;a href=&quot;https://www.3dhubs.com/&quot;&gt;3D Hubs&lt;/a&gt; to get this design printed. The final result looks great and was just $11!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/scanner.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;This was a fun project where I got to learn more about Nerves and designing 3D printed enclosures. It was also inexpensive, with the parts list totaling less than $40 for a very complex design. It is unlikely that others will find this particular scanner, but it should be possible to use any scanner with a serial protocol. The device has already come in handy several times to remind us to pick up more spices that we have run out of. I hope you enjoyed this post, and as always feel free to contact me with any questions you have if you are looking to build something similar!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;If your company could use help launching an embedded product like this, I run a full-stack connected device consulting firm that builds great products for clients. For more information, please check out my &lt;a href=&quot;/services&quot;&gt;Services page&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Fri, 20 Apr 2018 12:00:00 +0000</pubDate>
        <link>http://bcarrigan.com/2018/04/20/wunderscan/</link>
        <guid isPermaLink="true">http://bcarrigan.com/2018/04/20/wunderscan/</guid>
        
        
      </item>
    
      <item>
        <title>Moogfest 2017 Project</title>
        <description>&lt;p&gt;For Moogfest this year, Smashing Boxes put on an event for festival-goers that showcased our recent labs projects. We wanted to give attendees something to bring home and hack on, so I set out to design a programmable synthesizer for $20 or less.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/moog_2017.jpg&quot; alt=&quot;Synth Picture&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Check out the project writeup, complete with schematics, code, and build instructions, at the &lt;a href=&quot;https://github.com/smashingboxes/moogfest-2017&quot;&gt;Moogfest 2017 Synth&lt;/a&gt; repository on Github.&lt;/p&gt;
</description>
        <pubDate>Thu, 25 May 2017 12:00:00 +0000</pubDate>
        <link>http://bcarrigan.com/2017/05/25/moogfest-2017/</link>
        <guid isPermaLink="true">http://bcarrigan.com/2017/05/25/moogfest-2017/</guid>
        
        
      </item>
    
      <item>
        <title>Coffeebot</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://vimeo.com/204036166&quot;&gt;Video of Coffeebot In Action&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine this tragedy: you get into work in the morning and go to get a cup of coffee, only to finally sip it and find that it is yesterday’s luke-warm spoils. A simple paper solution for this would be to add a post-it to the coffee with the time that it was brewed, but this requires buy-in from the team. A digital solution for this would be an internet connected button that alerts the office when the coffee was brewed, but this does not help people who are physically at the machine. We set out to build a hybrid of these two ideas; a device that would both give immediate information at the coffee pot and also have a digital alert system for anyone waiting for a new pot to be brewed. We call our creation Coffeebot.&lt;/p&gt;

&lt;h2 id=&quot;take-one&quot;&gt;Take One&lt;/h2&gt;

&lt;p&gt;For our first take on this project, we wanted to use our LABS TIME to build a minimum viable product in a few hours or less. Luckily, we had a PARTICLE board in the office which allowed us to move quickly and have both a physical product as well as an internet connected tie-in. Coffeebot’s entire user interface is a screen and two buttons; whenever a new pot of coffee is made, the brewer simply presses a button that corresponds to that type of coffee.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/coffeebot_prototype.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When a button is pressed, it restarts the timer that is shown on the screen and uses IFTTT to post a message to our company’s Slack app. The first iteration was not pretty or polished, but it was adopted immediately thanks to how easy and friendly it was to use.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/coffeebot_slack.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;refining-the-design&quot;&gt;Refining the Design&lt;/h2&gt;

&lt;p&gt;The Coffeebot prototype quickly became one of the most used Labs projects in the office. There was only one problem: the mess of prototype wires was unruly and prone to disconnecting. We decided to solve this using our 3D printer and a custom Printed Circuit Board (PCB). We started by designing a simple PCB that would hold the Particle, a couple of buttons, and the LCD. We got 3 copies of this made at OSHPARK for $30.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/coffeebot_board.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next, we designed and 3D printed an enclosure that would hold the finished board. One cool feature of this enclosure is that the buttons are cut out of the case itself. The entire enclosure can be printed as a single piece and can press the buttons on the PCB without needing any additional parts.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/coffeebot_finished.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;While we use this project for tracking coffee, the general concept of an internet-connected screen with four buttons can be used for all kinds of things: a time tracker for work, a Slack responder, or even an automatic Pizza ordering system. If you have any questions feel free to drop them in the Github issue tracker. Happy brewing!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally posted at the Smashing Boxes’ blog: &lt;a href=&quot;https://smashingboxes.com/blog/introducing-coffeebot-slack-powered-alert-system-office-coffee-pot/&quot;&gt;link&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 14 Feb 2017 12:00:00 +0000</pubDate>
        <link>http://bcarrigan.com/2017/02/14/coffeebot/</link>
        <guid isPermaLink="true">http://bcarrigan.com/2017/02/14/coffeebot/</guid>
        
        
      </item>
    
      <item>
        <title>Opensource.com Article</title>
        <description>&lt;p&gt;With my talk coming up at
&lt;a href=&quot;https://allthingsopen.org/talk/creating-interactive-art-with-open-source/&quot;&gt;All Things Open&lt;/a&gt;, I
recently wrote an article about why and how I got started in doing digital art. In retrospect, it
is amazing how learning the tools discussed in this article changed the way that I approach
software. Huge thanks to Processing, Arduino, and the entire open source community for what they do;
I am honored to be a small part of such a wonderful community.&lt;/p&gt;

&lt;p&gt;You can check out my article &lt;strong&gt;How Open Soure is Enabling the Digital Art Age&lt;/strong&gt; at &lt;a href=&quot;https://opensource.com/life/16/10/how-open-source-enabling-digital-art-age&quot;&gt;https://opensource.com/life/16/10/how-open-source-enabling-digital-art-age&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 06 Oct 2016 12:00:00 +0000</pubDate>
        <link>http://bcarrigan.com/2016/10/06/opensource-com-article/</link>
        <guid isPermaLink="true">http://bcarrigan.com/2016/10/06/opensource-com-article/</guid>
        
        
      </item>
    
      <item>
        <title>Elixir Lunch and Learn</title>
        <description>&lt;p&gt;I have recently been working on Smashing Boxes’ first Elixir project, and it has been fantastic. As
a primarily Ruby shop, I wanted to create a presentation about what the experience has been like so
far. Just like my blog, I’ve wanted my presentations to be focused on content over style and tedium,
so I’ve switched over to &lt;a href=&quot;https://github.com/gnab/remark&quot;&gt;remark.js&lt;/a&gt; for my slide decks. A nice side
effect of this is that I can share them to a larger audience.&lt;/p&gt;

&lt;p&gt;If you use Ruby for web development and are interested in the tradeoffs that Elixir and Phoenix
bring, check out the full presentation &lt;a href=&quot;/elixir-ruby-experience&quot;&gt;here&lt;/a&gt; and let me know what you think.&lt;/p&gt;
</description>
        <pubDate>Tue, 20 Sep 2016 12:00:00 +0000</pubDate>
        <link>http://bcarrigan.com/2016/09/20/elixir-lunch-and-learn/</link>
        <guid isPermaLink="true">http://bcarrigan.com/2016/09/20/elixir-lunch-and-learn/</guid>
        
        
      </item>
    
      <item>
        <title>Shipping Fraqture</title>
        <description>&lt;p&gt;At SB, we’ve been working hard on shipping Fraqture, our open source art platform. We are happy to
announce that it will be on display in the 21C storefront window on Main St. in downtown Durham for
the next few months.&lt;/p&gt;

&lt;p&gt;Check out my
&lt;a href=&quot;http://smashingboxes.com/blog/introducing-fraqture-a-platform-for-uniting-tech-and-art&quot;&gt;recent post&lt;/a&gt;
about why and how we built Fraqture, and see our &lt;a href=&quot;http://www.fraqture.com&quot;&gt;splash page&lt;/a&gt; for more info
on what will be on display and how you can get involved.&lt;/p&gt;
</description>
        <pubDate>Fri, 20 May 2016 12:00:00 +0000</pubDate>
        <link>http://bcarrigan.com/2016/05/20/fraqture/</link>
        <guid isPermaLink="true">http://bcarrigan.com/2016/05/20/fraqture/</guid>
        
        
      </item>
    
      <item>
        <title>2015 In Review</title>
        <description>&lt;p&gt;My 2015 brought a &lt;em&gt;ton&lt;/em&gt; of change, and I am very happy with all of it. Using &lt;a href=&quot;http://blog.fogus.me/2015/12/29/the-best-things-and-stuff-of-2015/&quot;&gt;a format similar to Michael
Fogus’s&lt;/a&gt;, I thought it would
be cool to do a roundup of this year using my favorite things produced and consumed. While I don’t
track blog reading&lt;/p&gt;

&lt;h2 id=&quot;2015-in-pictures&quot;&gt;2015 In Pictures&lt;/h2&gt;

&lt;p&gt;The single biggest event this year was getting married to my wonderful partner of over 5 years, Jennifer.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/jb_353.png&quot; alt=&quot;Wedding Picture&quot; /&gt;&lt;/p&gt;

&lt;p&gt;My work situation churned a lot this year, but am happy to say that it was worth it to get to my
current position at SB.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/busan-3-19.jpg&quot; alt=&quot;Picture in SmashingBoxes shirt in Busan&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After things settled down, we expanded our family with our first cat, Pancake.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/pancake-3.png&quot; alt=&quot;Pancake&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;languages-used&quot;&gt;Languages Used&lt;/h2&gt;

&lt;p&gt;Working in three environments this year, I probably used more languages in a single year for
production code than I will ever use again. In order from most to least used, they are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;C&lt;/li&gt;
  &lt;li&gt;Ruby&lt;/li&gt;
  &lt;li&gt;Javascript&lt;/li&gt;
  &lt;li&gt;Python&lt;/li&gt;
  &lt;li&gt;Clojure&lt;/li&gt;
  &lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The winner of favorite language this year goes to Clojure. Right now, I am using it for an art project
for downtown Durham and it is a blast.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://giant.gfycat.com/IdioticHarshCentipede.gif&quot; alt=&quot;Image of the art project being worked on&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;favorite-books-read&quot;&gt;Favorite Books Read&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Mistborn Series&lt;/strong&gt;. This is definitely top of the list. Mistborn is a fantasy series by Brandon
Sanderson in a world where some are born with the ability to turn metals into superpowers. The main
characters are wonderful and the story is great.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Shadow of the Wind&lt;/strong&gt;. Another novel, this is somewhat of a meta-book, a book about how powerful
books can be.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Structure and Interpretation of Computer Programs&lt;/strong&gt;. I’ll admit to only being in Chapter 3 at the
moment, but this is where I began learning Clojure. It is a great book to open up and work through
a problem or two while having coffee on a weekend morning.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;places-travelled&quot;&gt;Places Travelled&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Istanbul, Turkey&lt;/strong&gt;.
Visited on our honeymoon, and loved it. A great blend between Western and Islamic culture with great
food and tons to see.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Santorini, Greece&lt;/strong&gt;.
The more romantic place we visited on our honeymoon. Santorini has beautiful views, sunsets, hikes,
and towns. The seafood and white wine here was superb.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Athens, Greece&lt;/strong&gt;.
Athens has a lot to offer in historical terms, but we found that our 4 day stay there was a bit too
long. We found Athens to be quite hectic and run down. On the last day we took a ferry to Hydra,
which was more relaxing and scenic.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Busan, South Korea&lt;/strong&gt;.
Traveled to Busan for 3 days for the IEEE Sensors conference. It was my first time in Asia, and I
really enjoyed it. The city itself was beautiful, with parks and beaches right in the downtown.
The food was good enough to where I started fermenting my own Kimchi when I got back!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Nashville, Tennessee&lt;/strong&gt;.
Did a three day trip here for the Fourth of July. We went out two nights in the downtown area,
which is a sprawl of bars, each with a different band playing in them. Really great city if you
enjoy live music.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Asheville, North Carolina&lt;/strong&gt;.
My favorite area in North Carolina since it reminds me of where I grew up. We went here several times
for getaways and with friends.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conferences-attended&quot;&gt;Conferences Attended&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Council for Entrepreneurial Development Tech Ventures 2015&lt;/strong&gt; &lt;em&gt;September 15 &amp;amp; 16, Raleigh NC&lt;/em&gt;.
Presented on a lunchtime panel about data management for the Internet of Things.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;All Things Open&lt;/strong&gt; &lt;em&gt;October 19 &amp;amp; 20, Raleigh, NC&lt;/em&gt;.
A great celebration of all things open source, from data to organizations. Would definitely recommend
folks who are interested in the Open Source community to check this out.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;IEEE Sensors 2015&lt;/strong&gt; &lt;em&gt;November 4th, Busan, South Korea&lt;/em&gt;.
Presented on a lunchtime panel about sensor challenges for the Internet of Things.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;favorite-games-played&quot;&gt;Favorite Games Played&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Heroes of the Storm&lt;/strong&gt;.
As a long time DotA player, this game was really refreshing. Using talents instead of items and
having multiple maps where certain characters have more or less of an advantage makes for a much
more dynamic game.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Rocket League&lt;/strong&gt;.
I only picked this up at the end of the year, but wish I had sooner. This game feels like what I
remember Mario Kart 64’s battle mode felt like when I was a kid: a fast paced, competitive yet fun
driving action game.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Puerto Rico&lt;/strong&gt;.
A 2-5 person board game that uses building and economy tactics. Random elements make every game feel
different, but do so in a more subtle way that makes it feel more fair than Catan or other games.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Hearthstone&lt;/strong&gt;.
Always need two blizzard games in the list! I played a &lt;em&gt;lot&lt;/em&gt; of Magic growing up, and Hearthstone
is perfect for playing a quick strategy card game when in a crunch.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Undertale&lt;/strong&gt;
A fantastic little RPG with witty dialog and a battle system that feels like an old platform game.
Definitely recommend this one.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;goals-for-2016&quot;&gt;Goals For 2016&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Keep better track of blog posts and books read&lt;/li&gt;
  &lt;li&gt;Get one piece of art in a public display&lt;/li&gt;
  &lt;li&gt;Make a single full-stack site using Clojure&lt;/li&gt;
  &lt;li&gt;Read Escher, Godel, Bach&lt;/li&gt;
  &lt;li&gt;Travel to Hawaii&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 01 Jan 2016 12:00:00 +0000</pubDate>
        <link>http://bcarrigan.com/2016/01/01/best-of-2015/</link>
        <guid isPermaLink="true">http://bcarrigan.com/2016/01/01/best-of-2015/</guid>
        
        
      </item>
    
      <item>
        <title>New Blog</title>
        <description>&lt;h2 id=&quot;new-message-new-medium&quot;&gt;New Message, New Medium&lt;/h2&gt;

&lt;p&gt;I’ve wanted to start a new blog for a while now, and decided to use part of my holiday break to do
so. There were a lot of really cool projects on &lt;a href=&quot;http://www.graphsandwords.com&quot;&gt;my old site&lt;/a&gt; that
still get quite a bit of traffic, but I wanted to move from a site where there are 2-3 project posts
per year to a site with more frequent smaller posts. I envision it being small tips or resolutions
to problems that we as programmers run into on a daily basis.&lt;/p&gt;

&lt;p&gt;I also wanted it to be much simpler. The old site looked pretty crowded (there is a search bar?!),
whereas the new one uses the
&lt;a href=&quot;https://github.com/IronSummitMedia/startbootstrap-clean-blog-jekyll&quot;&gt;clean-blog template&lt;/a&gt;
to make a very simple but elegant site. I plan on adding more static pages, but love the uncrowded
look of the large header and then the article to follow.&lt;/p&gt;

&lt;p&gt;The way that I edit content is simpler now too. Originally my old site used pure HTML, but this got tiresome
to edit, so I moved over to Wordpress. Wordpress is fine, but when I sit down to write I like looking
at a text editor more than a complex interface. For these reasons, I switched over to using Github pages
with Jekyll so that I could template in HTML and then write articles in Markdown. It is also nice to work
with the full site locally, make changes, and then push those changes to Github when I am ready to deploy.
A big thanks to Github for making this so easy!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;
&lt;img src=&quot;/img/wordpress.png&quot; alt=&quot;Wordpress&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;
&lt;img src=&quot;/img/atom.png&quot; alt=&quot;Atom&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Simplicity is beauty.&lt;/p&gt;
</description>
        <pubDate>Thu, 31 Dec 2015 12:00:00 +0000</pubDate>
        <link>http://bcarrigan.com/2015/12/31/new-blog/</link>
        <guid isPermaLink="true">http://bcarrigan.com/2015/12/31/new-blog/</guid>
        
        
      </item>
    
      <item>
        <title>USB Business Card Part 2</title>
        <description>&lt;p&gt;Link to the source code, schematic, layout, and BOM can be found at this project’s Github page: &lt;a href=&quot;https://github.com/Carrigan/USBBusinessCard&quot;&gt;https://github.com/Carrigan/USBBusinessCard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/card_2_done.png&quot; alt=&quot;Finished Product&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Since there is already a write-up on why and how I made the business card project, this post will only detail the changes made since the first version. If you would like more information on how this project was made and how it works, please visit the original write-up.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/card_2_comp.png&quot; alt=&quot;Comparison Picture&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The initial USB business card was a success in the fact that it worked as a functioning mass storage device in a business card form factor. However, it had a few minor problems which were discussed in the first article, as well as some areas that I thought could use improvement.&lt;/p&gt;

&lt;h2 id=&quot;changelog&quot;&gt;Changelog&lt;/h2&gt;

&lt;p&gt;The first and probably most obvious change is that the Molex USB connector was replaced by a few exposed traces on the circuit-board itself. I was against doing this in the original board because of the low quality finish on the boards I was buying. In addition, I had contacted a company which makes a USB connector directly on board to ask about the board thickness they use and was told that a snug fit requires a 2.4mm thick board. It is difficult to find inexpensive 2.0mm boards, let along 2.4mm. Since the board house I used for this batch was a higher quality board though, I decided to try the on-board connector and just use some thickening material on the back to make up for the thickness. I’m currently using three strips of electrical tape on the back of the boards and am happy to report that they have fit every USB port that I’ve tried them on.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/card_2_in.png&quot; alt=&quot;Picture in Computer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Traditionally, the Cortex M3 uses a 10 pin .05″ pitch connector for programming and debugging. This connector can be seen on the original board- it was by far the tallest component on the board. If somebody received this business card, they might be weary about actually putting it in their pocket since the sharp pins could scratch or tear at clothing. While browsing Dangerous Prototypes one day, I stumbled upon a &lt;a href=&quot;http://www.tag-connect.com/node/52&quot;&gt;connector-less programming port&lt;/a&gt; which had a Cortex SWD flavor, and decided to try it. Using the legless connector allows for very quick programming with a secure connection, and it uses even less board real estate. I would only recommend using this type of connector for proven boards though since you have to hold the pins in place manually while programming or debugging.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/card_2_prog.png&quot; alt=&quot;Programmed with connectorless&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Finally, a few minor connection changes were made. In the previous version, you can see a jumper going from one of the resistors to a pin on the microcontroller. I did not fully read the datasheet the first time through, and thus didn’t notice that there was a pin which was sampled at boot-up to determine if the micro would go into an In System Programming (ISP) routine. Because this pin was floating in the original, the micro would boot up into a USB bootloader every time instead of running the program I loaded onto it. A simple pull-up resistor in the new version solves this.&lt;/p&gt;

&lt;p&gt;While this project works exactly as intended now, I’m not sure that I want to call it finished yet. The problem is that while this is a nice novelty card, it loses its value to the owner after they have used it since 1MB flash drives aren’t exactly cutting edge technology. I made a smaller project that I didn’t do a write-up on that used the LPC1343 to emulate a keyboard. This keyboard project would randomly send out a caps lock keypress roughly once every two minutes, and then a second caps lock press within a second of the first. The idea was that you plug this device into a friend or coworker’s computer, and they get frustrated with random capitalizations that happen infrequently enough that they wouldn’t expect foul play. In order to integrate this into the next business card, all that would have to be added is a switch or pushbutton that can be sampled on power-up to determine what the board should emulate. I’ve got enough of the current batch to last a while, but look for these in the future.&lt;/p&gt;

&lt;p&gt;I hope that this post helps you in whatever USB adventures you go on- I’ve had several people email me saying that they’ve made their own versions since the first post went live. Please let me know if you make (or need help making) your own!&lt;/p&gt;
</description>
        <pubDate>Mon, 01 Jul 2013 12:00:00 +0000</pubDate>
        <link>http://bcarrigan.com/2013/07/01/usb-business-card-pt-2/</link>
        <guid isPermaLink="true">http://bcarrigan.com/2013/07/01/usb-business-card-pt-2/</guid>
        
        
      </item>
    
  </channel>
</rss>
