<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>axio.ms</title>
    <description>axio.ms is Matt Evans&apos;s blog and project-writeup site.  All content is copyright &amp;copy; 2002-2024 Matt Evans, unless otherwise stated.
</description>
    <link>http://axio.ms//</link>
    <atom:link href="http://axio.ms//feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sun, 16 Jun 2024 20:44:50 +0100</pubDate>
    <lastBuildDate>Sun, 16 Jun 2024 20:44:50 +0100</lastBuildDate>
    <generator>Jekyll v4.3.3</generator>
    
      <item>
        <title>MicroMac, a Macintosh for under £5</title>
        <description>
&lt;p&gt;&lt;a href=&quot;/images/umac/umac_startup.png&quot;&gt; 
    &lt;img src=&quot;/images/umac/umac_startup.png&quot; srcset=&quot;     /images/umac/umac_startup.png 454w&quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;a-microcontroller-macintosh&quot;&gt;A microcontroller Macintosh&lt;/h2&gt;

&lt;p&gt;This all started from a conversation about the RP2040 MCU, and building a simple desktop/GUI for it.  I’d made a comment along the lines of “or, just run some old OS”, and it got me thinking about the &lt;a href=&quot;https://en.wikipedia.org/wiki/Macintosh_128K&quot;&gt;original Macintosh&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The original Macintosh was released 40.5 years before this post, and is a pretty cool machine especially considering that the hardware is very simple.  &lt;a href=&quot;https://www.stevenlevy.com/insanely-great&quot;&gt;Insanely Great&lt;/a&gt; and &lt;a href=&quot;https://folklore.org/&quot;&gt;folklore.org&lt;/a&gt; are fun reads, and give a glimpse into the Macintosh’s development.  Memory was a squeeze; the original 128KB version was underpowered and only sold for a few months before being replaced by the &lt;em&gt;Macintosh 512K&lt;/em&gt;, arguably a more appropriate amount of memory.&lt;/p&gt;

&lt;p&gt;But, the 128 still runs &lt;em&gt;some&lt;/em&gt; real applications and, though it pre-dates MultiFinder/actual multitasking, I found it pretty charming.  As a tourist.  In 1984 the Mac cost roughly 1/3 as much as a VW Golf and, as someone who’s into old computers and old cars, it’s hard to decide which is more frustrating to use.&lt;/p&gt;

&lt;p&gt;So back to this £3.80 RPi Pico microcontroller board:  The RP2040’s 264KB of RAM gives a lot to play with after carving out the Mac’s 128KB – how cool would it be to do a quick hack, and play with a Mac on it?&lt;/p&gt;

&lt;p&gt;Time passes.  A lot of time.  But I totally delivered on the janky hack front:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/umac/umac_whole.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/1400/umac_whole.jpg&quot; srcset=&quot;            /assets/resized/480/umac_whole.jpg 480w,            /assets/resized/800/umac_whole.jpg 800w,            /assets/resized/1400/umac_whole.jpg 1400w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;del&gt;You won’t believe that this quality item didn’t take that long to build.&lt;/del&gt; So the software was obviously the &lt;em&gt;involved&lt;/em&gt; part, and turned into work on 3 distinct projects.&lt;/p&gt;

&lt;p&gt;This post is going to be a “development journey” story, as a kind of code/design/venting narrative.  If you’re just here for the pictures, scroll along!&lt;/p&gt;

&lt;h2 id=&quot;what-is-pico-mac&quot;&gt;What is pico-mac?&lt;/h2&gt;

&lt;p&gt;A Raspberry Pi RP2040 microcontroller (on a Pico board), driving monochrome VGA video and taking USB keyboard/mouse input, emulating a &lt;em&gt;Macintosh 128K&lt;/em&gt; computer and disc storage.  The RP2040 has easily enough RAM to house the Mac’s memory, plus that of the emulator; it’s fast enough (with some tricks) to meet the performance of the real machine, has USB host capability, and the PIO department makes driving VGA video fairly uneventful (with some tricks).  The basic Pico board’s 2MB of flash is plenty for a disc image with OS and software.&lt;/p&gt;

&lt;p&gt;Here’s the Pico MicroMac in action, ready for the paperless office of the future:&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/umac/umac_workstation.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/1400/umac_workstation.jpg&quot; srcset=&quot;            /assets/resized/480/umac_workstation.jpg 480w,            /assets/resized/800/umac_workstation.jpg 800w,            /assets/resized/1400/umac_workstation.jpg 1400w,    &quot; width=&quot;100%&quot; alt=&quot; The Pico MicroMac RISC CISC workstation of the future&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;The Pico MicroMac RISC CISC workstation of the future&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I hadn’t really used a &lt;em&gt;Mac 128K&lt;/em&gt; much before; a few clicks on a museum machine once.  But I knew they ran MacDraw, and MacWrite, and MacPaint.  All three of these applications are pretty cool for a 128K machine; a largely WYSIWYG word processor with multiple fonts, and a vector drawing package.&lt;/p&gt;

&lt;p&gt;A great way of playing with early Macintosh system software, and applications of these wonderful machines is via &lt;a href=&quot;https://infinitemac.org&quot;&gt;https://infinitemac.org&lt;/a&gt;, which has shrinkwrapped running the Mini vMac emulator by emscriptening it to run in the browser.  Highly recommended, lots to play with.&lt;/p&gt;

&lt;p&gt;As a spoiler, MicroMac does run MacDraw, and it was great to play with it on “real fake hardware”:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/umac/umac_workstation2.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/1400/umac_workstation2.jpg&quot; srcset=&quot;            /assets/resized/480/umac_workstation2.jpg 480w,            /assets/resized/800/umac_workstation2.jpg 800w,            /assets/resized/1400/umac_workstation2.jpg 1400w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Do you find “Pico Micro Mac” doesn’t really scan?  I didn’t think this taxonomy through, did I?)&lt;/p&gt;

&lt;p&gt;GitHub links are at the bottom of this page:  the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pico-mac&lt;/code&gt; repo has &lt;a href=&quot;https://github.com/evansm7/pico-mac?tab=readme-ov-file#hardware-contruction&quot;&gt;construction directions&lt;/a&gt; if you want to build your own!&lt;/p&gt;

&lt;h2 id=&quot;the-journey&quot;&gt;The journey&lt;/h2&gt;

&lt;p&gt;Back up a bit.  I wasn’t committed to building a Pico thing, but was vaguely interested in whether it was feasible, so started tinkering with building a &lt;em&gt;Mac 128K emulator&lt;/em&gt; on my normal computer first.&lt;/p&gt;

&lt;h3 id=&quot;the-three-rules&quot;&gt;The three rules&lt;/h3&gt;

&lt;p&gt;I had a few simple rules for this project:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It had to be fun.  It’s OK to hack stuff to get it working, it’s not as though I’m being paid for this.&lt;/li&gt;
  &lt;li&gt;I like writing emulation stuff, but I really don’t want to learn 68K assembler, or much about the 68K.  There’s a lot of love for 68K out there and that’s cool, but meh I don’t adore it as a CPU.  So, right from the outset I wanted to use someone else’s 68K interpreter – I knew there were loads around.&lt;/li&gt;
  &lt;li&gt;Similarly, there are a load of OSes whose innards I’d like to learn more about, but the shittiest early Mac System software isn’t high on the list.  Get in there, emulate the hardware, boot the OS as a black box, done.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I ended up breaking 2 of and sometimes all 3 of these rules during this project.&lt;/p&gt;

&lt;h3 id=&quot;the-mac-128k&quot;&gt;The Mac 128K&lt;/h3&gt;

&lt;p&gt;The machines are generally pretty simple, and of their time.  I started with schematics and &lt;em&gt;Inside Macintosh&lt;/em&gt;, PDFs of which covered various details of the original Mac hardware, memory map, mouse/keyboard, etc.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://tinkerdifferent.com/resources/macintosh-128k-512k-schematics.79/&quot;&gt;https://tinkerdifferent.com/resources/macintosh-128k-512k-schematics.79/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://vintageapple.org/inside_o/&quot;&gt;https://vintageapple.org/inside_o/&lt;/a&gt; &lt;em&gt;Inside Macintosh Volumes I-III&lt;/em&gt; are particularly useful for hardware information; also &lt;em&gt;Guide to Macintosh Family Hardware 2nd Edition&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Macintosh has:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A Motorola 68000 CPU running at &lt;del&gt;7.whatever MHz&lt;/del&gt; roughly 8MHz&lt;/li&gt;
  &lt;li&gt;Flat memory, decoded into regions for memory-mapped IO going to the 6522 VIA, the 8530 SCC, and the IWM floppy controller.  (Some of the address decoding is a little funky, though.)&lt;/li&gt;
  &lt;li&gt;Keyboard and mouse hang off the VIA/SCC chips.&lt;/li&gt;
  &lt;li&gt;No external interrupt controller: the 68K has 3 IRQ lines, and there are 3 IRQ sources (VIA, SCC, programmer switch/NMI).&lt;/li&gt;
  &lt;li&gt;“No slots” or expansion cards.&lt;/li&gt;
  &lt;li&gt;No DMA controller: a simple autonomous PAL state machine scans video (and audio samples) out of DRAM.  Video is fixed at 512x342 1BPP.&lt;/li&gt;
  &lt;li&gt;The only storage is an internal FDD (plus an external drive), driven by the IWM chip.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first three Mac models are extremely similar:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The &lt;em&gt;Mac 128K&lt;/em&gt; and &lt;em&gt;Mac 512K&lt;/em&gt; are the same machine, except for RAM.&lt;/li&gt;
  &lt;li&gt;The &lt;em&gt;Mac Plus&lt;/em&gt; added SCSI to a convenient space in the memory map and an 800K floppy drive, which is double-sided whereas the original was a single 400K side.&lt;/li&gt;
  &lt;li&gt;The &lt;em&gt;Mac Plus&lt;/em&gt; ROM also supports the 128K/512K, and was an upgrade to create the &lt;em&gt;Macintosh 512Ke&lt;/em&gt;.  ‘e’ for Extra ROM Goodness.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;em&gt;Mac Plus&lt;/em&gt; ROM supports the HD20 external hard disc, and HFS, &lt;em&gt;and&lt;/em&gt; Steve Chamberlin has &lt;a href=&quot;https://www.bigmessowires.com/rom-adapter/plus-rom-listing.asm&quot;&gt;annotated a disassembly of it&lt;/a&gt;.  This was the ROM to use:  I was making a &lt;em&gt;Macintosh 128Ke&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;mac-emulator-umac&quot;&gt;Mac emulator: umac&lt;/h3&gt;

&lt;p&gt;After about 8 minutes of research, I chose the &lt;a href=&quot;https://github.com/kstenerud/Musashi&quot;&gt;Musashi&lt;/a&gt; 68K interpreter.  It’s C, simple to interface to, and had a simple out-of-box example of a 68K system with RAM, ROM, and some IO.  &lt;em&gt;Musashi&lt;/em&gt; is structured to be embedded in bigger projects: wire in memory read/write callbacks, a function to raise an IRQ, call execute in a loop, done.&lt;/p&gt;

&lt;p&gt;I started building an emulator around it, which ultimately became the &lt;a href=&quot;https://github.com/evansm7/umac&quot;&gt;umac&lt;/a&gt; project.  The first half (of, say, five halves) went pretty well:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A simple commandline app loading the ROM image, allocating RAM, providing debug messages/assertions/logging, and configuring &lt;em&gt;Musashi&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Add address decoding: CPU reads/writes are steered to RAM, or ROM.  The “overlay” register lets the ROM boot at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x00000000&lt;/code&gt; and then trampoline up to a high ROM mirror after setting up CPU exception vectors – this affects the address decoding.  This is done by poking a VIA register, so decoded just that bit of that register for now.&lt;/li&gt;
  &lt;li&gt;At this point, the ROM starts running and accessing more non-existent VIA and SCC registers.  Added more decoding and a skeleton for emulating these devices elsewhere – the MMIO read/writes are just stubbed out.&lt;/li&gt;
  &lt;li&gt;There are some magic addresses that the ROM accesses that “miss” documented devices: there’s a manufacturing test option that probes for a plugin (just thunk it), and then we witness the RAM size probing.  The &lt;em&gt;Mac Plus&lt;/em&gt; ROM is looking for up to 4MB of RAM.  In the large region devoted to RAM, the smaller amount of actual RAM is mirrored over and over, so the probe writes a magic value at high addresses and spots where it starts to wrap around.&lt;/li&gt;
  &lt;li&gt;RAM is then initialised and filled with a known pattern.  This was an exciting point to get to because I could dump the RAM, convert the region used for the video framebuffer into an image, and see the “diagonal stripe” pattern used for RAM testing!  &lt;em&gt;“She’s alive!”&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Not all of the device code enjoyed reading all zeroes, so there was a certain amount of referring to the disassembly and returning, uh, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xffffffff&lt;/code&gt; sometimes to push it further.  The goal was to get it as far as accessing the IWM chip, i.e. trying to load the OS.&lt;/li&gt;
  &lt;li&gt;After seeing some IWM accesses there and returning random rubbish values, the first wonderful moment was getting the “Unknown Disc” icon with the question mark – real graphics!  The ROM was &lt;em&gt;REALLY DOING SOMETHING!&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;I &lt;em&gt;think&lt;/em&gt; I hadn’t implemented any IRQs at this point, and found the ROM in an infinite loop: it was counting a few Vsyncs to delay the flashing question mark.  Diversion into a better VIA, with callbacks for GPIO register read/write, and IRQ handling.  This also needed to wire into &lt;em&gt;Musashi&lt;/em&gt;’s IRQ functions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This was motivating to get to – remembering rule #1 – and “graphics”, even though via a manual memory dump/ImageMagick conversion, was great.&lt;/p&gt;

&lt;p&gt;I knew the &lt;a href=&quot;https://en.wikipedia.org/wiki/Integrated_Woz_Machine&quot;&gt;IWM&lt;/a&gt; was an “interesting” chip, but didn’t know details.  I planned to figure it out when I got there (rule #1).&lt;/p&gt;

&lt;h4 id=&quot;iwm-68k-and-disc-drivers&quot;&gt;IWM, 68K, and disc drivers&lt;/h4&gt;

&lt;p&gt;My god, I’m glad I put IWM off until this point.  If I’d read the “datasheet” (vague register documentation) first, I’d’ve just gone to the pub instead of writing this shitty emulator.&lt;/p&gt;

&lt;p&gt;IWM is very clever, but very very low-level.  The disc controllers in other contemporary machines, e.g. &lt;a href=&quot;https://en.wikipedia.org/wiki/Western_Digital_FD1771&quot;&gt;WD1770&lt;/a&gt;, abstract the disc physics.  At one level, you can poke regs to step to track 17 and then ask the controller to grab sector 3.  Not so with IWM:  first, the discs are Constant Linear Velocity, meaning the angular rotation needs to change appropriate to whichever track you’re on, and second the IWM just gives the CPU a firehose of crap from the disc head (with minimal decoding).  I spent a while reading through the disassembly of the ROM’s IWM driver (breaking rule #2 and rule #1): there’s some kind of servo control loop where the driver twiddles PWM values sent to a DAC to control the disc motor, measured against a VIA timer reference to do some sort of dynamic rate-matching to get the correct bitrate from the disc sectors.  I think once it finds the track start it then streams the track into memory, and the driver decodes the symbols (more clever encoding) and selects the sector of interest.&lt;/p&gt;

&lt;p&gt;I was sad.  Surely &lt;em&gt;Basilisk II&lt;/em&gt; and &lt;em&gt;Mini vMac&lt;/em&gt; etc. had solved this in some clever way – they emulated floppy discs.  I learned they do not, and do the smart engineering thing instead:  avoid the problem.&lt;/p&gt;

&lt;p&gt;The other emulators do quite a lot of ROM patching: the ROM isn’t run unmodified.  You can argue that this then isn’t a perfect hardware emulation if you’re patching out inconvenient parts of the ROM, but so what.  I suspect they were also abiding by a rule #1 too.&lt;/p&gt;

&lt;p&gt;I was going to do the same:  I figured out a bit of how the Mac driver interface works (gah, rule #3!) and understood how the other emulators patched this.  They use a custom &lt;em&gt;paravirtualised&lt;/em&gt; 68K driver which is copied over the ROM’s IWM driver, servicing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.Sony&lt;/code&gt; requests from the block layer and routing them to more convenient host-side code to manage the requests.  &lt;em&gt;Basilisk II&lt;/em&gt; uses some custom 68K opcodes and a simple driver, and &lt;em&gt;Mini vMac&lt;/em&gt; a complex driver with trappy accesses to a custom region of memory.  I reused the &lt;em&gt;Basilisk II&lt;/em&gt; driver but converted to access a trappy region (easier to route: just emulate another device).  The driver callbacks land in the host/C side and some cut-down &lt;em&gt;Basilisk II&lt;/em&gt; code interprets the requests and copies data to/from the OS-provided buffers.  Right now, all I needed was to read blocks from one disc:  I didn’t need different formats (or even write support), or multiple drives, or ejecting/changing images.&lt;/p&gt;

&lt;p&gt;Getting the first block loaded from disc took waaaayyy longer than the first part.  And, I’d had to learn a bit of 68K (gah), but just in the nick of time I got a Happy Mac icon as the System software started to load.&lt;/p&gt;

&lt;p&gt;This was still a simple Linux commandline application, with zero UI.  No keyboard or mouse, no video.  Time to wrap it in an SDL2 frontend (the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unix_main&lt;/code&gt; test build in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;umac&lt;/code&gt; project), and I could watch the screen redraw live.  I hadn’t coded the 1Hz timer interrupt into the VIA, and after adding that it booted to a desktop!&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/umac/umac_first_desktop.png&quot;&gt; 
    &lt;img src=&quot;/assets/resized/480/umac_first_desktop.png&quot; srcset=&quot;            /assets/resized/480/umac_first_desktop.png 480w,    &quot; width=&quot;100%&quot; alt=&quot; The first boot&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;The first boot&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;As an aside, I try to create a dual-target build for all my embedded projects, with a native host build for rapid prototyping/debugging; libSDL instead of an LCD.  It means I don’t need to code &lt;em&gt;at&lt;/em&gt; the MCU, so I can code in the garden.  :)&lt;/p&gt;

&lt;p&gt;Next was mouse support.  &lt;em&gt;Inside Macintosh&lt;/em&gt; and the schematics show how it’s wired, to the VIA (good) and the SCC (a beast).  The SCC is my second least-favourite chip in this machine; it’s complex and the datasheet/manual seems to be intentionally written to hide information, piss off readers, get one back at the world.  (I didn’t go near the serial side, its main purpose, just external IRQ management.  But, it’ll do all kinds of exciting 1980s line coding schemes, offloading bitty work from the CPU.  It was key for supporting things like AppleTalk.)&lt;/p&gt;

