<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Alnwlsn RSS Text</title><link>http://alnwlsn.com</link><description>Alnwlsn's text only RSS feed</description><atom:link href="https://alnwlsn.com/z/rss.xml" rel="self" type="application/rss+xml" /><item><title>TRS-80 Model 1 Original System Box</title><description><![CDATA[<pre>                     The TRS-80 Model 1 Original System Box

   2026-10-06 | [retrocomputing] [trs80]

   Happy TRS-80 Model 1 Level II 16K Day! The only day every 100 years
   where the current date matches the catalog number of the TRS-80
   keyboard: 26-1006. Depending on which version you have, it also might
   have been last Thursday, or the weekend.

                                   26-1006

   To celebrate, I decided to digitize the artwork from the original Model
   1 box I have, since I have not seen it captured elsewhere. That way,
   you can make your own, or make a tiny version for which a tiny computer
   can go inside (this is foreshadowing).

                              TRS-80 System Box

   Of course, the box does not say "Model 1" since this was the only model
   at that point. The box on the bottom is also apparently an original
   TRS-80 box ("LEVEL II" is visible in one corner) but is less
   interesting without the Tandy branding. I'll focus on the top one.

   To do this, I took views of two of the sides (the other 2 are the same
   as these) and traced it out in Inkscape.

   As best I could find based on some research and the helpful
   identification tool on https://www.whatfontis.com, these are decently
   close fonts:
     * Crystal Radio Kit - What people use for the "Radio Shack" font. It
       is close enough, but seems to be a more modern version; the one on
       the real box is a bit thinner, and the "k" in "Shack" is slightly
       different.
     * Sans - The closest match on my system (which is LMDE/Linux Mint)
       for the ®.
     * FreeSans - The closest match on my system for "Custom manufactured
       for Radio Shack...", and the T and C in the Tandy logo. The "R" is
       slightly off, but you can't tell.
     * Square 721 Extended - For the square-round "TRS-80", "Micro
       Computer System", and the contents. Spacing/kerning seems a bit
       different on the real box.
     * Futura Condensed Bold - For the "1056" and "16K" which were
       obviously added to the box later on the production line as a
       sticker.
     * The very tall and joined "II" I made manually from four rectangles.

   The overall dimension of my box measures 20+1/2 x 16+7/8 x 25 inches.

   The result is this:

                       svg drawing of the box artwork

Making the box

   Scaling down the above image and printing it on a laser printer is easy
   enough, but I also have a laser cutter with which to cut out a
   box-shaped pattern that can be folded into a box.

   I wrote a python program which produces gcode to be run directly on my
   laser cutter. A render of the gcode looks like this:

                                   render

   The red lines are rapid moves. You can remove them in the gcode, I only
   added them because they show up in the render, and I can trace out the
   sides of the box.

   Once cut, the pattern is folded, and the tab glued into the side to
   form a box. Then, the top and bottom are taped, making the finished box
   surprisingly sturdy.

                     comments | patronage | Alnwlsn 2026
