<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.0">Jekyll</generator><link href="https://rmconway.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://rmconway.com/" rel="alternate" type="text/html" /><updated>2021-12-01T19:58:58+00:00</updated><id>https://rmconway.com/feed.xml</id><title type="html">Ryan Conway</title><subtitle>Beep boop.</subtitle><author><name>Ryan Conway</name></author><entry><title type="html">Goodbye, Moto X</title><link href="https://rmconway.com/goodbye-moto-x/" rel="alternate" type="text/html" title="Goodbye, Moto X" /><published>2020-10-30T00:50:00+00:00</published><updated>2020-10-30T00:50:00+00:00</updated><id>https://rmconway.com/goodbye-moto-x</id><content type="html" xml:base="https://rmconway.com/goodbye-moto-x/">&lt;p&gt;In February of 2014, I bought a first-gen Moto X. Last week, six and a half years later, I retired it. I’m going to miss it.&lt;/p&gt;

&lt;p&gt;It’s not as mighty as it once was. One of the volume buttons only works if I press it real hard. The other one doesn’t work at all. Sometimes the GPS doesn’t work, and I need to look around for my cross street and manually enter my starting location. Some apps only work partially or don’t work at all, because its last update was Android 5, and neither its manufacturer nor app developers chose to support it further.&lt;/p&gt;

&lt;p&gt;But for the most important things? It’s great. The power button works. Calls work. Texting works. The original battery still lasts a full day on standby.&lt;/p&gt;

&lt;p&gt;In fact, the only reason I’m getting rid of it is because too much of it still works. It still runs web browsers, so a mindless distraction is there whenever my subconscious wants it. It still runs messenger apps, so conversations, with great company but foreign contexts, regularly seek my attention.&lt;/p&gt;

&lt;p&gt;My mind works best when it has just one thing to direct its attention to, and a smartphone, armed even with only basic apps, eagerly presents second things. All day, every day.&lt;/p&gt;

&lt;p&gt;I could, of course, choose to ignore those things. But I’ve come to accept that making that choice requires energy. Energy that comes from the same finite pool from which I draw to work, to interact with loved ones, to focus on things that help me relax.&lt;/p&gt;

&lt;p&gt;So here’s its replacement. It has no app store and a tiny screen. It’s nothing I don’t want it to be.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/goodbyemotox/20201030_003849.jpg&quot; alt=&quot;Replacement phone: LG L125DL&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m going to miss my Moto X, because it treated me well. I hope it’s the most capable phone I’ll ever want.&lt;/p&gt;</content><author><name>Ryan Conway</name></author><summary type="html">I'll miss you.</summary></entry><entry><title type="html">Using Elastic APM with Scalatra</title><link href="https://rmconway.com/elastic-apm-scalatra/" rel="alternate" type="text/html" title="Using Elastic APM with Scalatra" /><published>2020-08-14T00:47:00+00:00</published><updated>2020-08-14T00:47:00+00:00</updated><id>https://rmconway.com/elastic-apm-scalatra</id><content type="html" xml:base="https://rmconway.com/elastic-apm-scalatra/">&lt;p&gt;Elastic APM hooks into Scalatra pretty easily. Since APM’s Java agent &lt;a href=&quot;https://www.elastic.co/guide/en/apm/agent/java/current/supported-technologies-details.html#supported-app-servers&quot;&gt;supports the servlet API&lt;/a&gt;, particularly Jetty, you can instrument a Scalatra app by just &lt;a href=&quot;https://www.elastic.co/guide/en/apm/agent/java/current/setup-javaagent.html&quot;&gt;slapping on the agent&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But you may notice the resulting transaction names have only the request’s method and servlet’s name. For example, all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET&lt;/code&gt;s in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HealthServlet&lt;/code&gt; will fall under the transaction name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HealthServlet#doGet&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/elasticapmscalatra/doget.png&quot; alt=&quot;HealthServlet transaction names&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is much better than no monitoring at all, but if you’ve got multiple routes in a single servlet, you may wish to disambiguate them.&lt;/p&gt;

