Jekyll2023-06-05T20:44:20+00:00/feed.xmlKevin E ClaytorI'm a physicist and tinkerer.Netrunner Laser-Cut Identities2023-06-03T00:00:00+00:002023-06-03T00:00:00+00:00/2023/06/03/netrunner-ids<h1 id="netrunner-laser-cut-identities">Netrunner Laser-Cut Identities</h1>
<p>I’ve been using the laser cutter/engraver to generate some Netrunner identities for some time, and finally got some that look really good.</p>
<p>Initially, I started out by turning the standard card art grayscale and etching it into 1/8” acrylic.
As you can see from the image below, my first two attempts (bottom) came out inverted, as darker colors in the image are more etching and therefore lighter in the final raster.
(Personally, I kinda like the “ghost” look, and these are some of my favorites to play with.)
Inverting the image gave nice results for shaper and criminal.
You can also see that I was toying with opaque vs. transparent acrylic - I like the look of the transparent, but it does not help the legibility.</p>
<p><img src="/assets/images/netrunner-laser-identities/progress.jpg" alt="Progress shot of laser identities." /></p>
<p>As I was etching these, I learned;</p>
<ul>
<li>the darker acrylic looks much better as there is higher contrast between the acrylic and etching</li>
<li>the result is <strong>very</strong> sensitive to grayscale levels, I could not consistently get a good looking etch</li>
<li>for the lighter colors you can etch a negative, then paint over it, and wash the paint off the surface to get a neat effect (for a lot of work)</li>
</ul>
<p>At a circuit opener, I was talking with another player who had brought laser etched ids, and mentioned that I had done some initial experiments with cutting out slices of the acrylic from the card art.
While it looked very neat, it was also a <strong>lot</strong> of work - I had to sub-select specific vertices, split them off from the larger art, and ensure that just the space I wanted would get cut (vs. a larger center area falling out).
Thinking it over some more, I decided that it would be great to try with a simpler pattern.</p>
<p>Thankfully, <a href="https://inkscape.org/">inkscape</a> has a “trace bitmap” tool, so I used that to trace the Jinteki tree logo and, well, it looks awesome:</p>
<p><img src="/assets/images/netrunner-laser-identities/pe.jpg" alt="Laser cut Jinteki: Personal Evolution identity" /></p>
<p>I had decided to play both PE and Esâ, so I also had do one for xir.
The art here was much more detailed, and I was getting large chunks falling out, and not really capturing the image well on the first pass.
Subsequently, I went into the vector points, and edited the large “hollow” areas on the hood to be an outline instead, and widened the hair and eyes.
I think the result looks fantastic:</p>
<p><img src="/assets/images/netrunner-laser-identities/esa.jpg" alt="Laser cut Anarch: Esâ identity" /></p>
<p>(I actually forgot the link and MU icons on the first pass, and had to throw them back in the laser to etch just those portions, nearly making myself late to the CO I was playing in!)</p>
<p>Here are the vector art files if you’d like to make your own:</p>
<ul>
<li><a href="/assets/images/netrunner-laser-identities/Jinteki-PE.svg">Jinteki: PE</a></li>
<li><a href="/assets/images/netrunner-laser-identities/Anarch-Esa.svg">Anarch: Esâ</a></li>
</ul>K. E. ClaytorNetrunner Laser-Cut Identities I’ve been using the laser cutter/engraver to generate some Netrunner identities for some time, and finally got some that look really good.M.C. Escher Ice Cube Molds2023-01-17T00:00:00+00:002023-01-17T00:00:00+00:00/2023/01/17/escher-art<p>When staying at a bed-and-breakfast they had these really neat hexagonal ice cube trays.
We picked up some of our own when we got home, but the thought was planted - “what other shapes could I do?”
Of course, one of the most interesting space-filling tiling artists of all time has to be M.C. Escher.</p>
<h2 id="the-method">The Method</h2>
<p>This was unfortunately very painstaking.
The first step was to start with the art on the left - the sketch of the bird/fish space filling pattern.</p>
<p>Then, in <a href="https://www.gimp.org/">gimp</a> I adjusted levels, brightness / contrast, and saturation, and applied a threshold to reduce the image to just the black trace lines between the bird/fish.
In the center, you can see these lines cleaned up into only the center four-by-four bird/fish pattern.</p>
<p><img src="/assets/images/escher-ice/process.jpg" alt="Left: The image I was working from. Center: The line threshold in GIMP. Right: The three traced vector lines in Inkscape." /></p>
<p>Next, I imported these into <a href="https://inkscape.org/">inkscape</a> and did a vector trace (right).
This applied a lot of vector points, which I had to reduce by hand, once I had nice vector-art of just the bird/fish black lines, I made three copies. Remember, the trace of a thick line produces <em>two</em> vector lines, one for the interior line and one for the exterior line, and they are filled in-between.</p>
<p>This gave me;</p>
<ol>
<li>just the interior lines</li>
<li>just the outer lines, enlarged</li>
<li>just the outer trace of the outer lines, enlarged</li>
</ol>
<p>The interior lines (1) were separated and filled with the light turquoise / red in the image.
The outer lines, enlarged (2) were filled with black and form the outer wall.
The trace of the outer lines (3) form the base of the mold.</p>
<p>Each of these was imported into <a href="tinkercad.com/">TinkerCAD</a> and raised to a different height.
The base (3) is the shortest and supports the center pattern (1) and the wall (2), which is the tallest layer to hold the silicone in.</p>
<p><img src="/assets/images/escher-ice/tinker-cad.png" alt="TinkerCAD model of the vector art." /></p>
<p>Once the 3D model was done, it was off to the printer.</p>
<h2 id="the-molds">The Molds</h2>
<p>Naturally, I didn’t get the mold size correct the first time.
The first iteration (yellow) was much to small.
The second (red, 170% size) didn’t have enough space for the mold to flow into.
I had to head back to inkscape and manually move the verties around to get to the third (green) iteration, which was just right.</p>
<p><img src="/assets/images/escher-ice/3d-print-molds.jpg" alt="The three molds. From left to right, the first mold (yellow), second (red), and third (green)" /></p>
<h2 id="the-results">The Results</h2>
<p>The result is a pretty good mold.
I only relied on time and the viscosity of the silicone to allow it to seep into the cracks and allow the larger air bubbles to rise to the surface.</p>
<p>There are some small air bubbles within the mold, but the walls are intact and well-separate the different ice voids.
Ideally, I would have a vacuum chamber for this step to further degass the silicone and remove the rest of the bubbles.</p>
<p><img src="/assets/images/escher-ice/escher-mold.jpg" alt="The resulting mold." /></p>
<p>Putting some colored water into the mold makes for a really striking picture:</p>
<p><img src="/assets/images/escher-ice/escher-ice.jpg" alt="The mold filled with colored ice." /></p>
<h2 id="next-steps">Next Steps</h2>
<p>While writing this up kiddo pointed out that the mold has the birds facing the other direction - the opposite direction of the art!
Quite obvious in hind-sight as the mold is a negative of the 3D print.
Clearly I’ll have to do a left-right mirror and correct the ice cube tray to more closely match the art.</p>K. E. ClaytorWhen staying at a bed-and-breakfast they had these really neat hexagonal ice cube trays. We picked up some of our own when we got home, but the thought was planted - “what other shapes could I do?” Of course, one of the most interesting space-filling tiling artists of all time has to be M.C. Escher.Netrunner Card Boxes2022-11-21T00:00:00+00:002022-11-21T00:00:00+00:00/2022/11/21/netrunner-boxes<h1 id="netrunner-card-boxes">Netrunner Card Boxes</h1>
<p>I’ve been wanting to upgrade my Netrunner card storage for a while.
Now that I have access to a laser cutter/engraver, I finally took the plunge and designed these boxes.
I think they turned out fantastic!</p>
<p>I future proofed so;</p>
<ol>
<li>They’re deep enough to fit the entire FFG + Nisei card pool with room to expand (unsleeved)</li>
<li>They’re sized to fit dragon sleeves (I haven’t sleeved my collection yet, so I’m not sure if it works).</li>
</ol>
<p>Between all of the factions, mini-factions, neutral, and alt-art boxes it is <em>twelve</em> boxes for a full set.</p>
<h2 id="view-the-gallery"><a href="/galleries/netrunner-gallery">View the Gallery</a></h2>
<h2 id="download-the-files"><a href="#files">Download the Files</a></h2>
<h2 id="view-assembly-instructions"><a href="/galleries/netrunner-assembly">View Assembly Instructions</a></h2>
<h2 id="read-the-prototyping-log"><a href="#prototyping">Read the Prototyping Log</a></h2>
<p><img src="/assets/images/netrunner-boxes/illuminated.jpeg" alt="Final boxes illuminated" /></p>
<h2 id="files">Files</h2>
<p>Here are the files if you want to make you own:</p>
<ul>
<li>Version 3 <strong>final</strong> Coreldraw (cdr) files:
<ul>
<li><a href="/assets/images/netrunner-boxes/final/boxes_v3_final.cdr">Boxes</a></li>
<li><a href="/assets/images/netrunner-boxes/final/shading-test.cdr">Shading test</a></li>
<li><a href="/assets/images/netrunner-boxes/final/corp-hb.cdr">Corp - HB</a></li>
<li><a href="/assets/images/netrunner-boxes/final/corp-weyland.cdr">Corp - Weyland</a></li>
<li><a href="/assets/images/netrunner-boxes/final/corp-jinteki.cdr">Corp - Jinteki</a></li>
<li><a href="/assets/images/netrunner-boxes/final/corp-nbn.cdr">Corp - NBN</a></li>
<li><a href="/assets/images/netrunner-boxes/final/corp-netural.cdr">Corp - Neutral</a></li>
<li><a href="/assets/images/netrunner-boxes/final/runner-shaper.cdr">Runner - Shaper</a></li>
<li><a href="/assets/images/netrunner-boxes/final/runner-anarch.cdr">Runner - Anarch</a></li>
<li><a href="/assets/images/netrunner-boxes/final/runner-crim.cdr">Runner - Criminal</a></li>
<li><a href="/assets/images/netrunner-boxes/final/runner-minifac.cdr">Runner - Mini Factions</a></li>
<li><a href="/assets/images/netrunner-boxes/final/runner-netural.cdr">Runner - Neutral</a></li>
<li><a href="/assets/images/netrunner-boxes/final/generic-link.cdr">Neutral - Link Logo</a></li>
<li><a href="/assets/images/netrunner-boxes/final/generic-nisei.cdr">Neutral - Nisei Logo</a></li>
</ul>
</li>
<li>Version 3 <strong>beta</strong> Inkscape (svg) files:
<ul>
<li><a href="/assets/images/netrunner-boxes/final/boxes_v3_draft.svg">Single draft file</a></li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>NOTE:</strong> The card separator inserts are a bit wide once the boxes have been glued together - you should either;</p>
<ol>
<li>glue the boxes together with an insert in them to ensure the inserts fit, or</li>
<li>thin down the inserts a bit prior to laser cutting</li>
</ol>
</blockquote>
<blockquote>
<p><strong>NOTE:</strong> The hexagons are designed to have four shading levels (none, light, medium, dark).
You should run the shading test to determine which color levels you want to use and then adjust the groups in the boxes_v3_final accordingly.</p>
<p>To get the best effect, you may want to try different styles of plywood.
I found that “Lauan” plywood worked well for me.</p>
</blockquote>
<h2 id="prototyping">Prototyping</h2>
<h3 id="version-1">Version 1</h3>
<p>I knew what I wanted to do with the tab/slot construction and the first version largely shows that.</p>
<p><img src="/assets/images/netrunner-boxes/version1.jpg" alt="The version 1 box" /></p>
<p>However, this build really highlighted three shortcomings;</p>
<ol>
<li>I didn’t like the aesthetics of the full-acrylic front,</li>
<li>having the walls higher than the card height made it tough to extract cards,</li>
<li>the separator slots were too few and far between.</li>
</ol>
<p>At this point, I also realized that;</p>
<ol>
<li>I could have them stack since they have two “rails” on the bottom from the walls, and</li>
<li>The sides and top could really use some decoration.</li>
</ol>
<h3 id="version-2">Version 2</h3>
<p>Version two fixed most of these shortcomings and I was quite pleased with it.
The only major difference was the notches in the front of the lid were missing, which meant that it would slide back and forth.</p>
<p><img src="/assets/images/netrunner-boxes/version2.jpg" alt="The version 2 box on the right and version 3 on the left" /></p>
<p>Corrections on the version 2 box;</p>
<ol>
<li>the lid had alignment notches in the back but not in the front (see the picture above), as a result;
<ul>
<li>the front would wiggle back and forth</li>
<li>you had to check which was the front/back of the lid before putting it on</li>
</ul>
</li>
<li>who built it? I needed to to engrave my signature somewhere!</li>
</ol>
<h3 id="version-3">Version 3</h3>
<p>Version three solved these and I went ahead with building all 12!</p>
<p><img src="/assets/images/netrunner-boxes/gallery/gallery-9.jpeg" alt="Final versions holding cards" /></p>
<p>As mentioned above, I would still fix the insert width;</p>
<ol>
<li>insert is a bit too wide, would reduce by 1/8 - 1/4th of an inch.</li>
</ol>
<h2 id="assembly-instructions">Assembly Instructions</h2>
<p><a href="/galleries/netrunner-assembly">View the gallery</a> for assembly instructions.</p>K. E. ClaytorNetrunner Card Boxes I’ve been wanting to upgrade my Netrunner card storage for a while. Now that I have access to a laser cutter/engraver, I finally took the plunge and designed these boxes. I think they turned out fantastic!Installing Ruby/Jekyll on MacOS 13 on M12022-11-11T00:00:00+00:002022-11-11T00:00:00+00:00/2022/11/11/jekyll-serve-mac-13<p>Some notes on updating ruby-on-rails on my M1 Mac Air running OSX 13:</p>
<ol>
<li>Trying to install ruby via the <a href="https://jekyllrb.com/docs/installation/macos/">jekyll instructions</a> did not work
<ul>
<li>I kept having issues with <code class="language-plaintext highlighter-rouge">ruby -v</code> and <code class="language-plaintext highlighter-rouge">which ruby</code> returning the pre-installed system ruby (2.6.x)</li>
<li><code class="language-plaintext highlighter-rouge">ruby-install ruby</code> did not complete, instead erroring out with <code class="language-plaintext highlighter-rouge">symbol(s) not found for architecture arm64</code></li>
</ul>
</li>
<li>However, they linked to <a href="https://www.moncefbelyamani.com/why-you-shouldn-t-use-the-system-ruby-to-install-gems-on-a-mac/">Don’t use sudo gem install</a>.
Which led me to the <a href="https://www.moncefbelyamani.com/how-to-install-xcode-homebrew-git-rvm-ruby-on-mac/#start-here-if-you-choose-the-long-and-manual-route">full install instructions for mac</a>
<ul>
<li>The key was; <code class="language-plaintext highlighter-rouge">ruby-install ruby -- --enable-shared</code></li>
</ul>
</li>
<li>Once that worked, the rest proceeded smoothly until it came time to serve the site.</li>
<li>I needed to add two additional gems (the install instructions only mention the first)
<ul>
<li><code class="language-plaintext highlighter-rouge">bundle add webrick</code></li>
<li><code class="language-plaintext highlighter-rouge">bundle add rexml</code></li>
<li>You may need <code class="language-plaintext highlighter-rouge">bundle update</code> and <code class="language-plaintext highlighter-rouge">bundle install</code></li>
</ul>
</li>
<li>Then jekyll serve worked; <code class="language-plaintext highlighter-rouge">bundle exec jekyll serve --livereload</code></li>
</ol>K. E. ClaytorSome notes on updating ruby-on-rails on my M1 Mac Air running OSX 13:Sci-Fi Mobile2022-10-22T00:00:00+00:002022-10-22T00:00:00+00:00/2022/10/22/sci-fi-mobile<h1 id="sci-fi-mobile">Sci-Fi Mobile</h1>
<p>I recently took training for the 3D printers at my makerspace.
As a first project I found these neat “<a href="https://www.thingiverse.com/nakozen/designs">kit-cards</a>” on <a href="https://www.thingiverse.com/">Thingiverse</a>, a great resource for 3D print ideas.
These reminded me of the model airplane kits I used to build as a kid and the hours of fun I had there.
After printing them out and assembling them with kiddo, I thought they would look good in a mobile.
So I put together a quick laser-cut arc template and assembled them into a mobile.</p>
<p><img src="/assets/images/scifimobile/mobile.gif" alt="Assembled mobile turning around" /></p>
<p>They were also great test pieces for the 3D printer.
Using these I was able to test the different (raft/brim) adhesion options, adhesion material (I preferred the PLA, as it snapped off cleanly and could be soaked in water to remove the rest), and fill percentage.</p>
<p>The arc lengths on the mobile are all the same length, so I had to balance it to the models’ weights.
Here’s the weight balancing diagram:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Cylon = 6g Viper = 5g
EnterpriseD = 8g Tie Fighter = 11g
Enterprise A = 3g XWing = 5g
</code></pre></div></div>
<p>Link to my laser-cut arc template (SVG):</p>
<ul>
<li><a href="/assets/images/scifimobile/mobile-arcs.svg">Arc template</a></li>
</ul>
<p>Links to the ship models:</p>
<ul>
<li><a href="https://www.thingiverse.com/thing:4601489">Enterprise D</a></li>
<li><a href="https://www.thingiverse.com/thing:4587596">Enterprise A</a></li>
<li><a href="https://www.thingiverse.com/thing:4324875">Viper mk. II</a></li>
<li><a href="https://www.thingiverse.com/thing:4751456">Cylon Raider</a></li>
<li><a href="https://www.thingiverse.com/thing:4683520">X-Wing</a></li>
<li><a href="https://www.thingiverse.com/thing:4708937">Tie Advanced</a></li>
</ul>K. E. ClaytorSci-Fi MobileYAY Tracker2020-09-12T00:00:00+00:002020-09-12T00:00:00+00:00/2020/09/12/yaytracker<h1 id="the-yay-tracker">The YAY Tracker</h1>
<p>So I’ve been mostly enjoying pandemic work-from-home.
The one thing I don’t like though is the easy access to snacks.
So to try to reduce my snacking consumption, I thought I would try to replace the instant gratification of a snack with something else.</p>
<p>Thus was born the “YAY Tracker” - instead of going for a snack, I can click “YAY” (A button) and get some motivational support!
If I can’t refuse temptation and end up getting the snack, I can click the “NAY” (C button) and get some support for next time.
The total counts of “YAY”s (snack avoided) and “NAY”S (whoops snacked) are logged throughout the day and can be accessed (B button).</p>
<p>Here’s what it looks like:</p>
<p><img src="/assets/images/yaytracker/yaytracker.gif" alt="YAY Tracker in action" /></p>
<p>This project also appears at:</p>
<ul>
<li><a href="https://github.com/KEClaytor/YAYTracker">Github</a></li>
</ul>
<p>This project costs $43 and is easy:</p>
<ul>
<li>Hardware: Easy</li>
<li>Software: Easy</li>
<li>Assembly: Easy</li>
</ul>
<h1 id="hardware">Hardware</h1>
<p>The electronics <a href="http://www.adafruit.com/wishlists/510219">parts list</a> consist of:</p>
<ul>
<li>Feather M0</li>
<li>Featherwing OLED 128x32</li>
<li>Lithium Ion Battery 400 mAh</li>
</ul>
<h1 id="wiring--assembly">Wiring & Assembly</h1>
<p>Solider on some <a href="https://www.adafruit.com/product/2940">short headers</a> or <a href="https://www.adafruit.com/product/2830">stacking headers</a> to the feather, snap on the LCD featherwing, sandwiching in the battery, upload the code, and you’re good to go.</p>
<h1 id="code">Code</h1>
<p>The full source is on <a href="https://github.com/KEClaytor/YAYTracker">Github</a>, I re-used the button code from the <a href="https://github.com/KEClaytor/multi-pad">multipad project</a>.</p>
<p>There’s nothing too fancy about it - pull a random message from a list, increment a counter, keep track of display levels.
Regarding the dispaly, the <a href="https://learn.adafruit.com/circuitpython-display-support-using-displayio/introduction">Adafruit displayio has an excellent tutorial</a>.</p>
<h1 id="conclusion">Conclusion</h1>
<p>This was a fun introduction to the adafruit drawing library, but ultimately it did not help me in my snacking goals.
Some improvements would be longer battery life to where it could go the whole day on a charge, and storage of the yay/nay counts so they don’t reset when power is lost.</p>K. E. ClaytorThe YAY TrackerMordin Solus2020-04-29T00:00:00+00:002020-04-29T00:00:00+00:00/2020/04/29/mordin<h1 id="mordin-solus-timer">Mordin Solus Timer</h1>
<p>Kiddo found an old Mordin Solus (Mass Effect) action figure I had and wanted to know when it would light up again.
So we added four RGB LEDs, a speaker, and a button and turned it into a ambient light and pomodoro timer.</p>
<p>Here’s the summary video:</p>
<iframe width="560" height="315" src="https://www.youtube.com/watch?v=L14t6L6Qrks" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p>This project also appears at:</p>
<ul>
<li><a href="https://github.com/KEClaytor/Mordin">Github</a></li>
<li><a href="https://hackaday.io/project/171283-mordin-solus-light-timer">Hackaday</a></li>
</ul>
<p>This project costs at most $21 (cheaper if you have some parts already) and is easy:</p>
<ul>
<li>Hardware: Easy</li>
<li>Software: Easy</li>
<li>Assembly: Easy</li>
</ul>
<p><img src="/assets/images/mordin/front.jpg" alt="Front view" /></p>
<h1 id="hardware">Hardware</h1>
<p>The electronics <a href="http://www.adafruit.com/wishlists/504399">parts list</a> consist of:</p>
<ul>
<li>Trinket M0</li>
<li>4x neopixel RGB LEDs</li>
<li>1x small speaker</li>
<li>1x tactile button</li>
<li>Perma-protoboard</li>
</ul>
<p>The non-electronics parts consist of:</p>
<ul>
<li>Action figure</li>
</ul>
<h1 id="wiring">Wiring</h1>
<p>The wiring is really simple:</p>
<ul>
<li>5V power to LED’s</li>
<li>2 digital pins for LED control</li>
<li>Capacitor to smooth out LED power</li>
<li>Speaker to analog out</li>
<li>Pull down switch</li>
</ul>
<p>Here’s the schematic:</p>
<p><img src="/assets/images/mordin/Mordin_bb.png" alt="wiring schematic" /></p>
<p>And the circuit diagram:</p>
<p><img src="/assets/images/mordin/Mordin_schem.png" alt="circuit diagram" /></p>
<h1 id="code">Code</h1>
<p>The full source is on <a href="https://github.com/KEClaytor/Mordin">Github</a>, I re-used the button code from the <a href="https://github.com/KEClaytor/multi-pad">multipad project</a>, but the neat part is the trinary counter system.
Since there’s only one button, you can set the timer by pressing the button multiple times.
I toyed around with a number of ways of indicating the amount of time set.
For example, I would increase the light intensity with every click, but this was not as clear as I would have liked.
I also tried a binary on/off counter for the lights, but that couldn’t count high enough, and didn’t look good with the console lights turning off.</p>
<p>In Mass Effect, there are three colors as a major palette - orange (ambient / consoles), red (renegade), and blue (paragon).
So I adopted a trinary counter where there are three options for each bit (0, 1, or 2).
Orange would represent zero, red one, and blue two.
This provided clear distinctions as you increase time with clicks, and the colors blended nicely as the timer counts down.</p>
<p>Let’s see how to convert between decimal and trinary.
In binary, you multiply the bit value (0 or 1) by 2 raised to the bit index.
So 1 in decimal is 001 in binary; <code class="language-plaintext highlighter-rouge">0*2^2 + 0*2^1 + 1*2^0 = 0 + 0 + 1 = 1</code>.
5 in decimal is 101 in binary; <code class="language-plaintext highlighter-rouge">1*2^2 + 0*2^1 + 1*2^0 = 4 + 0 + 1 = 5</code>.
You can also think of this as the power of two’s place; [4’s place, 2’s place, 1’s place].</p>
<p>In trinary, you do the same thing; multiply the bit value (0, 1, or <em>2</em>) by <em>3</em> raised to the bit index.
So 1 in decimal is still 01 in trinary; <code class="language-plaintext highlighter-rouge">0*3^1 + 1*3^0 = 0 + 1 = 1</code>.
5 in decimal is now 12 in trinary; <code class="language-plaintext highlighter-rouge">1*3^1 + 2*3^0 = 3 + 2 = 5</code>.
You can also think of this as the power of three’s place; [9’s place, 3’s place, 1’s place].</p>
<p>The smaller bit representation in trinary also allows us to reach higher values with the same number of bits.</p>
<p>Here’s the code that converts a decimal to a trinary list:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">decimal_to_ternary</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">t_len</span><span class="o">=</span><span class="mi">3</span><span class="p">):</span>
<span class="s">""" Convert a decimal to a ternary key.
"""</span>
<span class="n">t</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="n">t_len</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">t_len</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
<span class="c1"># print(d, p, 3**(p-1), d // (3**(p-1)) )
</span> <span class="n">tp</span> <span class="o">=</span> <span class="n">d</span> <span class="o">//</span> <span class="mi">3</span> <span class="o">**</span> <span class="p">(</span><span class="n">p</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">t</span><span class="p">[</span><span class="n">p</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">tp</span>
<span class="n">d</span> <span class="o">-=</span> <span class="n">tp</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">**</span> <span class="p">(</span><span class="n">p</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">return</span> <span class="n">t</span>
</code></pre></div></div>
<p>and one that goes the other way from ternary to decimal:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">ternary_to_decimal</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
<span class="s">""" Convert a ternary key to a decimal.
"""</span>
<span class="n">d</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">p</span><span class="p">,</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
<span class="c1"># print(t, k, p, (3**p) * k)
</span> <span class="n">d</span> <span class="o">+=</span> <span class="p">(</span><span class="mi">3</span> <span class="o">**</span> <span class="n">p</span><span class="p">)</span> <span class="o">*</span> <span class="n">k</span>
<span class="k">return</span> <span class="n">d</span>
</code></pre></div></div>
<p>Here’s some output so you can visualize the conversion better;</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">ii</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">27</span><span class="p">):</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">decimal_to_ternary</span><span class="p">(</span><span class="n">ii</span><span class="p">)</span>
<span class="n">d</span> <span class="o">=</span> <span class="n">ternary_to_decimal</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"%02d: %13s = %2d"</span> <span class="o">%</span> <span class="p">(</span><span class="n">ii</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">d</span><span class="p">))</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> [9's, 3's, 1's] = decimal
00: [0, 0, 0] = 0
01: [1, 0, 0] = 1
02: [2, 0, 0] = 2
03: [0, 1, 0] = 3
04: [1, 1, 0] = 4
05: [2, 1, 0] = 5
06: [0, 2, 0] = 6
07: [1, 2, 0] = 7
08: [2, 2, 0] = 8
09: [0, 0, 1] = 9
10: [1, 0, 1] = 10
11: [2, 0, 1] = 11
12: [0, 1, 1] = 12
13: [1, 1, 1] = 13
14: [2, 1, 1] = 14
15: [0, 2, 1] = 15
16: [1, 2, 1] = 16
17: [2, 2, 1] = 17
18: [0, 0, 2] = 18
19: [1, 0, 2] = 19
20: [2, 0, 2] = 20
21: [0, 1, 2] = 21
22: [1, 1, 2] = 22
23: [2, 1, 2] = 23
24: [0, 2, 2] = 24
25: [1, 2, 2] = 25
26: [2, 2, 2] = 26
</code></pre></div></div>
<h1 id="assembly">Assembly</h1>
<p>Everything’s soldered into a perma-proto with some headers.
This allows me to unplug components as needed - which I had to do, as I had to re-solder the console LEDs.
Also, I may change the functionality in the future and wanted to make that easy.</p>
<p><img src="/assets/images/mordin/back.jpg" alt="Back / component view" /></p>
<h1 id="conclusion">Conclusion</h1>
<p>It felt good to get this project off the list.
I’d like to eventually work out some USB serial communications, so I can push notifications to Mordin from the PC, but right now I don’t have a very good use case for that.
So I’m happy enough to have him just changing light colors on the desk and available as a timer when needed.</p>K. E. ClaytorMordin Solus TimerMulti-Function-Button-Pad2020-02-15T00:00:00+00:002020-02-15T00:00:00+00:00/2020/02/15/multi-pad<h1 id="multi-function-button-pad">Multi-Function Button Pad</h1>
<p>When Josie and I saw the <a href="https://learn.adafruit.com/adafruit-trellis-diy-open-source-led-keypad/overview">Trellis</a> - an illuminated button pad - we just knew we needed to invent a project so we could get one.</p>
<p>This project is pretty simple hardware-wise, but flexible enough that it can be programmed with plenty of neat functions.
For example, you could label the buttons and turn it into a simple calculator. My audience is just learning addition and subtraction, so we made a couple of simple math games and a puzzle game.</p>
<p>Here’s the summary video:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/fImjUiyOqV8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p>This project also appears at:</p>
<ul>
<li><a href="https://github.com/KEClaytor/multi-pad">Github</a></li>
<li><a href="https://hackaday.io/project/169708-multi-pad">Hackaday</a></li>
</ul>
<p>This project cost $50 and is easy:</p>
<ul>
<li>Hardware: Easy</li>
<li>Software: Easy</li>
<li>Assembly: Easy</li>
</ul>
<h1 id="hardware">Hardware</h1>
<p>The electronics <a href="http://www.adafruit.com/wishlists/499921">parts list</a> consist of:</p>
<ul>
<li>Trinket M0</li>
<li>16x 3mm LEDs</li>
<li>1x trellis</li>
<li>1x soft button pad for trellis</li>
<li>1x 14-segment LED display</li>
<li>2x momentary pushbuttons</li>
<li>Half-sized perma-protoboard</li>
<li>2xAA battery holder</li>
<li>1x latching pushbutton (needed if battery holder doesn’t have a switch)</li>
<li>Female headers (optional)</li>
</ul>
<p>The non-electronics parts consist of:</p>
<ul>
<li>Cardboard box</li>
</ul>
<h1 id="wiring">Wiring</h1>
<p>I’ll introduce the full circuit diagram and then break it down.</p>
<p>The wiring schematic is quite busy, but very straightforward.</p>
<ul>
<li>connect everything to grouknd.</li>
<li>the Trellis and 14-segment display need I2C SDA and SCL</li>
<li>mode and select go to D3 and D4</li>
<li>power comes in through the power switch to Vbatt.</li>
</ul>
<p>One issue that I ran into was the power for the Trellis. When running off of USB, use USB power (V_usb) but when running off of batteries, the 3.4 V 2xAA cannot supply the V_usb pin correctly, so instead use the V_batt pin.</p>
<p>Here’s the schematic:</p>
<p><img src="/assets/images/multipad/breadboard.png" alt="wiring schematic" /></p>
<p>And the circuit diagram:</p>
<p><img src="/assets/images/multipad/schem.png" alt="circuit diagram" /></p>
<p>As always, prototype on a protoboard before transferring to the perma-proto.</p>
<p><img src="/assets/images/multipad/tools.jpg" alt="Getting the tools together" /></p>
<p>Soldering together the perma-proto was a bit of an exercise in trying to not overlap pins.
I ended up soldering the display headers to one side, and the rest (Trinket, button, power) headers to the other.
Thankfully, most of the pins on the 14-segment display are not connected - only the power and I2C pins are used.
Hence, the Trinket M0 can fit between these and power can be pulled out.
Also, the perma-proto was a tad to big to fit in the cardboard with the Trellis, so I sawed off the end and both sides.</p>
<p>Here’s the fully wired interior:</p>
<p><img src="/assets/images/multipad/open.jpg" alt="Wired interior" /></p>
<h1 id="code">Code</h1>
<p>The full source is on <a href="https://github.com/KEClaytor/multi-pad">Github</a>, but I’ll pull out a few snippets here to discuss design choices.</p>
<p>The code can be split into;
1) logic to switch between modes and
2) the logic for each mode.</p>
<p>For the buttons, I use a helper class that provides some useful button functions.
Instead of just “is the button pressed?” it also has functions to answer “was the button just pressed” and “was the button just released”.</p>
<p>The bulk of the button logic is done in the <code class="language-plaintext highlighter-rouge">update</code> function, which checks the current state and compares it to the previous state. This function needs to be called before the “check” functions (<code class="language-plaintext highlighter-rouge">.pressed()</code>, <code class="language-plaintext highlighter-rouge">just_pressed()</code>, and <code class="language-plaintext highlighter-rouge">just_released()</code>):</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Button</span><span class="p">():</span>
<span class="s">""" Button convenience class.
Create a button:
>>> button = Button(board.D3, digitalio.Pull.UP)
Update the button state:
>>> button.update()
And then check for conditions:
>>> if button.pressed():
>>> ...
>>> if button.just_pressed():
>>> ...
>>> if button.just_released():
>>> ...
"""</span>
<span class="p">...</span>
<span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s">""" Read the current button state and update internal state.
"""</span>
<span class="c1"># Update the last state
</span> <span class="bp">self</span><span class="p">.</span><span class="n">last_state</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span>
<span class="c1"># Read the current state
</span> <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">mode</span> <span class="o">==</span> <span class="n">digitalio</span><span class="p">.</span><span class="n">Pull</span><span class="p">.</span><span class="n">DOWN</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">button</span><span class="p">.</span><span class="n">value</span>
<span class="k">elif</span> <span class="bp">self</span><span class="p">.</span><span class="n">mode</span> <span class="o">==</span> <span class="n">digitalio</span><span class="p">.</span><span class="n">Pull</span><span class="p">.</span><span class="n">UP</span><span class="p">:</span>
<span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="o">=</span> <span class="ow">not</span> <span class="bp">self</span><span class="p">.</span><span class="n">button</span><span class="p">.</span><span class="n">value</span>
<span class="c1"># Update the pressed / released states
</span> <span class="bp">self</span><span class="p">.</span><span class="n">edge_up</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="p">.</span><span class="n">last_state</span>
<span class="bp">self</span><span class="p">.</span><span class="n">edge_down</span> <span class="o">=</span> <span class="ow">not</span> <span class="bp">self</span><span class="p">.</span><span class="n">state</span> <span class="ow">and</span> <span class="bp">self</span><span class="p">.</span><span class="n">last_state</span>
</code></pre></div></div>
<p>With this helper class, we can do some interesting ways of changing the mode.
To prevent accidental mode changes, we can require that the mode button be held down, and then the select button pressed.
Then when the desired mode is selected, releasing the mode button will create a new “game” in that mode.
Alternative we can create a new game by pressing (and releasing) the select button.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Change modes
</span><span class="n">button_mode</span><span class="p">.</span><span class="n">update</span><span class="p">()</span>
<span class="n">button_sel</span><span class="p">.</span><span class="n">update</span><span class="p">()</span>
<span class="k">if</span> <span class="n">button_mode</span><span class="p">.</span><span class="n">pressed</span><span class="p">():</span>
<span class="k">if</span> <span class="n">button_sel</span><span class="p">.</span><span class="n">just_released</span><span class="p">():</span>
<span class="n">MODE</span> <span class="o">=</span> <span class="p">(</span><span class="n">MODE</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="n">MODE_LABELS</span><span class="p">)</span>
<span class="n">display</span><span class="p">.</span><span class="k">print</span><span class="p">(</span><span class="n">MODE_LABELS</span><span class="p">[</span><span class="n">MODE</span><span class="p">])</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">button_mode</span><span class="p">.</span><span class="n">just_released</span><span class="p">()</span> <span class="ow">or</span> <span class="n">button_sel</span><span class="p">.</span><span class="n">just_released</span><span class="p">():</span>
<span class="n">init</span><span class="p">()</span>
</code></pre></div></div>
<p>In retrospect, kiddo had some difficultly holding down the mode button to select a game, so I removed that function, and instead when the mode button is released, it increments the mode.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Change modes
</span><span class="n">button_mode</span><span class="p">.</span><span class="n">update</span><span class="p">()</span>
<span class="n">button_sel</span><span class="p">.</span><span class="n">update</span><span class="p">()</span>
<span class="k">if</span> <span class="n">button_mode</span><span class="p">.</span><span class="n">just_released</span><span class="p">():</span>
<span class="n">MODE</span> <span class="o">=</span> <span class="p">(</span><span class="n">MODE</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="n">MODE_LABELS</span><span class="p">)</span>
<span class="n">display</span><span class="p">.</span><span class="k">print</span><span class="p">(</span><span class="n">MODE_LABELS</span><span class="p">[</span><span class="n">MODE</span><span class="p">])</span>
<span class="k">if</span> <span class="n">button_sel</span><span class="p">.</span><span class="n">just_released</span><span class="p">():</span>
<span class="n">init</span><span class="p">()</span>
</code></pre></div></div>
<p>To simplify the game logic, we just use a switch, and keep most of the variables needed for the game modes as global variables:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Main logic
</span><span class="k">if</span> <span class="n">MODE</span> <span class="o">==</span> <span class="n">FREE</span><span class="p">:</span>
<span class="n">logic_free</span><span class="p">()</span>
<span class="k">elif</span> <span class="n">MODE</span> <span class="o">==</span> <span class="n">GAME</span><span class="p">:</span>
<span class="n">logic_game</span><span class="p">()</span>
<span class="k">elif</span> <span class="n">MODE</span> <span class="o">==</span> <span class="n">ADD</span><span class="p">:</span>
<span class="n">logic_add</span><span class="p">()</span>
<span class="k">elif</span> <span class="n">MODE</span> <span class="o">==</span> <span class="n">SUB</span><span class="p">:</span>
<span class="n">logic_sub</span><span class="p">()</span>
</code></pre></div></div>
<p>Now onto the fun part - the particular modes:</p>
<h2 id="free-play">Free play</h2>
<p>Freeplay is the easiest mode - initialize to an empty grid and toggle the button when pressed.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">init_free</span><span class="p">():</span>
<span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">.</span><span class="n">fill</span><span class="p">(</span><span class="bp">False</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">logic_free</span><span class="p">():</span>
<span class="s">""" Free play
Trellis button press changes LED state.
"""</span>
<span class="n">just_pressed</span><span class="p">,</span> <span class="n">released</span> <span class="o">=</span> <span class="n">trellis</span><span class="p">.</span><span class="n">read_buttons</span><span class="p">()</span>
<span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">just_pressed</span><span class="p">:</span>
<span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">[</span><span class="n">b</span><span class="p">]</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">[</span><span class="n">b</span><span class="p">]</span>
</code></pre></div></div>
<h2 id="game-mode">Game mode</h2>
<p>In game mode, a button press changes not only that button, but the neighboring buttons.
The goal is to go from a mixed-board state to a full-on or full-off state.</p>
<p>We just have to watch for the edge cases - literally.
If the button pressed is near the edge, we don’t want to overflow and switch states on another row.
So for this case, we convert the button index to (i, j) coordinates and do not switch elements beyond the bounds of the grid.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">game_button_press</span><span class="p">(</span><span class="n">b</span><span class="p">):</span>
<span class="s">""" Invert button b and neighbors.
"""</span>
<span class="k">print</span><span class="p">(</span><span class="s">"game button: "</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
<span class="c1"># Convert button index to row and column
</span> <span class="n">i</span> <span class="o">=</span> <span class="n">b</span> <span class="o">//</span> <span class="mi">4</span>
<span class="n">j</span> <span class="o">=</span> <span class="n">b</span> <span class="o">%</span> <span class="mi">4</span>
<span class="c1"># Get coordinates of button and neighbors
</span> <span class="n">px</span> <span class="o">=</span> <span class="p">[(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">),</span> <span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">j</span><span class="p">),</span> <span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">j</span><span class="p">),</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="o">-</span><span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="o">+</span><span class="mi">1</span><span class="p">)]</span>
<span class="k">for</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">)</span> <span class="ow">in</span> <span class="n">px</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="mi">0</span> <span class="o"><=</span> <span class="n">i</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="n">i</span> <span class="o"><</span> <span class="mi">4</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="mi">0</span> <span class="o"><=</span> <span class="n">j</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="n">j</span> <span class="o"><</span> <span class="mi">4</span><span class="p">):</span>
<span class="n">idx</span> <span class="o">=</span> <span class="p">(</span><span class="n">i</span> <span class="o">*</span> <span class="mi">4</span><span class="p">)</span> <span class="o">+</span> <span class="n">j</span>
<span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span>
</code></pre></div></div>
<p>The fun bit is to initialize - start with a full grid, and then randomly push buttons a random number of times.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">init_game</span><span class="p">():</span>
<span class="s">""" Create a new random game board.
"press" a random button between 8-12 times
"""</span>
<span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">.</span><span class="n">fill</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span>
<span class="k">for</span> <span class="n">press</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">13</span><span class="p">)):</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
<span class="n">game_button_press</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>
<span class="n">display</span><span class="p">.</span><span class="k">print</span><span class="p">(</span><span class="n">press</span><span class="p">)</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.2</span><span class="p">)</span>
</code></pre></div></div>
<p>The button logic is virtually the same as the free play mode, but we replace the toggle with our new game_button_press function.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">logic_game</span><span class="p">():</span>
<span class="s">""" Game play
Trellis button press inverts button and neighbors.
"""</span>
<span class="n">just_pressed</span><span class="p">,</span> <span class="n">released</span> <span class="o">=</span> <span class="n">trellis</span><span class="p">.</span><span class="n">read_buttons</span><span class="p">()</span>
<span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">just_pressed</span><span class="p">:</span>
<span class="n">game_button_press</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="addition-game">Addition game</h2>
<p>Another mode is an addition game, where we display an addition problem and you have to illuminate the correct number of buttons.
A variant would be numbering the buttons and have to press the result.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">init_add</span><span class="p">():</span>
<span class="s">""" Create a new addition problem
Choose a + b = c such that a, b < 10, and c <= 16
These conditions ensure that "a+b=" is pritable, and reachable
"""</span>
<span class="k">global</span> <span class="n">addition_goal</span>
<span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">.</span><span class="n">fill</span><span class="p">(</span><span class="bp">False</span><span class="p">)</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">9</span><span class="p">)</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">16</span> <span class="o">-</span> <span class="n">a</span><span class="p">)</span>
<span class="k">if</span> <span class="n">b</span> <span class="o">></span> <span class="mi">9</span><span class="p">:</span>
<span class="n">b</span> <span class="o">=</span> <span class="mi">9</span>
<span class="n">c</span> <span class="o">=</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span>
<span class="n">display</span><span class="p">.</span><span class="k">print</span><span class="p">(</span><span class="s">"{a:d}+{b:d}="</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="n">b</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>
<span class="n">addition_goal</span> <span class="o">=</span> <span class="n">c</span>
</code></pre></div></div>
<p>Our initialization function is now a bit more complicated.
We pick two numbers that sum to 16, the first we can specify that a is between 1 and 9 (inclusive).
The second could exceed 9 (eg; 16 - 1), so we cap our second integer at 9.
Note that we use the global variable <code class="language-plaintext highlighter-rouge">addition_goal</code> so we can reference it in our <code class="language-plaintext highlighter-rouge">logic_add</code> function.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_active</span><span class="p">():</span>
<span class="s">""" Return an array of pressed buttons.
"""</span>
<span class="k">return</span> <span class="p">[</span><span class="n">ii</span> <span class="k">for</span> <span class="n">ii</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">16</span><span class="p">)</span> <span class="k">if</span> <span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">[</span><span class="n">ii</span><span class="p">]]</span>
<span class="k">def</span> <span class="nf">logic_add</span><span class="p">():</span>
<span class="s">""" Game play
Toggle buttons and see if we "win".
"""</span>
<span class="k">global</span> <span class="n">addition_goal</span>
<span class="n">just_pressed</span><span class="p">,</span> <span class="n">released</span> <span class="o">=</span> <span class="n">trellis</span><span class="p">.</span><span class="n">read_buttons</span><span class="p">()</span>
<span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">just_pressed</span><span class="p">:</span>
<span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">[</span><span class="n">b</span><span class="p">]</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">[</span><span class="n">b</span><span class="p">]</span>
<span class="n">active</span> <span class="o">=</span> <span class="n">get_active</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">active</span><span class="p">),</span> <span class="n">addition_goal</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">active</span><span class="p">)</span> <span class="o">==</span> <span class="n">addition_goal</span><span class="p">:</span>
<span class="n">display</span><span class="p">.</span><span class="k">print</span><span class="p">(</span><span class="s">" ={c:2d}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">c</span><span class="o">=</span><span class="n">addition_goal</span><span class="p">))</span>
<span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">16</span><span class="p">):</span>
<span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">[</span><span class="n">b</span><span class="p">]</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">[</span><span class="n">b</span><span class="p">]</span>
</code></pre></div></div>
<p>We want a bit of a visual “bang” when we reach the correct number, so we invert the keypad colors when the correct answer is reached.
This actually turned into a really neat result;</p>
<ul>
<li>it let’s you toggle lights back to the solution again,</li>
<li>when the correct answer is 8 (half of the display) - the display starts to alternate between modes.</li>
</ul>
<h2 id="subtraction-game">Subtraction game</h2>
<p>The subtraction game is similar to the addition game, we just have to specify the initial conditions somewhat differently;</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">init_sub</span><span class="p">():</span>
<span class="s">""" Create a new subtraction problem
Choose a - b = c such that a > 1, b > 0, and c > 0
"""</span>
<span class="k">global</span> <span class="n">subtraction_goal</span>
<span class="n">trellis</span><span class="p">.</span><span class="n">led</span><span class="p">.</span><span class="n">fill</span><span class="p">(</span><span class="bp">False</span><span class="p">)</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">9</span><span class="p">)</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">a</span><span class="p">)</span>
<span class="n">c</span> <span class="o">=</span> <span class="n">a</span> <span class="o">-</span> <span class="n">b</span>
<span class="n">display</span><span class="p">.</span><span class="k">print</span><span class="p">(</span><span class="s">"{a:d}-{b:d}="</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="n">b</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">)</span>
<span class="n">subtraction_goal</span> <span class="o">=</span> <span class="n">c</span>
</code></pre></div></div>
<h2 id="minimizing-code">Minimizing code</h2>
<p>At the end of implementing all of these game functions, I started to get some warnings that there were issues in importing modules.
Of course the microcontroller has limited memory and at some point we start running into that limit.
As a result, I had to trim out some excess code - print statements, un-needed branches, etc.</p>
<h1 id="assembly">Assembly</h1>
<p>With the pad prototyped and programmed, we can now add some visual “polish”.
I think many people would 3D print a case, but cardboard works great as well.</p>
<p>To install the components, you can trace their outline and then cut out the matching hole.
The 14-segment display is the easiest - just trace around on the back of the cardboard, and cut the window out with an xacto-knife.
Similarly, trace the button pad and cut out holes for each of the buttons.
It may hold together better if you cut a hole for each button, rather than a larger hole for the entire pad.
Once, they’re in place, hot glue the display and button pad into place.
The pushbuttons usually have a nut to keep them in place.
You can use the interior of this nut to trace the hole for the button.
Cut out the hole, thread the button through, and secure into place with the nut.</p>
<p><img src="/assets/images/multipad/front.jpg" alt="Installed components" /></p>
<p>I always want to always re-use components so instead of soldering the components directly into the perma-protoboard, I solder in headers and plug in the components later.
This is also useful, as to fit all the parts, I had to install components on both sides of the perma-protoboard.</p>
<p>When wiring the power to the Trellis, I found that the V_usb pin works better when plugged into usb (delivering 5V).
But when running from 2xAA batteries (2 x 1.7V = 3.4V) the V_usb doesn’t put out a consistent 5V.
As a result, it’s better to plug in the Trellis to the V_batt pin and let it run off of the 3.4V.
However, V_batt doesn’t drive the Trellis well when on USB.</p>
<h1 id="conclusion">Conclusion</h1>
<p>With a straightforward circuit diagram (ground, power, SDA, SCL, and 2x digital button pins) this project was quick to prototype.
You may have some difficultly getting everything to fit in a box though!</p>
<p>With the assembly done, you can really let your programming go wild though.
I demonstrated a few simple modes here, but some other neat modes would be:</p>
<ul>
<li>calculator</li>
<li>tetris</li>
<li>Conway’s game of life</li>
</ul>K. E. ClaytorMulti-Function Button PadInteractive NeoPixel “Clock”2020-01-03T00:00:00+00:002020-01-03T00:00:00+00:00/2020/01/03/neopixel-clock<h1 id="interactive-neopixel-clock">Interactive NeoPixel Clock</h1>
<p>This project started out as an LED clock for my kid, but quickly evolved into just a cool interactive light-show that they could enjoy (know your audience - the clock function was not a killer feature).</p>
<p>Here’s the summary video:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/8xB9JruYcvE" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p>In addition to the twelve RGB LEDs, the three buttons allow for eight modes of operation and the “soft-pot” touch potentiometer allows for continuous selection, and selections that can be mapped to the clock face.</p>
<p>This project also appears at:</p>
<ul>
<li><a href="https://github.com/KEClaytor/color-wheel-clock">Github</a></li>
<li><a href="https://hackaday.io/project/169298-interactive-neopixel-clock">Hackaday</a></li>
</ul>
<h1 id="hardware">Hardware</h1>
<p>The electronics <a href="https://www.adafruit.com/wishlists/499222">parts list</a> consist of:</p>
<ul>
<li>Trinket M0</li>
<li>12x 8 mm NeoPixel LEDs</li>
<li>3x latching LED pushbuttons</li>
<li>Soft-pot touch potentiometer</li>
<li>Mini protoboard</li>
<li>3x 220 Ohm resistors</li>
<li>1x 10k Ohm resistor</li>
<li>1x 10 uF capacitor</li>
<li>USB (A to micro) cable</li>
</ul>
<p>The non-electronics parts consist of:</p>
<ul>
<li>1/4” plywood “face”</li>
<li>cardboard</li>
<li>paint</li>
<li>glue</li>
</ul>
<h1 id="assembly">Assembly</h1>
<p>Cut the plywood into a circle and drill twelve 8mm (x”) holes in a circle pattern.
The LEDs can be press-fit in and then soldered together.
Drill a series of 2mm holes and connect them to feed the soft-pot leads through.
Glue the cardboard around the plywood, then cut three holds for the buttons.</p>
<h1 id="wiring">Wiring</h1>
<p>I’ll introduce the full circuit diagram and then break it down.
First the wiring schematic:</p>
<p><img src="/assets/images/interactive_clock/fritzing.png" alt="wiring schematic" /></p>
<p>And the circuit diagram:</p>
<p><img src="/assets/images/interactive_clock/schematic.png" alt="circuit diagram" /></p>
<p>The individual NeoPixels are all connected.
The cathodes (- terminal) and anodes (+ terminal) are all solidered together.
Since they don’t have any capacitors built in, and each nominally needs a 100nF capacitor (<a href="https://learn.adafruit.com/adafruit-neopixel-uberguide/the-magic-of-neopixels">see the NeoPixel uberguide</a>, we can add the capacitance and use a 1-10 uF capacitor between the V USB (5 V) and ground pins on the Trinket.
The data out from each NeoPixel is then connected to the data in of the next until the last pixel is reached.
The first data is the connected to digital pin 3 on the Trinket.</p>
<p>The soft-pot is nominally a 0-10 kOhm resistor, with a variable resistance depending on where it is touched.
We us a 10 kOhm resistor as suggested in the data sheet.
This shifts the touch range from ½ Vcc (1.8 V) to Vcc (3.2 V), and allows us to detect when there is no touch (when the sense voltage is less than 1.8 Vcc).
Unfortunately, I have noticed that when touch is removed the output voltage can fluctuate.</p>
<p>Here’s an image of the wiring with just the LEDs and the soft-pot:</p>
<p><img src="/assets/images/interactive_clock/breadboard_leds_touch.jpg" alt="breadboard with only leds and touch wheel" /></p>
<p>The buttons include an internal LED, which can be illuminated independently of the state of the button.
However, the Trinket is pretty low on input / output pins (this project uses all of them), and so I wired the LED in series with the latching button itself.
This way, when the button is pressed, the LED is lit.</p>
<p>In general the buttons can be wired in one of two ways.
In the “pull up” mode, when the switch is closed, sense output (the connection to the microcontroller) is pulled up to Vcc.
This requires that the pin in the microcontroller is put in “pull down” mode - ie an internal resistor connects that pin to ground.
Here’s a small schematic:</p>
<p><img src="/assets/images/interactive_clock/switch_pull_up.png" alt="switch that pulls microcontroller pin up" /></p>
<p>One advantage of this is that then when you read that pin in code, if the value is high the pin is being depressed.
One disadvantage is that if there is any other resistance in series with the internal resistor, you might not get a full digital on signal.</p>
<p>Initially I did it this way, but noticed that I was only getting a signal that fluctuated between 0.5 V and 1.2 V (you can test this by switching the pin to an analog input and reading the voltage).
In the schematic above, there should be no resistance in series with the microcontroller pin’s internal resistor.
However, when I wired this, I think I put the microcontroller connection between the LED and the 220 Ohm resistor, meaning the internal LED resistance was in series, giving me the lower voltage.</p>
<p>The second way is doing a “pull down” switch.
In this mode, the switch shorts the microcontroller pin to ground.
On the microcontroller side the pin is “pulled up” with an internal resistor so that it naturally reads digital high.
The schematic looks similar, but with the parts inverted;</p>
<p><img src="/assets/images/interactive_clock/switch_pull_down.png" alt="switch that pulls microcontroller pin down" /></p>
<p>The advantages of this are that it’s really easy to short the pin to ground (you don’t have to worry about damaging anything).
The disadvantage is that the button is depressed when the pin is digital low, so you have to invert the pin state in code.</p>
<p>When using either of the above schematics, you only need three wires coming out of the button - you can jumper one of the LED leads to the switch:</p>
<p><img src="/assets/images/interactive_clock/button.jpg" alt="button led jumper" /></p>
<p>Here’s the fully wired interior:</p>
<p><img src="/assets/images/interactive_clock/breadboard_led_touch_buttons.jpg" alt="clock internals fully wired up" /></p>
<h1 id="code">Code</h1>
<p>The code can be split into;
1) the button logic,
2) the LED display logic, and
3) the soft-pot interaction logic.</p>
<p>For the buttons, we create three digital inputs using the internal pull up resistors:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">digitalio</span>
<span class="c1"># Red Button
</span><span class="n">button_r</span> <span class="o">=</span> <span class="n">digitalio</span><span class="p">.</span><span class="n">DigitalInOut</span><span class="p">(</span><span class="n">board</span><span class="p">.</span><span class="n">D2</span><span class="p">)</span>
<span class="n">button_r</span><span class="p">.</span><span class="n">switch_to_input</span><span class="p">(</span><span class="n">pull</span><span class="o">=</span><span class="n">digitalio</span><span class="p">.</span><span class="n">Pull</span><span class="p">.</span><span class="n">UP</span><span class="p">)</span>
</code></pre></div></div>
<p>Next we create a function that reads the button state, doing the appropriate inversion for when the button is pressed and returns a tuple of the state of the three buttons:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">get_button_state</span><span class="p">():</span>
<span class="s">""" Read and return a tuple of which buttons are pressed.
returns:
(Red Pressed, White Pressed, Green Pressed)
"""</span>
<span class="n">buttons</span> <span class="o">=</span> <span class="p">[</span><span class="n">button_r</span><span class="p">,</span> <span class="n">button_w</span><span class="p">,</span> <span class="n">button_g</span><span class="p">]</span>
<span class="n">state</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="ow">not</span> <span class="n">b</span><span class="p">.</span><span class="n">value</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">buttons</span><span class="p">)</span>
<span class="k">return</span> <span class="n">state</span>
</code></pre></div></div>
<p>Finally, we switch our action / mode logic based on which buttons are pressed.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="k">if</span> <span class="n">buttons</span> <span class="o">==</span> <span class="p">(</span><span class="bp">False</span><span class="p">,</span> <span class="bp">False</span><span class="p">,</span> <span class="bp">False</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Lights off"</span><span class="p">)</span>
<span class="p">...</span>
<span class="k">elif</span> <span class="n">buttons</span> <span class="o">==</span> <span class="p">(</span><span class="bp">True</span><span class="p">,</span> <span class="bp">False</span><span class="p">,</span> <span class="bp">False</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Red button pressed"</span><span class="p">)</span>
<span class="p">...</span>
<span class="k">elif</span> <span class="n">buttons</span> <span class="o">==</span> <span class="p">(</span><span class="bp">False</span><span class="p">,</span> <span class="bp">True</span><span class="p">,</span> <span class="bp">False</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"White button pressed"</span><span class="p">)</span>
<span class="p">...</span>
<span class="k">elif</span> <span class="n">buttons</span> <span class="o">==</span> <span class="p">(</span><span class="bp">False</span><span class="p">,</span> <span class="bp">False</span><span class="p">,</span> <span class="bp">True</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Green button pressed"</span><span class="p">)</span>
<span class="p">...</span>
<span class="k">elif</span> <span class="n">buttons</span> <span class="o">==</span> <span class="p">(</span><span class="bp">True</span><span class="p">,</span> <span class="bp">False</span><span class="p">,</span> <span class="bp">True</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Red+Green = Follow mode"</span><span class="p">)</span>
<span class="p">...</span>
<span class="k">elif</span> <span class="n">buttons</span> <span class="o">==</span> <span class="p">(</span><span class="bp">True</span><span class="p">,</span> <span class="bp">True</span><span class="p">,</span> <span class="bp">False</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Red+White = Rainbow wheel"</span><span class="p">)</span>
<span class="p">...</span>
<span class="k">elif</span> <span class="n">buttons</span> <span class="o">==</span> <span class="p">(</span><span class="bp">False</span><span class="p">,</span> <span class="bp">True</span><span class="p">,</span> <span class="bp">True</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"White+Green = Rainbow picker"</span><span class="p">)</span>
<span class="p">...</span>
<span class="k">elif</span> <span class="n">buttons</span> <span class="o">==</span> <span class="p">(</span><span class="bp">True</span><span class="p">,</span> <span class="bp">True</span><span class="p">,</span> <span class="bp">True</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Red+White+Green = Rainbow fade"</span><span class="p">)</span>
<span class="p">...</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
</code></pre></div></div>
<p>The NeoPixel driver library is very simple to use, just create a pixel object on the digital output pin the pixels are connected to;</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">neopixel</span>
<span class="c1"># NeoPixel strip (of 16 LEDs) connected on D4
</span><span class="n">NUMPIXELS</span> <span class="o">=</span> <span class="mi">12</span>
<span class="n">BRIGHTNESS</span> <span class="o">=</span> <span class="mf">0.2</span>
<span class="n">ORDER</span> <span class="o">=</span> <span class="n">neopixel</span><span class="p">.</span><span class="n">GRB</span>
<span class="n">pixels</span> <span class="o">=</span> <span class="n">neopixel</span><span class="p">.</span><span class="n">NeoPixel</span><span class="p">(</span><span class="n">board</span><span class="p">.</span><span class="n">D4</span><span class="p">,</span> <span class="n">NUMPIXELS</span><span class="p">,</span> <span class="n">brightness</span><span class="o">=</span><span class="n">BRIGHTNESS</span><span class="p">,</span> <span class="n">auto_write</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
</code></pre></div></div>
<p>Then you can change the colors on a per-pixel basis.
Here we have told the pixels to not auto-show - ie do not update the pixel colors until the .show() method is called:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Initalize pixels OFF
</span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">NUMPIXELS</span><span class="p">):</span>
<span class="n">pixels</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">OFF</span>
<span class="n">pixels</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>
<p>Most of the pixel logic from here on out is just fancy manipulation of the colors.</p>
<p>Finally, there is the soft-pot logic.
Here we use an analog input to read the voltage across the third terminal.
If the voltage is less than 1.8 V then there is no touch and we return <code class="language-plaintext highlighter-rouge">None</code>.
This allows us to test later if there is a touch with <code class="language-plaintext highlighter-rouge">if voltage:</code>.
If there is a touch, it reads between 1.8 V and 3.2 V, we just map that to a new range (eg; 0-1, or 0-12 (12 pixels), or 0-255):</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">voltage_to_int</span><span class="p">(</span><span class="n">voltage</span><span class="p">,</span> <span class="n">int_max</span><span class="p">):</span>
<span class="s">""" Translate the circular touch pad to an integer range.
NOTE: If the voltage is below TOUCH_MIN_V, this returns None
"""</span>
<span class="k">if</span> <span class="n">voltage</span> <span class="o"><</span> <span class="n">TOUCH_MIN_V</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">mapped</span> <span class="o">=</span> <span class="n">translate</span><span class="p">(</span><span class="n">voltage</span><span class="p">,</span> <span class="n">TOUCH_MIN_V</span><span class="p">,</span> <span class="n">TOUCH_MAX_V</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">int_max</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">mapped</span> <span class="o">=</span> <span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">mapped</span><span class="p">)</span> <span class="o">+</span> <span class="n">int_max</span><span class="o">//</span><span class="mi">2</span><span class="p">)</span> <span class="o">%</span> <span class="n">int_max</span>
<span class="k">return</span> <span class="n">mapped</span>
</code></pre></div></div>
<p>Depending on how you align the soft-pot with the “top” of the clock, you may need a fixed offset.
For mine, the touch pad was installed “upside down” so we have to shift everything by ½ of the output range.
Once I have this value, I apply the modulo operation to ensure that the value does not exceed the <code class="language-plaintext highlighter-rouge">int_max</code> range.</p>
<p>For many of the modes I’ve implemented, I map the touch range to something between 0-NUMPIXELS, which maps to “which pixel is the press near”.
For the color-wheel selector, the range is mapped to the 0-255 range used in the basic adafruit color wheel demo.</p>
<p>Finally, when there is no touch, we may want a slowly changing demo.
For this, we just implement a counter which increments with our loop.
When there’s a touch, we can use the touch input, but when there’s no touch we use the counter input:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Initialize to zero
</span><span class="n">idx</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="c1"># Increment every loop
</span> <span class="n">idx</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="p">...</span>
<span class="k">elif</span> <span class="n">buttons</span> <span class="o">==</span> <span class="p">(</span><span class="bp">True</span><span class="p">,</span> <span class="bp">False</span><span class="p">,</span> <span class="bp">False</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Red button pressed"</span><span class="p">)</span>
<span class="c1"># If there is no press, cycle based on the loop index
</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">offset</span><span class="p">:</span>
<span class="n">offset</span> <span class="o">=</span> <span class="n">idx</span> <span class="o">//</span> <span class="mi">5</span>
<span class="n">set_three_colors</span><span class="p">(</span><span class="n">PINK</span><span class="p">,</span> <span class="n">PURPLE</span><span class="p">,</span> <span class="n">YELLOW</span><span class="p">,</span> <span class="n">offset</span><span class="p">)</span>
<span class="p">...</span>
</code></pre></div></div>
<h1 id="conclusion">Conclusion</h1>
<p>That’s it - a little bit of soldering and a few lines of code have a really neat clock face that we can play around with.
You can use the three buttons and the LED colors to set hour, minute, second.
You could also display the month, the phase of the moon, or something else that likes to count by twelves.
With three buttons, this still leaves you with 5 modes to add additional interactivity, as shown above.</p>K. E. ClaytorInteractive NeoPixel ClockInfluxDays2019-10-10T00:00:00+00:002019-10-10T00:00:00+00:00/2019/10/10/influxdays<h1 id="influxdays">InfluxDays</h1>
<p>I’ve been using the <a href="https://www.influxdata.com">InfluxDB</a> time series NoSQL database lately.
I applied and was accecpted for a talk at <a href="https://influxdays.com/past-events-san-francisco-2019/">InfluxDays</a>, their 2019 user conference.</p>
<p>Here’s the recording:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/bwarSNbC344" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p>I think it’s a decent match for research systems.
You can get up to speed fairly quickly without a background in database systems, and the time-series first aspect of it is good for a wide range of datasets.</p>
<p>Pros:</p>
<ul>
<li>Easy to get started with many clients across most languages and a HTTP API.</li>
<li>Telegraf also records system metrics</li>
<li>Chronograf integration gives you visualization with low overhead.</li>
<li>NoSQL means you can change your schema as the experiment evolves (and you don’t even need a schema to get started).</li>
</ul>
<p>Cons:</p>
<ul>
<li>Memory issues on IoT platforms (I’m using a snickerdoodle board).
<ul>
<li>Runs out of memory on start (supposedly fixed v1.7.8?)</li>
<li>Runs out of memory on compactions (fixed by moving to smaller shards)</li>
<li>Takes a long time to boot up and read lots of shards.</li>
</ul>
</li>
<li>Python client doesn’t read into Pandas tables very well (it can <em>write</em> from them well).
I usually export from chronograf into a .csv file and then <code class="language-plaintext highlighter-rouge">pivot_table()</code> a bunch.</li>
<li>Will drop points if the type doesn’t match (explicitly cast python variables).</li>
</ul>K. E. ClaytorInfluxDays