Plumbing My (Countertop) Dishwasher

2025-04-24 21:13 - Making

When I moved here, I knew I'd be without a dishwasher — which I had grown accustomed to. It's not really a big deal, but it is an inconvenience. I survived that way just fine for a couple years. I knew about "countertop" dishwashers. But they seemed to all require either a very awkward and probably destructive plumbing-in process, or used awkward sink faucet attachments (which would get in the way, needing to be hooked and unhooked each time). Possibly more hassle than the chore they'd be replacing. But then I learned about the existence of no-plumbing models: A drain hose simply runs towards the sink (or a bucket!), and water supply is simply poured in top, e.g. from a pitcher. I managed to find a cheap used one (~$300 new, $100 for the one I got) and have been enjoying it since.

But it does take four-ish round trips, waiting for the pitcher to fill, then carefully pouring it in without spilling. I've enjoyed the dishwasher enough that I put some thought into plumbing in the supply line. My used appliance didn't come with the original supply line, so it took some creativity. But I got a bit lucky!

The original supply line for the sink.

The supply for the sink is a ⅜" plastic line. After some measuring and research, I was confident I knew the thread (¾" BSP) for the inlet on the appliance. And I found an adapter from that to ⅜" push-in fitting.

My update: teeing the supply to feed both the sink and the dishwasher.

So with that adapter, and a hose and fitting kit I put this together: The existing incoming water supply now goes to a tee, which feeds the sink and also (via a shut off valve) the dishwasher. From there, it only takes two inconspicuous holes through the very back of the cabinet towards the fridge.

The dishwasher supply, hooked up. The dishwasher, in place between the sink and the fridge.

And there it is! Hooked it up with a 90° fitting to ensure there's no stress on the line, and set it back in place. If you look close, you can see the drain hose passing behind and then to the left of the faucet. When the dishwasher turns on, it opens a valve to fill its internal tank (which takes surprisingly long, perhaps it wants a bigger supply line) and from there it's all automatic. Worked first time, no leaks.

Balatro and Probability

2025-02-07 11:56 - Programming

I've been thoroughly enjoying Balatro lately. I've proceeded through the challenge mode, and completed 19 of the 20 challenges. The last one, though, seems very difficult. Enough that I went looking online for suggestions of how to beat it. I found a post claiming that the four of a kind hand is the winning strategy. I immediately scoffed. Drawing a four of a kind is just too hard! Right? Right??

I felt the need to validate my intuition, that it's too hard to draw a four of a kind. I tried to look up the chances online, but I have a problem. I want the chances for this game. It's a little like poker, but barely. Specifically: you draw a hand of eight cards. Then you get some number (three, normally) of discards, of up to five cards. Then you get some number of hands to play (four usually, so three can be "throw away" fake discards). Or: what I really want to know is how likely it is to: A) draw eight cards, then B) as necessary discard up to five of them, up to six times, and end up with a four of a kind in hand. Nothing I could search up quickly gave me the answer, but the "real answer" was to simply simulate it. I got nerd sniped, and as a result I did. Simulate it.

#!/usr/bin/env python

import collections
import random

DECK = list('23456789JQKA' * 4)
N = 10000

def draw_from(deck):
  i = random.randint(0, len(deck) - 1)
  return deck.pop(i)


results = []
for _ in range(N):
  deck = list(DECK)
  random.shuffle(deck)

  hand = []
  for _ in range(8):
    hand.append(draw_from(deck))

  for i in range(7):
    #print('hand', hand)
    cnt = collections.Counter(hand)
    com = cnt.most_common()
    if com[0][1] >= 4:
      #print('Found 4 of %s!' % com[0][0])
      results.append(i)
      break
    elif i == 6:
      results.append(None)
      break
    else:
      for _ in range(5):
        d = com[-1][0]
        cnt[d] -= 1
        com = [x for x in cnt.most_common() if x[1] > 0]
        #print('discard', d)
        hand.remove(d)
      for _ in range(5):
        hand.append(draw_from(deck))


for result, freq in collections.Counter(results).most_common():
  if result is None:
    print('Failed %.2f%%' % (freq / N * 100))
  else:
    print('Success with %d discards %.2f%%' % (result, (freq / N * 100)))

This is some quick and dirty code. I wrote it in almost exactly half an hour. (As of reading this post, I realize I shuffled the deck, so I didn't need the extra step of randomly choosing a card to draw. I could have "drawn from the top". Probably other minor mistakes of that nature. But I think it's a valid simulation.) And it tells me:

