A NeoPixel Pomodoro Timer

Similar documents
Circuit Playground Express Head-Tilt Ears

Clockwork Goggles. Created by John Park. Last updated on :03:10 PM UTC

Android GBoard Morse Code Control with Circuit Playground Express

Adafruit LED Sequins. Created by Becky Stern. Last updated on :02:00 AM UTC

NeoPixel Basketball Hoop

Feather Weather Lamp. Created by Ruiz Brothers. Last updated on :54:26 PM UTC

Guardian Shield+ Zelda Breath of the Wild

Circuit Playground Digital Input

Neon LED Signs. Created by John Park. Last updated on :11:09 PM UTC

Wind Blowing Emoji Prop

Tent Lantern. Created by Timothy Reese. Last updated on :17:25 AM UTC

Adafruit APDS9960 breakout

DIY Circuit Playground Shields

Toy Car Speed Timer. Created by Kirby Griese. Last updated on :13:49 PM UTC

Portable Apple Watch Charger

3D Printed Camera LED Ring

PyPortal View Master Created by Ruiz Brothers. Last updated on :51:28 AM UTC

Bunny Ears with MakeCode

BLE Light Switch with Feather nrf52840 and Crickit

NeoPixie Dust Bag with Circuit Playground Express

Adafruit Capacitive Touch Sensor Breakouts

Jewel Hair Stick. Created by Leslie Birch. Last updated on :47:17 PM UTC

Adabot Operation Game

Adafruit TPL5111 Reset Enable Timer Breakout

Adafruit TPL5110 Power Timer Breakout

NeoPixel Bike Light. Created by Ruiz Brothers. Last updated on :43:46 PM UTC

Circuit Playground Express Laser Tag

Adafruit VL53L0X Time of Flight Micro-LIDAR Distance Sensor Breakout

Pushrod Garage. Created by John Park. Last updated on :07:30 PM UTC

FLORA Pixel Brooch. Created by Becky Stern. Last updated on :19:07 PM EST

Adafruit Color Sensors

NeoMatrix 8x8 Word Clock

Crawling Animatronic Hand

TSL2561 Luminosity Sensor

Ping Pong Ball Launcher

NeoPixel Ring Bangle Bracelet

Mystical LED Halloween Hood

Adafruit Mini TFT " 160x80

i2c/spi LCD Backpack Created by lady ada Last updated on :11:04 PM UTC

CircuitPython Media Dial

Coffee Detonator: The TNT Plunger Grinder

7 Portable Multitouch Raspberry Pi Tablet

LED Eyes. Created by Ruiz Brothers. Last updated on :50:55 AM UTC

Adafruit DRV2605 Haptic Controller Breakout

Interior Purse Light. Created by Becky Stern. Last updated on :41:08 PM UTC

NeoPixel Fairy Crown. Created by Erin St Blaine. Last updated on :22:47 AM UTC

Trinket NeoPixel LED Longboard

Interactive Gift Box. Created by codingpro. Last updated on :47:40 AM UTC

MCP Bit DAC Tutorial

Trellis 3D Printed Enclosure

Adafruit 7-Segment LED FeatherWings

Bluetooth Controlled NeoPixel Headphones

Stumble-Bot. Created by Dano Wall. Last updated on :04:06 AM UTC

Adafruit MCP9808 Precision I2C Temperature Sensor Guide

Light-Up Angler Fish Embroidery

NeoPixel Manicure. Created by Sophy Wong. Last updated on :50:38 PM UTC

Getting Started with FLORA

Adafruit 8x16 LED Matrix FeatherWing

CircuitPython Snow Globe

Camera LED Ring Light

NeoPixel LED Cortana Costume

Adafruit LED Sequins. Created by Becky Stern. Last updated on :00:06 PM EST

Snake Charmer Box. Created by Dano Wall. Last updated on :07:25 PM UTC

Data Logging with Feather and CircuitPython

FLORA TV-B-Gone. Created by Becky Stern. Last updated on :32:57 PM UTC

Infinity Mirror Valentine's Candy Box

