Skip to content

Hardware

Adafruit Macropad Awesome List

Inspired by the CircuitPython Awesome List, I've created an Awesome List for the Adafruit Macropad.

I finally found some time to set mine up after I received mine this past summer in the Adafruit AdaBox subscription. I've 3D printed a couple of cases for it already and also programmed mine to work with the Onshape CAD software.

It's pretty cool and I was curious to see what others had programmed for macros for their applications and I figured why not create a list for everyone!

If you have any suggestions for what to add, please let me know or feel free to fork and send a pull request. Contributing guidelines are in the list and if you have any questions, you can find me on Twitter or on the Adafruit Discord (link in the list).

December 2021 Project Updates

Since my last blog post just over a month ago, I've been extremely productive. I have a few blog posts planned over the next week (no, really, I swear) sharing some updates on a number of projects I've been working on.

The 3D Printer I bought back in September has been keeping me very busy. Not just learning how to use it, but also constant tweaking and maintenance it needs. I bought a Creality Ender 3 v2 and as I like to joke about it, I have more time than money to spend on a 3D Printer. The Ender series is inexpensive, but it does take a lot of work to maintain it. I read once that you will learn as much about the hardware as you will about 3D printing if you buy an Ender series, and it's definitely true.

Learning CAD software has also been a time consuming process. Sure, there are thousands of pre-made plans you can print on your printer, but sometimes you need to customize something or build something just for you. In my case it's a new speaker stand for my AudioEngine P4s that integrates an Adafruit rp2040 Feather with Neopixel FeatherWing. Trying to figure out how to get the USB-C cable to work inside the stand was more complex than I thought it would be. As we speak, I'm printing - what I hope - is the final version.

The rp2040 Feather and NeoPixel FeatherWing is another project that I've spend considerable time on the last few months and more on that soon.

I also wrapped up version one of the Pi-Dial project last weekend. I made some tweaks to the code and it's working exactly as I want. If and when I get more time, I'm already thinking about version 2 which use Python's asyncio library. I could also pick up a Raspberry Pi Zero and make a version that uses that in a much smaller enclsoure. It may even be possible to embed that in a speaker stand as well, but the LCD would take up most of the front and I'd have to mount the rotary encoders on the side. I have enough projects though right now...

Last but not least, I've started working on Silver Saucer again. Looking at the blog, it's almost exactly a year since I spent significant time on it, which amused me. I just finished merging the change from Pyramid to FastAPI in and now I need to re-write the Discogs integration. I'll write more on that this week as well. I also have some cool things planned that will use an Adafruit MatrixPortal and LED matrices to display album art of what I'm listening to, but that's phase two.

More to come. Really!

Introducing pi-dial - Part 7 - Fixing the LCD

(Catch up on the pi-dial series of blog posts.)

In my last post, I teased about 3D printing the enclosure. That will come in the next blog post, I promise.

First, I wanted to talk about an issue I ran into with how text was displayed on the 16x2 LCD screen. Using the RPi_GPIO_i2c_LCD library when I changed the input, if the input had less characters than the new input, the extra characters were staying on the screen.

For example, if I started on the Xbox One input, I would see:

Input: XBoxOne

If I changed to the Vinyl input, I would see:

Input: Vinylne

Since XboxOne had 7 characters and Vinyl only had 5, the extra 2 characters were staying on the screen. If I used the clear() method, it caused the line to blink. When I stopped to think about it, it makes sense, as the method clears the space and redraws it. Since I’m constantly polling the receiver via the API to check the volume and input, it would redraw the screen based on the sleep call I make in the while loop at the end of the pidial-lcd.py file.

I really don’t want it to blink. It’s distracting when sitting on my desk.

A quick search later I came across the RPLCD library. They also have their docs on ReadtheDocs and after quickly scanning that, I used it as a replacement. I had to change just a few lines of code and voila! It’s clear command doesn’t blink and it actually clears the LCD before writing to it again and the extra characters were gone.

I’ve said it before and I’ll say it again: I love open source.

And make sure you thank an open source maintainer today.

Next up: 3D printing and assembly

pi-dial - Part 6 - When is a list not a list?

(Catch up on the pi-dial series of blog posts.)