&lt;p&gt;Life was almost complete at this point; with a working mouse I could build a new disc image (using &lt;em&gt;Mini vMac&lt;/em&gt;, an exercise in itself) with &lt;em&gt;Missile Command&lt;/em&gt;.  This game is pretty fun for under 10KB on disc.&lt;/p&gt;

&lt;p&gt;So:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Video works&lt;/li&gt;
  &lt;li&gt;Boots from disc&lt;/li&gt;
  &lt;li&gt;Mouse works, Missile Command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had no keyboard, but it’s largely working now.  Time to start on sub-project numero due:&lt;/p&gt;

&lt;h3 id=&quot;hardware-and-rp2040&quot;&gt;Hardware and RP2040&lt;/h3&gt;

&lt;p&gt;Completely unrelated to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;umac&lt;/code&gt;, I built up a circuit and firmare with two goals:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Display 512x342x1 video to VGA with minimal components,&lt;/li&gt;
  &lt;li&gt;Get the TinyUSB HID example working and integrated.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This would just display a test image copied to a framebuffer, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;printf()&lt;/code&gt; keyboard/mouse events, as a PoC.  The video portion was fun:  I’d done some &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;I2S&lt;/code&gt; audio PIO work before, but here I wanted to scan out video and arbitrarily control Vsync/Hsync.&lt;/p&gt;

&lt;p&gt;Well, to test I needed a circuit.  VGA wants 0.7V max on the video R,G,B signals and (mumble, some volts) on the syncs.  The R,G,B signals are 75Ω to ground:  with some maths, a 3.3V GPIO driving all three through a 100Ω resistor is roughly right.&lt;/p&gt;

&lt;p&gt;The day I started soldering it together I needed a VGA connector.  I had a DB15 but wanted it for another project, and felt bad about cutting up a VGA cable.  But when I took a walk at lunchtime, no shitting you, I passed some street cables.  I had a VGA cable – the rust helps with the janky aesthetic.&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/umac/street_wire.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/1400/street_wire.jpg&quot; srcset=&quot;            /assets/resized/480/street_wire.jpg 480w,            /assets/resized/800/street_wire.jpg 800w,            /assets/resized/1400/street_wire.jpg 1400w,    &quot; width=&quot;100%&quot; alt=&quot; Free VGA cable&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;Free VGA cable&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/evansm7/pico-mac/blob/main/src/pio_video.pio&quot;&gt;VGA PIO side&lt;/a&gt; was pretty fun.  It ended up as PIO reading config info dynamically to control Hsync width, display position, and so on, and then some tricks with DMA to scan out the config info interleaved with framebuffer data.  By shifting the bits in the right direction and by using the byteswap option on the RP2040 DMA, the big-endian Mac framebuffer can be output directly without CPU-side copies or format conversion.  Cool.  This can be fairly easily re-used in other projects: see &lt;a href=&quot;https://github.com/evansm7/pico-mac/blob/main/src/video.c&quot;&gt;video.c&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But.  I ended up (re)writing the video side three times in total:&lt;/p&gt;

&lt;p&gt;First version had two DMA channels writing to the PIO TX FIFO.  The first would transfer the config info, then trigger the second to transfer video data, then raise an IRQ.  The IRQ handler would then have a short time (the FIFO depth!) to choose a new framebuffer address to read from, and reprogram DMA.  It worked OK, but was highly sensitive to other activity in the system.  First and most obvious fix is that any latency-sensitive IRQ handler &lt;em&gt;must&lt;/em&gt; have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__not_in_flash_func()&lt;/code&gt; attribute so as to run out of RAM.  But even with that, the design didn’t give much time to reconfigure the DMA:  random glitches and blanks occurred when moving the mouse rapidly.&lt;/p&gt;

&lt;p&gt;Second version did double-buffering with the goal of making the IRQ handler’s job trivial: poke in a pre-prepared DMA config quickly, then after the critical rush calculate the buffer to use for next time.  Lots better, but still some glitches under some high load.  Even weirder, it’d sometimes just blank out completely, requiring a reset.  This was puzzling for a while; I ended up printing out the PIO FIFO’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FDEBUG&lt;/code&gt; register to try to catch the bug in the act.  I saw that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TXOVER&lt;/code&gt; overflow flag was set, and this should be impossible: the FIFOs pull data from DMA on demand with DMA requests and a credited flow-contr…OH WAIT.  If credits get messed up or duplicated, too many transfers can happen, leading to an overflow at the receiver side.&lt;/p&gt;

&lt;p&gt;Well, I’d missed a subtle rule in the RP2040 DMA docs:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Another caveat is that multiple channels should not be connected to the same DREQ.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the third version…… doesn’t break this rule, and is more complicated as a result:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;One DMA channel transfers to the PIO TX FIFO&lt;/li&gt;
  &lt;li&gt;Another channel programs the first channel to send from the config data buffer&lt;/li&gt;
  &lt;li&gt;A third channel programs the first to send the video data&lt;/li&gt;
  &lt;li&gt;The programming of the first triggers the corresponding “next reprogram me” channel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The nice thing – aside from no lock-ups or video corruption – is that this now triggers a Hsync IRQ during the video line scan-out, greatly relaxing the deadline of reconfiguring the DMA.  I’d like to further improve this (with yet another DMA channel) to transfer without an IRQ per line, as the current IRQ overhead of about 1% of CPU time can be avoided.&lt;/p&gt;

&lt;p&gt;(It would’ve been simpler to just hardwire the VGA display timing in the PIO code, but I like (for future projects) being able to dynamically-reconfigure the video mode.)&lt;/p&gt;

&lt;p&gt;So now we have a platform and firmware framework to embed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;umac&lt;/code&gt; into, HID in and video out.  The hardware’s done, fuggitthat’lldo, let’s throw it over to the software team:&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/umac/umac_annotated.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/umac_annotated.jpg&quot; srcset=&quot;            /assets/resized/480/umac_annotated.jpg 480w,            /assets/resized/800/umac_annotated.jpg 800w,            /assets/resized/1400/umac_annotated.jpg 1400w,            /assets/resized/2048/umac_annotated.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; How it all works&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;How it all works&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h3 id=&quot;back-to-emulating-things&quot;&gt;Back to emulating things&lt;/h3&gt;

&lt;p&gt;A glance at the native &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;umac&lt;/code&gt; binary showed a few things to fix before it could run on the Pico:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Musashi&lt;/em&gt; constructed a &lt;em&gt;huge&lt;/em&gt; opcode decode jumptable at runtime, in RAM.  It’s never built differently, and never changes at runtime.  I added a &lt;em&gt;Musashi&lt;/em&gt; build-time generator so that this table could be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const&lt;/code&gt; (and therefore live in flash).&lt;/li&gt;
  &lt;li&gt;The disassembler was large, and not going to be used on the Pico, so another option to build without.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Musashi&lt;/em&gt; tries to accurately count execution cycles for each instruction, with more large lookup tables.  Maybe useful for console games, but the Mac doesn’t have the same degree of timing sensitivity.  &lt;em&gt;REMOVED&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(This work is in my &lt;a href=&quot;https://github.com/evansm7/Musashi/commits/small-build/&quot;&gt;small-build&lt;/a&gt; branch.)&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pico-mac&lt;/code&gt; takes shape, with the ROM and disc image in flash, and enjoyably it now builds and runs on the Pico!  With some careful attention to not shoving stuff in RAM, the RAM use is looking pretty good.  The emulator plus HID code is using about 35-40KB on top of the Mac’s 128KB RAM area – there’s 95+KB of RAM still free.&lt;/p&gt;

&lt;p&gt;This was a good time to finish off adding the keyboard support to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;umac&lt;/code&gt;.  The Mac keyboard is interfaced serially through the VIA ‘shift register’, a basic synchronous serial interface.  This was logically simple, but frustrating because early attempts at replying to the ROM’s “init” command just were persistently ignored.  The ROM disassembly was super-useful again: reading the keyboard init code, it looked like a race condition in interrupt acknowledgement if the response byte appears too soon after the request is sent.  Shoved in a delay to hold off a reply until a later poll, and then it was just a matter of mapping keycodes (boooooorrrriiiiing).&lt;/p&gt;

&lt;p&gt;With a keyboard, the end-of-level MacWrite boss is reached:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/umac/umac_macwrite.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/umac_macwrite.jpg&quot; srcset=&quot;            /assets/resized/480/umac_macwrite.jpg 480w,            /assets/resized/800/umac_macwrite.jpg 800w,            /assets/resized/1400/umac_macwrite.jpg 1400w,            /assets/resized/2048/umac_macwrite.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One problem though:  it totally sucked.  It was suuuuper slow.  I added a 1Hz dump of instruction count, and it was doing about 300 KIPS.&lt;/p&gt;

&lt;p&gt;The 68000 isn’t an amazing CPU in terms of IPC.  Okay, there are some instructions that execute in 4 cycles.  But you want to use those extravagant addressing modes don’t you, and touching memory is spending those cycles all over the place.  Not an expert, but targeting about 1 MIPS for an about 8MHz 68000 seems right.  Only 3x improvement needed.&lt;/p&gt;

&lt;h3 id=&quot;performance&quot;&gt;Performance&lt;/h3&gt;

&lt;p&gt;I didn’t say I wasn’t gonna cheat:  let’s run that Pico at 250MHz instead of 125MHz.  Okay better, but not 2x better.  From memory, only about 30% better.  Damn, no free lunch today.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Musashi&lt;/em&gt; has a lot of configurable options.  My first goal was to get its main loop (as seen from disassembly/post-compile end!) small:  the Mac doesn’t report Bus Errors, so the registers don’t need copies for unwinding.  The opcodes are always fetched from a 16b boundary, so don’t need alignment checking, and can use halfword loads (instead of two byte loads munged into a halfword!).  For the Cortex-M0+/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;armv6m&lt;/code&gt; ISA, reordering some of the CPU context structure fields enabled immediate-offset access and better code.  The CPU type, mysteriously, was dynamically-changeable and led to a bunch of runtime indirection.&lt;/p&gt;

&lt;p&gt;Looking better, maybe 2x improvement, but not enough.  &lt;em&gt;Missile Command&lt;/em&gt; was still janky and the mouse wasn’t smooth!&lt;/p&gt;

&lt;p&gt;Next, some naughty/dangerous optimisations: remove address alignment checking, because unaligned accesses don’t happen &lt;em&gt;in this constrained environment&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;(Then, this work is in my &lt;a href=&quot;https://github.com/evansm7/Musashi/commits/umac-hacks&quot;&gt;umac-hacks&lt;/a&gt; branch.)&lt;/p&gt;

&lt;p&gt;But the real perf came from a different trick.  First, a diversion!&lt;/p&gt;

&lt;h4 id=&quot;rp2040-memory-access&quot;&gt;RP2040 memory access&lt;/h4&gt;

&lt;p&gt;The RP2040 has fast RAM, which is multi-banked so as to allow generally single-cycle access to multiple users (2 CPUs, DMA, etc.).  Out of the box, most code runs via XIP from external QSPI flash.  The QSPI usually runs at the core clock (125MHz default), but has a latency of ~20 cycles for a random word read.  The RP2040 uses a relatively simple 16KB cache in front of the flash to protect you from horrible access latency, but the more code you have the more likely you are to call a function and have to crank up QSPI.  When overclocking to 250MHz, the QSPI can’t go that fast so stays at 125MHz (I think).  Bear in mind, then, that your 20ish QSPI cycles on a miss become 40ish CPU cycles.&lt;/p&gt;

&lt;p&gt;The particular rock-and-a-hard-place here is that &lt;em&gt;Musashi&lt;/em&gt; build-time generates a ton of code, a function for each of its 1968 opcodes, plus that 256KB opcode jumptable.  Even if we make the inner execution loop completely free, the opcode dispatch might miss in the flash cache, and the opcode function itself too.  (If we want to get 1 MIPS out of about 200 MIPS, a few of these delays are going to really add up.)&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__not_in_flash_func()&lt;/code&gt; attribute can be used to copy a given function into RAM, guaranteeing fast execution.  At the very minimum, the main loop and memory accessors are decorated:  every instruction is going to access an opcode and most likely read or write RAM.&lt;/p&gt;

&lt;p&gt;This improves performance a few percent.&lt;/p&gt;

&lt;p&gt;Then, I tried decorating whole classes of opcodes: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;move&lt;/code&gt; is frequent, as are branches, so put ‘em in RAM.  This helped a lot, but the remaining free RAM was used up very quickly, and I wasn’t at my goal of much above 1 MIPS.&lt;/p&gt;

&lt;p&gt;Remember that &lt;a href=&quot;https://en.wikipedia.org/wiki/Reduced_instruction_set_computer&quot;&gt;RISC architecture&lt;/a&gt; is gonna change everything?&lt;/p&gt;

&lt;p&gt;We want to put some of those 1968 68K opcodes into RAM to make them fast.  What are the top 10 most often-used instructions?  Top 100?  By adding a 64K table of counters to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;umac&lt;/code&gt;, booting the Mac and running key applications (okay, playing &lt;em&gt;Missile Command&lt;/em&gt; for a bit), we get a profile of dynamic instruction counts.  It turns out that the 100 hottest opcodes (5% of the total) account for 89% of the execution.  And the top 200 account for a whopping 98% of execution.&lt;/p&gt;

&lt;p&gt;Armed with this profile, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;umac&lt;/code&gt; build post-processes the &lt;em&gt;Musashi&lt;/em&gt; auto-generated code and decorates the top 200 functions with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__not_in_flash_func()&lt;/code&gt;.  This adds only 17KB of extra RAM usage (leaving 95KB spare), and hits about 1.4 MIPS!  Party on!&lt;/p&gt;

&lt;p&gt;At last, the world can enjoy &lt;em&gt;Missile Command&lt;/em&gt;’s dark subject matter in performant comfort:&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/umac/umac_missile.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/1400/umac_missile.jpg&quot; srcset=&quot;            /assets/resized/480/umac_missile.jpg 480w,            /assets/resized/800/umac_missile.jpg 800w,            /assets/resized/1400/umac_missile.jpg 1400w,    &quot; width=&quot;100%&quot; alt=&quot; Missile Command on pico-mac&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;Missile Command on pico-mac&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h3 id=&quot;what-about-macpaint&quot;&gt;What about MacPaint?&lt;/h3&gt;

&lt;p&gt;Everyone loves MacPaint.  Maybe &lt;em&gt;you&lt;/em&gt; love MacPaint, and have noticed I’ve deftly avoided mentioning it.  Okay, FINE:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/umac/macpaint_mem.png&quot; alt=&quot;There is not enough memory for MacPaint!&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It doesn’t run on a &lt;em&gt;Mac 128Ke&lt;/em&gt;, because the &lt;em&gt;Mac Plus&lt;/em&gt; ROM uses more RAM than the original.  :sad-face:&lt;/p&gt;

&lt;p&gt;I’d seen this thread on 68kMLA about a “Mac 256K”: &lt;a href=&quot;https://68kmla.org/bb/index.php?threads/the-mythical-mac-256k.46149/&quot;&gt;https://68kmla.org/bb/index.php?threads/the-mythical-mac-256k.46149/&lt;/a&gt;  Chances are that the &lt;em&gt;Mac 128K&lt;/em&gt; was really a &lt;em&gt;Mac 256K&lt;/em&gt; in the lab (or maybe even intended to have 256K and cost-cut before release), as the OS functions fine with 256KB.&lt;/p&gt;

&lt;p&gt;I wondered, does the Mac ROM/OS need a power-of-two amount of RAM?  If not, I have that 95K going spare.  Could I make a “Mac 200K”, and then run precious MacPaint?&lt;/p&gt;

&lt;p&gt;Well, I tried a local hack that patches the ROM to update its global &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;memTop&lt;/code&gt; variable based on a given memory size, and yes, System 3.2 is happy with non-power-of-2 sizes.  I booted with 256K, 208K, and 192K.  However, there were some additional problems to solve:  the ROM memtest craps itself without a power-of-2 size (totally fair), and NOPping that out leads to other issues.  These can be fixed, though also some parts of boot access off the end of RAM.  A power-of-2 size means a cheap address mask wraps RAM accesses to the valid buffer, and that can’t be done with 192K.&lt;/p&gt;

&lt;p&gt;Unfortunately, when I then tested MacPaint it &lt;em&gt;still&lt;/em&gt; wouldn’t run because it wanted to write a scratch file to the read-only boot volume.  This is totally breaking rule #1 by this point, so we are staying with 128KB for now.&lt;/p&gt;

&lt;p&gt;However, a 256K MicroMac is extremely possible.  We just need an MCU with, say, 300KB of RAM…  Then we’d be cooking on gas.&lt;/p&gt;

&lt;h2 id=&quot;goodbye-friend&quot;&gt;Goodbye, friend&lt;/h2&gt;

&lt;p&gt;Well, dear reader, this has been a blast.  I hope there’s been something fun here for ya.  Ring off now, caller!&lt;/p&gt;

&lt;div class=&quot;gallery&quot;&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;umac &quot; href=&quot;/images/pg/umac/micromac.jpg&quot; title=&quot;The MicroMac!&quot;&gt;&lt;img src=&quot;/images/pg/umac/micromac-thumb.jpg&quot; srcset=&quot;/images/pg/umac/micromac-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;The MicroMac!&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;umac &quot; href=&quot;/images/pg/umac/umac_early_macwrite.jpg&quot; title=&quot;HDMI monitor, using a VGA-to-HDMI box&quot;&gt;&lt;img src=&quot;/images/pg/umac/umac_early_macwrite-thumb.jpg&quot; srcset=&quot;/images/pg/umac/umac_early_macwrite-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;HDMI monitor, using a VGA-to-HDMI box&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;umac &quot; href=&quot;/images/pg/umac/umac_desktop.png&quot; title=&quot;umac screenshot&quot;&gt;&lt;img src=&quot;/images/pg/umac/umac_desktop-thumb.png&quot; srcset=&quot;/images/pg/umac/umac_desktop-2x-thumb.png 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;umac screenshot&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;umac &quot; href=&quot;/images/pg/umac/umac_sys32_desktop.png&quot; title=&quot;System 3.2, Finder 5.3&quot;&gt;&lt;img src=&quot;/images/pg/umac/umac_sys32_desktop-thumb.png&quot; srcset=&quot;/images/pg/umac/umac_sys32_desktop-2x-thumb.png 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;System 3.2, Finder 5.3&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;umac &quot; href=&quot;/images/pg/umac/umac_missile.png&quot; title=&quot;Performance tuning&quot;&gt;&lt;img src=&quot;/images/pg/umac/umac_missile-thumb.png&quot; srcset=&quot;/images/pg/umac/umac_missile-2x-thumb.png 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Performance tuning&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;umac &quot; href=&quot;/images/pg/umac/umac_sys32_de.png&quot; title=&quot;Random disc image working OK&quot;&gt;&lt;img src=&quot;/images/pg/umac/umac_sys32_de-thumb.png&quot; srcset=&quot;/images/pg/umac/umac_sys32_de-2x-thumb.png 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Random disc image working OK&lt;/dd&gt;&lt;/dl&gt;

&lt;br style=&quot;clear: both;&quot; /&gt;

&lt;/div&gt;

&lt;h1 id=&quot;resources&quot;&gt;Resources&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/evansm7/umac&quot;&gt;https://github.com/evansm7/umac&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/evansm7/pico-mac&quot;&gt;https://github.com/evansm7/pico-mac&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.macintoshrepository.org/7038-all-macintosh-roms-68k-ppc-&quot;&gt;https://www.macintoshrepository.org/7038-all-macintosh-roms-68k-ppc-&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://winworldpc.com/product/mac-os-0-6/system-3x&quot;&gt;https://winworldpc.com/product/mac-os-0-6/system-3x&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://68kmla.org/bb/index.php?threads/macintosh-128k-mac-plus-roms.4006/&quot;&gt;https://68kmla.org/bb/index.php?threads/macintosh-128k-mac-plus-roms.4006/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.google.com/spreadsheets/d/1wB2HnysPp63fezUzfgpk0JX_b7bXvmAg6-Dk7QDyKPY/edit#gid=840977089&quot;&gt;https://docs.google.com/spreadsheets/d/1wB2HnysPp63fezUzfgpk0JX_b7bXvmAg6-Dk7QDyKPY/edit#gid=840977089&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 16 Jun 2024 00:00:00 +0100</pubDate>
        <link>http://axio.ms//projects/2024/06/16/MicroMac.html</link>
        <guid isPermaLink="true">http://axio.ms//projects/2024/06/16/MicroMac.html</guid>
        
        <category>Macintosh</category>
        
        <category>68K</category>
        
        <category>ARM</category>
        
        
        <category>projects</category>
        
      </item>
    
      <item>
        <title>Classical virtualisation rules applied to RISC-style atomics</title>
        <description>&lt;p&gt;In 1974, Gerald Popek and Robert Goldberg published a paper, “&lt;a href=&quot;https://doi.org/10.1145%2F361011.361073&quot;&gt;Formal Requirements for Virtualizable Third Generation Architectures&lt;/a&gt;”, giving a &lt;a href=&quot;https://en.wikipedia.org/wiki/Popek_and_Goldberg_virtualization_requirements&quot;&gt;set of characteristics&lt;/a&gt; for correct full-machine virtualisation.&lt;/p&gt;

&lt;p&gt;Today, these characteristics remain very useful.  Computer architects will informally cite this paper when debating Instruction Set Architecture (ISA) developments, with arguments like “but that’s not Popek &amp;amp; Goldberg-compliant!”&lt;/p&gt;

&lt;p&gt;In this post I’m looking at one aspect of computer architecture evolution since 1974, and observing how RISC-style atomic operations provide some potential virtualisation gotchas for both programmers and architects.&lt;/p&gt;

&lt;h2 id=&quot;principles-of-virtualisation&quot;&gt;Principles of virtualisation&lt;/h2&gt;

&lt;p&gt;First, some virtualisation context, because it’s fun!&lt;/p&gt;

&lt;p&gt;A key P&amp;amp;G requirement is that of &lt;em&gt;equivalence&lt;/em&gt;: it’s reasonable to expect software running under virtualisation to have the same behaviour as running it bare-metal!  This property is otherwise known as correctness. :-)&lt;/p&gt;

&lt;p&gt;P&amp;amp;G classify instructions as being &lt;em&gt;sensitive&lt;/em&gt; if they behave differently when running at a lower privilege level (i.e. the program can detect that it is being run in a different manner).  An ISA is said to be &lt;em&gt;classically virtualisable&lt;/em&gt; if:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Sensitive&lt;/em&gt; instructions are &lt;em&gt;privileged&lt;/em&gt;, and&lt;/li&gt;
  &lt;li&gt;Privileged instructions executed at a lower privilege level can be trapped to a higher level of privilege.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a classically-virtualisable system, perfect equivalence can then be achieved by running software at a lower than usual level of privilege, trapping all privileged/sensitive instructions, and emulating their behaviour in a VMM.  That is, if the design of the ISA ensures that &lt;em&gt;all “sensitive” instructions can be trapped&lt;/em&gt;, it’s possible to ensure the logical execution of the software cannot be different to running bare-metal.&lt;/p&gt;

&lt;p&gt;This virtualisation technique is called “privilege compression”.&lt;/p&gt;

&lt;p&gt;Note:  This applies recursively, running OS-level software with user privilege, or hypervisor-level software at OS/user privilege.  Popek &amp;amp; Goldberg formalise this too, giving properties required for correct nested virtualisation.&lt;/p&gt;

&lt;p&gt;System/360 and PowerPC are both &lt;em&gt;classically virtualisable&lt;/em&gt;, almost as though IBM thought about this. ;-)  Equivalent virtualisation can be achieved by:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Running an OS in user mode (privilege compression, for CPU virtualisation),&lt;/li&gt;
  &lt;li&gt;Catching traps (to supervisor mode/HV) when the guest OS performs a privileged operation,&lt;/li&gt;
  &lt;li&gt;In the hypervisor, operating on a software-maintained “shadow” of what &lt;em&gt;would have been the guest OS’s privileged CPU state&lt;/em&gt; were it running bare-metal.&lt;/li&gt;
  &lt;li&gt;Constructing shadow address translations (for memory virtualisation).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Linux’s KVM support on PowerPC includes a “PR” feature, which does just this: for CPUs without hardware virtualisation, guests are run in user mode (or “PRoblem state” in IBM lingo).&lt;/p&gt;