</pre>]]></description><link>https://alnwlsn.com/z/trs80/box/trs80-box.html</link><guid>https://alnwlsn.com/z/trs80/box/trs80-box.html</guid><pubDate>Tue, 06 Oct 2026 00:00:00 EST</pubDate></item><item><title>HP T1500 G3 UPS monitoring software</title><description><![CDATA[<pre>                      HP T1500 G3 UPS monitoring software

   2026-06-28 | [misc]

   Another old UPS from the eWaste bin, but this is a nice one! It is pure
   sine wave output, and can do 950 Watts, from a 36V Lead acid cell.

   I already use one of these on my bicycle generator, and then I found a
   second one. But I already had my main server on a different eWasted
   UPS, which, while a cheap model, did work with the CyberPower pwrstat
   tool to detect power outages and shut down the computer safely.

   That was until one day when I was finally experimenting with some local
   LLMs and the power draw of the server running a 3090 became too great
   for the poor CyberPower unit, and it shut itself off. So I knew I would
   finally need to switch to a different UPS.

                               and here it is

   My unit didn't come with any batteries, and the internal ones are very
   small, so I got 3x 20Ah 12V SLA cells for 36V. That part is easy. Make
   sure to use thick wire to tie the cells together in series. I used some
   #8 AWG, which is even thicker than the battery leads it comes with.

   The data connection is next. I could not get my server to detect
   anything on the USB connection (it does have a bad USB chipset
   somewhere), so I would have to use a serial port (which ironically is
   also USB). The 9-pin RS-232 port on the UPS uses a straight-through
   cable.

   The software is not so simple. This UPS is old (maybe 2009?), and is
   "Enterprise Grade". The only software I found for it either some
   enterprise software that dropped support for our G3 UPS since it got
   EOL'd long ago, or the Windows XP based HP Power Manager 4.2.6. I got
   Power Manager working on a Windows XP VM, in order to make sure the UPS
   works. It provides a http server with the status - input and output
   voltage, output in watts, etc.

   You can write a program to listen to that server and get the UPS
   information from it, but running a whole XP instance doesn't seem all
   that robust. It also doesn't account for anything that would shut down
   the system safely.

   I thought it might be possible to make a native Python program to
   connect to the UPS on the serial port. When the Power Manager connects
   in Windows XP, it does give a hint that it connects at 9600 baud and
   uses some protocol called "XCP". I looked briefly, but I didn't see any
   such simple UPS control software.

   Then, I gave the UPS over to OpenCode. This runs on a local LLM using
   LMStudio. My AI machine is a VM with 16 cores, 40 GB system RAM, and 1x
   3090 passed through to the VM. It runs Qwen 3.6 27B Q4_K_M with a
   context window of about 100000 tokens, and the whole system consumes
   about 550W when running. I passed the serial port for the UPS over to
   that machine, and gave OpenCode a copy of the Power Manager install
   folder from XP.

   It did look at some of the Power Manager files to get an idea of how to
   talk to the UPS, but ultimately looked at an existing implementation of
   XCP from the Network UPS Tools project (GPLv2) and implemented it in
   Python. From there, it is trivial for the AI to make the main set of
   the two features I needed, which are:
     * Monitor the UPS - you can get input and output voltage and
       frequency, battery voltage, connected power source (utility or
       battery).
          + Take all of these, format them as JSON, and publish to MQTT. I
            can pick up this using another script I have that takes it and
            puts in a time series database for logging.
     * If the UPS loses utility power, it will run for 10 more minutes. If
       the power does not come back within the 10 minutes, it commands my
       server to shutdown and power off, and tells the UPS to shut itself
       off after 15 minutes.
          + This sequence should leave plenty of capacity left in the
            batteries should I need to power the server back on during the
            power outage.

   The only feature it doesn't have is that when the utility power comes
   back on, the UPS does not support turning itself back on, you still
   have to go down and press the power button yourself. That's OK though,
   the main thing is that the server shuts down safely. Interestingly, you
   can power the UPS back on with a command over the serial port, but
   since you need the server computer to already be powered on in order to
   do this, it can't do it.

   If you have the same UPS, maybe you could make use of the script I now
   use - alnwlsn-hp-ups.zip.

                     comments | patronage | Alnwlsn 2026
</pre>]]></description><link>https://alnwlsn.com/z/misc/hp-ups/hp-ups.html</link><guid>https://alnwlsn.com/z/misc/hp-ups/hp-ups.html</guid><pubDate>Sun, 28 Jun 2026 00:00:00 EST</pubDate></item><item><title>Misc audio effects</title><description><![CDATA[<pre>                                 Slurm at Home

   2026-04-19 | [misc] [music]

   As a child who spent way too much time listening to sped up cassette
   tapes, it is always interesting how very simple effects can change how
   music sounds. Apparently, I'm not the only one, because on the
   internet, these sorts of things can crystalize into entire "genres":
     * pitching songs up and down
     * "slowed + reverb"
     * Nightcore - the aforementioned speed up
     * Beats 2 & 4 swapped
     * Every other beat removed

   And one kind that caught my attention this weekend:

   So in the old days of CDs and MP3s, the physical players of these would
   not only let you skip to the next song, but you could usually fast
   forward or rewind the audio itself. Unlike a tape, this didn't speed up
   the audio, instead it would play at normal speed, while the player
   would "skip" a few 10s of milliseconds as long as you held down the
   button.

   Somehow, somebody thought of lining up these "skips" with the beat of
   the song, creating something that seems sped up but also not really.
   They also pitched down the initial audio...

   ...and the result is now something the internet has named "Slurmcore".
   No relation to Futurama, at least I don't think so.

   There seemed to be some confusion about how this effect was achieved,
   but someone found out how to do it in Ableton Live using a built-in
   feature. But if you listen closely, you can tell what's going on; it's
   basically this:

                            diagram of slurmcore

   If you don't have or know how to use Abelton Live (which I don't), how
   can I do this?