In addition to controlling the volume of my receiver’s Zone 2, I want to be able to use a second rotary encoder to change the input. For example, I may want to change it from the Phono input that I use to listen to records to the Tuner input to listen to the radio.

The Denon AVR-3806 that I own has twelve total inputs, of which I’ve customized a few of the names. If I print out the list using the denonavr library, I see:

All inputs: ['8K', 'AUX', 'AppleTV', 'Bluetooth', 'HEOS Music', 'Phono', 'Retropie', 'SOURCE', 'TV Audio', 'Tuner', 'UBP-X700', 'Vinyl', 'Xbox One']

I’m using Python, so iterating through the list and / or slicing it to get to a specific position in the list shouldn’t be hard. Shouldn’t being the key word.

Spoiler: It was anything but.

If I attempted to iterate through the list, whenever the list position was equal to 8K, Bluetooth, or Source, the list would stop. It didn’t matter if I tried to move up or down the list. Now shame on me for not blogging about this earlier as it’s been almost two months since I worked on this particular piece, because I think there was another position in the list that did something similar. But of course I don’t have great notes or even a lot of commits to go back and look through.

I’m still very much a novice at coding, so I did what most newbies do. I wrote multiple if / else statements to try and figure out how to get around this limitation. I was 100% sure it was my code. Nothing I did worked, even as I wrote very procedural code to step through the list. I was on vacation the week I worked on this and spent literally the entire week trying to get this to work.

I finally broke down and asked my wife for help. In about five minutes she ripped up my if / else statements and wrote a methodthat passed a variable on whether the rotary encoder was turning up or down. And then she ran into the problem where the list would get stuck on positions 0, 3, and 7 (8k, Bluetooth, and Source) as you can see in the code’s comments. A couple hours later she was able to work around it. This was a fun way to spend a Friday night together!

Neither of us have any idea why those inputs caused the issue, but she was able to write a method to work around it.

The good news is that it wasn’t necessarily my code, the bad news is that I still have so much to learn about the best way to write my code.

The better news? The code is done! I have one program that monitors the current input and volume and displays it on the LCD and I have another program that can change the inputs and the volume.

Next up: 3D printing the enclosure.

Introducing pi-dial - Part 5 - A detour into async

(Catch up on the pi-dial series of blog posts.)

With the hardware prototyping done, it’s time to write the code that is at the hart of pi-dial.

I need the hardware to do four things, all in Zone 2 of the receiver: 1. Change the inputs (CD to Phono to Tuner, etc.) 2. Change the volume up or down 3. Mute and un-mute the receiver

I mentioned briefly in my first post that the denonavr open source project has the methods I’ll need for this. It is possible to directly interact with the receiver as it has all of the functionality built using custom HTTP calls. But it’s definitely not optimal, leading to projects like denonavr.

Late last year, the denonavr project was re-written to use Python’s async library. I know of async, but I don’t know how to use async.

I found the documentation for using async with denonavr kind of sparse. I opened an issue in the project’s Github repo and received some pointers to get started.

After that, it was time to dive into async itself. I purchased the Everything Bundle from Talk Python Training earlier this year which included the Async Techniques and Examples course.

I kind of understood it? But not enough that I am going to use it (right now). I’m still very much a novice at coding in Python and I’m just trying to build something between a prototype and minimum viable product. This was not a rabbit hole I wanted to go down right now, but I might in the future.

I did take some time to update the project’s documentation with more async examples to make it easier for the next person. Those changes were merged in a couple weeks ago, which is nice.

Thankfully, the denonavr project still includes legacy support, which was pretty easy to get up and running, with the exception of one thing, which I’ll cover next time.

A brief aside: I feel bad for users who ask a question and don’t get a response in open source projects. Even worse, in my opinion, is adding a bot that will automatically close those questions or issues after a period of time without a response. Having been involved with a couple of open source projects, I understand how frustrating it can be for users to ask the same questions over and over or have a repository filled with “issues” that really aren’t issues. But then write some documentation to help those users! Don’t ignore them and then automatically close the issue.

Next up: Finally writing the Python code to power pi-dial.

Introducing pi-dial - Part Four - Adding an LCD

Previously: Part 1 Part 2 Part 3

