Go Zamboni!

Projects, Stories, Cloud Yelling.

Forstner Holder!

February 23, 2025 — GoZamboni!

I don't recall exactly what I was doing, but I was trying to make some nice-looking holes with a spade bit and finally reached a tipping point. The holes were just a mess, even with the drill press. I'd heard of forstner bits before, and knew they made better holes, even in things like plexiglass, but had never used them. I pried my wallet open and ordered a set.

Oh my do they make nice holes. So smooth. I was even able to cut a hole in a sheet of paper, just to see if I could. It worked beautifully. Since then I've had a few occasions since then to use them and every time it's just a joy. So nice!

While the bits themselves are amazing, I had no place to keep them. I keep my spade bits in a toolbox. It's always hard to find the right one. The forstners came in a cardboard box, but it was like a game of tetris to put them back; they don't fit if you put them in order, it was hard to tell the sizes, and opening the little red boxes was fiddly.

red boxes in a box. no order to the size

Maybe I could do something with this evenly cut scrap lumber that's just lying around.

3 short 2x6s stacked on table

What's this?

back side of the 2x6s; there are two hinges

Let's open it up:

the top 2x6 flips up, revealing forstner bits

The project was an adventure. I'm very new to the router, so making the cutout in the lid was a bit rough, but there's enough space to close it over the bits, so I can't complain. I couldn't use the new bits to make the wells for the bits; the forstner shanks were all too big. I ended up using a tiny spade bit, so it seems like it's good to have both.

The bottom two boards are screwed together with wood screws and the middle and top use the hinges. If I were to do it again I think I'd try to organize the sizes a bit better.

Ultrasonic Sounder with Display

January 21, 2025 — GoZamboni!