part 1 - finding the BPM

   Looking at the image above, it doesn't look that complicated, but
   getting the beat exactly right isn't trivial. Since I don't have any
   better idea, I'm going to have to one-shot it using a fixed rate beat
   at the beginning of the song. So you need to know the BPM of the song
   exactly. If a song is between 123 and 124 beats per minute, you will be
   able to tell it is losing sync within 10 or so seconds. Those "tap for
   BPM" tools online that report in whole numbers aren't going to cut it;
   you need to know it is exactly 123.43 BPM. To that end, I make my own
   version.

   It is only then that you can realize the next problem: a lot of music
   does not have a long-term stable BPM! Unless preformed or remixed on a
   computer, a human drummer has no real reason to make sure beat 200 is
   exactly in phase and lines up at 97221 miliseconds after beat 1 for a
   123.43 BPM song.

   That's probably why we haven't seen these techniques until now - the
   commercial software has evolved enough to have features that track the
   beat in the song. So my versions only work on songs with very stable
   BPM.

part 2 - python

   Moving and selecting portions of lists is easy in python, but the first
   version out of the AI tool worked really slowly. That version worked by
   coming up with the timestamps for all the sections of audio, and
   passing that list to ffmpeg which would do the editing.

   In my eye, a much more sensible version would convert to an
   uncompressed "raw" format first, do the swaps and cutting in python,
   and then export that to ffmpeg to be re-rendered into a mp3 or
   whatever.

   I do feel that you should be able to do this with the little known and
   cryptic Nyquist prompt in Audacity, but neither I or ChatGPT know
   enough about it to get it correct.

  slurm

   Where BPM

import subprocess

## ********** User Inputs **********
BPM = 124.15
SCALING = 2 #multiply BPM period by this to get the interval on which to clip th
e audio
KEEP = 0.66 #proportion of audio to keep. 1 is all; 0.5 is 50%
PHASE = 0.66 #range 0 to 1. Within Keep, value of 0 keeps the audio at the begin
ning of the clip, and 1 keeps the audio at the end of the clip. Values in betwee
n select the middle of the clip.
## ******** end User Inputs ********


FRAME_JUMP = 1 / (BPM * SCALING / 60)

input_file = "input.mp3"
output_file = "slurm.mp3"

# Step 1: Decode MP3 -> raw PCM (16-bit signed, stereo, 44.1kHz)
pcm = subprocess.check_output(
    [
        "ffmpeg",
        "-i",
        input_file,
        "-f",
        "s16le",  # raw 16-bit PCM
        "-acodec",
        "pcm_s16le",
        "-ac",
        "2",  # stereo
        "-ar",
        "44100",  # sample rate
        "-",
    ]
)

sample_rate = 44100
channels = 2
bytes_per_sample = 2  # s16le = 2 bytes

frame_size = channels * bytes_per_sample  # bytes per frame


# Convert ms to frames
def s_to_frames(t):
    return int(sample_rate * t)


sframe_jump = s_to_frames(FRAME_JUMP)
sframe_width = int(sframe_jump*KEEP)
sframe_remainder = sframe_jump - sframe_width
sframe_offset = int(sframe_remainder*PHASE)

total_frames = len(pcm) // frame_size

# Step 2: Slice and rebuild buffer
output_pcm = bytearray()

pos = 0
while pos < total_frames:
    start_byte = max((pos + sframe_offset) * frame_size, 0)
    end_frame = min(pos + sframe_width + sframe_offset, total_frames)
    end_byte = end_frame * frame_size

    output_pcm.extend(pcm[start_byte:end_byte])

    pos += sframe_jump

