Categories
AI Arduino Projects Video

NeoPixel Christmas Tree 2025

I’ve hit the ground running this holiday season with two updates (so far!) to the NeoPixel Christmas Tree project.

Vibe coded goodies

I’ve been using an LLM to add some experimental goodies to the project such as:

  • A Python script that analyzes WAV files to synchronize light sequences to the beat
  • A local model rendered in Three.js to enable rapid prototyping

https://github.com/twstokes/neopixel-tree/pull/15

ESP32 upgrade

At the end of 2024 I experienced stability issues when updating many LEDs rapidly, e.g. a looping rainbow effect with a really low delay set. After some indeterminate amount of time the tree would crash and restart itself. I initially assumed it was bad C code on my part, but failed to spot anything and eventually put the tree away sometime in January of 2025 because it just got awkward. Now that we’re back to a socially acceptable time to have a Christmas tree out I’ve had a chance to do some deeper digging.

I added a new command to have the ESP return the reason it reset which was really helpful in confirming my suspicion that the ESP8266 just didn’t have enough oomph to drive pixels and run WiFi concurrently:

Fatal exception:4 flag:1 (Hardware Watchdog) epc1:0x40103341 epc2:0x00000000 e    pc3:0x00000000 excvaddr:0x00000000 depc:0x00000000

Long-ish story short I upgraded to an ESP32 with two cores (note: number of cores is model-dependent) and this provides the necessary headroom to bit bang LEDs and run WiFi stably. It required some re-soldering of the Perma-Proto board (since the pin layouts are different) as well as some code changes, but overall it was a relatively quick and straightforward upgrade. The tree hasn’t crashed since!

https://github.com/twstokes/neopixel-tree/pull/14

Categories
AI Fun Linux Nerd Sniped Projects

Snooping USB with GPT

I mentioned in my previous post about the Juicy Crumb DockLite G4 that there were two shortcomings with the product:

  1. Their software to control the brightness of the display was macOS only.
  2. The display doesn’t support sleep, so you’re required to press a physical button to turn off the backlight.

Thanks to some USB packet snooping and GPT, both of these problems were solved quickly. 😎

I wanted Linux software control

What started this exploration was that I wanted to control the display from Linux because it’s currently connected to a Raspberry Pi, not a Mac.

Step 0: Check for an open source repo

Negative. I didn’t spot anything, so it’ll take a little more grunt work. I did run their helper app through a disassembler and spotted functions like getting and setting the backlight via IOKit which were all expected. It wasn’t necessary to go this far, it just confirms that we have some USB packet snooping to do.

Step 1: Get lsusb output

Running lsusb with the display connected to the RPi showed:

Bus 001 Device 009: ID 4a43:1d8a Juicy Crumb Systems DockLite

So I grabbed the output of lsusb -v -d 4a43:1d8a to feed it to the LLM later to provide more details about its USB specs.

Step 2: Capture USB packets on the Mac

The double-edged sword of Apple’s security is that it introduces all sorts of challenges for devs who need to get to lower level functionality, like capturing raw data from interfaces.

To successfully USB capture packets on a Mac I had to:

  1. Disable SIP
  2. Bring up the USB controller interface (in my case there were a couple, so through trial-and-error I found the right one)
  3. Tell Wireshark to capture on that interface
  4. Change the brightness using the Juicy-Link app to generate packets
  5. Save the capture to a file

Step 3: Feed the LLM

With the data from steps 1 and 2 I:

  • Fed them to ChatGPT
  • Mentioned that the capture was from a utility setting the brightness
  • Asked for an equivalent Python script

Here’s what it produced.

Step 4: Run the script in Linux

After installing the pyusb module I could run python set-brightness.py 700 and it worked from Linux! The range from lowest backlight brightness to highest is around 700 – 5000.

The extra bonus (which I’m most excited about) is that sending a value of 0 will truly turn off the backlight! This isn’t even (currently) possible with their software. (Obligatory “do this at your own risk”)

Now I can hook into something like D-Bus signals to potentially toggle the display depending on the desktop’s locked state.

Success

✅ The display’s brightness can be set from any OS, not just macOS.

✅ The display’s backlight can be turned off via software.

Thanks GPT! 🤖

Categories
AI Apple Fun

Apple Watch DOOM + Audio

It took a couple LLM prompts plus a few simple syntax tweaks from my side and now DOOM on the Apple Watch has audio. 🤯 It was almost as easy as typing iddqd. To get around lack of SDL support, AVFoundation is used. Check out the GitHub commits.

Categories
AI SwiftUI

ChatGPT SwiftUI Christmas Tree 2024

Over the past couple of years (2022, 2023) I’ve compared the output of the following prompt to ChatGPT:

Draw a Christmas tree using SwiftUI

For 2024 I chose the GPT-4o model, and this is what it output:

import SwiftUI