A family member had a weight on a stretchy cord and needed to know how close to the ground it came at its lowest point. They were going to be doing this many times to dial in the starchiness over different weights. The plan was to take a video and use that to find the distance. While this would work, it seemed like something that I could over-engineer could be improved with a home electronics project. (They didn't end up using my device, but I had a good time making it.) Here's that story.

So, the idea is to have something that could

  1. measure distance accurately and fairly quickly,
  2. display that distance
  3. be used while focusing on something else
  4. didn't need a laptop

Here's what I came up with:

the final result

Connecting the Pieces

The above image has 3 pieces:

  1. a black battery, of whilch I will say no more
  2. the "sounder" on the left with two round speakers/sensors
  3. the "controller" on the right with a display, 2 buttons, and an LED.

My thought was that the sounder might get squashed if I wasn't careful and I wanted to be able to read the display from a bit of a distance. I ended up using some phone jacks (RJ-11) to separate the parts. I think that worked well.

Parts

It turns out that an SSD-1306 display (0.96" monochrome OLED with a resolution of 128x64) and an Ultrasonic distance sensor, HC-SR04 can both be procured without breaking the bank. (I spend ~$6 to get both.) They're both supported by circutipython on the Raspberry Pi Pico.

Side Note: many of the "data sheets" about the HC-SR04 don't mention the delay required between measurements, which is needed to avoid false echos from earlier measurements. The digikey notes linked above suggest at last a 60ms measurement cycle.

I used keyboard switches and keycaps that I had lying around.

You can cut to the chase and see the 3d-printer files and python at my sounder github repo.

Initial Success

When the hardware arrived, I attached the HC-SR04 to my pico and started to experiment. There are a number of tutorials out there showing how to hook things up and measure one distance. They all had mostly the same code and I got the early example to work without much problem. Fortunately, I needed a few more features, so I had to write some new code myself.

My plan was to take many measurements over a few seconds. I could then look at the values and find the lowest value. No problem, I'll just take 20 measurements in a loop and print out the results.

Ha!

Debugging multiple measurements

Measuring worked fine for 1,2,3,4,5, and 6 measurements, but as soon as I tried to take the 7th in a row everything would hang. Forever.

After some head scratching and some exploratory testing, I had an idea that was actually correct. After it's been triggered, the distance sensor sets its Echo pin high and keeps it high for a time proportional to the distance measured. For things far away Echo stays high longer, and for closer it's not held high as long.

My code:

  1. pulsed the HC-SR04's trigger pin
  2. waited for Echo to become high, (A in the code below) note the time, and then
  3. looped again, waiting for Echo to go low (B below) and note when that happened
  4. Calculated the distance and printed it out
    for x in range:
        trigger.value = True
        time.sleep(USEC_15) # trigger needs to be on for at least 10us
        trigger.value = False
A       while not self.echo.value:
            echo_on = time.monotonic_ns()
        # now we wait for it to fall, updating until it stops.
B       while self.echo.value:
            echo_off = time.monotonic_ns()

C       cm = ((echo_off - echo_on) * CM_PER_NS) / 2) + 0.5
        print(f"measured: {cm}cm")

The difference between the two times, echo_on and echo_off, would be used to calculate the distance.

My hypothesis was the the garbage collector (gc) was kicking off around the time of the first sleep, before A. This made the code effectively blind to any changes to the echo pin until the gc completed. By the time the code was running again we'd completely missed the echo pulse. We were stuck in A, waiting for a high signal that was never coming.

Quitting from the console showed that I was in fact hung in the first while loop.

Here's a diagram of what was going on. gc timeline

To avoid this issue I bracketed the distance measuring code with gc.disable() and gc.enable(). Things were surely going to work now. Ha! again.

The new code looked something like this:

        self.trigger_ping()
        gc.disable()
        # measure...
        gc.enable()
        return ...

This time I ended up with a really inscrutable and fatal error. After additionally scratching I tried adding a gc.collect() right after the gc.enable().

        self.trigger_ping()
        gc.disable()
        # measure...
        gc.enable()
        gc.collect() # critical to keep running
        return ...

Success! I don't understand how the gc decides to run, but apparently the only time it wanted to run was during that loop. Even though I was sleeping in other places in the code, the gc wasn't running then. Printing out gc.mem_free() was helpful in seeing that this was really the problem. (Each loop iteration showed a smaller number, until things broke.)

Folks might say that doing a gc.collect() every time isn't very efficient. While that maybe the case sometimes, as noted above, we need to wait at least 60ms between sending pulses, so we're not wanting for CPU cycles.

My initial attempts did a time.sleep() for the full 60ms, but after some though it was clear that we just need a total of 60ms from the last echo, so the current code is adaptive, and only sleeps long enough to give us that 60ms delay. This allows us to get more measurements than the naive implementation.

Controlling and Displaying Measurements

You'll notice there are two buttons on the controller, below the LED and screen. One is essentially <enter> while the other toggles the measurement type: one or many. Displaying one measurement is straightforward, but I'm proud of the many display. Here's a close-up of a many display:

the final result

The min and max measurements speak for themselves, as does the message. The little "smile" below thrills me.

While we're only showing values for two of the measurements: min and max, and don't have enough display resolution to do a proper graph, the dots below show the general distance measured.

In every case the display will use the same height for its graph, whether min and max differ by 10 or by 100 doesn't matter. We can see that the measurements started out "far away," came closer, than then went away again, but not as far as the initial measurement. I think it's a nice way to see what got measured and give you some insight into the measurements, particularly when paired with min and max values.

There's also an LED, which blinks to count down to a delayed start, allowing you to trigger the measurement and then move to actuate your contraption.

Schematic

Here's a schematic for the whole thing.

sounder schematic

Let me know if you make one.

Soap

January 21, 2025 — GoZamboni!

We seem to go through a lot of soap in the shower. It seems crazy that a bar of soap doesn't even last a week.

Recently I thought I would get the to bottom of this. While I didn't get to to the bottom, I did end up slightly below the surface; the rest of the family wasn't really on board.

The Idea: Determine how much soap I use in one shower.

The Plan: Weight the soap before and after. I thought that dry vs wet might be trouble, so I got the soap wet before the initial weighings.

The Results: I weighed my soap for 6 days. Folks started to get annoyed that the scale wasn't in its normal spot, so I ended up stopping my collection. Come on family, it's for science.

day Before(g) After(g) Used(g)
1 107.2 105 2.2
2 59.6 55.4 4.2
3 33.6 29.9 3.7
4 14.8 11.6 3.2
5 10.7 8.9 1.8
6 55.1 52.7 2.3

A new bar is ~112g. The average use above is ~3g. That gives us ~37 uses per bar; if we assume we can use up the whole thing, and that everyone uses the same amount as me. (Squishing an old soap onto a new bar is a post for another day.) I think the first assumption is reasonable, the second, probably less so.

As noted above, the family wasn't completely committed to this undertaking. Prioritizing family harmony over robust data collection, the experimennt ended. I've since learned to live with our current rate of soap consumption.