<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Big Button Studio</title>
  <subtitle>Interactive art and creative technology portfolio.</subtitle>
  <id>https://www.bigbuttonstudio.com/</id>
  <link href="https://www.bigbuttonstudio.com/feed.xml" rel="self"/>
  <link href="https://www.bigbuttonstudio.com/"/>
  <updated>2026-08-14T00:00:00Z</updated>
  <author><name>Em Shotton</name></author>
  <entry>
    <title>Getting a Graphical Desktop Working on the Raspberry Pi 3B+ with NixOS 25.11 and the 6.18 Kernel</title>
    <id>https://www.bigbuttonstudio.com/blog/nixos-raspberry-pi-3b-plus-vc4-desktop/</id>
    <link href="https://www.bigbuttonstudio.com/blog/nixos-raspberry-pi-3b-plus-vc4-desktop/"/>
    <updated>2026-08-14T00:00:00Z</updated>
    <content type="html">&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;The Raspberry Pi 3B+ stops at boot when vc4 KMS starts, on the 6.18 kernel with NixOS 25.11. The &lt;code&gt;v3d&lt;/code&gt; node in the firmware device tree has no &lt;code&gt;clocks&lt;/code&gt; property. So &lt;a href=&#34;https://elixir.bootlin.com/linux/latest/ident/clk_disable_unused&#34;&gt;&lt;code&gt;clk_disable_unused&lt;/code&gt;&lt;/a&gt; disables the V3D clock, and the driver locks the CPU. The fix is a one-line device tree overlay that adds the clock: &lt;code&gt;&amp;amp;v3d { clocks = &amp;lt;&amp;amp;firmware_clocks 5&amp;gt;; }&lt;/code&gt;. Add the overlay to the firmware partition. The steps are at the end of this post.&lt;/p&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;The Raspberry Pi 3B+ can run a graphical desktop with NixOS. The desktop needs the &lt;a href=&#34;https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/vc4&#34;&gt;vc4 KMS driver&lt;/a&gt; and a &lt;code&gt;/dev/dri/card0&lt;/code&gt; device. On a recent kernel, this does not work by default. The 3B+ stops at boot when vc4 KMS starts. This post describes the cause and a fix.&lt;/p&gt;