struct ChristmasTreeView: View {
    var body: some View {
        ZStack {
            // Background color
            Color.blue
                .edgesIgnoringSafeArea(.all)
            
            // Tree
            VStack(spacing: -20) {
                Triangle()
                    .fill(Color.green)
                    .frame(width: 150, height: 100)
                Triangle()
                    .fill(Color.green)
                    .frame(width: 180, height: 120)
                Triangle()
                    .fill(Color.green)
                    .frame(width: 210, height: 140)
                
                // Tree trunk
                Rectangle()
                    .fill(Color.brown)
                    .frame(width: 40, height: 60)
            }
            .offset(y: 50)
            
            // Star on top
            StarShape()
                .fill(Color.yellow)
                .frame(width: 40, height: 40)
                .offset(y: -170)
            
            // Decorations (Ornaments)
            Circle()
                .fill(Color.red)
                .frame(width: 15, height: 15)
                .offset(x: -40, y: -60)
            Circle()
                .fill(Color.yellow)
                .frame(width: 15, height: 15)
                .offset(x: 50, y: -30)
            Circle()
                .fill(Color.white)
                .frame(width: 15, height: 15)
                .offset(x: -30, y: 20)
            Circle()
                .fill(Color.blue)
                .frame(width: 15, height: 15)
                .offset(x: 40, y: 60)
        }
    }
}

// Triangle shape for the tree
struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        Path { path in
            path.move(to: CGPoint(x: rect.midX, y: rect.minY))
            path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
            path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
            path.closeSubpath()
        }
    }
}

// Star shape for the top of the tree
struct StarShape: Shape {
    func path(in rect: CGRect) -> Path {
        let points = [
            CGPoint(x: 0.5, y: 0),
            CGPoint(x: 0.61, y: 0.35),
            CGPoint(x: 1, y: 0.35),
            CGPoint(x: 0.68, y: 0.57),
            CGPoint(x: 0.79, y: 0.91),
            CGPoint(x: 0.5, y: 0.7),
            CGPoint(x: 0.21, y: 0.91),
            CGPoint(x: 0.32, y: 0.57),
            CGPoint(x: 0, y: 0.35),
            CGPoint(x: 0.39, y: 0.35)
        ].map { CGPoint(x: $0.x * rect.width, y: $0.y * rect.height) }
        
        return Path { path in
            path.move(to: points[0])
            for point in points.dropFirst() {
                path.addLine(to: point)
            }
            path.closeSubpath()
        }
    }
}

struct ChristmasTreeView_Previews: PreviewProvider {
    static var previews: some View {
        ChristmasTreeView()
    }
}

Which generated:

This is the best one yet! I didn’t have to add or change any code to make it work, it added ornaments and even a (floating 😀) star! 🎄

Categories
AI SwiftUI

ChatGPT SwiftUI Christmas Tree 2023

Revisiting last year’s challenge, let’s see how this year goes with GPT-4.

I used the same prompt from 2022:

Draw a Christmas tree using SwiftUI

Interestingly this time it included the Triangle type and it was almost line-for-line the exact one I substituted in last year.

import SwiftUI

struct ContentView: View {
    var body: some View {
        ZStack {
            // Background
            Color.white.edgesIgnoringSafeArea(.all)

            // Christmas Tree
            VStack {
                ForEach((1...5).reversed(), id: \.self) { i in
                    TreeLayer(width: CGFloat(i) * 60)
                }
                Rectangle()
                    .frame(width: 40, height: 50)
                    .foregroundColor(.brown)
            }
        }
    }
}

struct TreeLayer: View {
    var width: CGFloat

    var body: some View {
        ZStack {
            Triangle()
                .fill(Color.green)
                .frame(width: width, height: width * 0.75)
            // Add decorations here
        }
    }
}

struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: CGPoint(x: rect.midX, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        path.closeSubpath()
        return path
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

And here’s the output for 2022 and 2023! 🎄😀

Categories
AI Fun Projects

Do you hear what I hear?

Thanks to Whisper and this awesome port, the tree is responding to spoken words. 🗣🎄

Since the tree itself only has a low-powered MCU, we need another machine to act as a listener.

The architecture is:

  1. A machine in my office runs the Whisper model and listens for words.
  2. If certain keywords are found it finds a corresponding command to run (e.g. do a theater chase sequence in a green color).
  3. It sends that command to the tree over the network.

For now I’m running it from iOS and macOS, so I wrote the current implementation in Swift. The code is currently still in “hack” status, but working well!

Now it’s time to test it when talking to coworkers at Automattic.

Categories
AI SwiftUI

Can ChatGPT draw a Christmas tree in SwiftUI?

Here’s the prompt I gave ChatGPT:

Draw a Christmas tree using SwiftUI

Here’s the code I got:

struct ChristmasTree: View {
    var body: some View {
        ZStack {
            // draw the tree trunk
            Rectangle()
                .fill(Color.brown)
                .frame(width: 20, height: 100)

            // draw the tree branches
            ForEach(0..<5) { i in
                HStack(spacing: 5) {
                    ForEach(0..<5-i) { j in
                        Triangle()
                            .fill(Color.green)
                            .frame(width: 10, height: 10)
                    }
                }
                .offset(x: 0, y: -10 * CGFloat(i))
            }

            // draw the star on top of the tree
            Circle()
                .fill(Color.yellow)
                .frame(width: 10, height: 10)
                .offset(x: 0, y: -50)
        }
    }
}

The only major issue syntax-wise is that Triangle() isn’t defined – where did it get that?

A quick visit to Hacking with Swift had this nice snippet, which I dropped in.

struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()

        path.move(to: CGPoint(x: rect.midX, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        path.addLine(to: CGPoint(x: rect.midX, y: rect.minY))

        return path
    }
}

Now we can compile and…. tada!

A Christmas tree in SwiftUI drawn by ChatGPT

Not great, but darn impressive! 🎄🤯