Skip to content

Blog

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.

Introducing pi-dial - Part One

The backstory and overview

I’ve started work on yet another project: pi-dial

I’ve been looking at the Media Dial project on Adafruit for a while as a method to control the volume on my home theater receiver. I’m lucky that my home theater shares a wall with my home office, which made it easy to wire an extra set of speakers to use with Zone 2. This allows me to listen to my record collection while working in my office for my day job or when I’m at my workbench. My record player is still only 10 feet away, making it convenient to flip a record or put a new one on the turntable.

The dial runs CircuitPython, and would connect to my receiver using the denon-avr Python library or the HTTP POST commands that the receiver also excepts over the network. The dial would let me change the volume either up or or down and clicking the button would mute or unmute Zone 2. I’m lucky that Denon has a pretty well documented API using these HTTP commands and even better that Oliver aka scarface-4711 wrote the denonavr Python library to easily use Python commands to control the receiver and licensed it liberally with the MIT license.

The project became possible when my boss bought a 3D printer. When I first came across this project last year, I briefly looked at some co-op maker spaces and even a commercial company to see if I could get things printed, but during the pandemic lockdown it wasn’t going to work.

I’m doing this project for two reasons:

  1. I want to continue learning Python, especially with hardware like CircuitPython or the Raspberry Pi. This is very different than the web development I’ve done with Python so far.
  2. I like the idea of something physical to change the volume or mute. My partner walks into my office? Hit the button and mute it. Records have a different volume range? Move the dial!

I’m also excited to learn a whole new set of skills - breadboards, electricity, soldering, and more.

It also turns out I can already do this in Home Assistant, the Python-based open source home automation software, that is fantastic.

denonavr Python library configured in Home Assistant

I have a Firefox tab pinned for Home Assistant and the only reason is to be able to control my receiver. From here I can mute, change the volume or change the inputs. All using the denonavr Python library. I have a few other devices configured in Home Assistant, but I haven’t spent the time to set up a Lovelace UI or other automations. My house isn’t very “smart” and I definitely don’t have any voice assistants. I may make some of my own sensors some day, when I’m better at soldering and CircuitPython. But I digress - one step at a time.

But then I ran into a problem. CircuitPython has a limited subset of 3rd party libraries maintained by Adafruit. There is no denonavr module available, which abstracts away all of the HTTP POST commands to control the receiver over the network. But that’s ok - I can use the requests library that is in CircuitPython to do this. It will be more work, but it will help me learn that library better.

In the next blog post, I’ll share some of the challenges I ran into when I was ready to start prototyping.

Retiring My First Python Projects

It’s a sad weekend in my workshop. I’ve officially retired and archived my first two Python projects, NFLPool and MLBPool2.

I vividly remember Thanksgiving weekend a few years back when I decided it was time lo learn to code. I bought a few O’Reilly books on a Black Friday special and quickly learned that I can’t learn from books. It would be another year and a half before I buckled down and really learned Python.

I’m of the opinion that if you’re going to be community taught (I don’t believe you’re really self taught - you’re learning from the community of users and teachers, whether that’s in a formal course you’re taking online or asking questions on Reddit or Stack Overflow) you have to have an end goal in mind. It’s one of the most common questions I see on Reddit: “I’ve learned the basics, but I don’t know what to do now?” Most people need an itch to scratch, a problem to solve to really apply what they’ve learned.

NFLPool was that for me. We had spun NFLPool off of MLBPool2 and I was the commissioner. I was doing it all in a spreadsheet manually, each Tuesday having to look up the team and player stats and I knew there had to be a different way. And there was.

MLBPool2 was based on the NFLPool code, which made it easier to get up and running later. But neither project would have existed if it hadn’t been for Talk Python’s Python for Entrepreneurs course. The skeleton of both sites came from that course and modifying that code base taught me so much about Python and Pyramid, the Python web framework that powers both sites.

I didn’t know if I would stick with it, but here I am four or five years later, still learning and branching out in how I use Python. I’m still working on another web site, building it twice with two different frameworks (I really want to learn FastAPI) and also learning how to use hardware with Python. (More on my experiments with the Raspberry Pi, rotary encoders, soldering and more will be recapped in another post).

NFLPool never really had enough players to survive. MLBPool2 did, but in the second year of it being online, the commissioner changed some of the rules and how points were scored. Updating the code introduced a bug in how ERA is calculated and I was never able to find the bug.