$ python four_of_a_kind.py
Failed 42.50%
Success with 6 discards 15.02%
Success with 5 discards 13.09%
Success with 4 discards 11.59%
Success with 3 discards 8.99%
Success with 2 discards 5.69%
Success with 1 discards 2.72%
Success with 0 discards 0.40%

Indeed: using "all six" discards results in only a slightly-under-60% chance of finding a four of a kind. In a standard 52 card deck. (Changing slightly each run, as this is a random simulation, not exact probability.) So this might be a workable strategy, but it requires some luck in manipulating your deck, to be able to make the four of a kind hand regularly.

Made in December (2024)

2024-12-24 18:31 - Making

Candles that I

I find something cozy and pleasant about having a candle burning, during the winter. I've managed to find old partially used bits of various candles from a few places. So for a while I've been remelting these into the containers for other candles. Over time I've figured it out and can generally produce a candle that burns nicely and consistently. Or at least I can fix it part way. (When necessary, which is getting more rare.) The hardest part is getting the wick nicely centered. My most preferred method now is to stick a metal rod down the middle, with some paper card at the top to keep that end centered. When I remove it, the wick has the perfect spot to go to. Since the wax shrinks as it cools I usually want to add a top-up anyway, and this fills the remainder of the hole.

Christmas cookies.

I also prepared some christmas cookies. These are "M&M Magic Bars". They didn't come out as pretty as the pictures, but they sure look scrumptions!

Ingredients and equipment gathered for my first all-grain brew. Reaching the "mash in" step. Boiling the wort. Wort and yeast in the fermenter.  Beer soon!