&lt;p&gt;Note:  It is key that the hypervisor can &lt;em&gt;observe and control all of the guest’s state&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Today, most systems address the performance impact of all of this trap-and-emulate by providing &lt;a href=&quot;https://en.wikipedia.org/wiki/Hardware-assisted_virtualization&quot;&gt;hardware CPU and memory virtualisation&lt;/a&gt; (e.g. user, OS and hypervisor execution privilege levels, with nested page tables).  But, classically virtualisable ISA design remains important for clear reasoning about isolation between privilege levels and composability of behaviours.&lt;/p&gt;

&lt;h2 id=&quot;computers-in-1974-were-all-cisc&quot;&gt;Computers in 1974 were ~all CISC&lt;/h2&gt;

&lt;p&gt;All computers in 1974 were available in corduroy with a selection of Liberty-print input devices.  All consoles had ashtrays (not even joking tbh).&lt;/p&gt;

&lt;p&gt;Architecture-wise, IBM was working on early RISC concepts leading to the 701, but most of the industry was on a full-steam trajectory to &lt;em&gt;peak CISC&lt;/em&gt; (VAX) in the late 1970s.  It’s fair to say that “CISC” wasn’t even a thing yet; instruction sets were just complex.  P&amp;amp;G’s paper considered three contemporary computers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;IBM System/360&lt;/li&gt;
  &lt;li&gt;Honeywell 6000&lt;/li&gt;
  &lt;li&gt;DEC PDP-10&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;cisc-atomic-operations-and-synchronisation-primitives&quot;&gt;CISC atomic operations and synchronisation primitives&lt;/h3&gt;

&lt;p&gt;These machines had composite/”read-modify-write” atomic operations, similar to those in today’s x86 architectures.  System/360 had compare-and-swap, locked operations (read-operate-write), test-and-set, and PDP-10 had EXCHange/swap.&lt;/p&gt;

&lt;p&gt;These kinds of instructions are not &lt;em&gt;sensitive&lt;/em&gt; so, unless the addressed memory is privileged, atomic operations can be performed inside virtual machines without the hypervisor &lt;em&gt;needing&lt;/em&gt; to know.&lt;/p&gt;

&lt;h2 id=&quot;atomic-operations-in-risc-machines&quot;&gt;Atomic operations in RISC machines&lt;/h2&gt;

&lt;p&gt;Many RISC machines support multi-instruction synchronisation sequences built up around two instruction primitives:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Load-and-set-reservation&lt;/li&gt;
  &lt;li&gt;Store-conditional&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MIPS called these load-linked (LL) and store-conditional (SC), and I’ll use these terms.  ARMv8 has LDXR/STXR.  PowerPC has LWARX/STWCX.  RISC-V has LR/SC.  Many machines (such as ARMv8-LSE) also add composite operations such as CAS or atomic addition but still retain the base LL/SC mechanism, and sizes/acquire/release variants are often provided.&lt;/p&gt;

&lt;p&gt;The concept is that the LL simultaneously loads a value and sets a “reservation” covering the address in question, and a subsequent SC succeeds only if the reservation is still present.  A conflicting write to the location (e.g. a store on another CPU) clears the reservation and the SC returns a failure value without modifying memory; LL/SC are performed in a loop to retry until the update succeeds.&lt;/p&gt;

&lt;p&gt;An LL/SC sequence can typically be arbitrarily complex – a lock routine might test a location is cleared and store a non-zero value if so, whereas an update might increment a counter or calculate a “next” value, and so on.  Typically an ISA does not restrict what lies between LL and SC.&lt;/p&gt;

&lt;p&gt;Coming back to virtualisation requirements, the definition of a reservation is interesting because it’s effectively “hidden state” that the hypervisor cannot manage.  Typically, a hypervisor cannot easily read whether a reservation exists, and it can’t be saved/restored&lt;sup id=&quot;fnref:res_sr&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:res_sr&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.  CISC-like RmW atomic operations do not exhibit this property.&lt;/p&gt;

&lt;h2 id=&quot;problem-seen-problem-felt&quot;&gt;Problem seen, problem felt&lt;/h2&gt;

&lt;p&gt;Shall I get to the point?  I saw an odd but legal guest code sequence that can be difficult to virtualise.&lt;/p&gt;

&lt;p&gt;I’ve been trying to run MacOS 9.2 in KVM-PR on a PowerPC G4, and observed the NanoKernel acquire-lock routine happens to use a &lt;em&gt;sensitive instruction&lt;/em&gt; (&lt;em&gt;mfsprg&lt;/em&gt;) between a &lt;em&gt;lwarx&lt;/em&gt; and &lt;em&gt;stwcx&lt;/em&gt;.  This is strange, and guarantees a trap to the host &lt;em&gt;between&lt;/em&gt; the LL and SC operations.  Though the guest &lt;em&gt;should not&lt;/em&gt; be doing weird stuff when acquiring a lock, it’s still an architecturally-correct program.&lt;/p&gt;

&lt;p&gt;This means that &lt;em&gt;if&lt;/em&gt; the reservation isn’t preserved across the trap, the lock is never taken.  Forward progress is never achieved and virtualisation equivalence is not maintained (because the guest livelocks).&lt;/p&gt;

&lt;p&gt;Specifically, if the reservation is &lt;em&gt;always&lt;/em&gt; cleared on the trap, we have a problem.  If it is &lt;em&gt;sometimes&lt;/em&gt; kept, the guest program can progress.&lt;/p&gt;

&lt;p&gt;Since the state is hidden (the hypervisor can’t save/restore/re-create), correctness depends on two things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The hypervisor’s exception-emulation-return path not itself clearing the reservation every time for any possible trap&lt;/li&gt;
  &lt;li&gt;The ISA and hardware implementation guaranteeing the reservation is not always cleared by hardware&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This potential issue isn’t limited to PPC or the MacOS guest.&lt;/p&gt;

&lt;h2 id=&quot;software-guarantees&quot;&gt;Software guarantees&lt;/h2&gt;

&lt;p&gt;The hypervisor must guarantee two things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It must not intentionally clear reservations on all traps.&lt;/li&gt;
  &lt;li&gt;It must not accidentally do so as a side-effect of a chosen activity:
    &lt;ol&gt;
      &lt;li&gt;For example, using its own synchronisation primitives elsewhere,
 	   or by writing memory that would conflict with the guest’s reservation.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This can be challenging: context switching must be avoided in the T&amp;amp;E handler (no sleep or pre-emption), and it can’t take locks.&lt;/p&gt;

&lt;p&gt;In my MacOS guest experiment, KVM-PR does not &lt;em&gt;happen to currently&lt;/em&gt; use any synchronisation primitives on its emulation path, ew delicate – but I had tracing on, which does.  The guest locked up.&lt;/p&gt;

&lt;h2 id=&quot;hardware-guarantees&quot;&gt;Hardware guarantees&lt;/h2&gt;

&lt;p&gt;But does your CPU guarantee that reservations aren’t always cleared?&lt;sup id=&quot;fnref:res_clr&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:res_clr&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;That seems to depend.  This morning’s light reading gives:&lt;/p&gt;

&lt;h3 id=&quot;powerpc-architecture&quot;&gt;PowerPC architecture&lt;/h3&gt;

&lt;p&gt;PowerISA is comparatively clear on the behaviour (which isn’t surprising, as PowerISA is generally very clearly-specified).  PowerISA v3.1 section 1.7.2.1 describes reservations, listing specific reasons for reservation loss.  Some are the expected “lose the reservation if someone else hits the memory” reasons, but previous PowerISAs (e.g. 2.06) permitted embedded implementations to clear the reservation on all exceptions.  This permission was removed by 3.1; in my opinion a good move.  (I did just this, for reasons, in my homebrew PowerPC CPU, oops!)&lt;/p&gt;

&lt;p&gt;PowerISA does permit spontaneous reservation loss due to speculative behaviour, but is careful to require that forward progress is guaranteed (i.e. that an implementation doesn’t happen to clear the reservation every time for a given piece of code).&lt;/p&gt;

&lt;p&gt;Finally, it includes a virtualisation-related programming note stating a reservation may be lost if software executes a privileged instruction or utilizes a privileged facility (i.e. sensitive instructions).  This expresses intent, but isn’t specification: it doesn’t criminalise a guest doing wrong things unless it’s a rule that was there from the dawn of time.&lt;/p&gt;

&lt;p&gt;At any rate, this post is going to be old news to the PowerISA authors.  Nice doc, 8/10, good jokes, would read again.&lt;/p&gt;

&lt;h3 id=&quot;risc-v-architecture&quot;&gt;RISC-V architecture&lt;/h3&gt;

&lt;p&gt;The lack of any guest legacy permits the problem to be solved from the other direction.  Interestingly, the RISC-V ISA explicitly constrains the instruction sequences between LR/SC:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;The dynamic code executed between the LR and SC instructions
 can only contain instructions from the base “I” instruction set, 
 excluding loads, stores, backward jumps, taken backward branches, 
 JALR, FENCE, FENCE.I, and SYSTEM instructions.“
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a good move.  Tacitly, this bans sensitive instructions in the critical region, and permits an absence of progress if the guest breaks the rules.  Ruling out memory accesses is interesting too, because it can be useful for a hypervisor to be able to T&amp;amp;E any given page in the guest address space without repercussions.&lt;/p&gt;

&lt;h3 id=&quot;reservation-granule-size&quot;&gt;Reservation granule size&lt;/h3&gt;

&lt;p&gt;An LL operation is usually architecturally permitted to set an address-based reservation with a size larger than the original access, called the “reservation granule”.  A larger granule reduces tracking requirements but increases the risk of a kind of false sharing between locks where an unrelated CPU taking an unrelated lock could clear your CPU’s reservation.&lt;/p&gt;

&lt;p&gt;This is important to our hypervisor, because of guarantee #2 above: when emulating a sensitive instruction it must not access anything that always causes the reservation to clear.  You would hope the guest doesn’t soil itself by executing an instruction against its interests, so we can assume the guest won’t intentionally direct the hypervisor to hit on shared addresses, but if hypervisor and guest memory could ever coexist within a reservation granule there is scope for conflict.&lt;/p&gt;

&lt;p&gt;PowerPC defines the largest granule as, effectively, the (small) page size.  ARM defines it as 4KB (effectively, the same).  It’s a reasonable architectural assumption that guest and host memory is disjoint at page size granularity.&lt;/p&gt;

&lt;p&gt;RISC-V permits the reservation granule to be unlimited, which isn’t great&lt;sup id=&quot;fnref:rv_gran&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:rv_gran&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; – but later notes that “a platform specification may constrain the size and shape of the reservation set. For example, the Unix platform is expected to require of main memory that the reservation set be of fixed size, contiguous, naturally aligned, and no greater than the virtual memory page size.”&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;An ISA cannot be classically virtualised if it permits some aspect of trapping or emulation (such as the exception itself) to always cause a reservation to be cleared, unless sensitive instructions are prohibited from any region dependent on a reservation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In terms of computer science, it’s quite unsatisfying if it were possible to have a sequence of RISC instructions that cannot be classically virtualised due to hidden state.&lt;/p&gt;

&lt;p&gt;In practical terms, trap-and-emulate is alive and well in systems supporting nested virtualisation.  Although some ISAs provide a level of hardware support for NV, it tends to be assists to speed up use of privilege compression rather than more exception levels and more translation stages (which, to be fair, would be awful).  Consequently there is always &lt;em&gt;something&lt;/em&gt; hypervisor-privileged being trapped to the real hypervisor, i.e. T&amp;amp;E is used in anger.&lt;/p&gt;

&lt;p&gt;So, there are some hardware behaviours which must (continue to be) guaranteed and, unfortunately, some constraints on already-complex software which must be observed.&lt;/p&gt;

&lt;p&gt;I thought this small computer architecture safari might be interesting to others, and hope you enjoyed the read!&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h3&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:res_sr&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;In theory an ISA could provide the hypervisor with a previous reservation’s address, but re-creating it with a later LL raises ordering model questions! &lt;a href=&quot;#fnref:res_sr&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:res_clr&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Sorry for the double-negative, but this alludes to the possibility of architecture permissions (for example, statements like “X is permitted to spontaneously happen at any time”) leading to implementations taking convenient liberties such as “always do X when any cache line is fetched”.  If these decisions &lt;em&gt;were&lt;/em&gt; to exist, they would be impossible to avoid stepping on, even with a carefully-written hypervisor. &lt;a href=&quot;#fnref:res_clr&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:rv_gran&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;It would be terrible to permit an implementation to allow all hypervisor memory accesses to clear the reservation! &lt;a href=&quot;#fnref:rv_gran&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Mon, 16 May 2022 00:00:00 +0100</pubDate>
        <link>http://axio.ms//blog/2022/05/16/RISC-PopekGoldberg.html</link>
        <guid isPermaLink="true">http://axio.ms//blog/2022/05/16/RISC-PopekGoldberg.html</guid>
        
        <category>CompArch</category>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>A small ode to the CRT</title>
        <description>
&lt;p&gt;&lt;a href=&quot;/images/crtbox/crtbox_banner.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/crtbox_banner.jpg&quot; srcset=&quot;            /assets/resized/480/crtbox_banner.jpg 480w,            /assets/resized/800/crtbox_banner.jpg 800w,            /assets/resized/1400/crtbox_banner.jpg 1400w,            /assets/resized/2048/crtbox_banner.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built October 2018&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I used to hate &lt;a href=&quot;https://en.wikipedia.org/wiki/Cathode-ray_tube&quot;&gt;Cathode Ray Tubes&lt;/a&gt;.  As a kid in Europe, everything flickered at 50Hz, or made a loud whistle at 15.625KHz (back when I could still hear it).  CRTs just seemed crude, “electro-brutalist” contraptions from the valve era.  They were heavy, and delicate, and distorted, and blurry, and whistled, and gave people electric shocks when they weren’t busy imploding and spreading glass shards around the place.&lt;/p&gt;