&lt;p&gt;Fortunately, getting more precise transaction names isn’t too hard. By extending &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ScalatraServlet&lt;/code&gt;, you can hook into your routes and set a unique transaction name for each. Observe:&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;import co.elastic.apm.api.ElasticApm
import org.scalatra.{PathPatternRouteMatcher, RailsRouteMatcher, RegexRouteMatcher, Route, RouteTransformer, ScalatraServlet, SinatraRouteMatcher}

trait ScalatraApmServlet extends ScalatraServlet {
  /**
   * Calculate the route portion of an APM transaction name.
   *
   * We ignore a couple matchers that don't have obvious text representations. This should be fine for most purposes.
   *
   * @param transformers The route's transformers.
   * @return Transaction name path.
   */
  private def getApmTransactionPath(transformers: Seq[RouteTransformer]): String = {
    val transformersWithPath = transformers.filter {
      case _: PathPatternRouteMatcher =&amp;gt; true
      case _: RailsRouteMatcher =&amp;gt; true
      case _: RegexRouteMatcher =&amp;gt; true
      case _: SinatraRouteMatcher =&amp;gt; true
      case _ =&amp;gt; false
    }

    val concatenated = transformersWithPath.map(_.toString).mkString(&quot;; &quot;)
    if (concatenated != &quot;/&quot;) {
      concatenated
    } else {
      &quot;&quot;
    }
  }

  private def wrapWithApmTransactionName(transactionPath: String, action: =&amp;gt; Any): Any = {
    ElasticApm.currentTransaction.setName(s&quot;${request.getMethod} ${request.getServletPath}${transactionPath}&quot;)

    action
  }