# Step 3: Encode back to MP3
proc = subprocess.Popen(
    [
        "ffmpeg",
        "-y",
        "-f",
        "s16le",
        "-ac",
        str(channels),
        "-ar",
        str(sample_rate),
        "-i",
        "-",
        output_file,
    ],
    stdin=subprocess.PIPE,
)

proc.communicate(output_pcm)

  Cut in half

   Keeps discards every other beat.

import subprocess

## ********** User Inputs **********
BPM = 122
SCALING = 1 #range 1 to 10
PHASE = 1.70 #offset the interval. a phase of 1 offsets by 1 beat.
## ******** end User Inputs ********


FRAME_JUMP = 1 / (BPM * SCALING / 60)

input_file = "input.mp3"
output_file = "cut_half.mp3"

# Step 1: Decode MP3 -> raw PCM (16-bit signed, stereo, 44.1kHz)
pcm = subprocess.check_output(
    [
        "ffmpeg",
        "-i",
        input_file,
        "-f",
        "s16le",  # raw 16-bit PCM
        "-acodec",
        "pcm_s16le",
        "-ac",
        "2",  # stereo
        "-ar",
        "44100",  # sample rate
        "-",
    ]
)

sample_rate = 44100
channels = 2
bytes_per_sample = 2  # s16le = 2 bytes

frame_size = channels * bytes_per_sample  # bytes per frame


# Convert ms to frames
def s_to_frames(t):
    return int(sample_rate * t)


sframe_jump = s_to_frames(FRAME_JUMP)
sframe_offset = int(sframe_jump*PHASE)

total_frames = len(pcm) // frame_size

# Step 2: Slice and rebuild buffer
output_pcm = bytearray()

pos = 0


while pos < total_frames:

    part1 = max((pos + sframe_offset) * frame_size, 0)
    part2 = min(pos + sframe_offset + sframe_jump, total_frames) * frame_size

    output_pcm.extend(pcm[part1:part2])

    pos += sframe_jump*2

# Step 3: Encode back to MP3
proc = subprocess.Popen(
    [
        "ffmpeg",
        "-y",
        "-f",
        "s16le",
        "-ac",
        str(channels),
        "-ar",
        str(sample_rate),
        "-i",
        "-",
        output_file,
    ],
    stdin=subprocess.PIPE,
)

proc.communicate(output_pcm)

  swap beat 2 and 4

import subprocess
import random

## ********** User Inputs **********
BPM = 124
SCALING = 2
PHASE = 0.7
## ******** end User Inputs ********


FRAME_JUMP = 1 / (BPM * SCALING / 60)

input_file = "input.mp3"
output_file = "beat24.mp3"

# Step 1: Decode MP3 -> raw PCM (16-bit signed, stereo, 44.1kHz)
pcm = subprocess.check_output(
    [
        "ffmpeg",
        "-i",
        input_file,
        "-f",
        "s16le",  # raw 16-bit PCM
        "-acodec",
        "pcm_s16le",
        "-ac",
        "2",  # stereo
        "-ar",
        "44100",  # sample rate
        "-",
    ]
)

sample_rate = 44100
channels = 2
bytes_per_sample = 2  # s16le = 2 bytes

frame_size = channels * bytes_per_sample  # bytes per frame


# Convert ms to frames
def s_to_frames(t):
    return int(sample_rate * t)


sframe_jump = s_to_frames(FRAME_JUMP)
sframe_offset = int(sframe_jump*PHASE)

total_frames = len(pcm) // frame_size

# Step 2: Slice and rebuild buffer
output_pcm = bytearray()

pos = 0
while pos < total_frames:

    part1 = max((pos + sframe_offset) * frame_size, 0)
    part2 = min(pos + sframe_offset + sframe_jump, total_frames) * frame_size
    part3 = min(pos + sframe_offset + sframe_jump*2, total_frames) * frame_size
    part4 = min(pos + sframe_offset + sframe_jump*3, total_frames) * frame_size
    part5 = min(pos + sframe_offset + sframe_jump*4, total_frames) * frame_size

    # #click for aligning
    # output_pcm.extend([random.randint(0, 96) for _ in range(128*frame_size)])
    # output_pcm.extend(pcm[part1+128*frame_size:part5])


    output_pcm.extend(pcm[part1:part2])
    output_pcm.extend(pcm[part4:part5])
    output_pcm.extend(pcm[part3:part4])
    output_pcm.extend(pcm[part2:part3])


    pos += sframe_jump*4

