Skip to main content

Simple enough problem to explain: I have machines in various places round the house rendering stuff for work. If they crash, I don’t find out unless I check them periodically, but that’s not great for work/life balance. There are plenty of industrial-strength render managers, network monitors etc available, but the setup here is pretty diverse, Macs, Linux boxes, and I want to monitor a number of things: how hard each machine is working, what they’re doing, and (important for someone doing 3D renders) how much disk space is still left on my servers.

I thought it’d be cool if I could put a little screen on the fridge downstairs, just showing vital stats in a nice way.

Ages ago I bought a cheap little LCD screen from AliExpress, 8″ long, very narrow, for around £30. Weird shape for a screen, but I think they’re for people to stick inside PC cases:

The case is quite thick, though, and there’s a fair bit of empty space inside it, so I figured I could stick a little Raspberry PI microcontroller inside the thing – it could connect to my WiFi, display a webpage with my stats on it – giving me a nice contained “dashboard” I could mount somewhere, and all I’d need to do is feed it power.

Software-wise, I was going to need something on each client, sending out a regular message over the network, to say what it was doing, and something on the server to listen out for those messages, keep track of a few minutes’ worth of history, and create a JSON file with the latest data in it. The server would also need to serve a webpage to present the JSON data nicely, keep it refreshed.

Code

Most of the machines here run Linux, so a Bash script that’d gather CPU and GPU usage data and send it out as UDP broadcast messages would work: broadcast messages are sent out without a particular recipient in mind, so I could have any machine on the network listen out for them.

#!/bin/sh

UDP_PORT=43217
BROADCAST_ADDR="10.0.1.255"  # Broadcast to everyone on my LAN
HOSTN=$(hostname -s)

while true; do
    CPU=$(top -bn1 | awk '/Cpu/ { print $2}')
    GPU=$(nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits)
    MEM=$(free -m | awk '/Mem/{print $3}')
    APP=$(ps -eo %cpu,comm --sort=-%cpu | awk 'NR>1 && $2 != "ps" && $2 != "ssh" && $2 != "bash" && $2 != "top" {print $2; exit}')

    STATUS_MSG="${HOSTN} | CPU: ${CPU}% | GPU: ${GPU}% | MEM: ${MEM}MB | APP: ${APP}"

    # Send over network via UDP broadcast
    echo "$STATUS_MSG"
    printf "%s\n" "$STATUS_MSG" | nc -u -q0 -b $BROADCAST_ADDR $UDP_PORT

    sleep 0.25
done

This broadcasts messages like this every quarter of a second:

RENDER005 | CPU: 18% | GPU: 36% | MEM: 58803MB | APP: blender
RENDER005 | CPU: 10% | GPU: 10% | MEM: 58782MB | APP: blender
RENDER005 | CPU: 12% | GPU: 12% | MEM: 58755MB | APP: blender
...

Nice and simple; the first bit is the machine’s name, then each stat is separated with a pipe character. So … on to the server.

The server’s job is to listen out for these broadcasts, keep track of which machine is talking, deliver a webpage and a JSON file: ChatGPT recommended Python as the easiest approach, as it’s great for string manipulation and it’s easy to set up a simple webserver with it.

The code is on github: it’s been through a few iterations, now, adding new features. As well as collecting data from the broadcasts, it also periodically pings some external websites (to make sure I’m still online), checks disk space on a number of network drives, and it monitors throughput to the internet (which is kinda fun to see, but super useful if I’m waiting for a big Dropbox upload to complete before I can send out an email with the link).

The webpage is on that github page too; it’s fairly simple, with a chunk of JS periodically grabbing the latest stats from the server and refreshing the page. It’s not ideal at the moment as it clobbers the DOM every time, but it works.

Hardware

The LCD panel I’d bought had a particularly deep case, so I figured I could stuff a Raspberry PI in it. ChatGPT helped me get it set up (issues like the Pi’s Chromium browser putting up memory warnings, which you couldn’t dismiss without a mouse click … ChatGPT helped me disable that, get the Pi to boot up and automatically load my webpage fullscreen).

The Pi has an HDMI port, and the interface for the LCD has one too; managed to find a tiny HDMI-HDMI ribbon cable on Amazon. Meant the Pi and interface board ended up “floating” inside the case, but with some black card and Kapton tape, it all fitted pretty securely. More black card (not pictured) sat over the top to sandwich it all in cozily, keep everything from shorting out against itself or the case:

I hit the familiar problem of “it works fine until I close up the case, then there’s weird display flickering and glitches” … which I figured was probably down to the HDMI cable being unshielded, and pressed up against the circuit boards. I made some shielding sandwiches – aluminium tape, like the kind they wrap air-conditioning duct joints with, wrapped round the HDMI cable, then covered in Kapton tape – fixed it.

Before closing it all up, I drilled a hole in each corner of the case back, mounted some little magnets:

 

Final result: a little self-contained screen, with a single power cable hanging out. Feed it 5V and I get a live “state of the house” display.

 

 

ChatGPT

This was one of those projects I’ve wanted to do for years, but it wasn’t until ChatGPT came along that it became practical. It would likely have taken me a week or so on my own. It’s those little bugs and gotchas that end up eating your time, and this project was full of them. Peculiarities of the way the nc utility (the thing I used to broadcast status messages) works on Macs and Linux boxes, each requiring different flags and parameters to work properly; my desire to show “history” graphs on the webpage – yeah, I could have got there on my own, but being able to ask ChatGPT to throw a quick skeleton example together saved me a dozen Google / StackOverflow searches and got me up and running insanely fast. Needed to serve a webpage – fine, it’s only a couple of lines of Python, but ChatGPT already knew what I needed it to do, and provided code that did just that and no more. And the odd bits of configuration and fiddling necessary to get the RaspPi to behave…

There wasn’t a single insurmountable problem, rather it’d have been death by a thousand cuts. Which has killed so many of my projects.

All told, this took a weekend to get working, thanks to having Brice on hand. (Brice, you ask? Yeah, I know it’s silly, but I’ve got ChatGPT set up to choose a fresh name for itself each time I start a new chat context. “Base it on one of the headlines on the BBC News website” for a bit of entropy. It’s a bit too cute, perhaps, but I quite enjoy suspending my disbelief, chatting away with GPT as if it were a person: cos why the hell not. Mad to think you can develop an ongoing rapport with a machine, but here we are •

Leave a Reply