  override def get(transformers: RouteTransformer*)(action: =&amp;gt; Any): Route = {
    val transactionPath = getApmTransactionPath(transformers)
    super.get(transformers: _*) { wrapWithApmTransactionName(transactionPath, action) }
  }
  override def post(transformers: RouteTransformer*)(action: =&amp;gt; Any): Route = {
    val transactionPath = getApmTransactionPath(transformers)
    super.post(transformers: _*) { wrapWithApmTransactionName(transactionPath, action) }
  }
  override def put(transformers: RouteTransformer*)(action: =&amp;gt; Any): Route = {
    val transactionPath = getApmTransactionPath(transformers)
    super.put(transformers: _*) { wrapWithApmTransactionName(transactionPath, action) }
  }
  override def delete(transformers: RouteTransformer*)(action: =&amp;gt; Any): Route = {
    val transactionPath = getApmTransactionPath(transformers)
    super.delete(transformers: _*) { wrapWithApmTransactionName(transactionPath, action) }
  }
  override def options(transformers: RouteTransformer*)(action: =&amp;gt; Any): Route = {
    val transactionPath = getApmTransactionPath(transformers)
    super.options(transformers: _*) { wrapWithApmTransactionName(transactionPath, action) }
  }
  override def head(transformers: RouteTransformer*)(action: =&amp;gt; Any): Route = {
    val transactionPath = getApmTransactionPath(transformers)
    super.head(transformers: _*) { wrapWithApmTransactionName(transactionPath, action) }
  }
  override def patch(transformers: RouteTransformer*)(action: =&amp;gt; Any): Route = {
    val transactionPath = getApmTransactionPath(transformers)
    super.patch(transformers: _*) { wrapWithApmTransactionName(transactionPath, action) }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you make your servlets extend &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ScalatraApmServlet&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ScalatraServlet&lt;/code&gt;, their routes will be distinguished in APM.&lt;/p&gt;

&lt;p&gt;This works because when an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;action&lt;/code&gt; is evaluated, the APM agent will have already established a transaction for us. We’re just overriding its name.&lt;/p&gt;

&lt;p&gt;I use this with Scalatra 2.7.0. I imagine it’s prone to breaking with future Scalatra releases, but am hopeful it won’t be too hard to adapt when that happens.&lt;/p&gt;</content><author><name>Ryan Conway</name></author><category term="performance" /><category term="scala" /><category term="elasticsearch" /><summary type="html">And getting precise transaction names.</summary></entry><entry><title type="html">Ostrich: A Game Boy Sound System Player</title><link href="https://rmconway.com/ostrich/" rel="alternate" type="text/html" title="Ostrich: A Game Boy Sound System Player" /><published>2017-02-11T14:53:00+00:00</published><updated>2017-02-11T14:53:00+00:00</updated><id>https://rmconway.com/ostrich</id><content type="html" xml:base="https://rmconway.com/ostrich/">&lt;p&gt;Ostrich is a macOS app for playback of Game Boy Sound System files.&lt;/p&gt;

&lt;p&gt;Its retro interface visualizes the Game Boy’s pulse audio channels. Under the covers, it’s a Nintendo Game Boy emulator written in Swift.&lt;/p&gt;

&lt;p&gt;This app came way further than I expected it to. I owe a lot of that to &lt;a href=&quot;http://austinzheng.com&quot;&gt;Austin Zheng&lt;/a&gt; and his Swift tutelage. Love of retro game music and interest in embedded devices also helped.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/lEO4qYbvHXk&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;This post is mostly a discussion of the app’s implementation. You can &lt;a href=&quot;#gallery&quot;&gt;skip to the gallery&lt;/a&gt;, or &lt;a href=&quot;https://github.com/PumpMagic/ostrich&quot;&gt;build it yourself&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;

&lt;p&gt;A Game Boy Sound System file captures the bits of machine code in a Game Boy game that produce its music and sound effects. GBS files are audio synthesis instructions, and GBS players execute these instructions.&lt;/p&gt;

&lt;p&gt;Ostrich is comprised of&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;a Sharp LR35902 emulator
    &lt;ul&gt;
      &lt;li&gt;a CPU emulator&lt;/li&gt;
      &lt;li&gt;an APU (audio processing unit) emulator with AudioKit output&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;RAM, ROM, and data bus emulators&lt;/li&gt;
  &lt;li&gt;a GBS parser and media player model&lt;/li&gt;
  &lt;li&gt;a UI built on AppKit / Cocoa&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;sharp-lr35902&quot;&gt;Sharp LR35902&lt;/h1&gt;

&lt;p&gt;The Game Boy’s SoC, the Sharp LR35902, bundles computational and audio synthesis abilities. Its CPU is a lot like the Zilog Z80. Its audio unit is controlled by writing to a small region of memory.&lt;/p&gt;

&lt;h2 id=&quot;cpu-emulator&quot;&gt;CPU Emulator&lt;/h2&gt;

&lt;p&gt;The CPU is a class with an eight-bit integer for each core register. The logical registers and flags are wrappers of these core registers. Instructions are modeled as generic functions that accept data types of particular width and read/writeability, so that a single function may capture most or all variants of a given instruction family. Like ADD or LD.&lt;/p&gt;

&lt;p&gt;Instruction parsing is.. a really big switch statement.&lt;/p&gt;

&lt;h2 id=&quot;apu-emulator&quot;&gt;APU Emulator&lt;/h2&gt;

&lt;p&gt;The APU is modeled as a 48-byte RAM. It interprets writes as a normal memory would, but also passes the relevant data to independent representations of audio channels. Each audio channel has its own representations of frequency bits, duty bits, etc., and exposes a clock function that manipulates these representations and controls its AudioKit output.&lt;/p&gt;

&lt;h1 id=&quot;game-boy-emulator&quot;&gt;Game Boy Emulator&lt;/h1&gt;

&lt;p&gt;The Game Boy itself is modeled as an owner of an LR35902, some RAM, a data bus connecting everything, and, optionally, a game cartridge. It exposes means of inserting and removing cartridges, and exposes its LR35902 to anyone who wishes to clock it or have it call specific addresses.&lt;/p&gt;

&lt;h1 id=&quot;media-player&quot;&gt;Media Player&lt;/h1&gt;

&lt;p&gt;The media player is an owner of a Game Boy. When the user loads a GBS file, the player generates and inserts a cartridge into it. When the user plays a song, the player resets the Game Boy’s memory, initializes some of its registers like the stack pointer and program counter, calls a given address, and begins clocking it. The media player is a model, in the MVC sense, and is commanded by the controller.&lt;/p&gt;

&lt;h1 id=&quot;user-interface&quot;&gt;User Interface&lt;/h1&gt;

&lt;p&gt;The user interface is modeled after the original Dot Matrix Game Boy. It uses AppKit (Cocoa) to render playback data, including audio channel waveforms, and presents controls adapted from photos of the Game Boy’s hardware. The interface leverages AppKit’s Auto Layout and stack views to fit most desired sizes and orientations.&lt;/p&gt;

&lt;h1 id=&quot;gallery&quot;&gt;&lt;a name=&quot;gallery&quot;&gt;&lt;/a&gt;Gallery&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/ostrich/1.png&quot; alt=&quot;A wild Ostrich appeared&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Watching the pulse waveforms bounce around is so satisfying. Especially during intricate tunes like Double Dragon’s.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/ostrich/action-3.gif&quot; alt=&quot;Beeps and boops in harmony&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Auto Layout is amazing. Here’s a shot of the UI fully shrunk - the text goes into a Winamp-esque scroll mode when it doesn’t fit in the display.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/ostrich/2.png&quot; alt=&quot;A baby Ostrich&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s come a long from its statically sized start.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/ostrich/wip-2.png&quot; alt=&quot;A primal Ostrich&quot; style=&quot;width: 60%; &quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;</content><author><name>Ryan Conway</name></author><category term="projects" /><category term="emulation" /><category term="swift" /><summary type="html">Learning Swift and Cocoa by writing an emulator. ![Beep boop](/assets/img/ostrich/1.png)</summary></entry><entry><title type="html">Word Clock</title><link href="https://rmconway.com/word-clock/" rel="alternate" type="text/html" title="Word Clock" /><published>2017-01-19T21:59:00+00:00</published><updated>2017-01-19T21:59:00+00:00</updated><id>https://rmconway.com/word-clock</id><content type="html" xml:base="https://rmconway.com/word-clock/">&lt;p&gt;A &lt;strong&gt;word clock&lt;/strong&gt; communicates the time with letters rather than numbers or hands.&lt;/p&gt;

&lt;p&gt;I made this one as a Christmas gift for my dad in 2016. This post is a high-level retrospect of its development; the schematic, parts list, and firmware are linked toward the end.&lt;/p&gt;

&lt;p&gt;Before going too far, I want to point out that this turned out to be a beta build. I wound up with a rat’s nest of wires, crammed components, and light bleeding through the baffles. It was a good proof of concept, but I took what I learned and made a second version with a custom PCB, and the results were way better. See &lt;a href=&quot;#addendum&quot;&gt;Addendum&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Tech: ATMega328P + DS3231 + 98 5mm LEDs&lt;/li&gt;
  &lt;li&gt;Chassis: IKEA Ribba shadow box&lt;/li&gt;
  &lt;li&gt;Build date: November - December 2016&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;design&quot;&gt;Design&lt;/h1&gt;

&lt;p&gt;The word clock is a popular project among electronics hobbyists. It’s taken &lt;a href=&quot;https://www.google.com/search?site=&amp;amp;tbm=isch&amp;amp;source=hp&amp;amp;biw=1216&amp;amp;bih=699&amp;amp;q=word+clock+diy&quot;&gt;many different forms&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To me, its appeal is its elegance. It’s colloquial and casually imprecise, but accurate enough for most needs.&lt;/p&gt;

&lt;p&gt;I thought &lt;a href=&quot;http://www.instructables.com/id/Sleek-word-clock/&quot;&gt;scottbez1’s instructable&lt;/a&gt; captured that philosophy well, so I based my design heavily on his. Some of this repeats his work, but I made changes along the way.&lt;/p&gt;

&lt;h1 id=&quot;letter-mask&quot;&gt;Letter Mask&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/IMG_0088.JPG&quot; alt=&quot;Letter mask&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The letter mask is the clock’s face. It sits in front of an array of LEDs, transforming each one’s light into an individual letter.&lt;/p&gt;

&lt;p&gt;I made a letter mask in GIMP sized around the chassis’ dimensions. I went with AppleGothic for a sleek look, and left some space around the edges for aesthetics and the non-LED electronics. I placed each letter of the mask in its own cell of a grid, effectively monospacing the font.&lt;/p&gt;

&lt;p&gt;I had several copies printed out on transparencies at Kinko’s, cut three to fit, and carefully aligned and adhered them to each other using clamps and double-sided tape. The black ink isn’t perfectly opaque; stacking the sheets prevents light from bleeding through where it shouldn’t.&lt;/p&gt;

&lt;p&gt;I cut a hollow rectangle out of amber construction paper and adhered it to the front of the transparencies. It provides some color, and closes openings in the shadow box’s viewable area. I attached a black garbage bag to the rear of the masks to diffuse the light coming from the LEDs, making the display directly viewable and consistently bright.&lt;/p&gt;

&lt;h1 id=&quot;led-array&quot;&gt;LED Array&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/2016-12-08 18.23.08.jpg&quot; alt=&quot;LED array, front&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The LED array has 98 LEDs, each for an individual letter of the mask.&lt;/p&gt;

&lt;p&gt;I made the LED array by punching a bunch of holes in a piece of cardboard. After soldering a resistor to the anode of each LED, I placed each LED in a hole and soldered each resistor to each other to provide a common power connection. I connected the cathodes of each word-forming group of letters to each other, and brought out a wire from each common cathode for connection to the driver board.&lt;/p&gt;

&lt;p&gt;Later, I cut some baffles out of a cereal box and adhered them to the mask using hot glue. This minimizes inter-word light bleeding.&lt;/p&gt;

&lt;h1 id=&quot;driver-board&quot;&gt;Driver Board&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/2016-12-09 20.18.55.jpg&quot; alt=&quot;Driver board&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The driver board gates the ground connection of each word-forming LED group.&lt;/p&gt;

&lt;p&gt;Each word sits in front of a Darlington transistor of a ULN2003A chip whose input is a bit of a logical 24-bit shift register formed by three 4094 chips. The 4094s get their register values from a separate control board. I borrowed Scott’s circuit and soldered this board by hand.&lt;/p&gt;

&lt;h1 id=&quot;control-board&quot;&gt;Control Board&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/IMG_0083.JPG&quot; alt=&quot;Control board&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The control board listens to the user interface, figures out the time of day, and controls which words the driver board lights up. I designed this circuit myself and soldered the board by hand.&lt;/p&gt;

&lt;h1 id=&quot;microcontroller&quot;&gt;Microcontroller&lt;/h1&gt;

&lt;p&gt;I chose an ATMega328P for the clock’s brain since I had a few lying around, and AVRs are easy to interface with thanks to the Arduino environment. That said, any chip with basic peripherals and a modest amount of memory - I2C, PWM, analog in, 16K ROM - would have been fine.&lt;/p&gt;

&lt;h1 id=&quot;rtc&quot;&gt;RTC&lt;/h1&gt;

&lt;p&gt;The MCU gets the time from a DS3231 RTC. I got one from Adafruit that’s wrapped up in a convenient eight-pin breakout and sports a coin cell battery backup. This backup means the time isn’t lost when the clock is unplugged, at least for 3 to 4 years - the RTC draws about 0.84μA in standby and the battery I picked supplies 38mAh.&lt;/p&gt;

&lt;p&gt;The DS3231’s accuracy depends on its environment’s temperature. In the conditions of its paradisiacal southern California home, it’s accurate to a ±2 PPM window. In other words, for every million seconds of actual time that pass, the clock’s time will stray by at most two seconds in either direction. The clock will stray by no more than about a minute per year.&lt;/p&gt;

&lt;p&gt;I considered another design that opted for an external crystal rather than an RTC. It would’ve saved on cost, but require custom time-calculating firmware, be less accurate, and a challenge to battery back.&lt;/p&gt;

&lt;h1 id=&quot;power&quot;&gt;Power&lt;/h1&gt;

&lt;p&gt;The clock draws from a 7.5V 1A wall adapter. That’s fed raw to the LED array, and regulated down to 5V for the control and driver boards. I chose 7.5V instead of 5V because I didn’t want too much variance in each LED’s brightness when accounting for resistance and diode forward voltage drop tolerances.&lt;/p&gt;

&lt;p&gt;I was careful to pull down the driver board’s outputs, so that while the MCU is booting up the driver board won’t turn on all 98 LEDs simultaneously and overload the wall adapter. I picked out an adapter with a fuse just in case.&lt;/p&gt;

&lt;h1 id=&quot;user-interface&quot;&gt;User Interface&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/IMG_0103.JPG&quot; alt=&quot;User interface&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The user interface has two buttons for time adjustment (hour advance and minute advance) and a potentiometer for brightness adjustment. I sized the buttons to accommodate my dad’s large fingers; he’s 6’5”. The potentiometer controls the output enable pins of the shift registers using PWM for a dimming effect.&lt;/p&gt;

&lt;p&gt;Each interface component is panel-mounted to the rear of the shadow box. I hand drilled slots for each; a metric step bit was especially handy, since I didn’t have any metric bits coming into this project.&lt;/p&gt;

&lt;h1 id=&quot;firmware&quot;&gt;Firmware&lt;/h1&gt;

&lt;p&gt;After initializing each relevant pin, the firmware regularly polls the inputs and adjusts the RTC’s time and its output enable PWM pin width as appropriate. It periodically queries the RTC for the current time and updates the driver board’s shift registers every five minutes.&lt;/p&gt;

&lt;p&gt;Firmware comes naturally to me now thanks to years of experience, so this project’s was pretty straightforward. The most fun I had here was deciding how the clock should respond to the user pressing its buttons. Since the clock is only precise within five minutes, should the minute advance move the time forward by one minute or by five? How long should a user have to hold a button before that button’s action is repeated, if ever, considering the target user? Should the clock reset its second counter when its time is advanced? I spent nearly as much time answering these questions as I did writing the rest of the code.&lt;/p&gt;

&lt;h1 id=&quot;gallery&quot;&gt;Gallery&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/2016-12-08 18.16.55.jpg&quot; alt=&quot;LED array, back&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/IMG_0081.JPG&quot; alt=&quot;Disassembled word clock&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/IMG_0092.JPG&quot; alt=&quot;Disassembled word clock&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/2016-12-14 05.23.54.jpg&quot; alt=&quot;Assembled word clock&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/IMG_0079.JPG&quot; alt=&quot;Assembled word clock&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/IMG_0055.JPG&quot; alt=&quot;Assembled word clock&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;documents&quot;&gt;Documents&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.google.com/spreadsheets/d/17tVfDLt-0LMc_5vMZl31iws5Pd2t-Ol0GwKcZCI1BNE/edit?usp=sharing&quot;&gt;Parts list&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://drive.google.com/file/d/0B25ZTvO5IWPTbkVDSGpxVWZGc0k/view?usp=sharing&quot;&gt;Schematic&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/PumpMagic/WordClock&quot;&gt;Firmware&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://drive.google.com/file/d/0B25ZTvO5IWPTaTBoUm1YUEozOXM/view?usp=sharing&quot;&gt;Letter mask&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;lessons-learned&quot;&gt;Lessons Learned&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Source your components from reliable sellers on an as-needed basis. I used years-old LEDs purchased on eBay, and several were nonoperational before use. Several more burned out within a few hours of use and needed to be replaced. Desoldering isn’t as easy as soldering.&lt;/li&gt;
  &lt;li&gt;Size your project components precisely given the chassis before designing anything. I sized the letter mask without properly accounting for the electronics boards that would sit behind it, and had to fit the boards in a really limited space. I had to hack up the perfboards and make some gross serial connections that could’ve been parallel.&lt;/li&gt;
  &lt;li&gt;Review mechanical diagrams closely before purchasing components, and make sure you have the right tools to mount things. I made incorrect assumptions about the panel-mounted interface components and had to use some hacky drilling techniques to work around missing bit sizes and non-circular hole shapes. A laser cutter would have been lovely here.&lt;/li&gt;
  &lt;li&gt;Consider designing and fabbing a PCB after proving out the circuit. This project had a ton of electrical connections, and I soldered it all by hand, and it was tedious. Especially the LED array. Your time is valuable, and your project could use the reliability!&lt;/li&gt;
  &lt;li&gt;Try living with your creation for a while, if it’ll be a gift. Years later I still see this when I visit my parents, which is pretty cool, but then I think dang.. I totally should’ve made the words fade in and out when transitioning instead of having a hard cut.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;addendum&quot;&gt;Addendum&lt;/h1&gt;

&lt;p&gt;A few months after giving this as a gift, I received reports that a few more LEDs burned out. Instead of swapping them out, I took the opportunity to use what I learned to do better.&lt;/p&gt;

&lt;p&gt;I dumped the circuit into Fritzing, routed it out to a monster 9”x9” board, and got it fabbed. Connecting the controls to the board was kinda jank, but soldering everything was way easier, and didn’t require miles of wire. I also used Cree LEDs instead of eBay ones. They’ve been going strong for four years. Even the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IT IS&lt;/code&gt; letters, which have been on pretty much that entire time.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/2017-04-22 23.44.33.jpg&quot; alt=&quot;Before and after&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/wordclock/2017-03-22 00.32.37.jpg&quot; alt=&quot;Assembled board&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I probably still have the layout and Gerber files somewhere. No promises, but if you’re reading this and think they’d help, feel free to ping me. I’ll look for them.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;</content><author><name>Ryan Conway</name></author><category term="projects" /><category term="embedded" /><summary type="html">For when digits and hands are just too much. ![Assembled word clock](/assets/img/wordclock/IMG_0061.JPG)</summary></entry><entry><title type="html">Alien Animatronic</title><link href="https://rmconway.com/alien-animatronic/" rel="alternate" type="text/html" title="Alien Animatronic" /><published>2017-01-18T21:05:00+00:00</published><updated>2017-01-18T21:05:00+00:00</updated><id>https://rmconway.com/alien-animatronic</id><content type="html" xml:base="https://rmconway.com/alien-animatronic/">&lt;p&gt;&lt;img src=&quot;/assets/img/alienanimatronic/inaction.gif&quot; alt=&quot;Skreee!&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It was October, and it had been a while since I did anything with motors. So I built a little animatronic!&lt;/p&gt;

&lt;p&gt;I helped host a Halloween party at a friend’s place, and this guy greeted our guests from the back of my parked car.&lt;/p&gt;

&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Tech: DC gearmotor + high power LED&lt;/li&gt;
  &lt;li&gt;Mounting: Motor hub &amp;amp; mount + styrofoam ball + wooden dowels&lt;/li&gt;
  &lt;li&gt;Build date: October 2016&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;design&quot;&gt;Design&lt;/h1&gt;

&lt;p&gt;Since the party was at a house in the woods, the idea was to park my car askew and have an alien peek out the back, implying he was hiding out and up to no good. The idea pretty much fell into place after I saw &lt;a href=&quot;https://www.amazon.com/gp/product/B0108FJW0A/ref=oh_aui_detailpage_o05_s00?ie=UTF8&amp;amp;psc=1&quot;&gt;this sweet alien mask that kinda looks like a fish&lt;/a&gt;. I had a futuristic looking cape from an old cosplay that he could wear, and figured I could pulse a green LED behind him for some scifi vibes.&lt;/p&gt;

&lt;h1 id=&quot;animating-the-mask&quot;&gt;Animating the Mask&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/alienanimatronic/2017-01-20 20.54.14.jpg&quot; alt=&quot;Mask mounting assembly&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Getting radial motion from a shaft is easy enough: power a DC gearmotor through an H-bridge and control its speed with PWM. I got that with a Jameco motor, off-the-shelf H-bridge board and an Arduino. The hard part for me, coming from a nonmechanical background: how do you leverage that motion to rotate a 23” diameter mask?&lt;/p&gt;

&lt;p&gt;My answer was lots of adhesive! And wooden dowels, long screws, and a motor hub.&lt;/p&gt;

&lt;p&gt;The motor hub was a hobbyist unit that fit my 12V gearmotor’s shaft, and had several threaded holes with which one would normally attach a wheel. But I couldn’t just get 23” wheels. Even if I was willing to drop the money, they’d weigh down the motor. Instead, I took a few 2” screws, held them in the hub with nuts and thread locker, and crammed those screws into a styrofoam ball. I drove a bunch of wooden dowels through that ball, approximating the inner diameter of the mask, and held them in place with hot glue.&lt;/p&gt;

&lt;p&gt;If there’s anything I learned in mechatronics class, it’s that simple, brute force solutions can be really effective.&lt;/p&gt;

&lt;p&gt;I programmed a basic state machine to make the mask rotate back and forth. I gave it a low probability of doing a double-take, and an even lower probability of spinning in a complete circle, because why not?&lt;/p&gt;

&lt;h1 id=&quot;pulsing-the-high-power-led&quot;&gt;Pulsing the high-power LED&lt;/h1&gt;

&lt;p&gt;The cool thing about H-bridges is that it’s while they’re generally marketed for use with motors, they’re really just power drivers that you can use to put current through anything. The board I bought for driving the motor had two of them, so I used the one left over to drive a high power LED. I stuck the LED to a stock Intel CPU cooler I had lying around with some thermal paste and hot glue. I wanted to use thermal adhesive tape, but couldn’t get a hold of any in time. The passive cooling turned out to be plenty, and I didn’t have to drive the cooler’s fan.&lt;/p&gt;

&lt;p&gt;I made another state machine to pulse the LED on and off, usually slowly but sometimes quickly, and made it flicker randomly.&lt;/p&gt;

&lt;h1 id=&quot;mounting-the-components&quot;&gt;Mounting the Components&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/alienanimatronic/2016-10-29 00.22.08.jpg&quot; alt=&quot;Mask mounting assembly&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To mount the mask assembly, I attached the motor to a hobbyist bracket, attached that bracket to a slab of wood, and attached that slab of wood to another slab of wood with a couple right angle brackets.&lt;/p&gt;

&lt;p&gt;That second slab held the H-bridges and the Arduino, and mostly importantly provided a surface to strap the whole thing down. I tied it all down to a box of dumbbells with a bungee cord. Without a heavy platform, the whole thing jerked a bunch and probably would’ve fallen over. The motor generated a surprising amount of torque.&lt;/p&gt;

&lt;h1 id=&quot;making-the-alien-body&quot;&gt;Making the Alien Body&lt;/h1&gt;

&lt;p&gt;I put everything on top of a bunch of books that I held in place with wooden wedges. I draped the old cape over the books and propped up one of its long sleeves to the side of the cabin. I took advantage of it being hard to see at night and didn’t focus too much on the details.&lt;/p&gt;

&lt;h1 id=&quot;gallery&quot;&gt;Gallery&lt;/h1&gt;

&lt;p&gt;Here it is in action:&lt;/p&gt;

&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/xuWwpDGH8xo&quot; width=&quot;560&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h1 id=&quot;documents&quot;&gt;Documents&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.google.com/spreadsheets/d/1y8l7H8diMvLhjSow7c-xoUVFOXhhcHXPGHP0gJOvv8E/edit?usp=sharing&quot;&gt;Parts list&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/PumpMagic/OdysseyHalloween&quot;&gt;Firmware&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;lessons-learned&quot;&gt;Lessons Learned&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Don’t be afraid to use adhesives like hot glue and thread locker. Especially for oneoff projects such as this. At first I tried mounting the mask using only removable fasteners like keps nuts and lock washers, hoping to make everything readily reusable. Only after that failed several times did I resort to more permanent adhesives.. and they worked first try.&lt;/li&gt;
  &lt;li&gt;Don’t take mounting for granted! That took more time than anything else.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;where-to-go-from-here&quot;&gt;Where to Go from Here&lt;/h1&gt;

&lt;p&gt;It was rewarding to build something that felt alive and that made people smile. Or gave them the heebie jeebies. I’ll take that too.&lt;/p&gt;

&lt;p&gt;I’d like to play around with animatronics some more, and have already gotten some 16 gauge aluminum to do so.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;See you next Halloween!&lt;/strong&gt;&lt;/p&gt;</content><author><name>Ryan Conway</name></author><category term="projects" /><category term="animatronics" /><category term="embedded" /><summary type="html">A greeter for our Halloween party. ![Skreee!](/assets/img/alienanimatronic/2017-01-20 20.54.14.jpg)</summary></entry></feed>