&lt;p&gt;When I saw the film &lt;a href=&quot;https://en.wikipedia.org/wiki/Brazil_(1985_film)&quot;&gt;Brazil&lt;/a&gt;, I remember getting anxious about &lt;a href=&quot;https://www.imdb.com/title/tt0088846/mediaviewer/rm3136237312/?ref_=ext_shr_lnk&quot;&gt;exposed CRTs all over the place&lt;/a&gt; — seems I was the kind of kid who was more worried about someone touching the anode or electron gun than the totalitarian bureaucratic world they lived in. 🤷🏻‍♂️  As ever, I digress.&lt;/p&gt;

&lt;p&gt;Now in the 2020s, the CRT is pretty much gone.  We have astonishing flat-panel LCD and OLED screens.  Nothing flickers, everything’s pin-sharp, multi-megapixel resolutions, nothing whines (except me), and display life is pretty incredible for those of us old enough to remember &lt;a href=&quot;https://en.wikipedia.org/wiki/Monochrome_monitor&quot;&gt;green-screen&lt;/a&gt; computing (but young enough to still see the details).&lt;/p&gt;

&lt;p&gt;But, the march to betterness marches away from accessible:  if you take apart a phone, the LCD is a magic glass rectangle, and that’s it.  Maybe you can see some LEDs if you tear it apart, but it’s really not obvious how it works.&lt;/p&gt;

&lt;p&gt;CRTs are also magic, but in a pleasing 19th century top-hat-and-cane science kind of way.  Invisible beams trace out images through a foot of empty space.  They respond colourfully to magnets (also magic) held to their screens by curious children whose glee rapidly decays into panic, and trying to undo the effect using the other pole before their mother looks around and discovers what they’ve done (allegedly).&lt;/p&gt;