I thought I had turned off registration in both applications, but script kiddies are still registering themselves with NFLPool. Before they find a flaw with SQL injection or something else stupid, I’ve replaced both sites with a landing page that redirects to the project pages here. If it wasn’t for them, I would have left the site and scores up to show what had been built. I've officially put both repositories in Archive mode on Github.

In the end, they both served their purposes in giving me motivation to learn how to code and for that I’ll always be grateful.

My First PyCon

I'm attending my first PyCon today. I'm pretty excited. I know some argue whether virtual vs. physical is good, but as somebody new to the community, it's almost easier to come lurk. But if I knew more people, there's no question that the hallway track in a physical conference is always fun.

Just a few of the talks I'm looking forward to:

  • "Learning python during lockdown: a surprising bonding experience with my child"
  • "More Fun With Hardware and CircuitPython - IoT, Wearables, and more!"
  • "Diversity & Inclusion Workgroup Panel"
  • "Introduction to Pydantic"

And more! This will be a fun weekend.

Richard Stallman Must Go

Back when my blog ran on Wordpress and I had analytics and comments enabled, one of the most viewed blog posts was one from almost ten years ago, where I agreed with another blogger that the FSF should be forked.

With Richard Stallman re-joining the FSF this week and the valid outrage that has followed, I remembered the blog post but not the content. Larry Cafiero's original blog is no longer on the internet, but I found his original blog post and follow-up on the Internet Archive's Wayback Machine.

It just amazes me that after all these years, we're still arguing about someone as toxic as Richard Stallman being around. Ever since I saw his keynote at GUADEC and his awful, awful talk, I've wanted nothing to do with him. I disagreed with he how he spoke to GNOME Foundation members running for the board, his views on women and the disabled, and I strongly disagree with his views on trans people. He needs to be held accountable for his views and the things he has said, and the Free Software Foundation's board needs to be held accountable for allowing him to be re-appointed to their board.

I'm proud to add my name to this open letter from major organizations and over 1,000 people calling for Richard Stallman to be removed from all leadership positions, and the Board of the FSF to step down.

Richard Stallman must go. Again.

Silver Saucer Update - March 2021

Following up on my last blog post, I did deploy Silver Saucer a couple days later. I spent a day trying to track down why MLBPool2 kept loading when I visited silversaucer.com and it turns out that the nginx web server defaults to the application with the lowest port number. I had made a mistake in the nginx configuration in one little place that took me forever to figure out. And with that, Silver Saucer is live on the web.

It still has a nasty viewport problem and the screen height is not 100% and it’s driving me crazy. Maybe if I had finished my Coursera class on HTML, CSS and Javascript I would have figured it out…

A bad habit that I have is when I get stuck, I move on to a different project. I’m hoping that my subconscious will fix it, but it’s more out of frustration. So what did I do? I bought more training courses from Talk Python and decided to learn the FastAPI web framework instead. I was surprised to see such a new project in third place in the recent PSF survey, especially as it was the first time being included as a framework.

I decided to build my site along with the training, rather than the PyPI clone the training has you do. After sorting through a couple of different bugs, guess what? I still need to learn CSS.

I’ve switched courses, moving from Coursera to Pluralsight, and am taking a new class to learn HTML & CSS. I’ll get there one of these days.

Preparing Silver Saucer for Deployment

Pyramid, my favorite Python web framework, was released version 2.0 late Sunday. The biggest change is dropping legacy Python support. I've been running the beta version of 2.0 the last couple of months while developing Silver Saucer.

The Pyramid 2.0 release helped me change gears from my hardware project back to Silver Saucer. Under the mantra of “release early, release often” I decided to start prepping to deploy Silver Saucer. I knew I had a few things to work on still, especially the big “On This Day” feature I haven’t started.

Of course I didn’t save any notes from when I last deployed a Pyramid web app. I started to make a list of things I needed to do on my DigitalOcean droplet and realized that Silver Saucer’s IP on my server wasn’t set up correctly which was why I kept getting an SSL error. That was an easy fix when I remember Dependabot was still broken on Github.

I had tried adding a pre-commit hook to use the black linter to my Github repository. It required me to create a pyproject.toml file. But doing this caused Github’s Dependabot to think I was using Poetry to manage my Python modules, when I’m not. I didn’t have any luck on the Github forums, so I just removed the file from now. Dependabot started working again right away - spewing out over two dozen pull requests to go through. It took Azure Pipelines a little while to catch up in testing out all the builds with the PRs, but only one or two failed and I still have one more PR to test and / or fix.

