Homeboard: Versioning frames
Post by Nico Brailovsky @ 2025-03-23 | Permalink | Leave a comment
Since I've been fixing plenty of bugs, figured I should also start versioning my frame mount designs.
The Ikea-frame version should look something like this:
The design for this one lives here
You can download it an open it with Inkscape; remember to switch to outline mode in Inkscape, otherwise you're unlikely to see anything. The frames are designed for a laser engraver, and the cuts are about 1/100'th of a mm.
And the standalone vesion will hopefully look a bit less terrible than this, since this picture is from a few bug-revisions before:
The design for the standalone version:
Homeboard: A Hardware bug!
Post by Nico Brailovsky @ 2025-03-16 | Permalink | Leave a comment
I found my first hardware bug! Can you spot it? It's the big red circle:
The mmwave sensor was mounted too close to either the screen, or the power source (something I thought was a brilliant idea yesterday). Turns out that mounting it so close has an affect on this sensor: when the display is on, it blocks the sensor (and reads it as no-presence). When the display is off, for some reason the sensor picks it up as someone being present. This is bad, because on presence I turn the display on, and on vacancy off. I guess my living room put on a light show for my cats last night.
I suspect I could fix this in the firmware of the sensor, but that's pointless because I can't reverse engineer the sensor protocol anyway. What's the next best fix?
I moved the sensor out of the way, while I think of a better placement.
Homeboard: eInk display
Post by Nico Brailovsky @ 2025-03-15 | Permalink | Leave a comment
Homeboard gained a new form factor: slightly less crappy frame.
I now keep two Homeboards, one in my office -mostly for hacking- and one to display pictures. The one in my office didn't have a good space for the eInk display (spoiler alert: it still doesn't) making it awkward to see both the "real" display and the eink one. To fix this, I built a new mount based on a picture frame. This time all of the elements are mounted directly on the front frame (spoiler alert: this was a huge mistake), and I used transparent perspex material to cut it, so that all elements are visible (I do like this bit, the boards that make up Homeboard are quite pretty).
Mechanics
The build uses an Ikea picture frame, but replaces the front plate with my laser-cut front.
- The Ikea frame is great for this, it's built to support a front plate of 3-6mm, fitting a perspex sheet ferpectly.
- I'm happy with the display corner clips, too. You can see in the picture they hold the display, but are not too obtrusive (only partly due to the clips being transparent). Additionally, they are great to clip on small boards with no mount holes, like the radar sensor (top left in the picture).
- The ribbon connection to the display is hell. The position is awkward, and I can't fit it with a short (2cm) cable. I used a long one (15cm) but it looks untidy.
- Don't overtighten display screws! It's easy to put too much pressure and damage either the two perspex sheets, or the sandwiched display in the middle. I found for a 3mm perspex sheet with a laptop display, 10mm m2 screws loosely tightened (?) work best.
- If you use my mechanical drawings, be careful: between ID V1 and this one, there was bitrot in my svg, and the screws in the pi don't align anymore. Also, the display hole isi about 2mm too big for my panel, and I don't know why (my last cut it was 2mm to small!)
The back of the frame:
Some things I need to improve:
- Ribbon, long or short, placing is super hard. For V2 of this ID, I need to think of a better placement
- In fact, mounting everything to the front panel was a big mistake. It means that mounting things is awkward, because I need to work with a big panel. Any wiring mistake means I need to unmount the board, fix, test, remount. It's much much MUCH easier if I mount all the boards to a single main perspex board, then mount that to the main frame.
- Having a main board with alternative mount position should make it easier to make mounting the ribbon cable less terrible. I need to move the edp board 20mm to the right in this ID, but it's much easier if I don't need to carefully align this before I cut it.
- The corner clips are awesome! I can even use to hold sensors without a screw hole. Here I mounted the mmwave sensor (with no mount screw holes) using one of the corner clips.
- This doesn't work for the eInk display, unfortunately. I still need to figure out how to mount the eInk display without using tape.
Homeboard: eInk display
Post by Nico Brailovsky @ 2025-02-23 | Permalink | Leave a comment
What's better than one display? Two displays, of course.
When I see a picture in my Homeboard, I often remember when and where I took it (photos are, after all, a form of exomemory), but not always. In wwwslide, my home slideshow service, I workaround this with a QR code: a small QR code is displayed in a corner of the image, and I can scan it to read the metadata of the picture being displayed. This is a good solution, but I'm not entirely happy with it.
Today, I added an eInk display to my Homeboard project. I can show picture metadata (and maybe even a QR code!) without taking up valuable picture real-estate. I chose an eInk display because they are easy to source and work with, relatively cheap, and require very little power (Homeboard is powered by PoE). Some day, I'm hoping to use it as an extra low-power mechanism to show actual homeboard info (a clock? weather? price of memecoins? The options are endless!)
I couldn't get all of the manufacturer's examples to work (especially the partial refresh), but it works well enough to display a thing rendered with Cairo. The original manufacturer's examples had a custom rendering library which was quite unnecessary; my version of lib-eInk gets rid of all the custom rendering code, and uses Cairo to create graphics. Here's an example:
struct EInkDisplay display = eink_init();
cairo_t cr = eink_get_cairo(display);
// Get display's surface
cairo_surface_t *surface = cairo_get_target(cr);
const size_t width = cairo_image_surface_get_width(surface);
const size_t height = cairo_image_surface_get_height(surface);
// Configure "pen"
cairo_set_source_rgba(cr, 0, 0, 0, 1);
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size(cr, 20);
// Calculate text position
cairo_text_extents_t extents;
cairo_text_extents(cr, "Hola mundo", &extents);
double x = (width - extents.width) / 2 - extents.x_bearing;
double y = (height - extents.height) / 2 - extents.y_bearing;
// Draw
cairo_move_to(cr, x, y);
cairo_show_text(cr, text);
eink_render(display);
eink_delete(display);
Sidenote: my multiline code rendering seems to be eating pointers for breakfast, so struct S*
may be rendered as struct S
. I should fix this.
Homeboard V1, bootstrap V2
Post by Nico Brailovsky @ 2025-02-16 | Permalink | Leave a comment
With ~most~ some of the bugs fixed in the industrial design, it's time to setup a second Homeboard. That way I can experiment on one, while the other shows pretty pictures. Because my computer is also a new install, it's now a good opportunity to document the full bootstrap process from an almost brand new and clean Ubuntu 24.04.
Bootstrap a new devenv
- Get normal dev tools
sudo apt-get install build-essential git llvm vim
- The linker needs to learn how to build arm binaries:
sudo apt-get install crossbuild-essential-armel crossbuild-essential-armhf
- Clone the sw project:
git clone git@github.com:nicolasbrailo/homeboard.git
- Don't forget to
git submodule update --init --recursive
- Type
make xcompile-start
in the root of gpio_mon. It will, on its first run, setup the cross-compile environment. - The x-compile env will be "hardcoded" to some rpi image, for example
2024-11-19-raspios-bookworm-armhf.img.xz
. You probably want to update~/src/homeboard/pi_gpio_mon/rpiz-xcompile/mount_rpy_root.sh
to make it point to a newer image, ideally the same one you will use to bootstrap the sd card. - Once
make xcompile-start
finishes, you can check it succeeded;~/src/xcomp-rpiz-env/mnt
should contain a copy of the rpi environment (the x-compile root)
Bootstrap the OS
This article has been updated to work, but the gist of it is:
- Find the ISO you used for the x-compile env, then
sudo dd of=/dev/sdX if=./XXXX.img bs=8M status=progress
- Mount the SD card and enable ssh:
cd /media/$USER/bootfs && touch ssh && touch ssh.txt
- Create user (headless):
echo username:password > /media/$USER/bootfs/userconf.txt
- [Wayland] Add this magic to /boot/firmware/config.txt
dtoverlay=vc4-kms-v3d
gpu_mem=128
- [More Wayland] /boot/firmware/cmdline.txt needs to have
wayland=on
- Boot up with the SD card, then ssh into the device and do
sudo apt-get install mesa-utils-bin wayfire seatd
- Add Wayfire as a service
Build things
- Update the TARGET_IP in the makefile, then
make setup-ssh
to enable passwordless ssh - Start with the
gpio_mon
project, it's the simplest.cd ~/src/homeboard/pi_gpio_mon
. If youmake
, it will either fail or create a binary in the wrong format if you haven't set up the cross-compile environment (see "bootstrap new devenv"). - After
make
succeeds,file gpiomon
should show something likeELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked
. This means your system can now build binaries for your target platform. scp gpiomon $target
-> try out if your xcompile env works as expected
Build harder things
- Move on to
wl_display_toggle
(it's the smallest project that exercises the entire stack: cross compiler and Wayfire). - There are more system deps you'll need to install;
make install_system_deps
should take care of most of them. - There are deps for the x-compile env too;
make install_sysroot_deps
should take care of most of them. Some deps may move around, and you may need to find newer versions. - Now
cd wl_display_toggle
thenmake
andscp wl_display_toggle $TARGET
- ssh into the target, and try to shut off the display:
XDG_RUNTIME_DIR=/home/batman/run WAYLAND_DISPLAY="wayland-1" DISPLAY="" ./wl_display_toggle off
Install services
The homeboard doesn't do much nowadays, only show images; once you reached this point, and if things build and run, your build environment and target are ready to use. Just a few more arcane spells and we're done:
- Clean up binaries deployed ad-hoc, like gpio_mon and wl_display_toggle
make deploytgt
- In the target, try out hackimg
- Run
XDG_RUNTIME_DIR=/home/batman/run WAYLAND_DISPLAY="wayland-1" DISPLAY="" /usr/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3 /home/batman/homeboard/bin/hackimg /home/batman/homeboard/cfg/hackimg.cfg
- You'll need to create the cache dir manually, because hackimg is lazy and won't do it for you
- Run
- Once you checked hackimg runs,
vi ~/homeboard/cfg/pipresencemon.cfg
- Set the sensor pin to the GPIO acting as presence sensor
- Adapt the sensitivity to sensor type (mmwave vs PIR)
- It's recommendable to use the mock gpio for a test run
- Try out the ambience service
- In the target,
cd ~/homeboard/scripts && ./install_svc.sh
- this will install the ambience service and launch it. Wayfire should already be a service by now, so no install is included. - Use
~/homeboard/scripts/logs.sh
to see what's broken.
The target should be ready for production, in only about 30 simple steps!
Appendix: it hangs!
Homeboard: Industrial Design (bonus: Inkscape)
Post by Nico Brailovsky @ 2025-02-09 | Permalink | Leave a comment
My Homeboard project has officially left its cardboard pizza phase. Almost:
The 2 or 3 pixels above show the first "industrial design" of the homeboard. Or at least the parts that "work". It's hanging from a wall, like a real picture frame. Unfortunately it has bugs, and all its guts are hanging from the top.
I spent some time working on a mount, cut with a laser engraver. The mount has two main pieces: a frame for the display, and a horizontal mount that can be hanged from a hook in the wall. The vertical display frame slots into the horizontal mount, meaning there is no flimsy glue holding expensive equipment: gravity does the job. There are some screws and Ls to give it a nice shape, but the main stress between the hook in the wall and the display is supported by the material strength, not by glue. All the cool electronics fit in a small box on top of the horizontal mount. Or at least that's the idea.
As nice as my design is, it has bugs: You can see in the picture I forgot to consider that wires, especially fat cables such as HDMIs, have physical properties, such as bend radius. Without a slot for wiring, the electronics that fit nicely on the top box in my drawing, actually protrude from the top. The ribbon cable was mirrored in my drawing, meaning a weird 180-degree twist was needed to fit the screen to the main board. The box itself doesn't lock, because the "teeth" are slightly misaligned. And the screw holes for the Raspberry Pi are about a quarter mm out of alignment.
Attached to this post is my SVG design, with theoretical bug-fixes for the problems (version 3, if anyone is counting). I haven't tried printing it yet, and I wouldn't be surprised if V4 is required too.
Image above shows the outline; clicking on it should open the original svg, which is probably mostly blank because vector laser cuts have 0.001mm strokes. Download and open with Inkscape to see it (you may need to change the view mode to outline, too).
Bonus: misc Inkscape tips
My experience with anything that has colors is zero, and I had to spend time learning how Inkscape works to build the design above. Seeing a mechanical design you have in your head come to life with a laser cutter is incredibly rewarding, and I can see myself embarking in more ambitious designs some day, when I have more free time. Here's a list of things I learned and should remember next time I'm using Inkscape:
- It's easy to build complex shapes from basic ones using Path > Union/Difference/etc
- millimeter alignment is hard by hand, but using the position and size input boxes it becomes easy. Start all sub-assemblies in a new drawing, at (0,0), and follow the plans to build the full assembly.
- Actually, alignment by hand is easy (just not precise). It can be a time saver: Build guide-rules, then align by hand, finally adjust the position coordinates for precise fitting. For example, to place a screw hole in the bottom right corner, 3mm from the borders: the hard way is to calculate the position (width of board - 3mm - hole size / 2), same for height. The easy way: create a guide line at
width - 3mm
andheight - 3mm
. Place hole by hand, zooming in. The coordinates will usually be a few 100s or 10s of micrometers (um!) from the correct value, which you can then set by hand. - Actually, there's an even easier way: An element in inkscape will have 8 arrows around it. By default, the center of coordinates is the center of the object, but clicking on any of these arrows will make the coordinates relative to it. That means you can select the top center arrow of a screw hole, enter
board width - 3
to position it horizontally, then select the left center arrow and enterboard height - 3
to position it vertically. - When I write
board width - 3
I actually mean you can writeNNN - XXX
in the position boxes of Inkscape. They perform basic math operations. This is a huge time saver. - Most boards are regular, and have screw holes in symmetric positions vertically and horizontally. When this is the case, you can place all 4 screw holes by mirroring the first one: place the top left screw hole, then select it together with a box the size of the board. Mirror the board vertically, and place a new hole in the position of the first. Select both holes, mirror horizontally, etc. Voila, 4 screw holes with only one measurement!
Zigbee Boiler: bugfix addendum
Post by Nico Brailovsky @ 2025-01-07 | Permalink | Leave a comment
I came back from holidays with a mystery to solve: my automated heating system kept, for hours, trying to shut down heating. Why wasn't it turning off?
After a few weeks, once home (because my home automation has no inbound internet access, only message publishing to Telegram) the puzzle got more interesting: the logs said it was trying to shut heating down, however its state was never "on" in the first place. To top it off, the temp charts showed this: Crazy high spikes, as high as 25 degrees. Why did the system think it was off while the boiler was running?
I tried a few lines of investigation. Sometimes sensors misreport data, saying a room is 0, or Nan, or 50 degrees (C!), or some other unreasonable value, however those are filtered out. Maybe a bit flipped and the Zigbee name/alias is different, but no, I could control the boiler normally if I manually turned it on when it was off. It was like something else was controlling the device... and then I re-read my own article.
Turns out I never disconnected the original RF controller, I only added a new parallel one. I figured it'd be useful, should my Zigbee controller ever fail! Turns out I chucked the original RF control in a drawer, in a cold corner of my house where I have the heating off. In a cold week, the room where the controller was never reached the minimum temperature to turn the heating off, despite my careful network of sensors shouting "it's hot in here, turn it off".
Fun fact: I bought current sensor to detect when the boiler is on, but it doesn't match the Zigbee state. It was sitting in a drawer, next to the original RF controller. This would make a great fail of the week, too bad the Embedded Muse is no longer running.
I'm not looking forward to the gas bill next month, but at least my cats got to enjoy a balmy 24C winter break for a few weeks.
Homeboard: Wayland on X
Post by Nico Brailovsky @ 2024-10-28 | Permalink | Leave a comment
Besides cross-compiling to RaspberryPi, at times it's also useful to just run things locally. While faster than building on the target, the cycle of xcompile and deploy is still cumbersome for short sessions (i.e. when the target is usually offline, unpowered, and possibly lost somewhere in my house). For these situations, I found out I can run Wayland based apps on top of my X-based desktop, using Weston.
Weston is an implementation of Wayland. If you don't have it already, you can apt-get install weston
. If you do this in an X based desktop, you can still run weston in a terminal, inside X.
Between Wayland on X and cross-compiling to RaspberryPi, I can test my fork (hack) of Swayimg for RaspberryPi Zero.
Crosscompiling to RaspberryPi Zero
Post by Nico Brailovsky @ 2024-10-12 | Permalink | Leave a comment
Homeboard continues progressing, albeit at a snail pace. Using a RaspberryPi Zero as the base board means not only the project runs at a leisurely pace, but so do any attempts at compiling software in the target itself. Because I got tired of measuring my build times in minutes, I decided it's time to set up a cross-compiler from my PC to my homeboard. This means I can now build things in my reasonably fast PC, and deploy the resulting binary to the RaspberryPi Zero.
Setting up a cross compiler from scratch can be challenging, as it requires replicating a large chunk of your target. Luckily, the Raspberry Pi is a popular platform and plenty of articles explaining how to set up a x-compiler are available. Unluckily, I found most of them didn't work for me, with my host being Debian Bookworm. In the end I managed to find a combination of arcane spells to make x-compiling work.
First, get a Raspberry Pi Zero image, and mount it locally. This will be the sys-root of the target when x-compiling:
wget https://downloads.raspberrypi.com/raspios_armhf/images/raspios_armhf-2024-07-04/2024-07-04-raspios-bookworm-armhf.img.xz
xz -d 2024-07-04-raspios-bookworm-armhf.img.xz
# Find out the mount-start offset (multiply by 512)
fdisk -lu "$IMG_FNAME" | grep Linux | awk '{print $2}'
mkdir -p mnt
mount -o loop,offset=541065216 2024-07-04-raspios-bookworm-armhf.img.xz ./mnt
And to build things:
clang -target arm-linux-gnueabihf -mcpu=arm1176jzf-s --sysroot ./mnt/ test.c
That's all; this should create a binary in armv6 format, ready to be deployed to your target. A few things I discovered:
- I couldn't make this work with gcc. I don't know why.
- If your
--sysroot
isn't correct, things won't work. You won't get an error, but a binary will still be built; it will just be a binary with the wrong format, and you'll only know because it will segfault on start. Good luck trying to figure out if the segfault is yours, or from a problem in the build process.
I wrapped this in a convenient bash script so you can build a makefile that will x-compile easily, have a look here: https://github.com/nicolasbrailo/rpiz-xcompile
Homeboard: wwwslide
Post by Nico Brailovsky @ 2024-09-10 | Permalink | Leave a comment
Homeboard hasn't seen much progress during the holidays, except for a small but useful piece of software: I created a hacky way to serve pictures over a web interface. This is a fairly fundamental piece of infrastructure for my homeboard project; most of the time these will be displaying some ambient information, but most of the screen's real estate will be used to show my (reasonably large and decades spanning) personal picture collection.
wwwslide looks like this:
From the readme:
wwwslide is a client/server for LAN slideshows. If you have a large picture collection and want a way to display them in multiple places, wwwslide server will create a web interface to retrieve random pictures from a single url. The web client can display these pictures, but there is no reason to use the included client: you could curl wwwslide and pipe it to an image viewer.
wwwslide has a server that can be pointed to a local pictures directory. It expects that pictures will be grouped in albums, sorted by /$year/$arbitrary_name/.jpg (eg 2019/foo/bar/album/.jpg). On startup, it will pick up one album, randomly, and serve a few pictures from this album to anyone calling its /get_image web endpoint. Once it runs out of pictures for this album, it will select a new random album (with a new random subset of pictures).
The included client (which can be accessed on the root of the server) can be used to browser this picture (just point your browser to your wwwslide LAN address). It's not very smart, but it should work!
Remote control: each picture includes a QR code. Scanning the QR will take you to a local page with metadata of the shown image. This page can also be used to control wwwslide (eg to request that this album is displayed from the start, or to select a new album)
Reverse geolocation: the metadata of each picture includes a reverse-geolocation. No need to guess where you took a picture, wwwslide will guess for you (as long as your pictures have geotags in their exif data)
wwwslide v0 was just a Flask service sending local-disk jpg's, and I found I frequently wondered where a particular picture was taken, or wish I had a way to see more pictures from a specific location. I'm quite proud of the idea to implement this: wwwslide will watermark pictures with a qr-code that can be used to get more info on the shown picture, and to display more picture of a specific album.