No-Sew LED Wristband. Created by Kathy Ceceri. Last updated on :23:40 PM UTC

Adafruit 8x16 LED Matrix FeatherWing

The Scream: Interactive Screaming Painting

Adafruit Si7021 Temperature + Humidity Sensor

Adafruit GPIO Expander Bonnet for Raspberry Pi Created by Kattni Rembor. Last updated on :12:47 PM UTC

Celebration Spectacles

Chirping Plush Owl Toy

Getting Started with FLORA

Magical Mistletoe. Created by Leslie Birch. Last updated on :45:29 PM UTC

Simple LED Unicorn Horn

Crickit Dancing Marionette Kit Created by Dano Wall. Last updated on :03:11 PM UTC

Adafruit PowerBoost 500 Shield

PyPortal NeoPixel Color Picker Created by Kattni Rembor. Last updated on :42:41 PM UTC

FeatherWing Proto, Doubler and Tripler

14-Segment Alpha-numeric LED FeatherWing

Introducing Adafruit Trellis

NeoPixel Punk Collar. Created by Becky Stern. Last updated on :41:18 PM UTC

Solar Boost Bag. Created by Becky Stern. Last updated on :44:55 PM UTC

Monochrome OLED Breakouts

3D Printed 20w Amplifier Box

Bike Wheel POV Display

TRON Bag. Created by Becky Stern

Webcam Cover-Up Lego brick with Adabot Mini Fig

LED Lightbox. Created by Sam Clippinger. Last updated on :50:00 AM UTC

Adafruit MMA8451 Accelerometer Breakout

Adafruit AMG8833 8x8 Thermal Camera Sensor

Solder Dispenser Adabot Head

1.8" TFT Display Breakout and Shield

Adafruit PCF8523 Real Time Clock

7" Portable HDMI Monitor

IS31FL x9 Charlieplexed PWM LED Driver

LED Stego Flex Spike Hoodie

UFO Flying Saucer with Circuit Playground Express

ISS Pin. Created by Leslie Birch. Last updated on :27:30 PM UTC

Transcription:

A NeoPixel Pomodoro Timer Created by Dave Astels Last updated on 2018-08-25 12:59:42 AM UTC

Guide Contents Guide Contents Overview Parts Materials and Supplies Tools Hardware Which CircuitPython Board? Adafruit ItsyBitsy M0 Express - for CircuitPython & Arduino IDE How To Make Noise? Piezo Buzzer How To Show Time Progressing? NeoPixel Ring - 16 x 5050 RGB LED with Integrated Drivers How To Set Time? Rotary Encoder + Extras Making It Portable Adafruit LiIon/LiPoly Backpack Add-On for Pro Trinket/ItsyBitsy Final Wiring Diagram Code Assembly 2 3 4 5 5 6 6 6 6 7 7 7 8 8 8 8 8 10 19 Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 2 of 34

Overview I know many people who get value from a simple tool called the Pomodoro Technique. It's a time management technique used to break work into periods of activity separated by short breaks. Each activity period is called a pomodoro. Why "pomodoro"? The technique was developed by Francesco Cirillo, who named the technique "pomodoro" as a nod to the tomato kitchen timer he initially used. The technique has come a long way from those humble beginnings with phone apps, web versions, etc. I decided to make a simple hardware version in CircuitPython with a rotary encoder to set the time and mode! Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 3 of 34

Parts Here's what you'll need from Adafruit to build the Pomodoro Timer: 1 x Adafruit ItsyBitsy M0 Express - for CircuitPython and Arduino IDE What's smaller than a Feather but larger than a Trinket? It's an Adafruit ItsyBitsy M0 Express! ADD TO CART 1 x NeoPixel Ring - 16 x 5050 RGB LED with Integrated Drivers Round and round and round they go! 16 ultra bright smart LED NeoPixels are arranged in a circle with 1.75" (44.5mm) outer diameter. ADD TO CART 1 x Piezo Buzzer Piezo buzzers are used for making beeps, tones and alerts. This one is petite but loud! ADD TO CART 1 x Rotary Encoder + Extras This rotary encoder is the best of the best, its a high quality 24-pulse encoder, with detents and a nice feel. ADD TO CART 1 x LiIon/LiPoly Backpack Adafruit LiIon/LiPoly Backpack Add-On for Pro Trinket/ItsyBitsy ADD TO CART 1 x Breadboard-friendly SPDT Slide Switch These nice switches are perfect for use with breadboard and perfboard projects. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 4 of 34