With that done, I thought I’d fix just one or two little things. This led me down a path of refactoring the theme, especially how the header works. I had the header image included in every page, and I was able to put it in just the layout template.

I also have a weird CSS bug with the viewport where it’s not filling the website screen 100% vertically. I spent hours on that but my CSS skills aren’t there yet, especially when using a complex CSS theme created by someone else.

I’m going to push Silver Saucer live in the next few days. The irony of creating a random Discogs player when I just started listening to my entire collection alphabetically is not lost on me. Release early, release often!

Always Learning

I haven’t blogged about my progress on Silver Saucer over the last month, but that doesn't mean I haven't been up to anything. I’ve continued to take the HTML, CSS, and Javascript on Coursera. I find CSS... difficult. Unfortunately, I also received an email from the course instructor that Coursera will be removing this course soon, so that figures. But it's motivation to get it done.

One of the things I tackled a few weeks ago was making an applet to sit in the macOS menu bar to control the volume of my Denon receiver located in my home theater. The receiver’s Zone 2 controls the volume in my office, where I’m usually listening to records. It took 30 minutes to throw some code together using RUMPS to make the applet and the denonavr Python module to control the receiver.

That led me down a path of looking into making a hardware project to make a dial to control the volume. Adafruit has a great tutorial on building one using Circuit Python, but I don’t have a 3D printer (yet).

That led me into looking more into the denonavr Python module - which is also the same module that powers Home Assistant. I’ve now reinstalled Home Assistant - it’s been a couple of years since I last tried it out. Now I can just turn to a browser page I leave pinned and control my receiver from there. I’ve spent a few days playing and tweaking Home Assistant to see most of my “smart” devices, but I’ve never been all that into having a smart home (nor will you find any voice assistants here). Of course this has given me a dozen ideas of things to build or integrate. But I think I still want to build a dial!

Denon Control in Home Assistant

I also spent a few days doing a deep dive into Docker. I have a couple containers that I run on my NAS, but never really understood the right way to update or manage them. Now that I have a little more knowledge I realize how slick containers really are.

Finally, I was on vacation last week and started to tackle another project I’ve talked about with a friend for the last couple years. We need a pump (or pumps) programmed for a project. I had some old Raspberry Pi computers laying around and a year ago we quickly prototyped a solution. Unfortunately, we needed a more powerful pump - both in how it pumps and in power consumption.

I’ve been learning about circuits and electricity by watching a lot of YouTube videos and reading a number of books I’ve picked up over the years (especially on Humble Bundle) . I then went out and bought bench power supply unit (PSU) and multimeter for my workbench. I figured out how to power the breadboard with a 3.3v / 5v breadboard power supply first and then figured out how to use the PSU, too. It was kind of exciting to see a little blue LED light up the first time I got a circuit right:

Well, that’s exciting. Sometimes it’s the little things. pic.twitter.com/QM89RgzhjU

— Paul Cutler (@prcutler) February 12, 2021

Here’s a picture of my messy workbench below with the Pi hooked up to a breakout board which connects to the breadboard. Also connected to the breadboard is a DRV8825 motor controller which is connected to a NEMA step-motor.

In theory, I have everything hooked up correctly now to start prototyping. Now the hard part starts: Is the code wrong or the hardware hooked up incorrectly?!

My workbench

Listening to my Record Collection Alphabetically

My Record Collection

I love this blog post from Discogs. I’ve often thought of doing exactly what it lays out: listen to your record collection from A to Z.

I know there are a lot of records I rarely listen to. Whether I bought them because I love the artist, because of their rarity, or just because I was taking a chance on something new, I’m sure there are records collecting dust.

I’ve done a pretty good job, especially last year in the first year of the pandemic, selling off any records I know I won’t listen to again on Discogs. I also take some pride in that if the record is rare or was released on Record Store Day for example, not to overcharge when selling some of these.

I haven’t decided what I’m going to do about artists like Ryan Adams. I was a huge fan, but I haven’t listened to one song of his since news broke a couple years ago of how he abused women.

First up today: ABBA! I’ve always loved them, probably due to hearing them so much as a kid from my step-mother. The record on top of the photo is ABBA’s Waterloo, their second album. I actually picked this up at Electric Fetus a couple years ago and until today, I’m embarrassed to say, it was still sealed - 48 years later! But not anymore!

I’m lucky that my home theater is on the other side of the wall from my home office. My office is Zone 2 and it’s an eight foot walk to put a record on. This way while I’m working during the week or writing or tinkering on projects on the weekends I almost always have music on.

ABBA - Waterloo