While it may sound like I’ve been on a tear creating the pi-dial, the truth is somewhere in-between. While I’ve done a lot in the last month, there have been enough times where I’ve wandered down a rabbit hole doing research and getting distracted.

One of those times was thinking about how I was going to mount this contraption. One idea I had was to build a custom wooden box / case to mount the Pi in. The rotary encoder is panel mountable, so I thought I could drill a hole in the wood. I don’t have any wood working skills to really speak of, so I looked at this as another learning opportunity. (Like I need another learning project!)

I spent a lot of time on the MakerBot Thingiverse website browsing plans for 3D printers. I found a few plans that include a case for the Raspberry Pi and a hole the rotary encoder, but the plans were almost ten years old and for the original Raspberry Pi version 1 which hasn’t been around forever and all I have is a bunch of Raspberry Pi version 2s.

But then I found this plan that reminds me of an old car stereo. It would sit nicely on my desk:

Raspberry Pi Brackets and Enclosure

If you click through, I’m not sure I’ll use the power step down converter for the back plate, but it might be a nice project as well. That’s a problem for future Paul to deal with. I sent the plans to my friend with the 3D Printer and I’ll have to ask him if it’s possible to make the knob on the right equal to the one on the left, which is the rotary encoder (so I can use two of them).

Now I need to LCD to match the design. Another few hours of reading and research and this doesn’t look as hard as I thought it might be.

I looked at the Adafruit Pi Plate, which includes a 16x 2 screen (16 characters per row, 2 rows). It also has five buttons, and I don’t need that many. I did find some interesting 3D printer cases for it, but I’d rather use a rotary encoder to control the volume than buttons.

Looking at some of the tutorials on-line, I went with a cheap two-pack from Amazon. If you look at the photo below, you can see the LCD has 16 pins across above the LCD, but it also has an I2C interface on the left side where you can see the 4 pins coming out of it.

Working 16x2 LCD on a Raspberry Pi

The great thing about it using I2C - inter-integrated circuit - is that it makes using peripherals like this LCD a snap. Out of the 40 pins on a Raspberry Pi, only 4 are I2C, so you have to plan ahead on how you’re going to connect things to the Pi when using multiple peripherals, like 2 rotary encoders and an LCD. The rotary encoders were easy to hook up just using the jumper cables, so I didn’t have a concern.

My LCD came with the default address, 0x27, and I had it working within an hour using the RPi_GPIO_i2c_LCD library.

In less than 50 lines of Python, I was able to create a while loop using the denonavr library. Every 5 seconds in my prototype code it polls the receiver asking it for the current volume and current input and displays on the screen above.

Voila! This was probably the easiest part of the project so far.

Next up: A detour with Python’s async

Introducing pi-dial - Part Three - Prototyping

Previously: Part 1, Part 2

At the end of my second blog post, I had decided to switch from using the CircuitPython Media Dial to using a Raspberry Pi instead. The media dial is much smaller (and cuter!) than a full blown Raspberry Pi, but I need to start somewhere and writing one Python application for the Pi seems easier (for the moment) than two different programs.

It was time to start prototyping and write some code! There are a ton of guides on the internet for programming a Raspberry Pi with a rotary encoder. Unfortunately, a lot of them are older from when the Pi first came out almost ten years ago. The Python programs I found were either legacy Python 2 code or used old Raspberry Pi libraries in Python when I wanted to use the gpiozero library.

I hooked up T-Cobbler breakout board and cable from the Pi to the breadboard. This makes it simple to swap jumper cables to make sure it’s wired correctly.

Breadboard connected to a Raspberry Pi and Cobbler breakout board

I couldn’t quite get it at first. I ran one of the Python 2 programs I found and it was working. So I knew I had wired it up correctly and it was time to read the gpiozero docs some more.

Within three or four hours I had it all working. I wish I had kept better notes for these blog posts to share some of the broken code versus where I ended up, but the end result is what I wanted. I created a handful of different Python programs for testing all the different functions in pi-dial repo that I'll eventually need to clean up. (Don't look, the code is terrible, needs to be re-formatted, etc. etc.) If I moved the encoder clockwise, the volume goes up. Counter-clockwise, the volume goes down and if I press the button it either mutes or unmutes depending on its status.

