A long time ago I got some hardware from a friend. Among many cool things he gave me 2 over sized red emergency shutoff buttons. I knew that there was a project there. I knew how I was going to do it. But I had no idea what to build.
So let’s recap. I wanted to build the entire project inside the button, which is about the size of 2 stacked decks of cards. I knew I wanted it to do something on my computer and I knew I wanted it to be dramatic, like War Games. I started to brainstorm and then a lot of things interfered.
Last spring I built a stir plate for Cassie. And then we got a really nice soldering station. I did a few smaller projects and finally felt ready to finish this one. Despite this being a hardware project, the software was the hardest part. Because the Digispark is not as widely known as the Arduino, there isn’t as much documentation available. I found myself reading through the source code for the chip’s firmware to figure out how to communicate with the computer over USB. But eventually I got it working.
The Digispark uses the ATTiny
which has 6 configurable
i/o pins. Two are used for USB communication. I needed
one to detect the button press. That left me with 3 pins to do something cool. I
decided to use 2 LEDs, independently controlled. I was happy with that design -
but the sixth, perfectly good, unused pin bugged me. I decided it would be
really cool to have a buzzer go off during the “arming” sequence and that could
use the last pin. With that design I was fully utilizing the ATTiny.
When you twist the button it pops up and closes the circuit. When you push it, it opens the circuit. I decided the button would begin depressed. You would pull it up to “arm” it, and then push it down to “launch”. These sequences would be accompanied by blinking LEDs and annoying buzzers. Upon “launch” the computer would perform whatever action you had preconfigured.
Embedded programming is not something I am used to. The USB connection requires
constant refreshing and any call that blocks for too long will kill it. The
ATTiny requires you to define a function called
loop which is called
constantly as long as the chip is powered. There is no guarantee how often the
loop will be run or at what interval. Nothing inside the
loop function can
block. In most of the Arduino tutorials to blink an LED you do something like this:
However, this will not work because the
delay function hangs for too long and
kills the USB connection. To get around this I created some global variables to
keep track of when the LED was lasted cycled. That looked like this:
Debouncing works the same way. If the button has changed states and remained in the new state for a certain amount of time, you can safely assume it was actually pushed. Because the button is a mechanical device the connection when it is first pressed or released may ‘bounce’ open and closed. You need to account for this in the program, otherwise there may be intermittent bugs.
The next issue was hardware-related. The buzzer requires 5 volts. The pins are
supposed to output 5V, but this is the real world and specs come with a
tolerance. The pin was only putting out 4.5V, which was not enough to run the
buzzer. Fortunately the positive rail was actually supplying 5V, so I just
needed a way to turn the rail on or off for just the buzzer. This is the perfect
use-case for a transistor. Sure enough, running the 5V rail into the base of an
NPN transistor and pin 6 into the collector did the trick. I was able to use the
transistor as a valve, and turn the 5V rail on or off for the buzzer using a
I decided to use a small perfboard to hold all of the non-Digispark components. This one from Sparkfun, has traces which connect every 3 pins, so you can solder two components together on the board, without free-soldering the components directly.
The way it works
- Twist the button to arm it. This will sound a buzzer and flash the LEDs.
- When the button is pressed while armed, a command is sent to the connected computer.
- The computer runs the command and returns the status. The button displays an LED status accordingly.
- If the button is armed and not pressed for 30 seconds, it resets.
The hardware portion of this project is just an input device for the computer, so I needed some software on the computer to handle the USB connection and run whatever command was preconfigured. I used Ruby to prototype the USB connection and then just stuck with it for the daemon. There’s a good Digispark library out there. The logic of the daemon is pretty simple.
The code to handle the incoming USB message is here:
It’s not yet daemonized, but it gets the job done. I just got a shipment of really cool parts, so I expect to start the next project soon. Hopefully it won’t take as long to finish.
Here’s the final design. I haven’t included the power supply or USB hookup because those things are part of the Digispark itself. Pins 5 and 6 are tied directly to the data pins of the USB connector. Other than that, there is a voltage regulator to clean up the 5 volts coming over USB.