# Step 3: Encode back to MP3
proc = subprocess.Popen(
    [
        "ffmpeg",
        "-y",
        "-f",
        "s16le",
        "-ac",
        str(channels),
        "-ar",
        str(sample_rate),
        "-i",
        "-",
        output_file,
    ],
    stdin=subprocess.PIPE,
)

proc.communicate(output_pcm)

                     comments | patronage | Alnwlsn 2026
</pre>]]></description><link>https://alnwlsn.com/z/misc/slurm/slurm.html</link><guid>https://alnwlsn.com/z/misc/slurm/slurm.html</guid><pubDate>Sun, 19 Apr 2026 00:00:00 EST</pubDate></item><item><title>old QBASIC stuff</title><description><![CDATA[<pre>                    The Fish Game and other QBASIC oddities

   2026-02-08

   I don't think I've even looked at most of this stuff in 10-15 years,
   but since we live in the future now, you can run it in a browser, using
   js-dos. Check the rendering options in the settings of the applet
   below, there is a smooth option if the default pixelated setting is
   annoying.

                               IFRAME: vm.html

   How did you first learn to program? Everyone seems to have their own
   story. When I was a kid, it wasn't the 1980s, nor was it more recent
   times - it seems like nobody paid any thought to having kids "learn to
   code" at that time. So it would be something I had to come across on my
   own.

   This happened one day in elementary school. I had found a couple of the
   Usborne Computer Books in the school library. In the mid 2000's, these
   were horribly out of date, but I didn't know that. I was hooked
   immediately regardless, but for a while I didn't have a computer I
   could program on. There's something to be said for being able to turn
   on a computer like the TRS-80 and be ready to type in a program
   instantly. No programs to install, permissions issues, compiler
   options, or other things that you don't know about when you're a kid
   and have no idea how computers work.

   That changed when I was given a Sanyo MBC-555 (which I still have), and
   I could finally try out some of the BASIC programs in the book. That
   computer had BASIC, but also ran a non-PC compatible MS-DOS, which I
   also had no idea how to use. Using other 90's scrap computers, I began
   to learn DOS and how computers were put together and configured. I
   learned that BASIC was upgraded to QBASIC on MS-DOS, so I started using
   that. Eventually, I found a couple of mid 90's Toshiba Satellite Pro
   laptops at a garage sale for $13, and those became the computers I
   would carry around and write QBASIC programs on to amuse my friends.
   When I didn't have them, I would run DOSBox on a normal PC. One of my
   friends had an old Windows CE Handheld PC - we found a DOS emulator for
   it and would run QBASIC on it too. I started learning
   embedded/microcontrollers around the same time; these were also
   programmed in BASIC (since that's what I knew), with the PICAxe.
   Picking up some more complicated projects with the Arduino is how I
   finally moved on from BASIC.

   It's worth noting that Arduino and Raspberry Pi were brand new when I
   was a teenager, they were by no means the default thing where everyone
   learns Processing/C and Python like it is these days. I used QBASIC for
   quite a while though, since I was only making funny meme programs, I
   never needed anything else. Plus, QBASIC has an extensive manual built
   in to the program, so no need to go on the internet or get out a book
   to look things up. There was only one computer in the household that
   had internet anyways.

   I still think BASIC is an ideal language for kids to learn. It's
   refreshingly self contained in an always-online world, and you don't
   need to worry about it becoming obsolete, because that happened long
   ago.

What are the programs?

  the Fish Game

   When you leave teenagers unsupervised and let them freely code a
   program amongst themselves, the Fish Program is what you get. It's not
   really a game, it's more like an interactive slide show. Think
   something like The Impossible Quiz. We filled it full of insults, a
   bunch of inside jokes and random=funny humor, and that was the fish
   game.

   It started one day where one of us went "You should make a fish
   program. It would draw a fish on the screen and be like 'this is a
   fish'. That would be pretty cool." No drugs or alcohol were involved
   (indeed it was probably a lack of medication).

   For my part, I spent many more hours making and refining secret
   backdoor dev consoles, and getting the title screen just right, than I
   ever did adding content to the "game". I made several different
   versions where I tried different things; the most complete is probably
   v3A (they don't go in order, it went V1, V2, V3, V4, V3A, V5...)

   I don't know what else to say about it, but here's a contemporary
   account from me at the time. Make of it what you will.

   The fish program v2 does have the honor of being where my green fish
   logo comes from. It doesn't represent anything; it's just something I
   had and was like "why not use this for my profile picture everywhere."

    Sheep herding game

   Part of the fish program v3, this is one of the first things I made
   that has actually interactive graphics. Use the I J K L and Space keys.

  Faheem name generator

   There was this one kid called Faheem. Among one of the many unusual
   things he would do is that if you needed a hypothetical person's name
   to use as an example, whereas most people would pick something like
   "Dave" or "Bob", he would smash several short names together. So you
   would get things like "Joe Sam", "Bob Bill", and "Bob Joe Jim Bob". We
   thought this was so funny that we put every common name into a program
   and then fit them together in random lengths and orders. We would sit
   in from of the screen for hours, laughing at the best randomly
   generated names that would pop up. Yes, just like the meme. As for
   Faheem himself, he would go on to medical school and become a doctor.

  BASEMAP

   While learning MS-DOS things, I learned about text adventures like
   Colossal Cave, so I tried to make my own. I got most of a game engine
   done for it, but never got to make a real game. Added it to the end of
   Fish Game V2.

  UD45D

   Not quite a PONG clone, it has gravity and you can only hit the ball up
   and backwards, and have to drop it on an angle to get it across the
   field. A strange one, I made it before taking a physics class, so it's
   all based on kinematics.

   2 player, use A S D and J K L keys. Objective is to drop the ball as
   had as you can on your opponent's side of the triangle thing. If the
   ball hits the ground on your side, you lose all your points.

  Pie Pusher

   an extermely low-quality Cookie Clicker-inspired game.

  Morse

   a Morse Code generator. Lights up the letters as it sounds them out. I
   think I made this to teach myself morse code, but I still haven't.

  but wait, there's more!

   Either all or most of the QBASIC programs I used to have on my laptop
   are here on the emulator.

   Try opening QuickBasic directly and go to File -> Open to find even
   more old QBASIC programs. A lot of them involved using the PLAY to make
   various beeps, for some reason.

   In the Other folder there are some more programs that I didn't write,
   but they ended up in my archive anyways.

Release the files!

   If, for whatever reason, you want to run these on DOSBox yourself, or
   real hardware, for that matter, here are the files.

   For more fish game related stuff, you can try this old Dropbox link,
   although I've put every .BAS file I could find into the pack here.

                     comments | patronage | Alnwlsn 2026
</pre>]]></description><link>https://alnwlsn.com/z/qbasic/index.html</link><guid>https://alnwlsn.com/z/qbasic/index.html</guid><pubDate>Sun, 08 Feb 2026 00:00:00 EST</pubDate></item><item><title>Christmas Bells</title><description><![CDATA[<pre>                          Christmas self-playing Bells

   2026-01-31 | [misc] [music]

   It may be more than a month past Christmas, but this is something
   that's been in my "get to it whenever" box for a while.

   These are "Caroling Christmas Bells" made by "Capricorn Electronics".
   It consists of a plastic control box and twelve inexpensive tuned
   bells. Inside each one is a small coil (which looks suspiciously like
   it was taken from a cheap relay) which hits a clapper against the side
   of the bell, playing the note. It is quite old; you don't really see
   things like this nowadays since even cheap electronics can drive a
   speaker with either a decent quality synth or a full recording if you
   wanted to make a music box.

   These kits are still out there - you can find videos of them like this
   one.

                          caroling christmas bells

   The bells are on two reasonably long strands of wire, perhaps so you
   can hang them in a tree. I 3D printed some clips to attach them to a
   pole, for easier handling.

                                    bells

   On the PCB, there are only 4 ICs: a 5V regulator, an unknown
   microcontroller/sequencer IC, and two open-collector transistor arrays
   for driving the bells.

   The potentiometer changes the speed of playback. The bells have little
   damping, so it's better to keep it slower.

                                     pcb

   Search up the name "F Prineppi" as silkscreened on the PCB and you'll
   find a large list of patents, many of which relate to decorations and
   Christmas Lights. However, the patent for these bells is not one I
   found among them. The case does say "patent pending" so perhaps this
   one was not granted?

   Date codes on the chips suggest 1989, which seems about right when you
   go searching for these bells.

   The transistor array chips make it obvious how to drive the bells from
   another source. One pin on each strand is for +12V, and you connect the
   other pins to GND with a transistor. I tried 2x ULN2003A and an Arduino
   and it works just fine.

                                arduino nano

   However, the bells are a pretty limited way to play MIDI files, because
   they are only 12 of them, and they don't cover a full chromatic scale.
   The notes are:

