Sunday, January 15, 2023

Synchronized Whole-House Audio (Open Source Sonos)

This post is deprecated. See this github repo for a more complete description and helpful scripts.

 

 

 

 

Some years ago our Squeezebox player died. We kept the janky perl daemon and replaced the player with a raspberry pi, using its built-in DAC connected to the same 1980s-era audio amp, driving two sets of speakers. A house renovation demolished the speaker wiring between rooms, and it was finally time to move into the modern era.

Two neat innovations have come along in the two decades since we bought the squeezebox. The first is software: the Snapcast time-synchronized audio player lets you scatter tiny wifi receivers around your house and have them all play music synchronized together. The second is hardware: the HiFiBerry Amp puts a 60W audio amp into a pi-hat form factor powered by a surplus laptop charger.

This whole story hinges on raspberry pis, which have been unobtanium for a couple years, but hopefully the situation returns to normal soon.

This system is exactly the sort of thing that appeals to me: open source everything. But of course that means you get to (have to) choose bits and pieces at each layer. Here are the choices I've put together so far.

  • Music starts in a folder on the filesystem of my home server.
  • mpd, a music daemon on the server, indexes the music folder, accepts playlist requests, and plays the music as an audio stream to a named pipe.
  • Users can control the playlist request using a web browser. Another daemon ampd provides the web UI and issues commands to mpd. (I don't recall the channel that delivers mpc commands.)
  • When new music is installed on the server, I issue an mpc update command line to request mpd to re-index the filesystem.
  • The audio stream from the music player arrives at snapserver, the daemon that coordinates the time-synced players.
  • snapcast clients running on raspberry pis fetch the audio stream over TCP over WiFi from the snapserver. They play the audio back through the Linux alsa audio subsystem.
  • The drivers for the hifiberry hardware intercept the audio stream and convert it using a high-quality 16-bit DAC and send it out to the amp and speakers.

Direct application audio. It's also possible to manually generate streams of audio and shove them into another audio stream on the snapserver. For example, I can pass flags to vlc to deliver the audio to a TCP port. Each player station can be set to listen to different streams. Since I use this feature rarely, I have a single "fallback" stream set up to play the manual stream if it's present, and otherwise play the mpd playlist audio.

This scheme has a multi-second lag. vlc can be configured to delay the video to match, but it's a hassle and controls like pause feel weird with that much delay.

Future ambitions

More players. There's no reason I couldn't run two instances of mpd. This would let family play americana in the kitchen and living room while I'm listening to folk in the basement and shop.

Bluetooth input. I'd like to press the bluetooth hardware of one of the pis into service as a bluetooth endpoint so people can just send music from a phone or tablet directly to the big speakers in the room they're in. Because of the lag in the pipeline, this probably won't work for playing videos.

Better UI. The ampd UI is pretty clunky, slow, and surprising. Search doesn't work. My music collection has nice album art, but mpd ignores it so ampd fills in random searches from the Internet.


Monday, January 10, 2022

Birthday Jumbotron VI: Deliverance

 






Birthday Jumbotron V: Final Reckoning

Today started with seeing about getting onto that loading dock.

I called the number on the loading dock door and got the security desk.

That guy transferred me to Rodney, the security lead.

That guy forwarded me to Linda, the VP of Administration.

Linda forwarded me to the voice mail for Jon, the VP of Operations.

Jon actually called me back! He was cheerful, but the bad news was that they had a lot of restrictions on anything that might look like advertising -- or might be above a road and fall and kill some random dude. He said it'd be hard anytime, and definitely can't get it done today.

Well, I appreciated them even thinking about it, and it was worth a try!

So it looks like the deployment site will be the Pike Street path.

Saturday, January 8, 2022

Birthday Jumbotron III: Reconnaissance

I hung out with Jeremy today, 45 minutes later than I could have because I reconnoitered the convention center to see where I might deploy the Jumbotron.

My recollection from visiting his house was that the best option would be displaying it from the loading dock of the Convention Center. I'll just take a quick stroll down the street and ... there's a giant steel door and a phone for calling security. Hmm.

I strolled around the grounds a little bit. I found this spot from which I could look up and see Jeremy's building, the Meridian. I could set the display up on the concrete wall or hang it on the fence.

Here's the view of Jeremy's building from that spot.


I walked around the south side of the Center, then inside, looking for the security office. I never found it, but on the way around the north side at the sidewalk, I ran into a guy taking a smoke break who was wearing a Convention Center Security coat. I chatted with him, and he said "well, no guarantees, but call administration -- they might let you wander up on the loading dock." So that's promising!


Later, up in Jeremy's apartment, I checked out the view from above. In the lower-right corner of this shot, you can see the walkway and fence from above. I realize from this angle the display needs to basically be facing the sky. And you have to stand up to see it over the desk. So it'll work, but it's not ideal.


In the left side of this image, you can see the courtyard behind the Center, beyond the bright glass atrium. It has a nice line of sight, but it's pretty far away. Eight feet is a big display, but it might actually be difficult to make out at this distance. BANG! would probably come through, though.


This is the brass ring. The loading dock. Same line of sight as the Seattle star (photo upper left) and a Christmas tree cone (upper right), a bit closer than the courtyard, and it can be seen from a seated position in Jer's office. The display won't be huge -- those garbage compactors in the near corner of the loading dock are probably eight feet wide. But I think it's the best shot.

I think I'm ready for Monday. Hoping for minimum rain.

Friday, January 7, 2022

Birthday Jumbotron part II

The third spool of LEDs showed up. Woo!


I spliced some extra power wires in to overcome the high-resistance path that was causing the lower-right corner to render discolored and yellow. The wiring is now featured in the dictionary next to "janky", but the display looks pretty good. Most of the time. Except when the data wire from segment 3 to segment 4 flakes out and the bottom four rows freeze.

I spent some time hacking another animation into the Arduino code. My first try should have been trivial, but apparently I overflowed my static RAM budget and it crashes. I tried compressing the dynamic range of the PPMs, but that looked terrible. Then I realized that 1-bit images with a color lookup table is probably just fine, so I replaced the new animation with that, and also saved a bunch of bits by converting the "Happy Birthday" text from its ascii representation to the 1-bit format.

Here's the glory:



Thursday, January 6, 2022

Birthday Jumbotron

 Okay, I'm like 5 years late to the Neopixel bandwagon, but did you know that you can buy a Jumbotron by the foot?

My calendar dinged that Jeremy's birthday is coming up in a week. What to get him? I recall sitting in his 22nd floor apartment last hack day, looking at the Seattle Star, a big programmable LED display on the Macy's building. For $15 you can choose the pattern it displays for 15 seconds.

Well, for $15 I can buy a 5 meter spool of 90 programmable LEDs. For $45, I can buy three and rearrange them into an 8 foot by 1 foot Jumbotron. That thing should last at least 45 seconds. I'm actually saving money.

I had a denser spool of 300 LEDs sitting around on my bench, so the first step was to see how tricky this was. I unrolled the spool onto a scrap plank of pine and folded it back and forth into six rows of 50 LEDs. I taped it down with magic tape.

I grabbed an esp8266 off the leftover pile from the temperature sensor project. It's a bit of overkill for this task -- 240MHz with built-in WiFi -- but I'm out of 555s. I pulled up Arduino on my desktop, #included the Adafruit Neopixel library, and coded up a little animation.

It didn't light up right away. Hmm. I know I'd had these working before. Oh, yeah, I recall some LED strips that take 12V. Let's just dial up the voltage on the power supply. 6V. Nothing. 8V. Nothing. 12V: a brilliant red flash, a popping sound, and an acrid smell. Oops.

I look closer at the LED strip.


Yep, I've visibly burnt out the first several LEDs.

I look a little closer. The symbol "+5V" is printed every centimeter. But, you know, it was easier just to spin the knob on the power supply to find that out, right?

I look even closer, and notice the directional arrows. I was trying to pour the data into the output end of the spool. Duh.

I chopped off the dead LEDs and attached my controller and power supply to the other end, and it sprung to life. A few adjustments to pixel offsets to account for LEDs lost in the U-turns, and I was in business:

At one point, the display got dim halfway through, and the meter was showing 2A draw even when nothing was lit up -- oh, the stripped wires on the output end were shorting. I taped them up, and it's back to work again. So far I've overvolted and shorted this poor little display and it's barely worse for wear.


Okay, the concept is proven. Time to scale up. I found the aforementioned 90-LED 5m waterproof strips on Amazon for $15/ea, shipped overnight. I select quantity 3, and Amazon says "yeah, we can get those to you ... the day after Jeremy's birthday." Wait, wut? Fiddle around with the cart, and I figure out that they have quantity two in the nearby warehouse, and the others must be farther away. So I order two of the waterproof model, and one non-waterproof one. By the next day, Amazon's Ominous Massive Consumer Exploitation AI had rearranged the inventory, and they were willing to get another one of the waterproof ones here by Thursday. Okay, we're in business.

I began building out the display. I made it on two 1x4 foot panels of 2.7mm plywood so I can fold it up to carry it in the truck to wherever it's going to end up. Siggy helped me drill the panels.


Then we zip-tied the segments into place. 22 LEDs fit nicely in 4 feet, so I'm only wasting two LEDs on each row. I end up with a 44x6 display.

I had a lot of trouble soldering it together. The connection points are on the very ends of the board, poorly protected, so as I flipped the panels over and over, I broke wires off one end while soldering the other. In many cases, the wire took a piece of copper from the strip with it, making soldering it back unworkable.

I regrouped and rethought. All the broken points were power wires, which I can splice anywhere. So I peeled back more waterproofing silicone and spliced those in wherever. The data lines can't skip past LEDs, but none of those broke off. So I just rearranged the wires to return away from the edge of the board, and zip-tied all the pigtails to act as strain reliefs.

It's not quite perfect. At full brightness, the voltage drop at the end of the board is enough to turn the lights orange (the blue LEDs have a higher voltage threshold and drop out soonest). I suspect kinda-cold solder joints are adding too much resistance. But it works enough for now:

Things that are missing:

* I'm still waiting for the third LED spool

* Reconnoiter a place to deploy it. Can I get to the Convention Center loading dock? That's visible from Jeremy's place.

* Attach a power supply. A D-cell battery holder is coming from eBay.

* Add mounting holes and straps.

Improvements:

* Fix the cold solder joints.

* Add some spacers to keep the halves from bumping together and shorting.


Oh, and one more improvement. I wrote the display software in Arduino because it was quick and easy and came with a library to drive the pulse train for the programmable LEDs. But Arduino stinks! Jeremy and I have been building microcontroller software in our own lightweight operating system that we developed for the distributed system in my backyard rocketship treehouse. In the last month, Jeremy launched an effort to port it to the ESP32 platform. How dare I give him a birthday gift not built in his artisanal operating system?

Last night I worked on porting my Arduino code to rulos. The main challenge was bringing over the Adafruit Neopixel library.

The first issue was that it was written in C++, and rulos didn't have C++ support yet. I began hand-translating the class into a struct. Meanwhile, I casually mentioned that g++ didn't work in rulos in our group chat, and Jeremy said "hold my beer." Fifteen minutes later he said "try pulling now" -- he'd updated the build system in less time than it took me to port the library. Okay, that was pretty sweet.

Eventually Jeremy asked me what I was working on, and I had to be oblique. I'm pretty sure he knows it's a birthday present at this point, but everything else is still a surprise.

The next issue is that the Adafruit library in ESP32 mode uses the "RMT" peripheral, underlying hardware timing support, that crashes when I run it under rulos. I didn't want to debug that just yet, so I switched from that to the ESP8266 implementation which just does bitbanging in software. For reasons I don't understand, on the ESP32 I can't bang the bits very precisely or very reliably. Even with simple for() delay loops, the width of a pulse varies a by lot:

I'm not sure why bitbanging is so hard. The right thing to do is to get the RMT peripheral working in rulos, but I don't think I want to block this project on that. I hope Jeremy forgives me for celebrating his birthday with (ewww) Arduino code.

Monday, March 26, 2018

Electronics lab cart

I bit off a bigger project with my Shaper Origin:



That's a new cart to organize my electronics "lab", which consists of a soldering iron, a few instruments, and lots and lots of shoeboxes.

It's made with tab-and-slot construction. The sides have ellipses cut out to make it a bit lighter in a style reminiscent of  1940s aviation. The back has 0.2" plywood shear panels; holes are added here also to provide ventilation.

I used pre-finished plywood from China by way of Home Depot; two $45 sheets. I had trouble with the bit burning; I suspect using pre-finished wood was causing carbon to build up very quickly on the bits. Pre-finished wood would be really convenient if it didn't eat bits, but for the next project, I think I'll stick with sanded dry plywood.

I also had trouble with the bit jumping, which was very frustrating. Then, nearly at the end of the project, I discovered that the spindle clamp had been loose, probably the whole time. The locator pin had chewed up the spindle slot. :v( I cleaned things up with a file; hopefully things will work smoothly again now that I have the spindle clamped securely.

It was fun to draw furniture in Inkscape and then print it out. But for this project, it would have been a lot faster (and maybe a little prettier) to cut the outline on a bandsaw, dado the shelves into the sides, and just not bother with the mostly-decorative ellipse holes.