&lt;p&gt;The magnet-game is a clue: (most) CRTs use electromagnets that scans the invisible electron beam to light an image at the front.  There’s something enjoyable about moving the beam yourself, with a magnet in hand, and you can &lt;em&gt;kind of&lt;/em&gt; intuitively figure out how it works from doing this.  (Remember the &lt;a href=&quot;https://en.wikipedia.org/wiki/Fleming%27s_left-hand_rule_for_motors&quot;&gt;Left-hand Rule&lt;/a&gt;?)&lt;/p&gt;

&lt;p&gt;I started to warm to CRTs, maybe a fondness when I realised I hadn’t had to seriously use one for over a decade.  I wanted to build something.  I also like smol displays, and found an excellent source for a small CRT — a video camera viewfinder.  Home cameras had tiny CRTs, roughly 1cm picture size, but I looked to find a higher-end professional viewfinder because they tended to have larger tubes for a higher-quality image.  Eventually I found a Sony HVF-2000 viewfinder, from ca. 1980.&lt;/p&gt;

&lt;p&gt;This viewfinder contained a monochrome 1.5” CRT, and its drive circuitry on stinky 1970s phenolic resin PCBs.  All it needs are two turntables and an 8V DC power supply and composite video input.  It displays nice, sharp images on a cool white phosphor.&lt;/p&gt;

&lt;p&gt;I built this from it:&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/crtbox/crtbox_075.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/crtbox_075.jpg&quot; srcset=&quot;            /assets/resized/480/crtbox_075.jpg 480w,            /assets/resized/800/crtbox_075.jpg 800w,            /assets/resized/1400/crtbox_075.jpg 1400w,            /assets/resized/2048/crtbox_075.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; Small CRT floating in a box&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;Small CRT floating in a box&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I wanted to show the CRT from all angles, without hiding any of it, in the trusty “desktop curiosity” style.  The idea was to show off this beautiful little obsolete glass thingy, in a way that you could sorta guess how it worked.&lt;/p&gt;

&lt;p&gt;Switching it on with a pleasing clack, it starts silently playing a selection of 1980s TV shows, over and over and over:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/crtbox/crtbox_play_mm.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/crtbox_play_mm.jpg&quot; srcset=&quot;            /assets/resized/480/crtbox_play_mm.jpg 480w,            /assets/resized/800/crtbox_play_mm.jpg 800w,            /assets/resized/1400/crtbox_play_mm.jpg 1400w,            /assets/resized/2048/crtbox_play_mm.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had this on my desk at work, and a Young Person&lt;sup&gt;TM&lt;/sup&gt; came into my office one day to ask about it.  He hadn’t really seen a CRT close-up before, and we had a fun chat about how it worked (including waving a magnet at it – everyone has a spare magnet on their desk for these moments, don’t they?  Hello…?).  Yay!&lt;/p&gt;

&lt;p&gt;If you’re unfamiliar with CRTs, they work roughly like this:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The glass envelope contains a vacuum.&lt;/li&gt;
  &lt;li&gt;The neck contains a heating filament (like a lightbulb) which gives off electrons into the void.&lt;/li&gt;
  &lt;li&gt;This “electron gun” is near some metal plates (with variously high positive and negative voltages), which act to focus the fizz of electrons into a narrow beam, directing it forward.&lt;/li&gt;
  &lt;li&gt;The inside of the front face of the tube is covered by a phosphorescent material which lights up when hit with electrons.&lt;/li&gt;
  &lt;li&gt;The front face is connected to the anode terminal, a high positive voltage.  This attracts the beam of electrons, which accelerate to the front.&lt;/li&gt;
  &lt;li&gt;The beam hits the front and creates light in a small spot.  To create the picture, the beam is steered in rasters/lines using horizontal and vertical electromagnets wrapped around the neck of the tube.  (The magnets are called the “yoke”.)&lt;/li&gt;
  &lt;li&gt;For PAL at 50Hz, lines are drawn 15625 times a second.  Relying on the principle of persistence of vision, this creates the illusion of a steady image.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;/images/crtbox/crtbox_yoke.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/crtbox_yoke.jpg&quot; srcset=&quot;            /assets/resized/480/crtbox_yoke.jpg 480w,            /assets/resized/800/crtbox_yoke.jpg 800w,            /assets/resized/1400/crtbox_yoke.jpg 1400w,            /assets/resized/2048/crtbox_yoke.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tube is sealed and electron gun inside is largely invisible, but here you can see the malicious-looking thick anode wire, and how dainty the tube really is with the yoke removed:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/crtbox/crtbox_tiny_tube.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/crtbox_tiny_tube.jpg&quot; srcset=&quot;            /assets/resized/480/crtbox_tiny_tube.jpg 480w,            /assets/resized/800/crtbox_tiny_tube.jpg 800w,            /assets/resized/1400/crtbox_tiny_tube.jpg 1400w,            /assets/resized/2048/crtbox_tiny_tube.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: the anode voltage for this tube is, from memory, about 2.5 kilovolts, so not particularly spicy.  A large computer monitor will give you 25KV!  Did I mention the X-rays?&lt;/p&gt;

&lt;h1 id=&quot;circuit&quot;&gt;Circuit&lt;/h1&gt;

&lt;p&gt;The original viewfinder was a two-board affair, fitting in a strange transverse shape for the viewfinder case.  I removed a couple of controls and indicators unrelated to the CRT operation, and extended the wires slightly so they could be stacked.&lt;/p&gt;

&lt;p&gt;The viewfinder’s eyepiece looks onto a mirror, turning 90º to the CRT face — so the image is horizontally flipped.  This was undone by swapping the horizontal deflection coil wires, reversing the field direction.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/crtbox/crtbox_circuit.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/crtbox_circuit.jpg&quot; srcset=&quot;            /assets/resized/480/crtbox_circuit.jpg 480w,            /assets/resized/800/crtbox_circuit.jpg 800w,            /assets/resized/1400/crtbox_circuit.jpg 1400w,            /assets/resized/2048/crtbox_circuit.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The circuit’s pretty trivial.  It just takes a DC input (9-12V) and uses two DC-DC converter modules to create an 8V supply for the CRT board and a 5V supply for a Raspberry Pi Zero layered at the bottom.  The whole thing uses under 2W.  The Pi’s composite output drops straight into the CRT board.  The Pi starts up a simple shell script that picks a file to play.  There’s a rotary encoder on the back, to change channel, but I haven’t wired it up yet.&lt;/p&gt;

&lt;h1 id=&quot;case&quot;&gt;Case&lt;/h1&gt;

&lt;p&gt;For me, the case was the best bit.  I had just got (and since lost :((( ) access to a decent laser cutter, and wanted to make a dovetailed transparent case for the parts.  It’s made from 3mm colourless and sky-blue acrylic.&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/crtbox/crtbox_empty.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/crtbox_empty.jpg&quot; srcset=&quot;            /assets/resized/480/crtbox_empty.jpg 480w,            /assets/resized/800/crtbox_empty.jpg 800w,            /assets/resized/1400/crtbox_empty.jpg 1400w,            /assets/resized/2048/crtbox_empty.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; Rubber bands make the world go round&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;Rubber bands make the world go round&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The CRT is supported from two “hangers”, and two trays below hold the circuitry.  These are fixed to the sides using a slot/tab approach, with captive nuts.  In the close-up pictures you can see there are some hairline stress fractures around the corners of some of the tab cut-outs: they could evidently do with being a few hundred µm wider!&lt;/p&gt;

&lt;p&gt;The front/top/back/bottom faces are glued together, then the left/right sides are screwed into the shelves/hangers with captive M3 nuts.  This sandwiches it all together.&lt;/p&gt;

&lt;p&gt;The back holds a barrel-style DC jack, power switch, and (as-yet unused) rotary encoder.  The encoder was intended to eventually be a kind of “channel select”:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/crtbox/crtbox_power_switch.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/crtbox_power_switch.jpg&quot; srcset=&quot;            /assets/resized/480/crtbox_power_switch.jpg 480w,            /assets/resized/800/crtbox_power_switch.jpg 800w,            /assets/resized/1400/crtbox_power_switch.jpg 1400w,            /assets/resized/2048/crtbox_power_switch.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The acrylic is a total magnet for fingerprints and dust, which is excellent if you’re into that kind of thing.  There seems to also be little flecks filling the case, probably some &lt;a href=&quot;https://en.wikipedia.org/wiki/Aquadag&quot;&gt;aquadag&lt;/a&gt; flaking off the CRT.  This technology just keeps on giving.&lt;/p&gt;

&lt;h2 id=&quot;openscad&quot;&gt;OpenSCAD&lt;/h2&gt;

&lt;p&gt;The case is designed in OpenSCAD, and is somewhat parameterised:  the XYZ dimensions, dovetailing, spacing of shelves and so forth can be tweaked till it looks good.&lt;/p&gt;

&lt;p&gt;One nice OpenSCAD laser-cutting trick I saw is that 2D parts can be rendered into a “preview” 3D view, tweaked and fettled, and then re-rendered flat on a 2D plane to create a template for cutting.&lt;/p&gt;

&lt;p&gt;So, make a 3D prototype, change the parameters until it looks good (maybe printing stuff out to see whether the physical items actually fit!)…&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/crtbox/crtbox_3d.png&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/crtbox_3d.png&quot; srcset=&quot;            /assets/resized/480/crtbox_3d.png 480w,            /assets/resized/800/crtbox_3d.png 800w,            /assets/resized/1400/crtbox_3d.png 1400w,            /assets/resized/2048/crtbox_3d.png 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;…then change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mode&lt;/code&gt; variable, and the same parts are laid out in 2D for cutting:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/crtbox/crtbox_flat.svg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Feel free to hack on and re-use this template.&lt;/p&gt;

&lt;h1 id=&quot;resources&quot;&gt;Resources&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/downloads/crt_box.scad&quot;&gt;OpenSCAD box sources&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;pics&quot;&gt;Pics&lt;/h1&gt;

&lt;div class=&quot;gallery&quot;&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;crtbox &quot; href=&quot;/images/pg/crtbox/box_side_play.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/crtbox/box_side_play-thumb.jpg&quot; srcset=&quot;/images/pg/crtbox/box_side_play-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;crtbox &quot; href=&quot;/images/pg/crtbox/crt_face.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/crtbox/crt_face-thumb.jpg&quot; srcset=&quot;/images/pg/crtbox/crt_face-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;crtbox &quot; href=&quot;/images/pg/crtbox/test_fit.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/crtbox/test_fit-thumb.jpg&quot; srcset=&quot;/images/pg/crtbox/test_fit-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;crtbox &quot; href=&quot;/images/pg/crtbox/box_side.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/crtbox/box_side-thumb.jpg&quot; srcset=&quot;/images/pg/crtbox/box_side-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;crtbox &quot; href=&quot;/images/pg/crtbox/box_front.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/crtbox/box_front-thumb.jpg&quot; srcset=&quot;/images/pg/crtbox/box_front-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;crtbox &quot; href=&quot;/images/pg/crtbox/box_back.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/crtbox/box_back-thumb.jpg&quot; srcset=&quot;/images/pg/crtbox/box_back-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;crtbox &quot; href=&quot;/images/pg/crtbox/tiny_dmesg.jpg&quot; title=&quot;Tiny dmesg!&quot;&gt;&lt;img src=&quot;/images/pg/crtbox/tiny_dmesg-thumb.jpg&quot; srcset=&quot;/images/pg/crtbox/tiny_dmesg-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Tiny dmesg!&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;crtbox &quot; href=&quot;/images/pg/crtbox/play_ba.jpg&quot; title=&quot;Edmund Esq&quot;&gt;&lt;img src=&quot;/images/pg/crtbox/play_ba-thumb.jpg&quot; srcset=&quot;/images/pg/crtbox/play_ba-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Edmund Esq&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;crtbox &quot; href=&quot;/images/pg/crtbox/play_cs.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/crtbox/play_cs-thumb.jpg&quot; srcset=&quot;/images/pg/crtbox/play_cs-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;br style=&quot;clear: both;&quot; /&gt;

&lt;/div&gt;

</description>
        <pubDate>Tue, 08 Feb 2022 00:00:00 +0000</pubDate>
        <link>http://axio.ms//projects/2022/02/08/CRTBox.html</link>
        <guid isPermaLink="true">http://axio.ms//projects/2022/02/08/CRTBox.html</guid>
        
        <category>RaspberryPi</category>
        
        <category>CRT</category>
        
        
        <category>projects</category>
        
      </item>
    
      <item>
        <title>Mac SE/30 odyssey</title>
        <description>
&lt;p&gt;&lt;a href=&quot;/images/SE30/mac_banner.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/mac_banner.jpg&quot; srcset=&quot;            /assets/resized/480/mac_banner.jpg 480w,            /assets/resized/800/mac_banner.jpg 800w,            /assets/resized/1400/mac_banner.jpg 1400w,            /assets/resized/2048/mac_banner.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve always wanted an Apple &lt;a href=&quot;https://en.wikipedia.org/wiki/Macintosh_SE/30&quot;&gt;Macintosh SE/30&lt;/a&gt;.  Released in 1989, they look quite a lot like the other members of the original “compact Mac” series, but pack in a ton of interesting features that the other compact Macs don’t have.&lt;/p&gt;

&lt;p&gt;This is the story of my journey to getting to the point of owning a working Mac SE/30, which turns out not to be as simple as just buying one.  Stay tuned for tales of debugging and its repair.&lt;/p&gt;

&lt;p&gt;So, the Mac.  Check it out, with the all-in-one-style 9” monochrome display:&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/SE30/cute_mac.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/cute_mac.jpg&quot; srcset=&quot;            /assets/resized/480/cute_mac.jpg 480w,            /assets/resized/800/cute_mac.jpg 800w,            /assets/resized/1400/cute_mac.jpg 1400w,            /assets/resized/2048/cute_mac.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; The beautiful Macintosh SE/30&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;The beautiful Macintosh SE/30&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I mean, &lt;em&gt;look at it&lt;/em&gt;, isn’t it lovely?  :)&lt;/p&gt;

&lt;p&gt;The key technical difference between the SE/30 and the other compact Macs is that the SE/30 is much much less crap.  It’s like a sleeper workstation, compared to the Mac Plus, SE, or Classic.  8MHz 68K?  No!  &lt;em&gt;~16MHz 68030&lt;/em&gt;.  Emulating FP on a slow 68K?  No!  &lt;em&gt;It ships with a real FPU!&lt;/em&gt;  Limited to 4MB of RAM?  &lt;em&gt;Naw, this thing takes up to 128MB!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Look, I wouldn’t normally condone use of CISC machines (and – unpopular opinion – I’m not actually a 68K fan :D ), but not only has this machine a bunch of capability RAM-wise and CPU-wise, but &lt;em&gt;this machine has an MMU&lt;/em&gt;.  In my book, MMUs make things interesting (as well as ‘interesting’).  Unlike all the other compact Macs, this one can run &lt;em&gt;real operating systems&lt;/em&gt; like BSD, and Linux.  And, I needed to experience A/UX first-hand.&lt;/p&gt;

&lt;p&gt;Unpopular opinion #2:  I don’t really like ye olde Mac OS/System 7 either! :)  It was very cool at the time, and made long-lasting innovations, but lack of memory protection or preemptive scheduling made it a little delicate.  At the time, as a kid, it was frustrating that there was no CLI, or any way to mess around and program them without expensive developer tools – so I gravitated to the Acorn Archimedes machines, and RISC OS (coincidentally with the same delicate OS drawbacks), which were much more accessible programming-wise.&lt;/p&gt;

&lt;p&gt;Anyway, one week during one of the 2020 lockdowns I was reminded of the SE/30, and got a bit obsessed with getting hold of one.  I was thinking about them at 2am (when I wasn’t stressing about things like work), planning which OSes to try out, which upgrades to make, how to network it, etc.&lt;/p&gt;

&lt;p&gt;Took myself to that overpriced auction site, and bought one from a nearby seller.&lt;/p&gt;

&lt;h2 id=&quot;we-got-one&quot;&gt;We got one!&lt;/h2&gt;

&lt;p&gt;I picked it up.&lt;/p&gt;

&lt;p&gt;I was &lt;em&gt;so excited&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It was a good deal (&lt;em&gt;hollow laugh from future-Matt&lt;/em&gt;), as it came in a shoulder bag and included mouse/keyboard, an external SCSI drive and various cables.&lt;/p&gt;

&lt;p&gt;Getting it into the car, I noticed an &lt;em&gt;OMINOUS GRITTY SLIDING SOUND&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Oh, did I mention that these machines are practically guaranteed to self-destruct because either the on-board electrolytic caps ooze out gross stuff, or the on-board Varta lithium battery poos its plentiful and corrosive contents over the logic board?&lt;/p&gt;

&lt;p&gt;[If you own one of these machines or, let’s face it, any machine from this era, go right now and remove the batteries if you haven’t already!  Go on, it’s important.  (I’m also looking at you, Acorn RISC PC owners.)  I’ll wait.]&lt;/p&gt;

&lt;p&gt;I opened up the machine, and the first small clue appeared:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/crud1.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/crud1.jpg&quot; srcset=&quot;            /assets/resized/480/crud1.jpg 480w,            /assets/resized/800/crud1.jpg 800w,            /assets/resized/1400/crud1.jpg 1400w,            /assets/resized/2048/crud1.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Matt:&lt;/em&gt;  Oh.  That’s not a great omen.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Matt, with strained optimism:&lt;/em&gt;  “But maybe the logic board will be okay!”&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/bad_news.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/bad_news.jpg&quot; srcset=&quot;            /assets/resized/480/bad_news.jpg 480w,            /assets/resized/800/bad_news.jpg 800w,            /assets/resized/1400/bad_news.jpg 1400w,            /assets/resized/2048/bad_news.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Mac SE/30:&lt;/em&gt;  “Nah mate, proper fucked sry.”&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Matt:&lt;/em&gt;  :(&lt;/p&gt;

&lt;p&gt;At this point I’d like to say that the seller was a volunteer selling donated items from a charity shop, and it was clear they didn’t really know much about the machine.  It was disappointing, but the money paid for this one is just a charitable donation and I’m happy at that.  (If it were a private seller taking money for a machine that sounded like it washed up on a beach, it’d be a different level of fury.)&lt;/p&gt;

&lt;p&gt;Undeterred (give it up, Matt, come &lt;em&gt;on&lt;/em&gt;), I spent a weekend trying to resurrect it.  Much of the gross stuff washed off, bathing it in a sequence of detergents/vinegar/IPA/etc:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/bath1.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/bath1.jpg&quot; srcset=&quot;            /assets/resized/480/bath1.jpg 480w,            /assets/resized/800/bath1.jpg 800w,            /assets/resized/1400/bath1.jpg 1400w,            /assets/resized/2048/bath1.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/bath2.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/bath2.jpg&quot; srcset=&quot;            /assets/resized/480/bath2.jpg 480w,            /assets/resized/800/bath2.jpg 800w,            /assets/resized/1400/bath2.jpg 1400w,            /assets/resized/2048/bath2.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see some green discolouring of the silkscreen in the bottom right.&lt;/p&gt;

&lt;p&gt;Submerged in (distilled) water, you can see a number of tracks that vanish halfway, or have disappeared completely.  Or, components whose pads and leads have been destroyed!  The battery chemicals are very ingenious; they don’t just wash like lava across the board and destroy the top, but they also wick down into the vias and capillary action seems to draw them into the inner layers.&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/SE30/traxfuxd.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/traxfuxd.jpg&quot; srcset=&quot;            /assets/resized/480/traxfuxd.jpg 480w,            /assets/resized/800/traxfuxd.jpg 800w,            /assets/resized/1400/traxfuxd.jpg 1400w,            /assets/resized/2048/traxfuxd.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; Broken tracks, missing pads, missing components, missing vias&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;Broken tracks, missing pads, missing components, missing vias&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Poring over schematics and beeping out connections, I started airwiring the broken tracks (absolutely determined to get this machine running, as though some perverse challenge).&lt;/p&gt;

&lt;p&gt;But, once I found broken tracks on the inner layers, it moved from perverse to Sisyphean because I couldn’t just see where the damage was:  wouldn’t even &lt;em&gt;finding&lt;/em&gt; the broken tracks by beeping out &lt;em&gt;all&lt;/em&gt; connections be O(intractible)?&lt;/p&gt;

&lt;p&gt;Making the best decision so far in the odyssey, I gave up and searched for another SE/30.  At least I got a spare keyboard and mouse out of it.  But also, a spare enclosure/CRT/analog board, etc., which will be super-useful in a few paragraphs.&lt;/p&gt;

&lt;h2 id=&quot;meet-the-new-mac-same-as-the-old-mac&quot;&gt;Meet the new Mac, same as the old Mac&lt;/h2&gt;

&lt;p&gt;I found someone selling one who happeend to be in the same city (and it turns out, we even worked for the same company – city like village).  This one was advertised as having been ‘professionally re-capped’, and came loaded:  128MB of RAM, &lt;em&gt;and&lt;/em&gt; a sought-after Ethernet card.  Perfecto!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/inside1.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/inside1.jpg&quot; srcset=&quot;            /assets/resized/480/inside1.jpg 480w,            /assets/resized/800/inside1.jpg 800w,            /assets/resized/1400/inside1.jpg 1400w,            /assets/resized/2048/inside1.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/clean_nice.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/clean_nice.jpg&quot; srcset=&quot;            /assets/resized/480/clean_nice.jpg 480w,            /assets/resized/800/clean_nice.jpg 800w,            /assets/resized/1400/clean_nice.jpg 1400w,            /assets/resized/2048/clean_nice.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Paranoid me immediately took it apart to check the re-capping and battery.  :)  Whilst there was a teeny bit of evidence of prior capacitor-leakage, it was really clean for a 31 year old machine and I was really pleased with it.  The re-capping job looked sensible, check.  The battery looked new, but I’m taking no chances this time and pulled it out.&lt;/p&gt;

&lt;p&gt;I had a good 2 hours merrily pissing about doing the kinds of things you do with a new old computer, setting up networking and getting some utilities copied over, such as a Telnet client:&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/SE30/telnet.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/telnet.jpg&quot; srcset=&quot;            /assets/resized/480/telnet.jpg 480w,            /assets/resized/800/telnet.jpg 800w,            /assets/resized/1400/telnet.jpg 1400w,            /assets/resized/2048/telnet.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; Telnet client, life is complete&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;Telnet client, life is complete&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h3 id=&quot;disaster-strikes&quot;&gt;Disaster strikes&lt;/h3&gt;

&lt;p&gt;After the two hour happiness timer expired, the machine stopped working.  Here’s what it did:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/one_line.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/one_line.jpg&quot; srcset=&quot;            /assets/resized/480/one_line.jpg 480w,            /assets/resized/800/one_line.jpg 800w,            /assets/resized/1400/one_line.jpg 1400w,            /assets/resized/2048/one_line.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Otherwise, the Mac made the startup “bong” sound, so the logic board was alive, just unhappy video.&lt;/p&gt;

&lt;p&gt;I think we’re thinking the same thing: the CRT’s Y-deflection circuit is obviously broken.  This family of Macs have a common fault where solder joints on the Analogue board crack, or the drive transistor fails.  The excellent &lt;em&gt;“Dead Mac Scrolls”&lt;/em&gt; book covers common faults, and fixes.&lt;/p&gt;

&lt;p&gt;But, remember the first Mac:  the logic board was a gonner, but the Analog board/CRT seemed good.  I could just swap the logic board over, and I’ve got a working Mac again and can watch the end of Telnet Star Wars.&lt;/p&gt;

&lt;p&gt;It did exactly the same thing!  Bollocks, the problem was on the logic board.&lt;/p&gt;

&lt;h3 id=&quot;debugging-the-problem&quot;&gt;Debugging the problem&lt;/h3&gt;

&lt;p&gt;We were both wrong:  it wasn’t the Y-deflection circuit for the CRT.  The symptoms of &lt;em&gt;that&lt;/em&gt; would be that the CRT scans, but all lines get compressed and overdrawn along the centre – no deflection creating one super-bright line in the centre.&lt;/p&gt;

&lt;h4 id=&quot;debug-clues&quot;&gt;Debug clues&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Clue 1:  This line wasn’t super-bright.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s take a closer look:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/one_line_close.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/480/one_line_close.jpg&quot; srcset=&quot;            /assets/resized/480/one_line_close.jpg 480w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Clue 2:  It’s a dotted line, as though it’s one line of the stippled background when the Mac boots.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s interesting because it’s clearly not being overdrawn; multiple lines merged together would overlay even/odd odd/even pixels and come out solid white.  The line also doesn’t provide any of the “happy Mac” icon in the middle, so it isn’t one of the centre lines of the framebuffer.&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/SE30/bench_test.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/bench_test.jpg&quot; srcset=&quot;            /assets/resized/480/bench_test.jpg 480w,            /assets/resized/800/bench_test.jpg 800w,            /assets/resized/1400/bench_test.jpg 1400w,            /assets/resized/2048/bench_test.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; SE/30 logic board on The Bench, provided with +5V/+12V and probed with scope/LA&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;SE/30 logic board on The Bench, provided with +5V/+12V and probed with scope/LA&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;If you’ve an SE/30 (or a Classic/Plus/128/512 etc.) logic board on a workbench, they’re easy enough to power up without the Analog board/CRT but be aware the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/RESET&lt;/code&gt; circuitry is a little funky.  Reset is generated by the sound chip (…obviously) which requires both +5V and +12V to come out of reset, so you’ll need a dual-rail bench supply.  I’d also recommend plugging headphones in, so you can hear the boot chime (or lack of) as you tinker.  Note the audio amp technically requires -5V too, but with +5V alone you should still be able to hear something.&lt;/p&gt;

&lt;p&gt;This generation of machines are one of the last to have significant subsystems still being implemented as multi-chip sections.  It’s quite instructive to follow along in the schematic:  the SE/30 video system is a cluster of discrete X and Y pixel counters which generate addresses into VRAM (which spits out pixels).  Some PALs generate VRAM addresses/strobes/refresh, and video syncs.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Clue 3:  The video output pin on the chonky connector is being driven, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HSYNC&lt;/code&gt; is running correctly (we can deduce this already, though, because the CRT lights up meaning its HT supply is running, and that’s driven from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HSYNC&lt;/code&gt;).  But, there was no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VSYNC&lt;/code&gt; signal at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/SE30/schematic.png&quot;&gt; 
    &lt;img src=&quot;/assets/resized/800/schematic.png&quot; srcset=&quot;            /assets/resized/480/schematic.png 480w,            /assets/resized/800/schematic.png 800w,    &quot; width=&quot;100%&quot; alt=&quot; VSYNC comes from a PAL taking a Y-count from a counter clocked by &apos;TWOLINE&apos;&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;VSYNC comes from a PAL taking a Y-count from a counter clocked by &apos;TWOLINE&apos;&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Working backwards, I traced &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VSYNC&lt;/code&gt; from the connector to PAL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UG6&lt;/code&gt;.  It wasn’t simply a broken trace, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UG6&lt;/code&gt; wasn’t generating it.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UG6&lt;/code&gt; appears to be a comparator that generates vertical timing strobes when the Y line count &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VADR[7:0]&lt;/code&gt; reaches certain lines.&lt;/p&gt;

&lt;p&gt;The Y line count is generated from a dual hex counter, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UF8&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Clue 4:  The Y line count wasn’t incrementing at all.  That explains the lack of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VSYNC&lt;/code&gt;, as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UG6&lt;/code&gt; never saw the “VSYNC starts now” line come past.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UF8&lt;/code&gt; counter is clocked/incremented by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TWOLINE&lt;/code&gt; signal output from PAL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UG7&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Clue 5a:  PAL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UG7&lt;/code&gt;’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TWOLINE&lt;/code&gt; output was stuck/not transitioning.  Its other outputs (such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HSYNC&lt;/code&gt;) were transitioning fine.  PALs do die, but it seems unusual for only a single output to conk out.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Clue 5b:  PAL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UG7&lt;/code&gt; was unusually hot!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Clue 6, and the root problem:  Pulling the PALs out, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TWOLINE&lt;/code&gt; pin measures 3Ω to ground.  AHA!&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;debug-epiphany&quot;&gt;Debug epiphany&lt;/h4&gt;

&lt;p&gt;*&lt;strong&gt;Something is shorting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TWOLINE&lt;/code&gt; signal to a power rail.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s how the clues correspond to the observations:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There is no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VSYNC&lt;/code&gt;; the Y line count is stuck at 0.&lt;/li&gt;
  &lt;li&gt;The X counter is working fine.  (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HSYNC&lt;/code&gt; is produced, and a stippled pattern line is displayed correctly.)&lt;/li&gt;
  &lt;li&gt;The display shows the top line of the video buffer (from address 0, over and over) but never advances onto the next line.&lt;/li&gt;
  &lt;li&gt;The CRT Y deflection is never “charged up” by a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VSYNC&lt;/code&gt; so the raster stays in the centre on one line, instead of showing 384 identical lines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can work with this.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TWOLINE&lt;/code&gt; is shorted somehow.  Tracing it across the PCB, every part of the trace looked fine, except I couldn’t see the part that ran underneath &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C7&lt;/code&gt; (one of the originally-electrolytic caps replaced with a tantalum).  I removed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C7&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/the_problem.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/the_problem.jpg&quot; srcset=&quot;            /assets/resized/480/the_problem.jpg 480w,            /assets/resized/800/the_problem.jpg 800w,            /assets/resized/1400/the_problem.jpg 1400w,            /assets/resized/2048/the_problem.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See the problem?  It’s pleasingly subtle…&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/track_broke.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/480/track_broke.jpg&quot; srcset=&quot;            /assets/resized/480/track_broke.jpg 480w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How about now?  A tiny amount of soldermask has come off the track just south of the silkscreen ‘+’.  This was Very Close to the capacitor’s contact, and was shorting against it!  Above I thought it was shorting to ground:  it’s shorting to +5V (which, when you measure it might be a low number of ohms to ground).&lt;/p&gt;

&lt;p&gt;My theory is that it wasn’t completely contacting, or wasn’t making a good connection, and that the heat from my 2-hour joyride expanded the material such that it made good contact.&lt;/p&gt;

&lt;p&gt;You can see that there’s some tarnish on the IC above &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C7&lt;/code&gt; – this is damage from the previous &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C7&lt;/code&gt; leaking.  This, or the re-capping job, lifted the insulating soldermask leading to the short.&lt;/p&gt;

&lt;h4 id=&quot;fixed&quot;&gt;Fixed&lt;/h4&gt;

&lt;p&gt;The fix was simple, add some insulation using kapton tape and replace the capacitor:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/the_fix.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/the_fix.jpg&quot; srcset=&quot;            /assets/resized/480/the_fix.jpg 480w,            /assets/resized/800/the_fix.jpg 800w,            /assets/resized/1400/the_fix.jpg 1400w,            /assets/resized/2048/the_fix.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that, I could see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VSYNC&lt;/code&gt; being produced!  But would it work?&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/SE30/happymac.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/1400/happymac.jpg&quot; srcset=&quot;            /assets/resized/480/happymac.jpg 480w,            /assets/resized/800/happymac.jpg 800w,            /assets/resized/1400/happymac.jpg 1400w,    &quot; width=&quot;100%&quot; alt=&quot; The sweet 1bpp stippled smell of success&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;The sweet 1bpp stippled smell of success&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Yasssssss!  :)&lt;/p&gt;

&lt;p&gt;Time to put it all back together, trying not to touch or break the CRT.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/working_mac.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/working_mac.jpg&quot; srcset=&quot;            /assets/resized/480/working_mac.jpg 480w,            /assets/resized/800/working_mac.jpg 800w,            /assets/resized/1400/working_mac.jpg 1400w,            /assets/resized/2048/working_mac.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;and-now-for-something-completely-different-but-eerily-familiar&quot;&gt;And now for something completely different, but eerily familiar&lt;/h2&gt;

&lt;p&gt;I mentioned I wanted this particular model because it could run “interesting OSes”.  Did you know that, way before NeXT and OS X, Apple was a UNIX vendor?&lt;/p&gt;

&lt;h3 id=&quot;apple-aux-operating-system&quot;&gt;Apple A/UX operating system&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/images/SE30/aux2.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/aux2.jpg&quot; srcset=&quot;            /assets/resized/480/aux2.jpg 480w,            /assets/resized/800/aux2.jpg 800w,            /assets/resized/1400/aux2.jpg 1400w,            /assets/resized/2048/aux2.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve always wanted to play with Apple’s &lt;a href=&quot;https://en.wikipedia.org/wiki/A/UX&quot;&gt;A/UX&lt;/a&gt;.  By version 3.1, it had a very highly-integrated Mac OS ‘Classic’ GUI running on a real UNIX.&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/SE30/aux.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/aux.jpg&quot; srcset=&quot;            /assets/resized/480/aux.jpg 480w,            /assets/resized/800/aux.jpg 800w,            /assets/resized/1400/aux.jpg 1400w,            /assets/resized/2048/aux.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; It&apos;s like Mac OS, but... there&apos;s a UNIX dmesg too?&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;It&apos;s like Mac OS, but... there&apos;s a UNIX dmesg too?&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;It’s not X11 (though an X server is available), it really is running the Mac Toolbox etc., and it seems to have some similarities with the later OS X Blue Box/Classic environment in that it runs portions of Mac OS as a UNIX process.  In the same way as OS X + Blue Box, A/UX will run unmodified Mac OS applications.  The Finder is integrated with the UNIX filesystems in both directions (i.e. from a shell you can manipulate Mac files).&lt;/p&gt;

&lt;p&gt;These screenshots don’t do it justice, but there are good A/UX screenshots elsewhere.&lt;/p&gt;

&lt;p&gt;As an OS geek, I’m really impressed with the level of integration between the two OSes!  It’s &lt;em&gt;very&lt;/em&gt; thorough.  Since the usual UNIX development tools are available, there’s a bit of cognitive dissonance of being able to “program a Mac” right out of the box:&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/SE30/aux_dev.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/aux_dev.jpg&quot; srcset=&quot;            /assets/resized/480/aux_dev.jpg 480w,            /assets/resized/800/aux_dev.jpg 800w,            /assets/resized/1400/aux_dev.jpg 1400w,            /assets/resized/2048/aux_dev.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; A/UX example application&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;A/UX example application&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I mean, not just building normal UNIX command-line apps with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cc&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt; etc., but the development examples include Mac OS GUI apps as well!  It’s truly living in the future™.&lt;/p&gt;

&lt;h2 id=&quot;plug-for-rascsi&quot;&gt;Plug for RASCSI&lt;/h2&gt;

&lt;p&gt;Playing with ancient machines and multiple OSes is pretty painful when using ancient SCSI discs because:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Old discs don’t work&lt;/li&gt;
  &lt;li&gt;Old discs are small&lt;/li&gt;
  &lt;li&gt;Transferring stuff to and from discs means plugging it into your Linux box and… I don’t have SCSI there&lt;/li&gt;
  &lt;li&gt;Old discs don’t work and will pretend to and then screw up and ruin your week&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built a &lt;a href=&quot;https://github.com/akuker/RASCSI&quot;&gt;RASCSI&lt;/a&gt; adapter (write-up and PCB posting TBD), software and circuit originally by GIMONS.  This is a Raspberry Pi adapter that allows a userspace program to bit-bang the SCSI-I protocol, serving emulated disc/CD-ROM images from SD card.  It works beautifully on the SE/30, and lets it both have several discs present at once, and switch between images quickly.&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/SE30/rascsi_me.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/rascsi_me.jpg&quot; srcset=&quot;            /assets/resized/480/rascsi_me.jpg 480w,            /assets/resized/800/rascsi_me.jpg 800w,            /assets/resized/1400/rascsi_me.jpg 1400w,            /assets/resized/2048/rascsi_me.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; Homemade RASCSI clone, SCSI emulator for Raspberry Pi&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;Homemade RASCSI clone, SCSI emulator for Raspberry Pi&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The end, seeeeeeya!&lt;/p&gt;

&lt;h1 id=&quot;resources&quot;&gt;Resources&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/mac_The_Dead_Mac_Scrolls_1992&quot;&gt;https://archive.org/details/mac_The_Dead_Mac_Scrolls_1992&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://winworldpc.com/product/a-ux/3x&quot;&gt;https://winworldpc.com/product/a-ux/3x&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://68kmla.org/bb/index.php&quot;&gt;https://68kmla.org/bb/index.php&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 02 Oct 2021 00:00:00 +0100</pubDate>
        <link>http://axio.ms//blog/2021/10/02/MacSE30.html</link>
        <guid isPermaLink="true">http://axio.ms//blog/2021/10/02/MacSE30.html</guid>
        
        <category>Macintosh</category>
        
        <category>68K</category>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>32-bit hat, with LEDs</title>
        <description>&lt;p&gt;&lt;em&gt;Built in November 2015 (now-traditional multi-year writeup delay applied)&lt;/em&gt;&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/LEDHat/ledhat1.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/ledhat1.jpg&quot; srcset=&quot;            /assets/resized/480/ledhat1.jpg 480w,            /assets/resized/800/ledhat1.jpg 800w,            /assets/resized/1400/ledhat1.jpg 1400w,            /assets/resized/2048/ledhat1.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; A hat, bejewelled with 38 RGB LEDs&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;A hat, bejewelled with 38 RGB LEDs&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Is this thing on..?  It’s been a while since I’ve written one of these.&lt;/p&gt;

&lt;p&gt;So, the hat.  It’s been on the writeup pile for almost 6 years, nagging away.  Finally it’s its time to &lt;em&gt;shine&lt;/em&gt;!  &lt;em&gt;NO PUN ESCAPES&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Anyway, the hat.  It seemed like a good idea, and I even wore it out dancing.  I know, so cool.&lt;/p&gt;

&lt;p&gt;This hat had been through at least two fancy-dress events, and had a natty band aftermarket mod even before the LEDs.&lt;/p&gt;

&lt;p&gt;Long story short, got a hat, put a battery, ARM Cortex-M0 microcontroller, accelerometer in it and a strip of full-colour RGB LEDs around it.  The LEDs then react to movement, with an effect similar to a spirit level:  as it tilts, a spark travels to the highest point.  The spark rolls around, fading out nicely.&lt;/p&gt;

&lt;center&gt;
&lt;iframe width=&quot;640&quot; height=&quot;360&quot; src=&quot;https://www.youtube-nocookie.com/embed/YWFVsa3OCCk&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/center&gt;

&lt;h2 id=&quot;hardware&quot;&gt;Hardware&lt;/h2&gt;

&lt;p&gt;Pretty much full bodge-city, and made in a real rush before a party.&lt;/p&gt;

&lt;p&gt;Parts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Charity shop Trilby (someone’s going to correct me that this is not an ISO standard Trilby and is in fact a Westcountry Colonel Chap Trilby, or something).  Bugger it – a &lt;em&gt;hat&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;A WS2812B strip of 38 LEDs.  38 is what would fit around the hat.&lt;/li&gt;
  &lt;li&gt;Cheapo ADXL345 board.&lt;/li&gt;
  &lt;li&gt;Cheapo STM32F030 board (I &amp;lt;3 these boards! So power, such price wow).&lt;/li&gt;
  &lt;li&gt;Cheapo Li-Ion charging board and 5V step-up module all-in-one (AKA “powerbank board”).&lt;/li&gt;
  &lt;li&gt;Li-Ion flat/pouch-style battery.&lt;/li&gt;
  &lt;li&gt;Obviously some hot glue in there somewhere too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No schematic, sorry, it was quite freeform.  The battery is attached to charging board.  That connects to the rest of the system via a 0.1” header/disconnectable “power switch” cable.  The 5V power then directly feeds the LED strip, from Cortex-M0 board (which then generates 3.3V itself).&lt;/p&gt;

&lt;p&gt;The ADXL345 accelerometer is joined directly to the the STM32 board at what &lt;em&gt;was&lt;/em&gt; the UART header, which is configured for I&lt;sup&gt;2&lt;/sup&gt;C:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/LEDHat/boards.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/boards.jpg&quot; srcset=&quot;            /assets/resized/480/boards.jpg 480w,            /assets/resized/800/boards.jpg 800w,            /assets/resized/1400/boards.jpg 1400w,            /assets/resized/2048/boards.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The STM32 board is also stripped of any unnecessary or especially pointy parts, such as jumpers/pin headers, to make it as flat and pain-free as possible.&lt;/p&gt;

&lt;p&gt;The LED strip is bent into a ring and soldered back onto itself.  5V and ground are linked at the join, whereas DI enters at the join and DO is left hanging.  This is done for mechanical stability, and can’t hurt for power distribution too.  Here’s the ring in testing:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/LEDHat/test_run.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/test_run.jpg&quot; srcset=&quot;            /assets/resized/480/test_run.jpg 480w,            /assets/resized/800/test_run.jpg 800w,            /assets/resized/1400/test_run.jpg 1400w,            /assets/resized/2048/test_run.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The electronics are mounted in an antistatic bag (with a hole for the power “switch” header pins, wires, etc.), and the bag sewn into the top of the hat:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/LEDHat/inside.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/inside.jpg&quot; srcset=&quot;            /assets/resized/480/inside.jpg 480w,            /assets/resized/800/inside.jpg 800w,            /assets/resized/1400/inside.jpg 1400w,            /assets/resized/2048/inside.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The LED ring is attached via a small hole, and sewn on with periodic thread loops:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/LEDHat/closeup.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/closeup.jpg&quot; srcset=&quot;            /assets/resized/480/closeup.jpg 480w,            /assets/resized/800/closeup.jpg 800w,            /assets/resized/1400/closeup.jpg 1400w,            /assets/resized/2048/closeup.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;software&quot;&gt;Software&lt;/h2&gt;

&lt;p&gt;The firmware goes through an initial “which way is up?” calibration phase for the first few seconds, where it:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Lights a simple red dotted pattern to warn the user it’s about to sample which way is up, so put it on quick and stand as naturally as you can with such exciting technology on your head,&lt;/li&gt;
  &lt;li&gt;Lights a simple white dotted pattern, as it measures the “resting vector”, i.e. which way is up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This “resting vector” is thereafter used as the reference for determining whether the hat is tilted, and in which direction.&lt;/p&gt;

&lt;h3 id=&quot;tilt-direction-vectors&quot;&gt;Tilt direction vectors&lt;/h3&gt;

&lt;p&gt;The main loop’s job is to regulate the rate of LED updates, read the accelerometer, calculate a position to draw a bright spark “blob”, and update the LEDs.&lt;/p&gt;

&lt;p&gt;The accelerometer returns a 3D vector of a force; when not being externally accelerated, the vector represents the direction of Earth’s gravity, i.e. ‘down’.&lt;/p&gt;

&lt;h4 id=&quot;trigonometry-is-both-fun-and-useful&quot;&gt;Trigonometry is both fun and useful&lt;/h4&gt;

&lt;p&gt;Roughly, the calculations that are performed are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Relative to “vertical” (approximated by the resting vector), calculate the hat’s tilt in terms of angle of the measured vector to vertical, and its bearing to “12 o’clock” in the horizontal (XY) plane.&lt;/li&gt;
  &lt;li&gt;Convert the bearing of the vector into a position in the LED hoop.&lt;/li&gt;
  &lt;li&gt;Use the radius of the vector in the XY plane as a crude magnitude, scaling up the spark intensity for a larger tilt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All this talk of tilt and gravity vectors assumes the hat isn’t being moved (i.e. worn by a human).  It doesn’t correct for the fact that the hat is likely actually accelerating, rather than sitting static at a tilt but, hey, this is a hat with LEDs and not a rocket.  It is incorrect and looks good.&lt;/p&gt;

&lt;h3 id=&quot;floating-point&quot;&gt;Floating-point&lt;/h3&gt;

&lt;p&gt;I never use floating point in any of my embedded projects.  I’m a die-hard fixed-point kind of guy.  You know where you are with fixed point.&lt;/p&gt;

&lt;p&gt;Sooo anyway, the firmware uses the excellent Qfplib, from &lt;a href=&quot;https://www.quinapalus.com/qfplib-m0-tiny.html&quot;&gt;https://www.quinapalus.com/qfplib-m0-tiny.html&lt;/a&gt;.  This provides &lt;em&gt;tiny&lt;/em&gt; single-precision floating point routines, including the trigonometric routines I needed for the angle calculations.  Bizarrely, with an embedded hat on, it was way easier using gosh-darnit real FP than it was to do the trigonometry in fixed point.&lt;/p&gt;

&lt;h3 id=&quot;framebuffer&quot;&gt;Framebuffer&lt;/h3&gt;

&lt;p&gt;The framebuffer is only one dimensional :)  It’s a line of pixels representing the LEDs.  Blobs are drawn into the framebuffer at given position, and start off “bright”.  Every frame, the brightness of all pixels is decremented, giving a fade-out effect.  The code drawing blobs uses a pre-calculated colour look-up table, to give a cool white-blue-purple transition to the spark.&lt;/p&gt;

&lt;h3 id=&quot;driving-the-ws2812b-rgb-leds&quot;&gt;Driving the WS2812B RGB LEDs&lt;/h3&gt;

&lt;p&gt;The WS2812B LEDs take a 1-bit stream of data encoding 24b of RGB data, in a fixed-time frame using relative timing of rising/falling edges to give a 0 or 1 bit.&lt;/p&gt;

&lt;p&gt;The code uses a timer in PWM mode to output a 1/0 data bit, refilled from a neat little DMA routine.  Once a framebuffer has been drawn, the LEDs are refreshed.  For each pixel in the line, the brightness bits are converted into an array of timer values each representing a PWM period (therefore a 0-time or a 1-time).  A double-buffered DMA scheme is used to stream these values into the timer PWM register.&lt;/p&gt;

&lt;p&gt;This costs a few bytes of memory for the intermediate buffers, and is complicated, but has several advantages:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It’s completely flicker-free and largely immune to any other interrupt/DMA activity compared to bitbanging approaches.&lt;/li&gt;
  &lt;li&gt;It goes on in the background, freeing up CPU time to calculate the next frame.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Though the CPU is pretty fast, this allows LEDHat to update at over 100Hz, giving incredibly fluid motion.&lt;/p&gt;

&lt;h1 id=&quot;Resources&quot;&gt;Resources&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Firmware sourcecode:  &lt;a href=&quot;https://github.com/evansm7/LEDHat&quot;&gt;https://github.com/evansm7/LEDHat&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;/images/LEDHat/model.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/model.jpg&quot; srcset=&quot;            /assets/resized/480/model.jpg 480w,            /assets/resized/800/model.jpg 800w,            /assets/resized/1400/model.jpg 1400w,            /assets/resized/2048/model.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 15 Sep 2021 00:00:00 +0100</pubDate>
        <link>http://axio.ms//projects/2021/09/15/LEDHat.html</link>
        <guid isPermaLink="true">http://axio.ms//projects/2021/09/15/LEDHat.html</guid>
        
        <category>LEDs</category>
        
        <category>ARM</category>
        
        
        <category>projects</category>
        
      </item>
    
      <item>
        <title>Colourclock</title>
        <description>&lt;p&gt;&lt;em&gt;Original concept circa 2008, revisited 2012, hardware designed Feb 2014, firmware designed April-August 2014, project completed August 2014, installed in spare room in 2016, written up in March 2018 (jeeez…)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Acrylic, LEDs, ARM Cortex-M0 microcontroller.&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/colourclock/cc_front_wall1.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/cc_front_wall1.jpg&quot; srcset=&quot;            /assets/resized/480/cc_front_wall1.jpg 480w,            /assets/resized/800/cc_front_wall1.jpg 800w,            /assets/resized/1400/cc_front_wall1.jpg 1400w,            /assets/resized/2048/cc_front_wall1.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; Colourclock&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;Colourclock&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h1 id=&quot;idea&quot;&gt;Idea&lt;/h1&gt;

&lt;p&gt;This has been a long-running project.  My notebooks contain sketches of the idea from about 10 years back but I finally made PCBs at the beginning of 2014, finishing construction and firmware late 2014 – and it’s taken me about 4 years to write it up.&lt;/p&gt;

&lt;p&gt;The basic concept is 60 radial RGB LEDs in a circle, with light and colour representing analogue ‘hands’.  Where the hands cross, pleasing colours result.  Joy.  There is really nothing like the solid punchy colours of RGB LEDs!  Except, maybe, lasers.&lt;/p&gt;

&lt;p&gt;So if you’re colourblind, this might suck for you.  Don’t build one.  Sorry.  If not, build one!&lt;/p&gt;

&lt;h1 id=&quot;design-concept&quot;&gt;Design concept&lt;/h1&gt;

&lt;p&gt;The design is visually simple.  It consists of:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A ring-shaped PCB, with LEDs mounted around its circumference&lt;/li&gt;
  &lt;li&gt;The LED leads go out radially in the same plane as the PCB and support a ring of opal translucent acrylic, laser cut, glued onto the upper face of 60 5mm RGB LEDs&lt;/li&gt;
  &lt;li&gt;The outer ring sorta ‘floats’&lt;/li&gt;
  &lt;li&gt;A ring of matching acrylic, screwed onto the front of the PCB&lt;/li&gt;
  &lt;li&gt;A set of wire stand-offs, to hang the PCB and to hold it about 1.5cm from the wall&lt;/li&gt;
  &lt;li&gt;Three microswitches built into the stand-offs – you activate a switch by pushing the whole clock back onto the wall&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The 5V power supply is meant to be chased into the wall.  Most of the photos below date from previous test installations, but this video shows it in its final place, with wires buried in a (not yet painted) channel.&lt;/p&gt;

&lt;p&gt;There are a number of “faces” to display time in different styles.  A core part of the idea is that each R, G or B hand blends together with the others, mixing and making beaucoup de other colours.&lt;/p&gt;

&lt;center&gt;&lt;iframe width=&quot;640&quot; height=&quot;360&quot; src=&quot;https://www.youtube-nocookie.com/embed/l1uee6Bw5QA&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;&lt;/center&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;hardware-design-and-operation-pcb-layout&quot;&gt;Hardware design and operation, PCB layout&lt;/h1&gt;

&lt;p&gt;Okay, I want a circular PCB with LEDs shooting directly off the edge.  I don’t want 240 wires or LED strips.  Now what?&lt;/p&gt;

&lt;p&gt;Doing the maths showed that if I wanted 60x 5mm LEDs around the edge of a circular PCB, the circular edge would have to be about 110mm diameter at a minimum.  Well, Eagle is right out, I figure.  The free version won’t let me make a board above 80x100mm.&lt;/p&gt;

&lt;p&gt;So, I took a different approach by splitting an annular/ring PCB into four boards covering a 90° arc.  I designed one common arc-shaped PCB that is used four times, with the board being much smaller than Eagle’s limit.&lt;/p&gt;

&lt;p&gt;The second problem is I’m way too lazy to spend an hour typing a list of coordinates into Eagle to move LED footprints into place, so I wanted to automate placement of the LEDs and board outline.  However, to avoid having to mess with editing proprietary binary file formats, I needed CAD software with plain-text files.  Then I could just wield the Power of Perl…&lt;/p&gt;

&lt;p&gt;I looked at KiCad.  I looked at gEDA.  I looked at KiCad again.  (Still on my list to learn.)  I ended up abandoning hope, but when Eagle 6 came out (XML file format!), I was back in business.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/colourclock/cc_quadrants.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/cc_quadrants.jpg&quot; srcset=&quot;            /assets/resized/480/cc_quadrants.jpg 480w,            /assets/resized/800/cc_quadrants.jpg 800w,            /assets/resized/1400/cc_quadrants.jpg 1400w,            /assets/resized/2048/cc_quadrants.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/colourclock/cc_pcb_circle.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/cc_pcb_circle.jpg&quot; srcset=&quot;            /assets/resized/480/cc_pcb_circle.jpg 480w,            /assets/resized/800/cc_pcb_circle.jpg 800w,            /assets/resized/1400/cc_pcb_circle.jpg 1400w,            /assets/resized/2048/cc_pcb_circle.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;circuit-operation&quot;&gt;Circuit operation&lt;/h2&gt;

&lt;p&gt;There’s a bit that drives the LEDs, and a microcontroller that controls that bit.&lt;/p&gt;

&lt;p&gt;Since I was now designing a PCB for a quadrant of the clock, the most practical approach was to design a circuit to drive 15x RGB LEDs and replicate that four times, chaining quadrants together into a ring of 60.&lt;/p&gt;

&lt;p&gt;I chose a single-chip 16-bit constant-current driver, which contain a latch and shift register.  The first decision was:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Either drive 15 LEDs of one colour at a time and light R,G,B in sequence,&lt;/li&gt;
  &lt;li&gt;or, use one 16-bit driver to drive 5 RGB LEDs (15 LEDs) at a time and light each of 3 groups in sequence.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I didn’t want to risk ‘colour trails’ similar to what you get with a cheaper DLP projector.  (They have one DLP device with a &lt;em&gt;colour wheel&lt;/em&gt; to display a red frame, green frame, blue frame, etc.)&lt;/p&gt;

&lt;p&gt;To resolve this indecision, I prototyped it with a single LED.  Though option 2 was going to be harder to route, I chose to use 15 outputs of one 16-bit driver to illuminate one third of the RGB LEDs in a quadrant at a time – with three MOSFETs to select which third to illuminate.  It’s fairly subjective, but in this design the colours look better to my eyes and I sleep that bit better at night.&lt;/p&gt;

&lt;p&gt;In the assembled ring, each quadrant’s 16-bit driver is a shift register that chains through neighbour quadrants, making a 64-bit shift register driven by SPI.  There are three MOSFETs per quadrant and these are driven by three common gate signals.&lt;/p&gt;

&lt;p&gt;I took some time to choose a microcontroller, weighing up:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Price&lt;/li&gt;
  &lt;li&gt;DMA abilities to SPI for the shift registers&lt;/li&gt;
  &lt;li&gt;Desired colour depth and refresh rate, therefore requirements for SPI speed, CPU speed and RAM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A deeper colour depth (more PWM bits) influences how much CPU time is spent calculating display bitstreams, how many bits need to be shifted out during a frame, and how much RAM is used for buffering it.  With a non-infinite SPI clock rate, you trade off refresh rate for colour depth.&lt;/p&gt;

&lt;p&gt;I ended up with the STM32F051C6T6, a 48MHz ARM Cortex-M0 with 32KB of flash and 4KB of RAM.  This is a really cool microcontroller, especially when using DMA.  Though it doesn’t sound worth it, the firmware uses DMA to clock out the 64 bits of state for one scan of the LEDs using DMA, because then the code doesn’t have to busy-wait on the SPI send register and can save a significant amount of CPU time.  (It’s triggering the SPI output in an interrupt, too, so avoiding busy-wait is especially important.)&lt;/p&gt;

&lt;p&gt;The display drivers also have a handy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/OE&lt;/code&gt; output enable pin, which acts on all outputs.  I’ve used this to provide a global brightness control by wiring them all to a fast timer PWM output on the STM32.  Whilst the CPU is generating scan data to PWM the LED driver pins individually, the output enable PWM runs much faster and further modulates the LED drive.  For example, if the CPU through SPI says that one LED is on for 1 out of 64 ticks, a 20% duty PWM on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/OE&lt;/code&gt; pin makes it 20% as bright again.&lt;/p&gt;

&lt;p&gt;The microcontroller uses its internal oscillator and PLL for general operation, freeing the external oscillator for use with a 32KHz watch crystal to drive the RTC.&lt;/p&gt;

&lt;p&gt;Some of the STM32’s ADC input pins are broken out onto a header for &lt;em&gt;interesting sensors&lt;/em&gt;.  As an experiment, I built a ring-shaped capacitive touch sensor that plugs on top – the idea was that you could ‘dial’ to set the time.  It worked OK, but the practical sense distance (to a fingertip) is too low to work through 3mm acrylic – so the final design uses bog standard switches for input.  Another analog input measures ambient light levels with an LDR.&lt;/p&gt;

&lt;p&gt;The rest of the circuit is LDO and decoupling.&lt;/p&gt;

&lt;p&gt;View a PDF of the &lt;a href=&quot;/downloads/colourclock_schematic_revA.pdf&quot;&gt;schematic&lt;/a&gt;.  There’s one annoying error on the RevA board:  I brought out the SWD pins to a 4-pin header for programming, along with ground and Vcc.  Instead of Vcc, I should’ve wired &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/RESET&lt;/code&gt; (which is useful for programming).  I tend to bring out things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/RESET&lt;/code&gt; to test pads even if there’s an on-board reset circuit, so for programming this can be poked with a pin, but it’s not as convenient as just one connector.&lt;/p&gt;

&lt;h2 id=&quot;board-layout&quot;&gt;Board layout&lt;/h2&gt;

&lt;p&gt;As mentioned above, I automated the initial placement of the LEDs and board outline by generating XML into the Eagle .brd file using a perl script.  Rather than being a sledgehammer for a nut, this turned out to be really flexible as I could very easily re-place the LEDs on a different radius, or with different spacing, to tinker with the overall size whilst routing the board.&lt;/p&gt;

&lt;p&gt;The boards are all populated in an identical way except for the first board which is also populated with the LDO regulator and microcontroller/crystal.  Each board joins to its neighbours with large pads that route common power, MOSFET gate signals, clock and latch signals, plus pass the dataOut of the shift register on one board into the next board’s dataIn.&lt;/p&gt;

&lt;p&gt;I managed to route the signals such that the board could be an arc shape rather than a full pie-wedge, leading to ring-shaped megaPCB when assembled.  It’s still a bit wide for my taste, but it was the best I could do with a 2-layer board.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/colourclock/cc-board.png&quot;&gt; 
    &lt;img src=&quot;/assets/resized/800/cc-board.png&quot; srcset=&quot;            /assets/resized/480/cc-board.png 480w,            /assets/resized/800/cc-board.png 800w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See the &lt;a href=&quot;#Resources&quot;&gt;Resources&lt;/a&gt; section for CAD files.&lt;/p&gt;

&lt;h2 id=&quot;software&quot;&gt;Software&lt;/h2&gt;

&lt;p&gt;With a powerful uC and 18-bit colour, there’s good potential for smooth transitions and nice blending.  I didn’t end up doing any really busy ‘sparkle’ or watery/physics-style effects in the end, opting just for simple variants on red for hours, green for minutes, blue for seconds.  I tried some more busy effects, but it was hugely annoying to look at – less is more.&lt;/p&gt;

&lt;p&gt;The firmware implements PWM at 6 bits per channel (262K colours) by flattening RGB framebuffer values into a raw bitstream of ‘on/off’ values that is streamed through the shift registers at 24MHz.  At this bit depth, it refreshes the display at 300Hz (meaning it completes a full 6-bit PWM cycle for all ‘thirds’ at 300Hz).  Altogether, this uses around 29% of the CPU time for display.&lt;/p&gt;

&lt;p&gt;This flattening uses a lot of memory.  64 levels per channel for 3 channels for 60 LEDs equals 1440 bytes of buffering to flatten out the framebuffer into a format that the DMA/timer IRQ handlers can just spit out through SPI, and then that’s double buffered.  (Note that it doubles for each extra bit of colour depth!)   The alternative of calculating the PWM on/off state of each LED in the IRQs sounds reasonable at first, i.e. perform the conversion piecemeal instead of doing it all upfront every framebuffer update.  However, it saves a &lt;em&gt;lot&lt;/em&gt; of CPU time to burn the RAM on a buffer and calculate it only when the framebuffer is updated, especially since the framebuffer update/animation rate is usually way less than 300Hz.  There is a better alternative again still:  Binary Code Modulation would allow the IRQ frequency to be drastically reduced to 6 per frame (with a variable/exponential time delay between IRQs) instead of 2&lt;sup&gt;6&lt;/sup&gt;, and would save most of that burned memory – I used PWM here to keep it simple, but have used BCM in other LED projects.  (Watch this space.)&lt;/p&gt;

&lt;p&gt;Reality bites:  I found the MOSFETs don’t turn off that nicely, so some ‘thirds’ bleed into the next-displayed ‘third’.  This is worked around by simply adding some dead-time to the scan, so one FET can settle and switch off before the next scan data arrives.  This negatively affects the overall refresh rate, but significantly reduces the bleed between unrelated LEDs.&lt;/p&gt;

&lt;p&gt;I didn’t bother with gamma correction for the framebuffer output, since the sine-wavey gradients written into the framebuffer had a similar effect.&lt;/p&gt;

&lt;p&gt;The 6 bit colour looks good – good enough, at least – but the 300Hz update looks wonderful!&lt;/p&gt;

&lt;h3 id=&quot;scaling-brightness&quot;&gt;Scaling brightness&lt;/h3&gt;

&lt;p&gt;LEDs are punishingly bright.  Even with just 60 of them, the clock is enough to light up the room a fair bit.  The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/OE&lt;/code&gt; signal is PWM’d, as described above, and the firmware controls this using the LDR’s brightness measurement.  There’s also almost zero CPU overhead to this dimming technique, because it’s applied by a free-running timer PWM output.  The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/OE&lt;/code&gt; signal PWM rate is about 93KHz (faster than the colour-generating PWM) so it doesn’t cause visible ‘beats’ in the output brightness.&lt;/p&gt;

&lt;p&gt;There’s another UI mode included in the firmware to set upper/lower brightness thresholds, to adjust the LDR response to a room.  These parameters are stored in flash using a trivial journal/log technique to reduce the amount of flash erase wear and tear on an update.  Given I’ve changed this exactly twice, it’s overkill, but still a fun thing to try.&lt;/p&gt;

&lt;h3 id=&quot;simulation-build&quot;&gt;Simulation build&lt;/h3&gt;

&lt;p&gt;Tinkering with the display styles took a lot of hack-compile-test cycles, which was a bit slow to do on real hardware.  (I also wanted to start before I’d finished soldering.)  So, I added a build target to build the firmware as a regular Linux/MacOS binary, swapping the LED drive and STM32-specific code with an SDL/OpenGL front-end:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/colourclock/Testbuild_screengrab4.png&quot;&gt; 
    &lt;img src=&quot;/assets/resized/800/Testbuild_screengrab4.png&quot; srcset=&quot;            /assets/resized/480/Testbuild_screengrab4.png 480w,            /assets/resized/800/Testbuild_screengrab4.png 800w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve used this technique in other microcontroller/embedded projects and it’s been really useful.  Give a little thought to abstraction and interfaces, and you can build a harness to test/run/develop your firmware natively on your laptop.&lt;/p&gt;

&lt;h1 id=&quot;Resources&quot;&gt;Resources&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Eagle design files for schematic and PCB layout:  &lt;a href=&quot;https://github.com/evansm7/colourclock-hw&quot;&gt;https://github.com/evansm7/colourclock-hw&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Firmware sourcecode:  &lt;a href=&quot;https://github.com/evansm7/colourclock-fw&quot;&gt;https://github.com/evansm7/colourclock-fw&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;gallery&quot;&gt;Gallery&lt;/h1&gt;

&lt;div class=&quot;gallery&quot;&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_oblique_wall1.jpg&quot; title=&quot;Colourclock, test mount&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_oblique_wall1-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_oblique_wall1-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Colourclock, test mount&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_oblique_wall2.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_oblique_wall2-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_oblique_wall2-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_wall_sm.jpg&quot; title=&quot;Icy minimalist time...&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_wall_sm-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_wall_sm-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Icy minimalist time...&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_wall_trail.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_wall_trail-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_wall_trail-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_wall_trail2.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_wall_trail2-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_wall_trail2-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_in_situ.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_in_situ-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_in_situ-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_wall_new.jpg&quot; title=&quot;New room, pre-decoration&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_wall_new-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_wall_new-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;New room, pre-decoration&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_bending_jig.jpg&quot; title=&quot;LEDs bent to same dims&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_bending_jig-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_bending_jig-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;LEDs bent to same dims&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_led_mounting.jpg&quot; title=&quot;Positioning LEDs against pattern&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_led_mounting-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_led_mounting-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Positioning LEDs against pattern&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_leds_in.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_leds_in-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_leds_in-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_pcb_done.jpg&quot; title=&quot;All soldered&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_pcb_done-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_pcb_done-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;All soldered&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_soldered_done.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_soldered_done-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_soldered_done-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_feet1.jpg&quot; title=&quot;Making standoffs/feet out of wire&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_feet1-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_feet1-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Making standoffs/feet out of wire&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_feet2.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_feet2-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_feet2-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_feet3.jpg&quot; title=&quot;Feet against wall, with switches&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_feet3-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_feet3-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Feet against wall, with switches&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_rear1.jpg&quot; title=&quot;Rear assembled, 3x switches and hoop&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_rear1-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_rear1-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Rear assembled, 3x switches and hoop&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_rear_sw1.jpg&quot; title=&quot;View in back&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_rear_sw1-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_rear_sw1-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;View in back&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_rear_sw2.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_rear_sw2-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_rear_sw2-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_back_complete.jpg&quot; title=&quot;Some bodged-on extra decoupling...&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_back_complete-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_back_complete-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Some bodged-on extra decoupling...&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_test1.jpg&quot; title=&quot;First test, no smoke!&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_test1-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_test1-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;First test, no smoke!&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_test2.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_test2-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_test2-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_test3.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_test3-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_test3-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_test4.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_test4-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_test4-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_test5.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_test5-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_test5-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_test6.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_test6-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_test6-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_test_mix.jpg&quot; title=&quot;Exotic colour mixes&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_test_mix-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_test_mix-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Exotic colour mixes&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_psychedelic.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_psychedelic-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_psychedelic-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_side.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_side-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_side-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_darktest1.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_darktest1-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_darktest1-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_darktest2.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_darktest2-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_darktest2-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_darktest3.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_darktest3-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_darktest3-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_intensity_levels.jpg&quot; title=&quot;Test of 64-level brightness&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_intensity_levels-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_intensity_levels-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Test of 64-level brightness&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_acrylic_test1.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_acrylic_test1-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_acrylic_test1-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_acrylic_test2.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_acrylic_test2-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_acrylic_test2-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_capsense0.jpg&quot; title=&quot;Capacitive touch sensor&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_capsense0-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_capsense0-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Capacitive touch sensor&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_capsense1.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_capsense1-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_capsense1-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;cc &quot; href=&quot;/images/pg/colourclock/cc_capsense2.jpg&quot; title=&quot;Nice idea, but wasn&apos;t sensitive enough.&quot;&gt;&lt;img src=&quot;/images/pg/colourclock/cc_capsense2-thumb.jpg&quot; srcset=&quot;/images/pg/colourclock/cc_capsense2-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Nice idea, but wasn&apos;t sensitive enough.&lt;/dd&gt;&lt;/dl&gt;

&lt;br style=&quot;clear: both;&quot; /&gt;

&lt;/div&gt;
</description>
        <pubDate>Mon, 19 Mar 2018 00:00:00 +0000</pubDate>
        <link>http://axio.ms//projects/2018/03/19/Colourclock.html</link>
        <guid isPermaLink="true">http://axio.ms//projects/2018/03/19/Colourclock.html</guid>
        
        <category>LEDs</category>
        
        <category>ARM</category>
        
        
        <category>projects</category>
        
      </item>
    
      <item>
        <title>Pickle plus mains</title>
        <description>&lt;p&gt;Back in February 2014, it was a cold (but dry) winter’s night and we decided to warm our hearts with some science.&lt;/p&gt;

&lt;p&gt;Our experiment was to observe the conduction of electricity through a pickle, using salt ions.  We connected a pickle to the mains supply.  Pickles have a strong table salt content, which means a lot of sodium.  Sodium, when suitably excited by electricity, will emit light with peaks around 589nm.  This is the distinctive “sodium yellow” that is familiar from street lighting.&lt;/p&gt;

&lt;p&gt;I won’t go on about excitation energy and ionisation potential (I last studied chemistry a cool 20 years ago…) but suffice it to say that mains voltage will ionise the hell out of many things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Caution!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;I have to point out that doing this kind of thing is dangerous, especially if you don’t take strong safety measures.  Mains electricity is NOT a toy; don’t go near it unless you are able to do so safely.  I will not be held responsible if you hurt yourself!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;experimental-setup&quot;&gt;Experimental setup&lt;/h2&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/pickle/pickle_apparatus.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/pickle_apparatus.jpg&quot; srcset=&quot;            /assets/resized/480/pickle_apparatus.jpg 480w,            /assets/resized/800/pickle_apparatus.jpg 800w,            /assets/resized/1400/pickle_apparatus.jpg 1400w,            /assets/resized/2048/pickle_apparatus.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; The experimental setup&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;The experimental setup&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;… okay, let me explain:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We selected a dry area, away from anything conductive or flammable.  We chose a concrete patio in the garden, with plenty of space.&lt;/li&gt;
  &lt;li&gt;We placed the pickle on a garden chair with a layer of safety cardboard.&lt;/li&gt;
  &lt;li&gt;The mains power is obviously a dangerous part.  Using a long extension lead, we separated ourselves from the experiment by several metres.  This allowed us to start/stop the current from a distance.&lt;/li&gt;
  &lt;li&gt;I didn’t do this alone and ensured my experiment partner knew what to do in case of an electric shock.  We didn’t rush and we always made sure to verbally cross-check that power is off when checking/re-arranging the connections.&lt;/li&gt;
  &lt;li&gt;We inserted two iron nails into the ends of the pickle as electrodes.  The electrodes were about 1cm deep in the pickle, separated by about 3cm.  We connected the electrodes to an IEC mains cable using a set of crocodile clip/cables.&lt;/li&gt;
  &lt;li&gt;We tied down the electrical cables, so they were limited in what they could touch in the case of them sparking and jumping.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We prepared for the following:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The pickle is going to fizz &amp;amp; spit, and this will move the electrodes
    &lt;ul&gt;
      &lt;li&gt;The electrodes might touch together – if that happens, it will be pretty bad and can send sharp hot metal flying.&lt;/li&gt;
      &lt;li&gt;Or, they might fall or fly elsewhere, and touch something else.  We made sure that if that were to happen, that something else couldn’t be a living thing.  (Cat locked indoors 😿)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Fire 💥
    &lt;ul&gt;
      &lt;li&gt;We kept fire-extinguishing apparatus nearby (extra points for CO&lt;sub&gt;2&lt;/sub&gt; extinguishers for electrical fires)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;A nasty stink 🤢&lt;/li&gt;
  &lt;li&gt;Cool pictures, using a long zoom lens so I didn’t have to go lean over it while it fried.  :-)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;lighting-it-up&quot;&gt;Lighting it up&lt;/h2&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/pickle/pickle_light1.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/pickle_light1.jpg&quot; srcset=&quot;            /assets/resized/480/pickle_light1.jpg 480w,            /assets/resized/800/pickle_light1.jpg 800w,            /assets/resized/1400/pickle_light1.jpg 1400w,            /assets/resized/2048/pickle_light1.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; Pickle glow&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;Pickle glow&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;We retreated and powered it up for about two seconds.  A fizzing, buzzing and lovely yellow glow resulted!  With a few more tries, we got it glowing for a few seconds at a time.