0: 329.63*2,  # E5
1: 293.66*2,  # D5
2: 261.63*2,  # C5
3: 246.94*2,  # B4
4: 233.08*2,  # A#4 / Bb4
5: 220.00*2,  # A4
6: 196.00*2,  # G4
7: 185.00*2,  # F#4 / Gb4
8: 174.61*2,  # F4
9: 164.81*2,  # E4
10: 146.83*2, # D4
11: 130.81*2, # C4

   It reminds me of my thoughts about replacing the drums of cheap wind-up
   music boxes with 3D printed versions to make your own song. Usually,
   they just pick the notes they will need for the song and make a special
   comb just for those notes.

   Because there are open collectors on the original board, you can also
   hook up the original board to a microcontroller directly (I used the
   same Arduino with pullup resistors) and log the entire note sequence.
   Is this even worth doing? Probably not, but I did it.

   The arduino constantly spits out strings like "000000001000", with a 1
   if the note is on, and a 0 if the note is off. I logged it with a
   python program, then condensed the log with wait times between note
   changes instead of the full log. It ends up looking something like
   this:

...
0017643 000000001000
0017666 000000000000
0017816 000000100100
0017839 000000000000
0017984 000001000000
0018012 000000000000
0018157 000010000000
0018180 000000000000
0018330 001000000000
0018353 000000000000
0018497 010000000010
0018526 000000000000
...

   with the left column as the timestamp, and the right colum as the notes
   to play.