&lt;p&gt;The configuration is &lt;code&gt;nixos-25.11&lt;/code&gt; stable, &lt;a href=&#34;https://github.com/NixOS/nixos-hardware&#34;&gt;&lt;code&gt;nixos-hardware&lt;/code&gt;&lt;/a&gt;, the &lt;a href=&#34;https://github.com/raspberrypi/linux&#34;&gt;&lt;code&gt;linux-rpi&lt;/code&gt;&lt;/a&gt; 6.18 kernel, and the standard firmware-KMS path (&lt;a href=&#34;https://github.com/raspberrypi/firmware/blob/master/boot/overlays/README&#34;&gt;&lt;code&gt;dtoverlay=vc4-kms-v3d&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;The symptom&lt;/h2&gt;
&lt;p&gt;When vc4 starts the display, the 3B+ stops at boot. There is no &lt;code&gt;card0&lt;/code&gt;, there is no HDMI output, and the boot does not finish. A serial console shows the cause. Route the PL011 UART to the GPIO header with &lt;code&gt;dtoverlay=disable-bt&lt;/code&gt;, because the mini-UART is not reliable on the 3B+. The vc4 component master binds its parts and then stops:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[   16.15] vc4-drm soc:gpu: bound 3f400000.hvs (ops vc4_hvs_ops [vc4])
[   16.15] vc4-drm soc:gpu: bound 3f902000.hdmi (ops vc4_hdmi_ops [vc4])
[   16.17] vc4-drm soc:gpu: bou                        &amp;lt;- the log stops here
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The log stops in the middle of a line. This is the sign of a hard lockup. The CPU stops on a bus access. It is not a clean &lt;code&gt;-EPROBE_DEFER&lt;/code&gt;. The kernel parameter &lt;a href=&#34;https://elixir.bootlin.com/linux/latest/ident/clk_ignore_unused&#34;&gt;&lt;code&gt;clk_ignore_unused&lt;/code&gt;&lt;/a&gt; makes the 3B+ boot. This is a large clue, but the parameter keeps every unused clock enabled, which is not a precise fix.&lt;/p&gt;
&lt;h2&gt;The cause&lt;/h2&gt;
&lt;p&gt;The vc4 driver uses the component framework. It starts only after all of its sub-devices bind: HVS, HDMI, the pixelvalves, and V3D. On the 3B+, the boot stops at V3D.&lt;/p&gt;
&lt;p&gt;The V3D node in the device tree has no &lt;code&gt;clocks&lt;/code&gt; property. So &lt;code&gt;vc4_v3d&lt;/code&gt; gets no clock, and it depends on the firmware to keep the V3D clock enabled. The firmware keeps it enabled, until &lt;a href=&#34;https://elixir.bootlin.com/linux/latest/ident/clk_disable_unused&#34;&gt;&lt;code&gt;clk_disable_unused&lt;/code&gt;&lt;/a&gt; runs early in boot (about 2.4 seconds). This function finds a clock that no driver holds, and disables it. &lt;code&gt;vc4_v3d&lt;/code&gt; binds later (about 16 seconds) and reads the &lt;code&gt;V3D_IDENT&lt;/code&gt; registers. The V3D block has no clock, and the CPU stops on the read. &lt;code&gt;clk_ignore_unused&lt;/code&gt; prevents the problem, because it does not disable the clock.&lt;/p&gt;
&lt;p&gt;The first hypothesis was the HDMI state-machine clock and the &lt;code&gt;simpledrm&lt;/code&gt; framebuffer. Both were wrong. The cause is V3D.&lt;/p&gt;
&lt;p&gt;The correct fix is in the upstream kernel. &lt;a href=&#34;https://github.com/raspberrypi/linux/pull/7156&#34;&gt;raspberrypi/linux #7156&lt;/a&gt; gives the node its clock:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;amp;v3d {
    clocks = &amp;lt;&amp;amp;firmware_clocks 5&amp;gt;;   /* 5 = RPI_FIRMWARE_V3D_CLK_ID */
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;vc4_v3d&lt;/code&gt; holds the clock and enables it again. &lt;code&gt;clk_disable_unused&lt;/code&gt; then has no effect.&lt;/p&gt;
&lt;h2&gt;Why the problem happens on NixOS&lt;/h2&gt;
&lt;p&gt;The fix is already in the 6.18 kernel source. It is in the kernel&#39;s own compiled DTBs. But the 3B+ still stops at boot. The reason is the source of the device tree.&lt;/p&gt;
&lt;p&gt;The NixOS Pi sd-image sets &lt;a href=&#34;https://search.nixos.org/options?channel=25.11&amp;amp;query=useGenerationDeviceTree&#34;&gt;&lt;code&gt;useGenerationDeviceTree = false&lt;/code&gt;&lt;/a&gt;. With this setting, the board uses the firmware DTB from the &lt;a href=&#34;https://github.com/raspberrypi/firmware&#34;&gt;&lt;code&gt;raspberrypifw&lt;/code&gt;&lt;/a&gt; package, not the kernel DTB. In &lt;code&gt;nixos-25.11&lt;/code&gt;, &lt;code&gt;raspberrypifw&lt;/code&gt; has the date April 2025. This date is before #7156. So the kernel is from June 2026, and the device tree is from April 2025. The board uses the old device tree.&lt;/p&gt;
&lt;p&gt;This is a version mismatch. It is not a kernel bug or a NixOS bug. The kernel is new, and the firmware DTB is old.&lt;/p&gt;
&lt;h2&gt;The fix: patch the firmware DTB with an overlay&lt;/h2&gt;
&lt;p&gt;One option is to use the kernel DTB. Do not use this option. The kernel DTB fixes V3D, but it describes the USB controller (&lt;code&gt;dwc_otg&lt;/code&gt;) in a way that the April firmware does not expect. In a test, a USB sound card lost audio data continuously. There were 4 FIQ errors at boot with the firmware DTB, and 22 continuous errors with the kernel DTB. Keep the firmware DTB, and add only the missing property with a small overlay.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;v3d-clocks-overlay.dts&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/dts-v1/;
/plugin/;
/ {
    compatible = &amp;quot;brcm,bcm2835&amp;quot;;
    fragment@0 {
        target = &amp;lt;&amp;amp;v3d&amp;gt;;
        __overlay__ {
            clocks = &amp;lt;&amp;amp;firmware_clocks 5&amp;gt;;
        };
    };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the NixOS sd-image, compile the overlay at build time. Copy it to the firmware partition. Add a line to &lt;code&gt;config.txt&lt;/code&gt; after &lt;code&gt;dtoverlay=vc4-kms-v3d&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;language-nix&#34;&gt;&lt;code class=&#34;language-nix&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt;
  v3dClocksOverlay &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; pkgs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;runCommand &lt;span class=&#34;token string&#34;&gt;&#34;v3d-clocks.dtbo&#34;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#39;&#39;
    &lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token antiquotation important&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;pkgs&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;dtc&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;/bin/dtc -@ -I dts -O dtb -o &#34;$out&#34; &lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token antiquotation important&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;token url&#34;&gt;./v3d-clocks-overlay.dts&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt;
  &#39;&#39;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token keyword&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;
  sdImage&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;postBuildCommands &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#39;&#39;
    # After you finalize config.txt with `dtoverlay=v3d-clocks` added:
    $MT/mcopy -o -i &#34;$img@@$OFF&#34; &lt;span class=&#34;token interpolation&#34;&gt;&lt;span class=&#34;token antiquotation important&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;{&lt;/span&gt;v3dClocksOverlay&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/span&gt; ::/overlays/v3d-clocks.dtbo
  &#39;&#39;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;token punctuation&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The overlay is on the firmware partition. The overlay becomes active after a reflash, not after a &lt;code&gt;deploy-rs&lt;/code&gt; push. After the reflash, the 3B+ boots correctly. The &lt;code&gt;card0&lt;/code&gt; device appears, and the desktop starts. There is no kernel parameter, and the sound card works.&lt;/p&gt;
&lt;h2&gt;Fix in steps&lt;/h2&gt;
&lt;p&gt;These commands create a small flake that builds a graphical sd-image for the Pi 3B+ with the fix. Run them in a new, empty directory. The flake uses &lt;code&gt;nixos-hardware&lt;/code&gt;&#39;s &lt;a href=&#34;https://github.com/NixOS/nixos-hardware/tree/master/raspberry-pi/3&#34;&gt;&lt;code&gt;raspberry-pi-3&lt;/code&gt;&lt;/a&gt; module, the &lt;code&gt;sd-image-aarch64.nix&lt;/code&gt; module, and a minimal XFCE desktop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Create the overlay source file.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;cat&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; v3d-clocks-overlay.dts &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;EOF&#39;
/dts-v1/;
/plugin/;
/ {
    compatible = &#34;brcm,bcm2835&#34;;
    fragment@0 {
        target = &amp;lt;&amp;amp;v3d&gt;;
        __overlay__ {
            clocks = &amp;lt;&amp;amp;firmware_clocks 5&gt;;
        };
    };
};
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. Create a NixOS module that compiles the overlay and adds it to the image.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;cat&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; vc4-fix.nix &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;EOF&#39;
{ pkgs, lib, ... }:
let
  v3dClocksOverlay = pkgs.runCommand &#34;v3d-clocks.dtbo&#34; { } &#39;&#39;
    ${pkgs.dtc}/bin/dtc -@ -I dts -O dtb -o &#34;$out&#34; ${./v3d-clocks-overlay.dts}
  &#39;&#39;;
in
{
  sdImage.compressImage = lib.mkForce false;
  sdImage.postBuildCommands = lib.mkAfter &#39;&#39;
    eval &#34;$(${pkgs.util-linux}/bin/partx &#34;$img&#34; -o START,SECTORS --nr 1 --pairs)&#34;
    OFF=$((START * 512))
    MT=${pkgs.mtools}/bin
    $MT/mcopy -o -i &#34;$img@@$OFF&#34; ${v3dClocksOverlay} ::/overlays/v3d-clocks.dtbo
    cfg=$(mktemp)
    $MT/mtype -i &#34;$img@@$OFF&#34; ::config.txt &gt; &#34;$cfg&#34;
    printf &#39;\n[all]\ndtoverlay=v3d-clocks\n&#39; &gt;&gt; &#34;$cfg&#34;
    $MT/mcopy -o -i &#34;$img@@$OFF&#34; &#34;$cfg&#34; ::config.txt
  &#39;&#39;;
}
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. Create the flake. It builds a graphical (XFCE) image and imports the fix module.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;cat&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; flake.nix &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#39;EOF&#39;
{
  inputs = {
    nixpkgs.url = &#34;github:NixOS/nixpkgs/nixos-25.11&#34;;
    nixos-hardware.url = &#34;github:NixOS/nixos-hardware&#34;;
  };

  outputs = { self, nixpkgs, nixos-hardware, ... }: {
    nixosConfigurations.rpi3 = nixpkgs.lib.nixosSystem {
      system = &#34;aarch64-linux&#34;;
      modules = [
        nixos-hardware.nixosModules.raspberry-pi-3
        &#34;${nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix&#34;
        ./vc4-fix.nix
        ({ pkgs, ... }: {
          system.stateVersion = &#34;25.11&#34;;

          # A minimal graphical desktop, auto-logged-in.
          services.xserver.enable = true;
          services.xserver.displayManager.lightdm.enable = true;
          services.xserver.desktopManager.xfce.enable = true;
          services.displayManager.autoLogin = {
            enable = true;
            user = &#34;pi&#34;;
          };

          users.users.pi = {
            isNormalUser = true;
            initialPassword = &#34;raspberry&#34;;
            extraGroups = [ &#34;wheel&#34; &#34;video&#34; ];
          };

          # 1 GB board: add swap.
          swapDevices = [{ device = &#34;/swapfile&#34;; size = 1024; }];
        })
      ];
    };
  };
}
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;4. Build the image.&lt;/strong&gt; The Pi 3B+ is &lt;code&gt;aarch64&lt;/code&gt;. On an &lt;code&gt;x86_64-linux&lt;/code&gt; builder, set &lt;code&gt;boot.binfmt.emulatedSystems = [ &amp;quot;aarch64-linux&amp;quot; ]&lt;/code&gt; first, to build with QEMU. If the directory is a git repository, run &lt;code&gt;git add .&lt;/code&gt; first, because flakes ignore untracked files.&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;nix build &lt;span class=&#34;token builtin class-name&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token comment&#34;&gt;#nixosConfigurations.rpi3.config.system.build.sdImage&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note on the kernel.&lt;/strong&gt; This flake tracks &lt;a href=&#34;https://github.com/NixOS/nixos-hardware&#34;&gt;&lt;code&gt;nixos-hardware&lt;/code&gt;&lt;/a&gt;, so the &lt;code&gt;linux-rpi&lt;/code&gt; kernel can be newer than the binary cache. If the cache does not have the kernel, the build compiles it. Under QEMU emulation on an &lt;code&gt;x86_64&lt;/code&gt; builder, this can take a few hours. To avoid a long build, pin &lt;code&gt;nixos-hardware&lt;/code&gt; to a revision whose kernel is in the cache. Add the commit to the flake URL, for example &lt;code&gt;nixos-hardware.url = &amp;quot;github:NixOS/nixos-hardware/&amp;lt;commit&amp;gt;&amp;quot;;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Find the image file.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;ls&lt;/span&gt; result/sd-image/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;6. Find the SD card device name.&lt;/strong&gt; The name is like &lt;code&gt;/dev/sda&lt;/code&gt; or &lt;code&gt;/dev/mmcblk0&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;lsblk&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;7. Write the image to the SD card.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CAUTION: The next command erases all data on the target device. Make sure that the device is the SD card, and not a disk. Replace &lt;code&gt;/dev/sdX&lt;/code&gt; with the device name from step 6.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;dd&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;result/sd-image/*.img &lt;span class=&#34;token assign-left variable&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;/dev/sdX &lt;span class=&#34;token assign-left variable&#34;&gt;bs&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;4M &lt;span class=&#34;token assign-left variable&#34;&gt;conv&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;fsync &lt;span class=&#34;token assign-left variable&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;progress&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;8. Boot the board.&lt;/strong&gt; Insert the SD card into the 3B+, and start it. The XFCE desktop starts and logs in as user &lt;code&gt;pi&lt;/code&gt;. The &lt;code&gt;card0&lt;/code&gt; device is present.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;If the 3B+ stops at boot when KMS starts on a recent kernel, examine the &lt;code&gt;v3d&lt;/code&gt; node in the device tree. If the node has no &lt;code&gt;clocks&lt;/code&gt; property, the firmware DTB is older than the kernel. A one-line overlay is a better fix than &lt;code&gt;clk_ignore_unused&lt;/code&gt; or a full DTB change. The complete fix is in the nixpkgs domain. Keep &lt;code&gt;raspberrypifw&lt;/code&gt; at the same version as the kernel.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Prove It</title>
    <id>https://www.bigbuttonstudio.com/blog/prove-it/</id>
    <link href="https://www.bigbuttonstudio.com/blog/prove-it/"/>
    <updated>2026-05-19T00:00:00Z</updated>
    <content type="html">&lt;p&gt;I haven&#39;t written a line of code in over 5 months now.&lt;/p&gt;
&lt;p&gt;At the start of the new paradigm I was reading a lot more code, and I think it was making me better at the craft of coding. I&#39;d see best practices used in languages I was unfamiliar with to solve a problem that I had articulated. I was reading code where I understood the intent and I was working backwards to understand the implementation. A few months in I started to trust the generated code more. The sense that I was sharpening the craft of writing code gave way to the sense that I was losing it.&lt;/p&gt;
&lt;p&gt;Now, for many personal and professional projects, I hold a mental model of how all the pieces fit together, but it&#39;s not grounded in having read the actual code. It&#39;s grounded in the conversations I&#39;ve had about how the system works. Sometimes that understanding has diverged from reality, either because I misunderstood something or because a question I asked got answered incorrectly.&lt;/p&gt;
&lt;p&gt;It feels like I&#39;ve stepped up the management chain: less time writing code, more time articulating what I want clearly enough, more time in conversations about what got built. But I don&#39;t think the core of the work has changed that much.&lt;/p&gt;
&lt;h2&gt;Designing Things&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;/assets/images/gen/blog/prove-it/designing-things.jpeg&#34; alt=&#34;A black cat wearing round wire-framed glasses and a star-patterned bow tie, held up to look studious.&#34;&gt;&lt;/p&gt;
&lt;p&gt;If I&#39;m honest I was never a great software engineer, it was always a means to an end. I began my career in R&amp;amp;D where I was mostly following my curiosity and testing if an idea worked, and code was the tool I used to do that. Through working with some brilliant mentors and being humbled in numerous code reviews I eventually learned a little more of the craft of coding. I now sometimes get handed research code that needs to be moved to production and feel a pang of nostalgia as I&#39;m trying to figure out where to add the error handling.&lt;/p&gt;
&lt;p&gt;Most of the architecture work I&#39;ve done has been around answering questions. In most companies those questions start with money:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;How much will it cost to run this new model?&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;and then break down into smaller sub-questions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;What compression do we need to get X many video frames from Y cameras to be decoded on a server with Z CPUs?&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;How many GPUs do we need to process X many video frames?&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;What is the optimal batch size and model instance count for this model architecture on this model of GPU?&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Answering them gives you the raw data you need to make decisions about how to build a system. Running experiments to answer these questions is probably how I spend &amp;gt;80% of my time when doing architecture work. There is of course other work involved, but these experiments have some of the largest impact on the final architecture of a system. Most of the code written to answer these experiments is thrown away, but the knowledge gained from writing them is retained. I&#39;ve worked on greenfield projects where we spent a few months running these types of experiments and then only spent a week or two to build the system. When done well the systems that resulted from this process were minimal &amp;amp; beautiful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prove why a design decision is correct, don&#39;t speculate.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As far as I can tell this hasn&#39;t changed under the new paradigm, the work is still figuring out what questions we need to ask and running the experiments to get the answers. Code is now cheaper to write and throw away, we can run more experiments quicker and hopefully iterate to something better.&lt;/p&gt;
&lt;p&gt;The other side of the coin with code being cheaper to write is I rarely see code being removed. Problems are solved with speculative additions. End-to-end tests &amp;amp; performance metrics are often harder to write (especially any that involve humans in the loop), but with wide-ranging additions being so cheap to make, it&#39;s more important than ever to have them. Otherwise your systems will balloon in size and complexity and become harder to reason about.&lt;/p&gt;
&lt;p&gt;I don&#39;t know how to review PRs anymore. You open the diff and see ten thousand green lines added to an already complicated system. New functionality, new abstractions, new dependencies. The code might work, but the harder question is whether it should exist at all, and how do you prove that?&lt;/p&gt;
&lt;p&gt;In a pipeline with a subjective quality measure, a media processing chain, an AI model, anything where the output is &amp;quot;does this feel right,&amp;quot; measuring the impact of a change is hard. Even objective metrics get hard when the end-to-end tests aren&#39;t in place, or when running them needs a human in the loop, or physical hardware, or both. The change in front of you might be a real improvement, might be neutral, might be a regression that no one notices for three weeks.&lt;/p&gt;
&lt;p&gt;What&#39;s the end-to-end impact of this change? What metrics are you using to measure that? What&#39;s your sample size? Is the test you&#39;re pointing me at actually exercising the thing you changed, or just the thing you added? You need to be both a data scientist and a research engineer now, designing the experiment that proves the change earns its place, and rigorously measuring the result.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prove why additions are needed, don&#39;t speculate.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When I was first told &amp;quot;every line of code you write is a piece of technical debt that you&#39;re adding to the system&amp;quot; it crystallized a lot of the frustrations I&#39;d had with my own work and pushed me to build simpler systems. Systems that solved the immediate problems and didn&#39;t speculate about future problems. The new paradigm is challenging this for me in strange ways. I&#39;m still trying to build for the problem in front of me, but code feels so cheap.&lt;/p&gt;
&lt;p&gt;I recently trained a MIDI generating LLM as a side project. I&#39;d written it in Python, then spent a few hours &lt;a href=&#34;/web-toys/midi-generator/&#34;&gt;porting it to run in the browser with WebAssembly&lt;/a&gt;, and then to C++ so I could run it on an STM32 microcontroller with a neural accelerator. Each port unlocked a different way of interacting with the thing I&#39;d made. The browser version let anyone with a web browser play with it without costing me any compute. The embedded version opened the possibility of a dedicated hardware synthesizer. Two wildly different product directions, both trivial to explore.&lt;/p&gt;
&lt;p&gt;Probably one of them, maybe both, I&#39;ll never touch again. In the old framing that&#39;s three weekends spent on dead-end ports, code I&#39;ve taken on the maintenance burden of. In the new paradigm it&#39;s cheap exploration, code that exists and doesn&#39;t matter.&lt;/p&gt;
&lt;p&gt;The ability to try an idea, fail quickly, and pivot is faster than ever, and I think what&#39;s actually changed is where the discipline lives. It&#39;s not in writing less code. It&#39;s in deleting more of it. Pruning your git tree so that your context window doesn&#39;t fill up with dead ends and experiments. Proving that each addition is valuable and measurably improves your system.&lt;/p&gt;
&lt;h2&gt;Debugging Things&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;/assets/images/gen/blog/prove-it/rd-live-events.jpg&#34; alt=&#34;A hand pointing at a laptop running a BBC Research &amp;amp; Development broadcast tool at the Edinburgh Festivals.&#34;&gt;&lt;/p&gt;
&lt;p&gt;I started my career in the broadcast industry where once or twice a year I&#39;d work on some kind of live production. Snakes of cables would wind their way from a stage to a cramped port-a-cabin around the back of the venue. It would be filled with the fan noise of some experimental broadcast system whirring away in a server rack. There would be a huddle of engineers standing around broadcast monitors and computer screens frantically debugging things as the clock counted down. We&#39;d have to be ready to capture what was happening on stage when it started happening. No ifs, buts, or maybes.&lt;/p&gt;
&lt;p&gt;It left me with a taste for a good SEV. You have to bring calm, focused, methodical problem solving into a stressful situation. You have to act as an information filter, sorting facts from assumptions, organizing information as it comes in and using that to form hypotheses about how a system might be broken. Testing those hypotheses and integrating your findings back into the pool of facts you&#39;re using to synthesize new ones. I love a good Agatha Christie mystery and this tickles the same part of my brain that loves an unraveling mystery, the amassing of proof that lets you reach a conclusion.&lt;/p&gt;
&lt;p&gt;The new paradigm is both a blessing and a curse to debugging.&lt;/p&gt;
&lt;p&gt;The curse is that Claude presents facts and assumptions with the same confidence. You read a line of reasoning, it sounds right, and you don&#39;t always notice the assumption the chain rests on is a guess about how the code behaves. The chain builds on it, you build on the chain, and before you know it you&#39;re going in circles with no sense of where the wrong turn was. The way I find out is usually by interrogating the model about why it tried something, and watching the justification dissolve.&lt;/p&gt;
&lt;p&gt;The defence I&#39;ve landed on is not very subtle. Give it everything. All the logs, all the error messages, screenshots of metrics graphs, anything concrete about how the system is behaving. It eats context fast and triggers compaction more often than I&#39;d like, but it makes it harder for an assumption to slip into the chain and pass itself off as a fact. When you ask why a decision was made you can ask Claude to point to the data the decision rests on. I find myself building quick tools to make this easier, throwaway Python scripts that produce spectrograms of audio data, anything that turns the system&#39;s behavior into something inspectable.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/assets/images/gen/blog/prove-it/oscilloscope-synth-debugging.jpeg&#34; alt=&#34;A workbench with an oscilloscope, bench power supply, debug probe, and the synth prototype with white knobs, surrounded by a tangle of cables.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Over the 2025 holidays when it felt like the new paradigm began to take hold I was debugging the firmware for a synth engine I&#39;d been working on, one of my last hand-written pieces of code. I was seeing glitches in the audio output on my oscilloscope, and I couldn&#39;t figure out why. It was one of those problems that was pulling the joy from the work. Out of a mix of frustration and curiosity I hooked a debug probe onto the RP2040, plugged my oscilloscope into my LAN so I could pull screenshots from it over the network, and pointed Claude Code at it.&lt;/p&gt;
&lt;p&gt;I watched as my code got rewritten, the lights on my debug probe flashed as the code was loaded onto the circuit board, and the oscilloscope made a clicking noise as it captured a screenshot of the waveform. This cycle repeated a few times as I sat watching it. Unceremoniously the problem was solved. The synth engine worked. What might&#39;ve taken me hours of frustration at best, or joined the pile of abandoned projects at worst, was solved. My focus was back on the parts that felt fun.&lt;/p&gt;
&lt;p&gt;I think this worked because I&#39;d built a loop that could be iterated on. I&#39;d articulated the end state, remove the glitch that periodically drops the output to zero. I&#39;d given the model the tools to make changes, the code and the debug probe. And I&#39;d given it a way to measure success, the oscilloscope screenshots. It was harder for Claude to make assumptions about what does and doesn&#39;t work when after each change it could see the impact.&lt;/p&gt;
&lt;h2&gt;Concluding Things&lt;/h2&gt;
&lt;p&gt;I&#39;m in the process of changing jobs, and there&#39;s something wonderful about calling old colleagues to ask them to be references. I get to check in with some of the smartest people I know and catch up about how their personal and professional lives are going. There&#39;s electricity in the air. Everyone is figuring out how to do the work in this new paradigm. There&#39;s less cynicism than I expected, and more excitement. People have been telling me about experimental projects that used to need management approval getting finished in a lazy afternoon and already shipping. About the personal project they had shelved being picked back up and revisited through this new lens.&lt;/p&gt;
&lt;p&gt;I&#39;ll be moving back to a startup from the relative comfort of a large organization. It seems like a mad time to be doing it. The tech sector is up in the air, and I have a job that should weather the storm for at least a few more years. But the pull of getting to play in this new paradigm in an environment where my work can have such a direct impact on the success or failure of a company is incredibly alluring. That live production energy, the feeling of being cramped in a port-a-cabin with smart people solving problems to a deadline.&lt;/p&gt;
&lt;p&gt;The craft of my work has been changed forever. The work itself hasn&#39;t changed. Gather data, hypothesize, prove it, build, ship.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/assets/images/gen/blog/prove-it/conclusion.jpeg&#34; alt=&#34;Container ships at a port, with loading cranes silhouetted against a hazy sky.&#34;&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Getting the Kingst LA2016 Logic Analyzer Working on Linux with Sigrok</title>
    <id>https://www.bigbuttonstudio.com/blog/getting-the-kingst-la2016-logic-analyzer-working-on-linux-with-sigrok/</id>
    <link href="https://www.bigbuttonstudio.com/blog/getting-the-kingst-la2016-logic-analyzer-working-on-linux-with-sigrok/"/>
    <updated>2026-01-17T00:00:00Z</updated>
    <content type="html">&lt;p&gt;Here&#39;s how I got the Kingst LA2016 logic analyzer working on Linux with the open-source sigrok/PulseView software.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/assets/images/gen/blog/kingst-la2016/kingst-la2016.jpeg&#34; alt=&#34;Kingst LA2016 Logic Analyzer&#34;&gt;&lt;/p&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;When you first plug in the LA2016 and try to scan for devices with sigrok, you&#39;ll likely see something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sigrok-cli --scan
sr: resource: Failed to open resource &#39;kingst-la-01a2.fw&#39;
sr: kingst-la2016: MCU firmware upload failed.
The following devices were found:
demo - Demo device with 13 channels: D0 D1 D2 D3 D4 D5 D6 D7 A0 A1 A2 A3 A4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or possibly it won&#39;t show up at all:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sigrok-cli --scan
The following devices were found:
demo - Demo device with 13 channels: D0 D1 D2 D3 D4 D5 D6 D7 A0 A1 A2 A3 A4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two issues here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Old sigrok version&lt;/strong&gt; - The Kingst LA2016 driver was added to libsigrok relatively recently. The version in Ubuntu/Debian repositories (0.5.2) is too old.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Missing firmware&lt;/strong&gt; - The LA2016 needs firmware uploaded to its MCU and FPGA each time it&#39;s connected. This firmware isn&#39;t open source and must be extracted from Kingst&#39;s official software.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&#39;s how to fix both.&lt;/p&gt;
&lt;h2&gt;Step 1: Get the Nightly Sigrok Builds&lt;/h2&gt;
&lt;p&gt;Grab the nightly AppImages directly from sigrok.org:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; ~/Software  &lt;span class=&#34;token comment&#34;&gt;# or wherever you keep things&lt;/span&gt;
&lt;span class=&#34;token function&#34;&gt;mkdir&lt;/span&gt; logic-analyzer &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; logic-analyzer

&lt;span class=&#34;token function&#34;&gt;wget&lt;/span&gt; https://sigrok.org/download/binary/pulseview/pulseview-NIGHTLY-x86_64-release.AppImage
&lt;span class=&#34;token function&#34;&gt;wget&lt;/span&gt; https://sigrok.org/download/binary/sigrok-cli/sigrok-cli-NIGHTLY-x86_64-release.AppImage
&lt;span class=&#34;token function&#34;&gt;chmod&lt;/span&gt; +x *.AppImage&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These contain a much newer libsigrok with full Kingst support.&lt;/p&gt;
&lt;h2&gt;Step 2: Download the Kingst Linux Software&lt;/h2&gt;
&lt;p&gt;Download KingstVIS for Linux from: https://www.qdkingst.com/en/download&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;tar&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-xzf&lt;/span&gt; KingstVIS_v3.6.5.tar.gz&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 3: Extract the Firmware&lt;/h2&gt;
&lt;p&gt;Clone the sigrok-util repository, which contains the extraction script:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; clone https://github.com/sigrok/sigrok-util.git&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now run the extractor against the Linux binary:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;python3 sigrok-util/firmware/kingst-la/sigrok-fwextract-kingst-la2016 KingstVIS/KingstVIS&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see output like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;resource fwusb/fw01A2, file kingst-la-01a2.fw, size 5430, checksum 720551a9
resource fwfpga/LA2016, file kingst-la2016-fpga.bitstream, size 178542, checksum 20694ff1
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script extracts firmware for the entire Kingst product line. For the LA2016, you need &lt;code&gt;kingst-la-01a2.fw&lt;/code&gt; and &lt;code&gt;kingst-la2016-fpga.bitstream&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Step 4: Install the Firmware&lt;/h2&gt;
&lt;p&gt;Copy the firmware files where sigrok can find them:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;mkdir&lt;/span&gt; /usr/share/sigrok-firmware
&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;cp&lt;/span&gt; kingst-la*.fw /usr/share/sigrok-firmware/
&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;cp&lt;/span&gt; kingst-la*-fpga.bitstream /usr/share/sigrok-firmware/&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 5: Set Up Permissions&lt;/h2&gt;
&lt;p&gt;Create a udev rule so you don&#39;t need root to access the device:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;tee&lt;/span&gt; /etc/udev/rules.d/60-kingst.rules &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#39;EOF&#39;
# Kingst Logic Analyzers
SUBSYSTEM==&#34;usb&#34;, ATTR{idVendor}==&#34;77a1&#34;, ATTR{idProduct}==&#34;01a2&#34;, MODE=&#34;0666&#34;, GROUP=&#34;plugdev&#34;
EOF&lt;/span&gt;

&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; udevadm control --reload-rules
&lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; udevadm trigger&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unplug and replug your LA2016.&lt;/p&gt;
&lt;h2&gt;Step 6: Verify It Works&lt;/h2&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;./sigrok-cli-NIGHTLY-x86_64-release.AppImage &lt;span class=&#34;token parameter variable&#34;&gt;--scan&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should now see:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The following devices were found:
kingst-la2016 - Kingst LA2016 with 16 channels: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Launch PulseView:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;./pulseview-NIGHTLY-x86_64-release.AppImage&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Select the Kingst LA2016 from the device dropdown and you&#39;re ready to capture.&lt;/p&gt;
&lt;h2&gt;Quick Capture Test&lt;/h2&gt;
&lt;p&gt;From the command line, capture 1000 samples at 1 MHz:&lt;/p&gt;
&lt;pre class=&#34;language-bash&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;./sigrok-cli-NIGHTLY-x86_64-release.AppImage &lt;span class=&#34;token parameter variable&#34;&gt;-d&lt;/span&gt; kingst-la2016 &lt;span class=&#34;token parameter variable&#34;&gt;--config&lt;/span&gt; &lt;span class=&#34;token assign-left variable&#34;&gt;samplerate&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;1M &lt;span class=&#34;token parameter variable&#34;&gt;--samples&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1000&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-O&lt;/span&gt; ascii&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Troubleshooting&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&amp;quot;Cannot open device&amp;quot; or permission denied&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check &lt;code&gt;lsusb | grep 77a1&lt;/code&gt; to confirm the device is connected&lt;/li&gt;
&lt;li&gt;Verify udev rules: &lt;code&gt;cat /etc/udev/rules.d/60-kingst.rules&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Make sure you replugged the device after adding rules&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&amp;quot;Failed to open resource&amp;quot; errors&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Firmware files aren&#39;t where sigrok expects them&lt;/li&gt;
&lt;li&gt;Try &lt;code&gt;SIGROK_FIRMWARE_DIR=/path/to/firmware ./pulseview-NIGHTLY-x86_64.AppImage&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Device detected but capture fails&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Try a lower sample rate first (1 MHz)&lt;/li&gt;
&lt;li&gt;Check USB connection - use a good cable and preferably USB 3.0 port&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Tested with Kingst LA2016, KingstVIS v3.6.5, and sigrok nightly builds from January 2026.&lt;/em&gt;&lt;/p&gt;
</content>
  </entry>
</feed>