What would happen is that the electrodes would burn a hole that would then dry up and lose contact, needing rearrangement.&lt;/p&gt;

&lt;p&gt;Note that the pickle is glowing only on one end, around one electrode.  Given that mains is AC, there shouldn’t be a distinction between either electrode (there is no overall “anode” or “cathode”).  Wikipedia says that this phenomenon is a mystery (at least, a mystery to me and the Wikipedia article author, I guess).&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/pickle/pickle_light2.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/pickle_light2.jpg&quot; srcset=&quot;            /assets/resized/480/pickle_light2.jpg 480w,            /assets/resized/800/pickle_light2.jpg 800w,            /assets/resized/1400/pickle_light2.jpg 1400w,            /assets/resized/2048/pickle_light2.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;&lt;/figcaption&gt;&lt;/figure&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/pickle/pickle_light3.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/1400/pickle_light3.jpg&quot; srcset=&quot;            /assets/resized/480/pickle_light3.jpg 480w,            /assets/resized/800/pickle_light3.jpg 800w,            /assets/resized/1400/pickle_light3.jpg 1400w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;In some labs, a crispy and burned pickle is a delicacy amongst chemists.&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/pickle/pickle_burnt.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/pickle_burnt.jpg&quot; srcset=&quot;            /assets/resized/480/pickle_burnt.jpg 480w,            /assets/resized/800/pickle_burnt.jpg 800w,            /assets/resized/1400/pickle_burnt.jpg 1400w,            /assets/resized/2048/pickle_burnt.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; An electrically-sautéed pickle, yum!&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;An electrically-sautéed pickle, yum!&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Gross.&lt;/p&gt;