so let's hear it

   Ok. I don't know much about sound in the browser, so I'll have to rely
   on ChatGPT to make me a player.

   (BUTTON) Play (BUTTON) Pause Speed: 1.00× line: —

   Something that stands out to me is the quantization of note events to
   the nearest 16th note, which give the sequence a very rigid feel.
   Still, that's about a half-hour of music in one chip from 1989.
   Contemporary ROMs of the time for things like computers were already
   holding hundreds of kB, but those aren't the sort of thing you find on
   a cheap novelty christmas product. There's at least roughly 4,400
   events stored in that chip.

   If you want the sequence for yourself, here it is. You can play it back
   using this crude python program.

                     comments | patronage | Alnwlsn 2026
</pre>]]></description><link>https://alnwlsn.com/z/misc/bells/bells.html</link><guid>https://alnwlsn.com/z/misc/bells/bells.html</guid><pubDate>Sat, 31 Jan 2026 00:00:00 EST</pubDate></item><item><title>Robot Arm Introduction</title><description><![CDATA[<pre>                            Robot Arm: Introduction

   2026-01-15 | [robot-arm]

   Until I write a proper writeup for this project, here is the summary I
   posted to the Teensy Forum:

   https://forum.pjrc.com/index.php?threads/teensy-4-1-as-a-robot-arm-cont
   roller.77603/#post-364701

   Hello all,

   After the startling Adafruit/Sparkfun showdown this week, I figure it's
   as good of a time as any to share my robot arm project on here that
   I've been working on sporadically for the past couple years.

   This is a small (if you consider 70lb small) "laboratory grade" robot
   arm that I found surplus. Somewhat expectedly, the software needed to
   control it is long gone, along with the company that built it. Luckily,
   the power electronics were all still usable. The motor amplifiers work
   on a -10 to 10V signal, and the encodes are quadrature with RS422
   signal levels.

   I can't say I really know how to build robot arm software properly (I
   only took one controls class in college); I think most would use an
   FPGA for this but I know MCUs better and figured a really fast one
   might do the trick.

   The Teensy 4.1 takes the place of the original control electronics
   board (some old DSP chips, FPGAs and a tiny embedded 486 computer), and
   uses these things:
     * IntervalTimer interrupt at 1000 Hz, to run the main control/timing
       loop runs PID controllers, handles reading encoders and writing to
       motors
          + encoders are handled by dedicated encoder counting chips
          + analog -10 to 10V signal to the motor amps handled by some DAC
            chips
          + both communicate with the teensy over SPI
          + plenty of processor time left over for the other non-realtime
            tasks
     * UARTs, to communicate with peripherals at 100 Hz over RS422
          + first one is the yellow box with levers (another surplus item)
            to select modes, move individual joints, see what's going on
          + second one is a scaled down 'sense arm' which I can move
            around and the big arm will copy it
          + a third spare UART
     * PSRAM, for record/play
          + I can record either the current encoder positions or the
            inputs to the PID controllers in real time, and then play them
            back later (by feeding them into the PID setpoint)
          + at 1000 Hz and uint32_t's, 16MB of PSRAM gives several minutes
            of recording time
     * SD card
          + for storing the encoder position between reboots so I don't
            have to re-home the arm each time
          + will probably eventually store saved PSRAM record/play
            recordings
     * DUAL_SERIAL over USB
          + one for a status screen which I can use to see a bunch of
            values at once
          + one for a command line interface, so I can change things like
            PID gains, etc. without needing to reflash the board
     * CAN bus
          + currently unused, plans are to use it to talk to a future Hand
            (unless I decide to use a UART instead)
     * Ethernet
          + at some point, I think IK solving and motion planning will be
            done on a regular computer; ethernet will connect it to the
            Teensy

   ... and almost every pin on the 4.1 is used for something.

   That "yellow box with levers" also has a Teensy inside, an old Teensy
   3.2, which I picked because I had an extra one lying around and it has
   a bunch of ADCs and the libraries to drive a small color LCD.

   Of course it still isn't finished, I don't even have a good algorithm
   to move smoothly between two positions yet, let alone any IK stuff or
   motion planning, but I can move it around and poke at stuff. Of course
   it still isn't finished, I don't even have a good algorithim to move
   smoothly between two positions yet, let alone any IK stuff or motion
   planning, but I can move it around and poke at stuff.

   I do plan to do a full write up of it eventually, but if you'd rather
   listen to me ramble about it for an hour and a half I have made a
   couple of videos:

   first part: https://youtu.be/1EmZlVSyIFY

   building a 'sense arm' https://youtu.be/Zyvq_y_IzGc

   I've been very pleased with the Teensy. I've actually been leveraging
   some of the audio features of the 4.0 for another project at work, but
   I don't think I can comment on that one.

                     comments | patronage | Alnwlsn 2026