I couldn't get this tweet to embed correctly, but if you click through, you can see and hear a 12 second video of me muting and unmuting my receiver over Zone 2 using the Raspberry Pi over the network.

Next up: I can’t keep it simple

Introducing pi-dial - Part Two - Oops

Previously: Introducing pi-dial Part 1

During this year’s PyCon, I completed two exercises in the Microsoft booth virtually. A week later I was rewarded with two $50 gift certificates to Adafruit! (Thanks Microsoft!)

Using the gift certificates, I bought a soldering iron and the parts I needed to make the CircuitPython Media Dial. But I’m impatient, and while I was waiting for those to ship I thought I’d try and build a prototype in CircuitPython using a Circuit Playground Express (aka CPX) I already had.

I ran into a couple errors trying to get one of the libraries in the CircuitPython Bundle to load on the CPX. I’ve already been lurking in the Adafruit Discord channel which is a very friend community and I decided to ask for help. After fixing my library issue and talking about my goals for the project, a kind person pointed out a problem that I hadn’t thought of:

There is no network connection on the Circuit Playground Express or the Trinket I purchased that powers the Media Dial.

Oops.

I take network connectivity via wireless or wired for granted on all my devices. It just never occurred to me that the microcontrollers don't have network access unless you physically add it.

I started to think about how I could get around it and researched how I could fix this. Two ideas jumped out at me:

  1. Add a wifi co-processor, using a second circuit board that is soldered to the Trinket M0 microcontroller that I already purchased for the project.
  2. The Media Dial project as created sees the dial as a USB device and the dial rotations as a keypress. I could write a Python application that runs on the Mac I’ll plug the dial into to listen for a certain keypress from the Trinket M0 and then send the commands to the receiver from this new Python program.

I quickly abandoned #1. I have no idea how to modify or create designs for a 3D printer and all the wifi co-processors were much bigger than the Trinket M0. I’m not going to figure out how to cram those together.

The second option was intriguing. On the plus side, by running a Python program on macOS, I get access to the denonavr library making interacting with my receiver a breeze. But the more I thought about it, the more I questioned the solution. The way it would work:

  • The Media Dial is seen on macOS as a USB device. This shouldn’t be too hard, the code is available in the guide on Adafruit. (“This shouldn’t be too hard” - famous last words.)
  • If each turn of the rotary encoder is a key press (that can be programmed using CircuitPython in the code linked above), my Python application listens for that keypress and then sends an API call to my receiver.
    • When turning the rotary encoder, is each step a keypress?
    • How quickly does each step turn and does it turn the volume up or down too quickly?
    • How much lag is there between turning the rotary encoder, the CircuitPython program interpreting that, and then sending it to the Python program on my Mac, which sends the API command over the network to the receiver.

I looked into which keys presses are reserved in macOS for shortcuts and programs, looking for a keypress that doesn’t conflict with anything else. I only need 3: volume up, volume down, and a button that turns mute on or off depending on its state.

The other concern I thought of was having to start the Python application every time I reboot my MacBook. Coding two different apps seems like a lot of work and I started to think about it differently. What if I used a Raspberry Pi instead? Yes, it’s a lot bigger and bulkier, but I only have to write one application and I can wire the buttons directly to the GPIO buttons on the Pi. The tradeoff of a bigger physical device instead of a dial seems worth it compared to the above. Besides, this is more of a learning exercise. I'll consider this a prototype to see how I do in adding a rotary encoder to a Raspberry Pi.

So that’s what I’m doing. More on that in the next blog post.

Roundup 6/17/11

A few links to wrap up your week:

A look back at needles

gramophone reproducer

Scientific American dips into it’s archives looking at an article originally published in 1919 discussing needles on the “new” phonograph:

At the time of this article’s publication, July 5, 1919, the phonograph had only been a household item for 10 to 15 years. While it was still considered a novel invention, persons such as F.D. Hall of Chicago began looking for ways to improve upon it—specifically its sound quality. The phonograph made “tinny” and “harsh sounds,” and was also described as being “unduly loud.” Further, the metal needle often wore down the records after a short while.

The article goes on to share the story and pictures of using and manufacturing bamboo as a record needle. As the article points out at the end, we now use mostly diamonds in our needles. To think how far we’ve come…