I took up home brewing this summer. I started with a very basic kit, then I tried making a hard cider (both turned out well), I made a mead (just bottled, haven't tried it yet besides tiny sips, seems good but strong), and then most recently I did an "all grain" brew. Above is the gathered ingredients and equipment, then the "mash in" step, where the grains are steeped in hot water to convert the starches to sugars. After these are carefully removed the "wort" as it's now called is boiled. Finally, it all goes in a vessel with some yeast to ferment. Since it was my first time with the full process, I had some learning opportunities, but I'm cautiously optimistic that it will turn out well. The yeast are definitely doing their work.

Quickly view installed (Gentoo, portage) package sizes

2024-12-13 11:04 - Linux

I've known about gentoolkit for a while. For that time equery has been my standard way to inspect portage. Turns out there's (a whole collection of useful portage tools, including) also the "q applets". These Q applets are typically faster, and one of them will report the installed size of packages.

$ qsize -m | sort -t , -k 3 -n | column -t -s ,
...
sys-devel/gcc: 1746 files (1742 unique)                  100 non-files    282 MiB
dev-lang/rust-bin: 131 files (125 unique)                31 non-files     474 MiB
sys-kernel/linux-firmware: 4204 files                    379 non-files    1218 MiB
sys-kernel/gentoo-sources: 81770 files                   5322 non-files   1291 MiB

I've got reason to inspect installed package size and I just found this. And it executes in only a few seconds on my machine with nearly 800 packages installed.

Dance Pad Adapter

2024-11-14 23:02 - Making

The History

Years ago there was justin.tv (with an interesting history, read the linked WikiPedia page!). The very short version is that it was focused on transmitting live video across the internet when this was very novel, generated success primarily around live streaming of video game play, and eventually became twitch.tv. I watch there somewhat regularly. Since it's live, it's possible and typical for viewers to interact with the streamer (typically viewers text chat and streamers speak/reply out loud).

One of my favorite streamers features a gimmick of using a dance pad to play games that were not intended for it. This is, to put it mildly, quite an impediment to the game play (and thus a source of extra challenge and entertainment). Situations (i.e. too difficult parts) arise where it becomes necessary to change to a regular controller and then back. To do this, the system menus must be opened and closed, and the game resumed. Often, the in-game settings must also be tweaked. A tedious process. At some point, I realized I could probably do something about this. And a useful project to hack on would be nice. So...

The Thing

More specifically, this situation involves a Nintendo GameCube Action Pad plus a GameCube Controller Adapter, to allow the GameCube native dance pad to be connected (via USB) to the Nintendo Switch console.

First just like the commercial product (though I'm not providing four ports) my project also allows a (any, but intended for the dance pad) wired GameCube controller to be plugged in, adapting this controller to instead act like a wired USB controller for the Nintendo Switch. To this we add the ability to configure button mappings — choosing which pressed button triggers which virtual output button. Specifically, the way some games expect the face (A/B/X/Y) buttons to be used on a typical controller don't mix well with the dance pad (which has A and B, but not X nor Y). There is a physical switch on the adapter, to choose between two of these custom mappings, for example one tweaked to work with a specific game but another that passes buttons through by default (the most direct possible translation).

Next, it also allows a wired Super Nintendo controller to be plugged in at the same time. Both of these controllers are always active, and either one (or technically both, though this would be confusing) can be used at any time. This allows switching to a more standard controller for more difficult game play sections, with no need to tweak any settings. It's the real "killer feature" that makes this adapter specifically better for this unusual situation.

At some point I realized I could build this with almost only parts I already have in stock, spares from previous projects. So I ended up doing no formal hardware design like I otherwise might have. Rather I simply planned out the wiring and built a single instance of it by hand. Since the spare parts I already had available were small, this turned out to be quite a challenge.

The tiny hand-wired hardware for my Dance Pad Adapter.

Here's that hardware. On the back side of what you can see is a (clone) Arduino Pro Micro. Added between its two rows of pins are a voltage regulator (the largest chip at the left) and a single transistor (the smaller chip to the right of that) perfboard — all the holes you can see are one tenth of one inch away from each other. This picture has the (green, black, and red) wires for the GameCube controller already present, near the top left. Later the wires for the Super Nintendo controller and switch were added. Towards the end of assembly, heaps of hot melt glue were added to keep everything in place.

The Dance pad adapter, in its 3D printed case.

These were installed into a 3D printed case, designed for the purpose. The Arduino's USB port is exposed on the back (not visible in this picture) while the front side has the two extension cords that serve as the ports to plug the controllers into. (These extensions are all I had to buy to build this. No better way to add a standard game console "controller port" to a small hobby project.) On the face is the switch to select which of the two customized button mappings are active.

The Process

This took around a month of spare time, beginning to finish. I learned a lot! A whole lot of open source already existed to take care of the more standard parts of this (reading the controller inputs, emulating the USB controller for signal outputs). Along the way I discovered the LUFA (Lightweight USB Framework for AVRs) library. Very impressive, it supports a significant amount of functionality all on a tiny (32kB flash, 2.5kB RAM) microcontroller. Specifically I use USB HID (for the joystick) and Mass Storage (to allow a computer to reconfigure the button mappings).

My First Homebrew

2024-07-26 11:56 - Making

The two fermenters are ready at the sides, the wort is cooling in an ice bath in the sink. Close up of the wort cooling in the sink.

I don't drink a lot, but some. Recently I got it into my head to try home brewing. After a while I picked up some cheap used equipment, and supplies including an easy ingredient kit at a local home brew shop. I've just barely completed the steps for my first brew, here's how it went. Above was the result of brew day: After boiling the malt extract and hops I had the wort, here pictured cooling down in the sink. Beside that are two fermenters: a pail and a jug style. I started with the jug. (Which is capable of a 5 gallon brew, but for my first go I did a one gallon batch.) In the first picture you can see it full of foam. That's the no rinse sanitizer. Brewing is all about convincing some yeast to work for you, and just about the most important part of that is keeping out the bacteria that want to ruin things. You don't rinse the sanitizer because if you did, it would introduce more new bacteria!

The wort in the fermenter, day one. The wort in the fermenter, day two.

Once the wort was cooled down enough to not kill it, I pitched the yeast in and set up the airlock. The yeast will "fart" out a bunch of CO2, which needs to escape the fermenter (or otherwise the pressure will eventually explode). The airlock lets the CO2 out without letting anything else (bacteria!) in. Above you can see day one: some of the sanitizer foam is sitting on top of the wort. By the next day plenty of yeast has grown, and has bubbled some sanitizer out of the airlock. For this first go I was trying to follow the kit's instructions as closely as possible, but here's where it went wrong.

It takes a fair deal of time for the yeast to do their work. I had read up ahead of time, and had it in my head that about one week was the right time for this fermentation step to take. The instructions want me to do a secondary fermentation (which per my reading is not terribly common), but I did it. However I missed a detail: the instructions wanted primary fermentation to be 4-6 days and (emphasis theirs) before it completes — but it only said that in a note on the side, which I missed. I had been checking more or less daily, and ended up switching to secondary on day 5, but I believe it was too late. The instructions told me to wait "about two weeks" for secondary fermentation, but daily monitoring showed absolutely no activity. So I cut it short after just one week.

So after carefully sanitizing everything involved again, I transferred the beer (I think it is indeed beer now, not just wort) to the bottling pail. (It's got a narrower outlet, which the hose fits on, and more of a siphon inlet. The fermenter pail simply has a hole, a bit above the bottom, to facilitate transferring the liquid but not the sediment.) And bottled! The

My first home brew beer, bottled.

Here's bottling day, complete. I planned to use a couple capped bottles (for longer term storage/comparison) but focused on my flip-tops. But did the math in my head wrong so I had extra bottles, which is better than too few! One of the more subtle steps of bottling day is to actually add a bit of sugar to the beer. The remaining yeast processes this, in the bottles, to add carbonation. So although I'm "done", the instructions say I should wait two weeks (and up to an extra week!) for this bottle conditioning to complete. Almost there!

My one gallon kit filled about ten and a half bottles. Or, a bit less than two six packs. This does not excite me, but a five gallon brew can be done easily for the price of two or three (quality) six packs, and if I shop carefully, for less than a (cheap) 15 or 18 pack. Assuming my first results here are good, I'll be aiming in that direction. Though, I'm still figuring out how/whether I can handle boiling five gallons all at once. (I think you can get away with boiling less, then topping up with more water after.) Also: five gallons is a lot of beer to drink!

Laptop Arm Stand

2024-04-22 16:51 - Making

My laptop computer, on the fold out table in the sofa.

For a bit over a year, I've had my laptop sitting on this fold out table built into the middle seat of my sofa. It's okay, but it's not great. It's a bit too far to the side and too far back to make usage really comfortable and practical. For a while I've thought about mounting an arm that would hold the laptop in a more adjustable and flexible place. I finally did something about it, with the commercial arm arriving just yesterday. With it in hand I saw exactly how it worked and what size it is, so this morning I started drafting plans for how to mount it. Something made of wood, attached to and/or wedged under the structure of the sofa. I simplified through a few revisions.

The wooden stand I designed and built to hold my laptop arm.

This is what I came up with. It's an L shape, with the arm mounted to the top of the vertical leg, and the horizontal leg fitting under the front of the sofa. The horizontal leg prevents side-to-side tipping by pressing against either the floor or the sofa. A triangular brace makes that reliably strong, and a second prevents the vertical leg from tipping away from the sofa. The L part was designed somewhat carefully, while the rest was just made to fit. Everything was material already available in the basement. A C clamp is hidden by the triangular brace, holding the horizontal leg of the L to the sofa's metal frame.

The laptop now resting on a flexible arm, letting me position it just where I like.

And here it is. I can pull the laptop almost down into my lap and push it further away from me, with very little effort. I've got pretty much exactly what I was hoping for.

Edging for Drainage

2024-01-24 16:26 - General

The drainage in the backyard here is not great. At the corner of the driveway, there's a tiny drainage ditch which should help. But that corner of the drive way is not (is no longer?) the low spot. So when there's enough rain, a significant portion of the driveway ends up forming a large puddle. Last time I visited Mom, I brought back a 2-stroke edge trimmer, with the idea that it's the perfect thing to dig a tiny supplemental "ditch" along that edge of the driveway, to help get the water away. (Into the existing drainage ditch, from the too-low parts of the driveway. As-is the grass and dirt comes right up to the concrete, holding the water in pretty effectively.)

Today there was just enough of a break between the snow (now melting) and rain (forecast to start this morning, but not here yet) that I thought: now's the time! I gave it a shot and couldn't start it. (I'd guess it's been over a decade since it has run.) I checked the spark plug: very dirty, but I could see at least a faint spark. So I dribbled some gas straight in the cylinder, and then I did get it to start, and run but for only a second or so. So what's the problem? Fuel delivery!

Took off the tiny carburetor to investigate. Got surprised by the air intake path: there's only one option from the carburetor side, but it comes through a strange path. I can just see a foam air filter peeking out in one spot, but not where air gets into the other side of it. But then I wondered: wait, where does the fuel come in? Found the intake on the carburetor, which didn't have a fuel hose attached. Found that and ... quickly learned that it's hardened to unusability. And in fact, already broken in half. This is probably the primary problem.

It's so bad that a piece of it crumbled in my hand, just holding it, while trying to figure out the size. With the bit that was left, I managed to figure out that a 1/16″ fit a little loosely but a 5/64″ fit snugly. It snakes a complex path from the tank to the carburetor, so in addition to needing the right size to connect on either end, I'll need it small enough to fit through all those passages, too!

RGH3 Modding an XBox 360

2024-01-11 23:24 - Gaming

I have a significant collection of video game consoles. Wherever possible and practical, I prefer to "mod" them. Mod is short for modify, and generally means lifting the restrictions that they come with — mostly for copy protection. These mods have many benefits but a significant one is being able to run games without relying on the optical drive the whole time. In these ten or twenty year old devices, the moving optical drives often fail.

Back in 2015 I tried to mod the XBox 360 I had at the time. Long story short, I failed to remove the "x-clamp" (which holds the heat sink down to the APU) and damaged the logic board in the process. (I still have all those parts, maybe I should try some trace repair again.) More recently I accepted an XBox (OG) and XBox 360 that was being given away. Then even more recently I heard about the "RGH3" mod. The 360 has pretty good protection built in. For a long time, besides some simple piracy-only based modifications "RGH" (reset glitch hack) was the only way to mod a 360 for full control. The idea is based around sending a very brief reset signal to the CPU at just the right time, so that as it is performing verification checks very early in the booting process, the check (and only the check) can be corrupted. Then the console otherwise boots as normal, but has not verified its firmware. This used to take a special extra "mod chip" and quite a bit of very careful wiring. The third version, RGH3, amazingly uses the console itself to send this reset glitch signal. That makes it a comparatively very simple modification: just two wires. (Two permanently, a few more temporarily.)

The SMC end of the two RGH3 wires.

Last month, I opened up the 360. I confirmed that it's a newish (relatively speaking) "Corona" variant. Because it's new, it's designed to prevent RGH style attacks. Because the modding community(/related business(es)) is clever (and a bit lucky) there is a "POST fix adapter" to restore access to the otherwise removed signal trace. It just so happens that this is one of the outermost "pins" of the APU, so a carefully positioned piece of wire can touch it. And the adapter is designed to hold a little pin in just the right spot. That's one end of one of the two wires needed. The picture above is the other end of the two wires. With POST dealt with, these are the two "easy" remaining connections. They're tiny, but they're exposed and directly available. (It's not unusual for all sorts of important signal traces to have test points in commercial products, intended for use during design and manufacture.) I'd estimate that the white circles drawn around these two points, where my added blue wires terminate, to be about one millimeter across. Possibly one and a half. Those blue wires are 30 gauge "wire wrapping" wires. The wire itself is 0.255 millimeter in diameter. The higher of those two solder points I did rather well on. The lower/rightmost one isn't as great. The exposed wire is too long and the solder has barely attached it. But attached it is! I got away with it.

The third, and most difficult, RGH3 solder point.

Here's the remaining wire end. It's attached to a via, which starts off covered in solder mask. The point of this mask is to prevent solder from going here. This must be scraped off, without damaging the tiny and fragile exposed copper part of the via. I did so with a fiberglass "scratch pen". I slightly exposed an adjacent via and track, so those were covered with some polyimide tape before proceeding. For this one, the via-as-solder-point is hardly wider than that quarter-millimeter wire. This one took a few tries, and I ended up leaving a little extra solder on the capacitor. Once this tiny joint was complete however, I didn't want to mess with it.

I added some tiny dabs of hot glue to keep the wires in place, to avoid breaking these fragile joints through mechanical fatigue. I also added an in-line resistor on one of the lines as suggested by the guide I was following. The following steps were mostly software based. When first flashing Xell (xenon linux loader — xenon is the code name for the first XBox 360 variant) the first time it didn't take. I turned the 360 on and Xell should have launched, but the standard retail dashboard did, instead. After sleeping on it I wrote Xell a second time and somehow this time it worked. This is just the first step however, unlocking the next step: the XeBuild which actually modifies the console's behavior. (It requires knowing the per-device encryption key, which Xell somehow is able to read.) This also did not boot after flashing it, this time Xell remained. I tried writing a second time, and this did not work. But Xell also supports writing this sort of update, so I tried a third time using it (instead of the PicoFlasher I used to get this far) and it worked. From there I just had to install some custom software and I was in full control, huzzah!