</pre>]]></description><link>https://alnwlsn.com/z/robot-arm/teensy-intro.html</link><guid>https://alnwlsn.com/z/robot-arm/teensy-intro.html</guid><pubDate>Thu, 15 Jan 2026 00:00:00 EST</pubDate></item><item><title>soviet.c</title><description><![CDATA[<pre>                                    soviet.c

   2025-11-15 | [software]

   It's that falling block game that we all know and love. I did this
   before, but I wanted to clean it up to make porting easier for the
   occasional quick demo.

   If you have something that can run C code and display a grid of
   squares, you should be able to run this on it now with little
   difficulty. A demo program using SDL2 is included to show how it's
   done.

   It is partially based on the Gameboy version (that's the one I'm most
   familiar with), but it's not an exact clone.
     * Nintendo Rotation System
     * no wall kick
     * frame-based DAS
     * 7-bag randomizer
     * adjustable playfield size (defines in soviet.h)
     * gravity increases 11% per level (x0.9) until 1 frame
     * original Nintendo scoring system which is probably not quite right

   SDL2 demo: sovietblock.zip

                     comments | patronage | Alnwlsn 2026
</pre>]]></description><link>https://alnwlsn.com/z/sw/soviet_c.html</link><guid>https://alnwlsn.com/z/sw/soviet_c.html</guid><pubDate>Sat, 15 Nov 2025 00:00:00 EST</pubDate></item></channel></rss>