&lt;h1 id=&quot;references&quot;&gt;References&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Glowing_pickle_demonstration&quot;&gt;https://en.wikipedia.org/wiki/Glowing_pickle_demonstration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Sat, 17 Mar 2018 00:00:00 +0000</pubDate>
        <link>http://axio.ms//blog/2018/03/17/Pickle.html</link>
        <guid isPermaLink="true">http://axio.ms//blog/2018/03/17/Pickle.html</guid>
        
        <category>Dangerous</category>
        
        <category>Science</category>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>Maths is Fun and Useful but Lasers Are More So</title>
        <description>&lt;p&gt;Way back when, I made a cool but basic paper sculpture (in &lt;a href=&quot;/projects/2009/04/18/Maths-is-fun.html&quot;&gt;Maths is Fun and Useful&lt;/a&gt;) by hand, using a perl script for the shape template.  For cultural reasons, I wanted to make a gift out of paper and it had to be Heaps Good.&lt;/p&gt;

&lt;p&gt;I had finally got access to a laser cutter (omgomg) and this was my chance to build something similar but doublegood.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MATHS IS FUN AND USEFUL, NOW IN ULTRAHD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/lasersMoreFun/paper_graph0.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/paper_graph0.jpg&quot; srcset=&quot;            /assets/resized/480/paper_graph0.jpg 480w,            /assets/resized/800/paper_graph0.jpg 800w,            /assets/resized/1400/paper_graph0.jpg 1400w,            /assets/resized/2048/paper_graph0.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; A 3D graph made out of 240 slices of paper&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;A 3D graph made out of 240 slices of paper&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;If you’re wondering, the function &lt;em&gt;z = f(x,y)&lt;/em&gt; (for a given position on the horizontal plane) is given by:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;z = cos(r&lt;sup&gt;1.6&lt;/sup&gt; ⋅ (1 + (cos(π ⋅ r/√50) ) )&lt;/em&gt;, where &lt;em&gt;r = √(x&lt;sup&gt;2&lt;/sup&gt; + y&lt;sup&gt;2&lt;/sup&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;(Sorry, no MathJax and LaTeX equations here.)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/lasersMoreFun/paper_graph1.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/paper_graph1.jpg&quot; srcset=&quot;            /assets/resized/480/paper_graph1.jpg 480w,            /assets/resized/800/paper_graph1.jpg 800w,            /assets/resized/1400/paper_graph1.jpg 1400w,            /assets/resized/2048/paper_graph1.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first, I’d planned to use bolts to hold it all together.  The script places two holes for this; however, M3 bolts would look too chunky on this size of piece, so I cut 2mm holes.  But, M2 bolts don’t generally come in 55mm+ lengths.&lt;/p&gt;

&lt;p&gt;OK, maybe thread some 2mm rod?  My local hardware shoppe sells die, but jeez they’re expensive.&lt;/p&gt;

&lt;p&gt;OK, maybe I’ll glue it with Pritt-Stick?  Mine ran out.  I’m glad, as with 240 slices this would’ve taken about a year and would’ve been very messy.  Plus, the glue volume would separate the slices and it probably wouldn’t have sat so flat.&lt;/p&gt;

&lt;p&gt;I finally settled on two sections of 2mm rod (bits of wire coathanger), whose ends were crimped to keep the stack in place.  First, I straightened the stack against an engineer’s square, inserted the rods (with one end already crimped), then clamped the paper stack together and crimped the remaining open wire ends.  I just crushed the ends in a vice to flatten them.&lt;/p&gt;

&lt;p&gt;The final piece is much more stable than I thought it’d be without adhesive.  As it’s fairly tightly-packed, there’s a ton of friction between the paper slices.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/lasersMoreFun/paper_graph2.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/paper_graph2.jpg&quot; srcset=&quot;            /assets/resized/480/paper_graph2.jpg 480w,            /assets/resized/800/paper_graph2.jpg 800w,            /assets/resized/1400/paper_graph2.jpg 1400w,            /assets/resized/2048/paper_graph2.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because I cleverly predicted the slices would get badly mixed up during construction, the script also cuts notches in the bottom of each slice with the slice’s sequence number in binary.  This means that it’s possible to re-order them once they fall on the floor (I don’t like jigsaw puzzles).  It also creates a cool pattern on the bottom of the finished object:&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/lasersMoreFun/paper_graph3.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/1400/paper_graph3.jpg&quot; srcset=&quot;            /assets/resized/480/paper_graph3.jpg 480w,            /assets/resized/800/paper_graph3.jpg 800w,            /assets/resized/1400/paper_graph3.jpg 1400w,    &quot; width=&quot;100%&quot; alt=&quot; Binary slice sequence number&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;Binary slice sequence number&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&quot;get-de-script&quot;&gt;Get de script&lt;/h2&gt;

&lt;p&gt;This marvel of perl engineering is available on Github, &lt;a href=&quot;https://github.com/evansm7/graphslicer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An SVG generated from this script, with the parameters used to build this model, is available &lt;a href=&quot;/downloads/example-3d-graphslices-50x50-240s.svg&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please send pictures of interesting functions made out of glass, acrylic, stone, etc.!&lt;/p&gt;
</description>
        <pubDate>Sat, 12 Aug 2017 00:00:00 +0100</pubDate>
        <link>http://axio.ms//projects/2017/08/12/Lasers-more-fun.html</link>
        <guid isPermaLink="true">http://axio.ms//projects/2017/08/12/Lasers-more-fun.html</guid>
        
        <category>SVG</category>
        
        <category>Perl</category>
        
        
        <category>projects</category>
        
      </item>
    
      <item>
        <title>Internet of kitchen lighting, with OSC</title>
        <description>&lt;p&gt;This project was completed in June 2015.  Wow, is it 2017 already?&lt;/p&gt;

&lt;p&gt;In this post, I’d like to say that I wrote a useful little bit of software and built up a crappy hack to demonstrate it but, secretly, the crappy hack came first and I’ve retroactively found something vaguely useful in it.  Let’s go:&lt;/p&gt;

&lt;h2 id=&quot;let-there-be-lights&quot;&gt;Let there be lights&lt;/h2&gt;

&lt;p&gt;We moved house and needed some temporary* under-cupboard lighting for our transitional* 1970s kitchen.  Why buy purpose-designed, expensive and great-looking strip lighting when I can instead hack them together myself using hot glue and scrap wire?&lt;/p&gt;

&lt;p&gt;Then, I can value-add by using an ESP8266 module to make the lights remote-controllable!&lt;/p&gt;

&lt;p&gt;I chose &lt;a href=&quot;http://opensoundcontrol.org/introduction-osc&quot;&gt;Open Sound Control&lt;/a&gt; (OSC) for this, which is traditionally used for media signalling, i.e. ‘a better MIDI, over the network’.  I don’t know why I did this instead of using something like Blynk or an HTTP-based control page.&lt;/p&gt;

&lt;p&gt;At least I can control my kitchen lights from Ableton Live if the need arises.  That need has not arisen so far.&lt;/p&gt;

&lt;p&gt;This project is a bit hacky for kitchen lighting, but the OSC+ESP8266 portion might be useful for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Controlling real actuators from music sequencers (e.g. solenoids! Boom! Tish!)&lt;/li&gt;
  &lt;li&gt;Cheap stage lighting/effects using ESP8266s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how the majestic and now well-worn kitchen looks with the new lighting:&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/OSC-lights/workbench_lit.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/workbench_lit.jpg&quot; srcset=&quot;            /assets/resized/480/workbench_lit.jpg 480w,            /assets/resized/800/workbench_lit.jpg 800w,            /assets/resized/1400/workbench_lit.jpg 1400w,            /assets/resized/2048/workbench_lit.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; The 1970s kitchen countertop is now well-lit...&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;The 1970s kitchen countertop is now well-lit...&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2 id=&quot;leds&quot;&gt;LEDs&lt;/h2&gt;

&lt;p&gt;I found some Chip-on-Board (CoB) warm white &lt;a href=&quot;http://www.dx.com/p/diy-3w-3000k-285-315lm-cob-led-rectangle-strip-dc-12-14v-142034&quot;&gt;LED strips on DealExtreme&lt;/a&gt;
for a couple of quid each.  They’re very thin and can therefore be stuck/screwed to the underside of the cupboards without casting light sideways/into the eyes of the person using sharp kitchen implements.  I spread out four of them to provide a wide corridor of light.  They’re spaced about 50cm apart and the illumination is pretty sharp and even.&lt;/p&gt;

&lt;p&gt;They’re quoted to be 315 lumens at 3W, which is almost certainly lies except for the ‘3W’ part, but with four they’re plenty bright.&lt;/p&gt;

&lt;p&gt;The four LED strips are daisychained from the controller box, connected in parallel.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/OSC-lights/led_strip.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/led_strip.jpg&quot; srcset=&quot;            /assets/resized/480/led_strip.jpg 480w,            /assets/resized/800/led_strip.jpg 800w,            /assets/resized/1400/led_strip.jpg 1400w,            /assets/resized/2048/led_strip.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; &quot; /&gt;
 &lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;detour--leds-in-parallel&quot;&gt;Detour:  LEDs in parallel&lt;/h3&gt;

&lt;p&gt;Generally, connecting LEDs in parallel is Not As Good As It Sounds.  An LED must be current-limited and when several are put in parallel there is the opportunity for one in the chain to draw slightly more than 1/N of the total supply – LEDs aren’t all identical.  Now if that LED is close to the limit anyway, thermal runaway can occur:  the LED heats up as it draws more current, causing &lt;em&gt;Vf&lt;/em&gt; to drop and therefore &lt;em&gt;more&lt;/em&gt; current to flow, causing it to heat up further, then &lt;em&gt;BANG&lt;/em&gt;.  Now, there’s N-1 LEDs and the total current to the remaining set is that bit higher and the process can continue.&lt;/p&gt;

&lt;p&gt;Each CoB LED strip is running about 10V at about 0.3A.  The forward voltage of a white LED is usually over 3V, so I believe these strips are parallel sets of 3 LEDs in series.  As there’s no series resistor here, how does this work?  Well, where LEDs are used in parallel configurations they are carefully characterised and matched to make sure no imbalance occurs in the parallel circuit.&lt;/p&gt;

&lt;p&gt;I estimate about 30 die per strip (so ~10 series chains in parallel) and I’m connecting four strips in parallel so there are at least 40 chains that need to be “perfectly matched”.&lt;/p&gt;

&lt;p&gt;Given that these strips were £2 from DealExtreme, I’m not confident the LED die have been carefully characterised and matched within a strip let alone across multiple strips.  They’re also very bright, so I have a fair leeway to under-drive them and stay cool.&lt;/p&gt;

&lt;p&gt;The benefit of waiting 18 months to do a project writeup is I can comment that they remain perfectly reliable over time.  :-)&lt;/p&gt;

&lt;p&gt;The LEDs are driven from a 12V power brick (rated for 50W), using a &lt;a href=&quot;http://www.dx.com/p/buck-constant-voltage-constant-current-module-blue-dc-dc-5a-239099&quot;&gt;constant-current buck converter&lt;/a&gt; to step down the 12V but maintain the chosen current.  The converter board has a convenient input for on/off/PWM control of the output.&lt;/p&gt;

&lt;h2 id=&quot;hardware&quot;&gt;Hardware&lt;/h2&gt;

&lt;p&gt;Assembled in a nice little box, the controller hardware consists of the CC power board, an ESP8266, hot glue (I wasn’t joking about that part), a cap and a 3.3V regulator:&lt;/p&gt;

&lt;figure&gt;
 &lt;a href=&quot;/images/OSC-lights/controller_box.jpg&quot;&gt; 
    &lt;img src=&quot;/assets/resized/2048/controller_box.jpg&quot; srcset=&quot;            /assets/resized/480/controller_box.jpg 480w,            /assets/resized/800/controller_box.jpg 800w,            /assets/resized/1400/controller_box.jpg 1400w,            /assets/resized/2048/controller_box.jpg 2048w,    &quot; width=&quot;100%&quot; alt=&quot; Inside of controller box&quot; /&gt;
 &lt;/a&gt; 
&lt;figcaption&gt;Inside of controller box&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Yes, that heatsink is virtually useless inside a sealed box.  Well, it spreads the heat a little.  The board doesn’t really get warm enough to worry though.&lt;/p&gt;

&lt;p&gt;There’s a momentary button on the outside for the old-fashioned way of switching lights on and off, i.e. by pressing a button on the thing you wish to control.&lt;/p&gt;

&lt;h2 id=&quot;software&quot;&gt;Software&lt;/h2&gt;

&lt;p&gt;You’ll be amazed that writing the software took a lot longer than building the hardware (given the apparent quality of both…).&lt;/p&gt;

&lt;p&gt;The ESP8266 firmware is available via my (now-resurrected) Github:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/evansm7/OSC-lights&quot;&gt;github.com/evansm7/OSC-lights&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It builds against the excellent &lt;a href=&quot;https://github.com/SmingHub/Sming&quot;&gt;Sming&lt;/a&gt; framework.  I recommend Sming, it’s nicer than Arduino and has a bunch of useful drivers and a good Out-of-box experience.&lt;/p&gt;

&lt;p&gt;The OSC parser is hooked up to a callback function:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;osc_reg_handler(&quot;/crossfader&quot;, cb_cf);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, &lt;em&gt;cb_cf&lt;/em&gt; is called when an OSC message is sent to that address, with float/int arguments unpacked.&lt;/p&gt;

&lt;p&gt;I’m using an iOS app called &lt;a href=&quot;https://itunes.apple.com/gb/app/control-osc-+-midi/id413224747&quot;&gt;Control&lt;/a&gt;, which is pretty cool.  It outputs OSC messages from user-definable UI layouts.  The ‘crossfader’ address is emitted by the default ‘DJ’ layout, which has a draggable bar and buttons which control the lights.  This saves defining my own UI.&lt;/p&gt;

&lt;p&gt;The OSC parser is pretty easy to use and hopefully will be useful for very basic on/off/value-type messages.  Aside from a couple of debug statements, it isn’t tied to Sming, or ESP8266.&lt;/p&gt;

&lt;h2 id=&quot;closing-remarkz&quot;&gt;Closing remarkz&lt;/h2&gt;

&lt;p&gt;Yes, the cabinets need a paint.  Or to be different, new cabinets.&lt;/p&gt;

&lt;p&gt;Oh, and about security.  Unfortunately, my kitchen countertop lights aren’t cloud-controlled and don’t even have a route to the internet.  But that’s OK, as I can pretend we’re switching the lights on and off when we’re away from home and it’s all the same &lt;em&gt;because we’re not there anyway&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://twitter.com/internetofshit&quot;&gt;@internetofshit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;images&quot;&gt;Images&lt;/h1&gt;

&lt;div class=&quot;gallery&quot;&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;osc-lights &quot; href=&quot;/images/pg/OSC-lights/Control.png&quot; title=&quot;&apos;Control&apos; UI&quot;&gt;&lt;img src=&quot;/images/pg/OSC-lights/Control-thumb.png&quot; srcset=&quot;/images/pg/OSC-lights/Control-2x-thumb.png 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&apos;Control&apos; UI&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;osc-lights &quot; href=&quot;/images/pg/OSC-lights/controller_external.jpg&quot; title=&quot;Control box on/off button&quot;&gt;&lt;img src=&quot;/images/pg/OSC-lights/controller_external-thumb.jpg&quot; srcset=&quot;/images/pg/OSC-lights/controller_external-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Control box on/off button&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;osc-lights &quot; href=&quot;/images/pg/OSC-lights/controller_inside.jpg&quot; title=&quot;Closeup inside control box&quot;&gt;&lt;img src=&quot;/images/pg/OSC-lights/controller_inside-thumb.jpg&quot; srcset=&quot;/images/pg/OSC-lights/controller_inside-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Closeup inside control box&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;osc-lights &quot; href=&quot;/images/pg/OSC-lights/controller_mounted.jpg&quot; title=&quot;Bogan wiring&quot;&gt;&lt;img src=&quot;/images/pg/OSC-lights/controller_mounted-thumb.jpg&quot; srcset=&quot;/images/pg/OSC-lights/controller_mounted-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Bogan wiring&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;osc-lights &quot; href=&quot;/images/pg/OSC-lights/strips.jpg&quot; title=&quot;&quot;&gt;&lt;img src=&quot;/images/pg/OSC-lights/strips-thumb.jpg&quot; srcset=&quot;/images/pg/OSC-lights/strips-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;osc-lights &quot; href=&quot;/images/pg/OSC-lights/bright_overhead.jpg&quot; title=&quot;Bright!&quot;&gt;&lt;img src=&quot;/images/pg/OSC-lights/bright_overhead-thumb.jpg&quot; srcset=&quot;/images/pg/OSC-lights/bright_overhead-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Bright!&lt;/dd&gt;&lt;/dl&gt;

&lt;dl class=&quot;gallery-item&quot;&gt;
&lt;dt class=&quot;gallery-icon&quot;&gt;
&lt;a class=&quot;gallery-link&quot; rel=&quot;osc-lights &quot; href=&quot;/images/pg/OSC-lights/led_proto.jpg&quot; title=&quot;Prototype with a smaller LED &amp;amp; serial&quot;&gt;&lt;img src=&quot;/images/pg/OSC-lights/led_proto-thumb.jpg&quot; srcset=&quot;/images/pg/OSC-lights/led_proto-2x-thumb.jpg 300w&quot; class=&quot;thumbnail&quot; width=&quot;150&quot; height=&quot;150&quot; /&gt;
&lt;/a&gt;
&lt;/dt&gt;
&lt;dd class=&quot;gallery-caption&quot;&gt;Prototype with a smaller LED &amp;amp; serial&lt;/dd&gt;&lt;/dl&gt;

&lt;br style=&quot;clear: both;&quot; /&gt;

&lt;/div&gt;

</description>
        <pubDate>Tue, 03 Jan 2017 00:00:00 +0000</pubDate>
        <link>http://axio.ms//projects/2017/01/03/Internet-of-kitchen.html</link>
        <guid isPermaLink="true">http://axio.ms//projects/2017/01/03/Internet-of-kitchen.html</guid>
        
        <category>IoT</category>
        
        <category>ESP8266</category>
        
        
        <category>projects</category>
        
      </item>
    
      <item>
        <title>Axio.ms site refresh</title>
        <description>&lt;p&gt;This is the obligatory “first post on new platform” post, coupled with a bit of “first post in 4 years, I’m still alive”.&lt;/p&gt;

&lt;p&gt;The original axio.ms site was a very HTML1.0 affair and while this was kind of intentional-tongue-in-cheek-chic, it was also a PITA to update.  It wasn’t looking good for regular posts.&lt;/p&gt;

&lt;p&gt;As well as real life happening, I’ve had a change in job since the last phase of semi-frequent posting of projects and hacks.  Part of that means some hoops and paperwork that I’m supposed to jump through on a &lt;em&gt;per-project basis&lt;/em&gt; and this has just meant I’ve effectively stopped writing things up.  Yes, I have to prove that it won’t hurt business to post firmware for a hat with LEDs on it, that kind of thing.&lt;/p&gt;

&lt;p&gt;But, this will change shortly (for the progress of hacks and therefore sanity).  There’s a backlog of a few projects that need writing up and I hope they’ll be of interest – watch this space!&lt;/p&gt;
</description>
        <pubDate>Sun, 29 May 2016 00:00:00 +0100</pubDate>
        <link>http://axio.ms//blog/2016/05/29/Site-refresh.html</link>
        <guid isPermaLink="true">http://axio.ms//blog/2016/05/29/Site-refresh.html</guid>
        
        
        <category>blog</category>
        
      </item>
    
  </channel>
</rss>