ADD TO CART 1 x Lithium Ion Polymer Battery - 3.7v 500mAh Lithium ion polymer (also known as 'lipo' or 'lipoly') batteries are thin, light and powerful. This battery has a capacity of 500mAh for a total of about 1.9 Wh. ADD TO CART 1 x Silicone Cover Stranded-Core Wire - 50ft 30AWG Blue Silicone-sheathing wire is super-flexible and soft, and its also strong! Available in various colors. ADD TO CART Materials and Supplies Tools 3D Printer and Filament Superglue Small diameter heatshrink tubing Wire Strippers Wire Cutters Pliers Pin-vise drill and 0.8mm drill bit Soldering Iron Solder Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 5 of 34

Hardware My goal was to make a pomodoro timer using a few basic parts and write the code for it using CircuitPython. My requirements were pretty simple: 1. write the code in CircuitPython, 2. have an audible alert to indicate transition between the work/break phases, 3. have a display of some sort to show progress through each phase, and 4. have a way to set the length of each phase. Which CircuitPython Board? To satisfy requirement 1, I needed to use an Adafruit M0 or M4 microcontroller based board. I decided to see if I could make it work with an M0. These boards are great for simple CircuitPython projects, but if you have much code it's easy to run into memory limitations. For very simple projects, a Trinket M0 does a great job, but I eventually decided to use a rotary encoder and wanted to try out the new rotaryio support in the latest 3.0 release of CircuitPython. That ruled the Trinket out. So that left the M0 Express versions of the Feather or ItsyBitsy. Either would work, but I decided to use the ItsyBitsy: it's smaller, so it would give me a bit more flexibility when it came time to designing a case. It would provide a bit more of a challenge, as well, due to the lack of mounting holes that the Feather has. Your browser does not support the video tag. Adafruit ItsyBitsy M0 Express - for CircuitPython & Arduino IDE $11.95 IN STOCK ADD TO CART How To Make Noise? For requirement 2, I used the same piezo buzzer I used in my Humidity monitor guide (https://adafru.it/bis). It's loud enough, small, and works nicely with the CircuitPython pulseio library. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 6 of 34

Piezo Buzzer $1.50 IN STOCK ADD TO CART How To Show Time Progressing? When I thought about requirement 3, I thought about my toaster's interface. It uses a continuous knob and a radial display for setting darkness. What isn't obvious from the photo is that the highest valued segment of the dial blinks as it counts down. Also, as the count down proceeds, the number of segments lit decreases. When none remain lit, the toast is done. This immediately reminded me of a Light Emitting Diode (LED) NeoPixel ring. NeoPixel Ring - 16 x 5050 RGB LED with Integrated Drivers $9.95 IN STOCK ADD TO CART Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 7 of 34

How To Set Time? For the final requirement I was still thinking about my toaster and a rotary encoder as input made sense, especially since I'd decided to use a ring as a display. Rotary Encoder + Extras $4.50 IN STOCK ADD TO CART Rotating the encoder could be used to set the times, while the push switch could be used to cycle through modes: and so on. timer -> set work time -> set break time -> timer I'd implemented rotary encoder handling in CircuitPython in a previous guide (https://adafru.it/bit), but this was a good chance to try out the new bundled rotary encoder support. Making It Portable All that was left was adding a LiPo backpack so that it could be battery powered. The backpack is required since the ItsyBitsy doesn't have on-board battery support. That's one of the tradeoffs compared to the Feather. The nice thing about the backpack is that it doesn't increase the footprint, but it does add to the thickness of the ItsyBitsy. That wouldn't be a problem since the case will have plenty of thickness to accommodate it. Adafruit LiIon/LiPoly Backpack Add-On for Pro Trinket/ItsyBitsy $4.95 IN STOCK ADD TO CART Final Wiring Diagram That's it: an ItsyBitsy M0 Express with a NeoPixel ring, a rotary encoder, and a piezo buzzer. Add in a LiPo backpack, power switch, and battery for power, and that's it. Below is the wiring diagram. The only difference is that I'm using a 500mAh LiPo in my build. Making the case bigger will allow use of a bigger battery. That makes no difference to the Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 8 of 34

wiring, though. Wiring is pretty straight-forward and described in detail on the Assembly page. The battery connects to the LiPo backpack. The slide switch does as well, allowing the device to be turned off when not in use. The backpack is connected to the power connections on the ItsyBitsy, typically (as I did) by using the included bit of long-pin header. The buzzer connects to ground and D12. The Neopixel ring has it's power connected to the ItsyBitsy's 3v output, and it's ground to the ItsyBitsy's. The ring has two ground connections, and the second can be used during construction to avoid having to connect everything directly to the ItsyBitsy's ground. The ring's Data In connects to D11. The center of the encoder, and one side of its switch connects to ground. The other side of its switch connects to D10, while its encoder pins connect to D9 and D7. Done as shown, the code will work correctly with the rotation direction. If you reverse the encoder connections, rotation will be opposite what's expected. Reverse the connections or switch the encoder pins in the code to correct it. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 9 of 34

Code As with most of my projects these days, I used CircuitPython for this. Are you new to using CircuitPython? No worries, there is a full getting started guide here (https://adafru.it/cpy-welcome). Adafruit suggests using the Mu editor to edit your code and have an interactive REPL in CircuitPython. You can learn about Mu and installation in this tutorial (https://adafru.it/ano). We'll go through the code, piece by piece, starting with the imports and setup. import time from math import ceil import board import rotaryio import neopixel import debouncer import digitalio import pulseio # Setup the hardware encoder = rotaryio.incrementalencoder(board.d9, board.d7) button = debouncer.debouncer(board.d10, digitalio.pull.up, 0.01) strip = neopixel.neopixel(board.d11, 16, brightness=1, auto_write=false) Here the rotary encoder is set up using the new built-in support for the rotation of the switch and I use my debouncer library to clean up the encoder push switch. Finally the NeoPixel strip is configured. If you are unfamiliar with NeoPixels, there is a great guide on them (https://adafru.it/dhw). Now let's jump to the main loop: Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 10 of 34

strip.fill(0x000000) strip.show() work_time = 6 break_time = 2 time_to_check = 0 state = False mode, dial_color, time_remaining, increment = compute_mode_settings(true) while True: # check whether the rotary encoder has been pushed. If so enter time-set mode. button.update() if button.fell: work_time = set_timer(0x400000, work_time) break_time = set_timer(0x004000, break_time) strip.fill(0x000000) strip.show() mode, dial_color, time_remaining, increment = compute_mode_settings(true) now = time.monotonic() if now >= time_to_check: #only check each second time_remaining -= 1 if time_remaining <= 0: # time to switch modes? strip.fill(0x000000) # clear the dial strip.show() # make some noise beep(2, 0.5, 0.25, 4000) mode, dial_color, time_remaining, increment = compute_mode_settings(not mode) state = not state # have the top pixel toggle between the dial color and white show_time(dial_color, ceil(time_remaining / increment), state) #update the dial time_to_check = now + 1.0 Each time through the loop, it checks for a push on the encoder switch. That's the job of the debouncer's update function. If one was detected (i.e. the button signal went from high to low... it fell) the code enters the mode to set the length of the work phase, followed by the break phase, then back to timing mode. The next step checks to see if it's time to update the timer. It does this every second (as defined by the last line). So once a second, the time remaining is decreased by 1 (second) and the ring is updated. If it reaches 0, it's time to make some noise and change mode. In addition to the setup and loop there are a handful of helper functions. last_position = 0 def check_encoder(): global last_position position = encoder.position if position > last_position: direction = 1 elif position < last_position: direction = -1 else: direction = 0 last_position = position return direction The check_encoder function tracks the position of the encoder, comparing it to the last known position. Based on that Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 11 of 34

comparison it returns -1, 0, or 1 to indicate that the encoder rotated counter-clockwise, didn't move, or rotated clockwise. def show_time(color, value, bright): strip.fill(0x000000) if value > 0: for i in range(1, value + 1): strip[16 - i] = color if bright: strip[16 - value] = 0x404040 strip.show() The show_time function updates the time displayed on the NeoPixel ring. It sets pixels to the specified color starting at pixel zero (which is at the bottom of the ring in the final build) and moving clockwise. The final pixel is handled differently. Depending on the bright parameter, it's either set to the same color as the rest, or white. In the main loop we saw that this value gets toggled each second. The result is that the highest pixel blinks. Note that for the work and break times, each pixel is worth a different number of seconds. This is because it's typical for the work phase to be a half hour, an hour, or longer, while the break phase is usually between five and fifteen minutes. To give a better, brighter display, I decided to use a different scale for each. This is all adjustable in the compute_mode_settings function: def compute_mode_settings(new_mode): work_time_increment = 600 # each work phase pixel is worth 10 minutes break_time_increment = 300 # each break phase pixel is worth 5 minutes if new_mode: return True, 0x400000, work_time * work_time_increment, work_time_increment else: return False, 0x004000, break_time * break_time_increment, break_time_increment Depending on which mode is being entered, the appropriate set of values is returned. These are used to update the variables in the loop (see above). When there's a switch from one phase to the next, we use the piezo buzzer and pulseio to make some noise. The beep function does that: def beep(count, duration, interstitial, freq): pwm = pulseio.pwmout(board.d12, duty_cycle = 0, frequency=freq) for _ in range(count): pwm.duty_cycle = 0x7FFF time.sleep(duration) pwm.duty_cycle = 0 time.sleep(interstitial) pwm.deinit() This is straightforward. It sets up the pulse-width modulation (PWM), loops for the number of beeps requested, and shuts down the PWM. For each beep it sets the duty cycle to 50%, waits for the beep duration, sets the duty cycle to 0% (effectively turning of the sound), then waits for the interstitial duration before playing the next beep (if any). Next is maybe the most interesting function. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 12 of 34

def set_timer(color, value): global last_position time_setting = value last_position = encoder.position for i in range(16): strip[i] = color strip.show() for i in range(16): strip[i] = 0x000000 strip.show() while True: show_time(color, time_setting, False) button.update() if button.fell: return time_setting direction = check_encoder() time_setting += direction if time_setting > 16: time_setting = 16 if time_setting < 0: time_setting = 0 To start, it initializes from the current encoder position and flashes the ring. Following that there's a loop that shows the current setting (using the show_time function we looked at above) and checks the encoder push switch. If it was pressed the current setting is returned. Otherwise the encoder's rotation is checked and the time setting changed based on the result. Finally the time setting is capped at 0 and 16, which reflects the size of the ring. That's it. Each piece is fairly simple, but the overall functionality is interesting and useful. Below is all of it, with comments. """ The MIT License (MIT) Copyright (c) 2018 Dave Astels Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ # pylint: disable=global-statement import time from math import ceil Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 13 of 34

from math import ceil import board import rotaryio import neopixel import debouncer import digitalio import pulseio # Setup the hardware encoder = rotaryio.incrementalencoder(board.d9, board.d7) button = debouncer.debouncer(board.d10, digitalio.pull.up, 0.01) strip = neopixel.neopixel(board.d11, 16, brightness=1, auto_write=false) last_position = 0 def check_encoder(): """Check if the encoder has been rotated. returns the direction (-1 or +1) if it has, 0 if not. """ global last_position position = encoder.position if position > last_position: direction = 1 elif position < last_position: direction = -1 else: direction = 0 last_position = position return direction def show_time(color, value, bright): """Show remaining time on the ring. :param int color: the RGB value to use :param int value: how many pixels to light :param boolean bright: whether the highest pixel should be brighter (i.e white) """ strip.fill(0x000000) if value > 0: for i in range(1, value + 1): strip[16 - i] = color if bright: strip[16 - value] = 0x404040 strip.show() def set_timer(color, value): """Set a time remaing value :param int color: the color to use on the ring :param int value: the initial value (number of pixels to light) Returns the new setting """ global last_position time_setting = value last_position = encoder.position for i in range(16): strip[i] = color strip.show() Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 14 of 34

for i in range(16): strip[i] = 0x000000 strip.show() while True: show_time(color, time_setting, False) button.update() if button.fell: return time_setting direction = check_encoder() time_setting += direction if time_setting > 16: time_setting = 16 if time_setting < 0: time_setting = 0 def beep(count, duration, interstitial, freq): """Make some noise :param int count: the number of beeps to make :param float duration: the length (in seconds) of each beep :param float interstitial: the length (in seconds) of the silence between beeps :param int freq: the frequency of the beeps """ pwm = pulseio.pwmout(board.d12, duty_cycle = 0, frequency=freq) for _ in range(count): pwm.duty_cycle = 0x7FFF time.sleep(duration) pwm.duty_cycle = 0 time.sleep(interstitial) pwm.deinit() def compute_mode_settings(new_mode): """Compute settings for a new mode :param boolean new_mode: the new mode Returns boolean mode - the new mode int dial_color - the dial color for the new mode int time_remaining - the initial time-remaining for the new mode int increment - the pixel increment for the new mode """ work_time_increment = 600 break_time_increment = 300 if new_mode: return True, 0x400000, work_time * work_time_increment, work_time_increment else: return False, 0x004000, break_time * break_time_increment, break_time_increment # Initialize things strip.fill(0x000000) strip.show() work_time = 6 break_time = 2 time_to_check = 0 state = False mode, dial_color, time_remaining, increment = compute_mode_settings(true) Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 15 of 34

# The main loop while True: # check whether the rotary encoder has been pushed. If so enter time-set mode. button.update() if button.fell: work_time = set_timer(0x400000, work_time) break_time = set_timer(0x004000, break_time) strip.fill(0x000000) strip.show() mode, dial_color, time_remaining, increment = compute_mode_settings(true) now = time.monotonic() if now >= time_to_check: #only check each second time_remaining -= 1 if time_remaining <= 0: # time to switch modes? strip.fill(0x000000) # clear the dial strip.show() # make some noise beep(2, 0.5, 0.25, 4000) mode, dial_color, time_remaining, increment = compute_mode_settings(not mode) state = not state # have the top pixel toggle between the dial color and white show_time(dial_color, ceil(time_remaining / increment), state) #update the dial time_to_check = now + 1.0 """ The MIT License (MIT) Copyright (c) 2018 Dave Astels Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- Debounce an input pin. """ import time import digitalio class Debouncer: """Debounce an input pin""" DEBOUNCED_STATE = 0x01 Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 16 of 34

UNSTABLE_STATE = 0x02 CHANGED_STATE = 0x04 def init (self, pin, mode=none, interval=0.010): """Make am instance. :param int pin: the pin (from board) to debounce :param int mode: digitalio.pull.up or.down (default is no pull up/down) :param int interval: bounce threshold in seconds (default is 0.010, i.e. 10 milliseconds) """ self.state = 0x00 self.pin = digitalio.digitalinout(pin) self.pin.direction = digitalio.direction.input if mode is not None: self.pin.pull = mode if self.pin.value: self. set_state(debouncer.debounced_state Debouncer.UNSTABLE_STATE) self.previous_time = 0 if interval is None: self.interval = 0.010 else: self.interval = interval def set_state(self, bits): self.state = bits def unset_state(self, bits): self.state &= ~bits def toggle_state(self, bits): self.state ^= bits def get_state(self, bits): return (self.state & bits)!= 0 def update(self): """Update the debouncer state. Must be called before using any of the properties below""" self. unset_state(debouncer.changed_state) current_state = self.pin.value if current_state!= self. get_state(debouncer.unstable_state): self.previous_time = time.monotonic() self. toggle_state(debouncer.unstable_state) else: if time.monotonic() - self.previous_time >= self.interval: if current_state!= self. get_state(debouncer.debounced_state): self.previous_time = time.monotonic() self. toggle_state(debouncer.debounced_state) self. set_state(debouncer.changed_state) @property def value(self): """Return the current debounced value of the input.""" return self. get_state(debouncer.debounced_state) Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 17 of 34

@property def rose(self): """Return whether the debounced input went from low to high at the most recent update.""" return self. get_state(self.debounced_state) and self. get_state(self.changed_state) @property def fell(self): """Return whether the debounced input went from high to low at the most recent update.""" return (not self. get_state(self.debounced_state)) and self. get_state(self.changed_state) Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 18 of 34

Assembly The picture below shows all the parts, ready for assembly. The STL files for printing the case are at the bottom of this page. We'll start by mounting the LiPo backpack on the ItsyBitsy using the long header pins provided. Remember to trim the header pins afterward. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 19 of 34

In order to use the switch for power, the trace between the switch pads must be cut. That's shown here. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 20 of 34

The next step is to add wires for the NeoPixels, buzzer, and rotary encoder. I used yellow for these. Be sure to make them plenty long, you can always trim them later. 120cm (4-5 inches) is fine. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 21 of 34

Next add two wires for the power switch. I used green for these. Again, 120cm or so (4-5 inches) is plenty. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 22 of 34

The last step for the ItsyBitsy is ground and power. The ground hole is being used by the LiPo backpack, so you will need to solder the ground wire onto the end on the pin on the backpack. The next step requires a fine drill bit. I have a small jewelers drill that's idea for this, and a drill bit that fits a throughhole PCB hole. Position the NeoPixel ring in its place such that the Data-out connection is at the bottom. Drill a hole in the case at the Data-In, one power, and both ground holes. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 23 of 34

Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 24 of 34

Feed the power, ground, and Neopixel connection (from D11 of the ItsyBitsy) through their respective holes you just drilled in the case and solder them to appropriate holes in the ring. Verify that it's soldered on both sides just in case the plated holes were compromised during the marking/drilling (it's unlikely but you can't be too safe). Cut another short (around 60cm or 2-3 inches) length of black wire and feed that through the second ground hole in the case. Solder that to the second ground connection on the ring. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 25 of 34

Now gently pull the wires through the front case and settle the ring into the circular groove. A few dabs of superglue will help it stay in place. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 26 of 34

Next use a bit of superglue to hold the buzzer in place. There is a ring inside the top part of the case to help get it in the right spot. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 27 of 34

Next we can wire the rotary encoder. Start by stripping/tinning about 1 cm (0.5 inches) from the end of the ground wire coming from the ring. Connect this to one side of the encoders switch (the side with two connections) and the middle connection (of 3) on the other side. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 28 of 34

Connect the correct wire from the ItsyBitsy's digital pins to the encoders other pins (see the wiring diagram). I used short pieces of heatshrink on the connections to strengthen them as well as avoid any chance of shorting. Always a good idea. Wire the two green wires from the LiPo backpack to the center pin and one end pin of the slide switch. Which pins they connect to doesn't matter. I generally clip one of the end pins off. You can see this in the photos below. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 29 of 34

Connect the final wire from the ItsyBitsy (from D12) to the buzzer. It doesn't matter which connection. Cut a piece of black wire to reach from the ground connection on the encoder to the other connection of the buzzer. Again, I used some heatshrink on these connections. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 30 of 34

Now we can use superglue to mount the ItsyBitsy and the power switch. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 31 of 34

All that's left is to connect a battery and seal up the case (careful not to catch any of the wiring between the parts of the case. My case design is snug so it might take a little finessing. I used a few dabs of superglue to hold the two pieces together. It could be redesigned with a fancier clip system if desired. Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 32 of 34

Adafruit Industries https://learn.adafruit.com/a-neopixel-pomodoro-timer Page 33 of 34

Here are the STL design files for the case for you to use to print a case (or sent out to be printed): https://adafru.it/bj9 https://adafru.it/bj9 https://adafru.it/bja https://adafru.it/bja Adafruit Industries Last Updated: 2018-08-25 12:59:41 AM UTC Page 34 of 34