Skip to content

Screens

Fugleramme serves the collage over HTTP, so it can show on more than the e-ink panel.

Two pictures

The frame draws two pictures of the same birds, with the same mode, style and names:

  • The panel's page is what the e-ink panel shows. It is dithered to the panel's six inks, on plain paper, at the panel's own size and Rotation. New birds change it at most as often as Panel refresh allows, and it stays up while BirdNET-Go is unreachable.
  • The web page, at /collage.png, is in full colour on textured paper, at the size you pick under Resolution. With Lock to panel on it has the panel's shape, otherwise its own Aspect. It is drawn whenever something asks for it, so new birds show up straight away.

The preview on the admin page is the panel's page in full colour, with any changes you have not saved yet. Without a panel it shows the web page.

Overview of different rendering endpoints

Six ways to show them, easiest first.

From another device

Open http://<host>.local:8080/ (or whichever address you have Fugleramme running on) on a phone, tablet or laptop on the same network.

On a tablet, add the page to the home screen and it opens like an app, with its own icon and no address bar:

  • iPad: in Safari, tap Share, then Add to Home Screen.
  • Android: in Chrome, open the ⋮ menu, then Add to home screen.

Note

The icon remembers the address it was saved with, so use the hostname, or give the machine running Fugleramme a static IP lease in your router.

On HDMI, Raspberry Pi OS Desktop

When the frame can't connect to a network, the screen shows the Wi-Fi setup.

The desktop OS already has a browser and a session to run it in:

chromium --kiosk http://localhost:8080/

To start it with the desktop, add the same line to ~/.config/labwc/autostart (create the file if it isn't there):

chromium --kiosk http://localhost:8080/ &

Note

The package is chromium on Trixie. Older guides say chromium-browser, which is now an empty package that pulls in chromium anyway.

On HDMI, Raspberry Pi OS Lite

Lite has no browser and no display server, so installing chromium on its own is not enough. It needs a compositor and a session too. cage is the smallest one that will do: it runs a single app fullscreen and has nothing to configure.

ssh <user>@<host>.local
sudo apt install --no-install-recommends chromium cage

cage draws a mouse pointer in the middle of the screen, even with no mouse plugged in, and the page can't hide it. A cursor theme whose cursors are a single transparent pixel does:

sudo mkdir -p /usr/share/icons/invisible/cursors
sudo python3 - <<'EOF'
import struct
# An Xcursor file: a header, one table-of-contents entry and one transparent 1x1 image.
IMAGE, SIZE = 0xFFFD0002, 1
header = b"Xcur" + struct.pack("<III", 16, 0x10000, 1)
toc = struct.pack("<III", IMAGE, SIZE, 28)
chunk = struct.pack("<IIIIIIIII", 36, IMAGE, SIZE, 1, 1, 1, 0, 0, 0) + struct.pack("<I", 0)
open("/usr/share/icons/invisible/cursors/default", "wb").write(header + toc + chunk)
EOF
for name in left_ptr arrow pointer hand1 hand2 text xterm watch wait progress crosshair grab grabbing; do
  sudo ln -sf default /usr/share/icons/invisible/cursors/$name
done
printf '[Icon Theme]\nName=invisible\n' | sudo tee /usr/share/icons/invisible/index.theme
mkdir -p ~/.icons/default
ln -sfn /usr/share/icons/invisible/cursors ~/.icons/default/cursors

The browser picks the theme up through XCURSOR_THEME in the service below. cage loads the theme called default no matter what that says, which is what the last two lines are for.

Then a service, so it comes up with the Pi. PAMName and TTYPath are what give the browser a login session and a seat on the screen, which is the part that is missing on Lite:

sudo tee /etc/systemd/system/fugleramme-kiosk.service <<'EOF'
[Unit]
Description=Fugleramme HDMI kiosk
After=systemd-user-sessions.service getty@tty1.service fugleramme-frame.service
Conflicts=getty@tty1.service

[Service]
User=<user>
PAMName=login
TTYPath=/dev/tty1
TTYReset=yes
TTYVHangup=yes
StandardInput=tty-force
Environment=XCURSOR_THEME=invisible
ExecStart=/usr/bin/cage -- /usr/bin/chromium --kiosk --ozone-platform=wayland --noerrdialogs --disable-infobars http://localhost:8080/
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl disable getty@tty1.service
sudo systemctl enable --now fugleramme-kiosk

Replace <user> with your username, and 8080 with FRAME_PORT if you changed it.

Disabling getty@tty1 is not optional. Without it the login prompt and the kiosk are both pulled in by the same boot transaction, systemd drops one of the two conflicting jobs, and often it is the kiosk - which starts fine by hand and then doesn't come back after a reboot.

Important

This gives up the login prompt on the attached screen, so SSH becomes the only way in. sudo systemctl enable getty@tty1.service puts it back.

Logs, when it doesn't come up:

journalctl -u fugleramme-kiosk -f

Note

A portrait screen is rotated by the display, not by the frame. The Rotation setting on the admin page changes the shape of the page, or Portrait does with Lock to panel off, and only the e-ink panel turns its own pixels; for HDMI, rotate the output in /boot/firmware/cmdline.txt (e.g. video=HDMI-A-1:1920x1080@60,rotate=90).

On a Samsung Frame TV

Courtesy of Conrad Jackson (@conradj): fugleramme-samsung-frame sends the collage to a Samsung Frame TV while it is in Art Mode. It runs next to the frame and checks for a new collage every 15 minutes. Setup is in that repo's README.

The TV only offers its own mats when the picture is exactly its size. For that, turn off Lock to panel on the admin page and set Resolution to 4K and Aspect to 16:9. Set Margin to 0 too, since the TV adds its own mat - with an e-ink panel beside it, turn off Uniform first, so the panel keeps its own margin.

A Samsung Frame showing a Fugleramme collage among framed artwork in a living room

Close-up of the collage on the Samsung Frame

Photos by Conrad Jackson, from Fugleramme for Samsung Frame TV.

On an external e-ink panel

Fugleramme provides image data endpoints which allow an e-ink panel driven by another device to render its content:

  • /frame.png returns the page as a six-color, dithered PNG in the panel's native orientation, which can be passed straight to the inky Python library for an Inky Impression.
  • /frame.e6 returns a custom image format that's convenient to use with the Seeed Studio 13.3" Spectra 6 panel on a XIAO EE02.

Important

Like the kiosk, /frame.e6 and /frame.png need no sign-in: anyone who can reach the frame can read them.

To activate these endpoints, enable External e-ink panel on the admin page, under Frame. Fugleramme then serves the pages as http://<host>.local:8080/frame.png and http://<host>.local:8080/frame.e6, already dithered and rotated as necessary.

Choose your panel size under External panel size. Rotation, Margin and Panel refresh all apply to it. Until you choose one, it gets the same as the Inky connected to this frame, or a 13.3" page if there is none.

Buttons

An external panel's buttons work just like the frame's own, through a form POST to /press:

curl -X POST http://<host>.local:8080/press -d button=A -d password=<admin password>
  • button - A, B, C or D (there is no 5 second hold for the Wi-Fi setup)
  • password - only needed if the admin page requires sign-in

Responses: 204 on success, 400 for an unknown button, 401 for a wrong or missing password, 429 after too many attempts, and 404 if External e-ink panel is not enabled.

The password is sent unencrypted over plain HTTP. If the frame is reachable from the internet or an unsafe network, serve it over HTTPS behind a reverse proxy.

Inky example code

examples/inky/main.py is a Python program for a Pi with an Inky Impression, acting as a client of a remote Fugleramme. It checks /frame.png every 5 seconds and downloads it only if it has changed. If the frame can't be reached, the panel keeps showing its last page. The panel's buttons trigger the corresponding actions on the remote frame via /press.

Tick External e-ink panel on the frame's admin page and set External panel size to match your panel, then on the Pi with the Inky, one command:

curl -fsSL https://raw.githubusercontent.com/arnegiacomo/fugleramme/main/examples/inky/install.sh | bash

It asks for the frame's address and the admin password (leave it empty if auth is not enabled), enables SPI and I2C, and sets the client up as a systemd service. If it tells you to reboot, do it - an image should show on the display once the Pi is online. Rerun command again to update; your answers are kept.

Logs: journalctl -u fugleramme-client -f

Contributed by MT Lewis. Tested on a 13.3" panel with a Pi Zero 2 W.

EE02 example code

examples/ee02/main.py is a MicroPython program for the XIAO EE02, which runs from a battery and can hang anywhere in Wi-Fi range. It wakes for the interval the page's header carries in its Panel refresh setting. So a 10 minute setting has it fetch every 10 minutes, and it never fetches more often than its own 5 minute floor, even when Panel refresh is off. It downloads the page only if it has changed, and redraws the panel only once the download checks out. If something goes wrong, it writes an error message on the panel instead.

  1. Install MicroPython on the board (e.g. for the XIAO EE02)
  2. Set your Wi-Fi credentials and Fugleramme host in config.py. A frame served over HTTPS on port 443 is fetched over TLS.
  3. Upload main.py, boot.py and config.py to the board (e.g. with mpremote)

boot.py runs on every boot before main.py. It is what lets the panel update its own program: set FIRMWARE in config.py to a URL serving the panel's latest program (as plain file, no sign-in or redirect), and the panel checks it on its wakes. A new program is staged, and after a restart it gets one trial wake; if the panel reaches the frame cleanly on it, the update is kept, otherwise the board goes back to the program it had before. Leave FIRMWARE at None and nothing is downloaded.

Contributed by Lorenz Schmid. Tested on a 13.3" panel with a XIAO EE02.

Much of the program is generic and reusable, but the connection to the 13.3" panel and the controller board is specific. Running a smaller panel or another board? A program for it, and the packing it needs, would make a welcome contribution - see Contributing.

E6 file format

The E6 file format is not an official standard. It simply consists of a stream of ink values, one pixel at a time. Upon request, a microcontroller downloads the stream and uses it directly to program its connected e-ink panel.

To avoid programming corrupted data or pixels laid out for a different screen size (i.e., when the configuration does not match the e-ink panel), a header with all relevant configuration details is sent ahead of the payload. If the header's configuration does not match the microcontroller's, the payload is discarded.

The header is 20 bytes long, little-endian, and is followed directly by the pixel data.

Offset Size Field
0 4 E6F1
4 2 Version, 1
6 1 Encoding, 1 (the colour codes below)
7 1 Poll minutes, from Panel refresh
8 2 Width, 1200
10 2 Height, 1600
12 4 Length of the pixels, 960000
16 4 CRC-32 of the pixels

The pixels are already oriented the way the panel's controllers read them, so a client only needs to stream them. Two pixels to a byte, the left one in the high half; black 0, white 1, yellow 2, red 3, blue 5, green 6. The panel has two controllers, one per half: the first 480000 bytes are the left half of every row, top to bottom, for the first controller, and the rest are the right halves for the second. With a different Inky connected, the file carries that panel's page, which its width and height reveal, so check them.

The response carries an ETag; send it back as If-None-Match and an unchanged page responds with 304 and nothing to download. 404 means the setting is off, and 503 that the frame has not drawn a page yet.

As a desktop wallpaper and/or screensaver (MacOs)

Turn off Lock to panel on the admin page and set Resolution and Aspect to match your screen, then:

curl -fsSL https://raw.githubusercontent.com/arnegiacomo/fugleramme/main/examples/wallpaper-macos.sh | sh -s -- http://<your-fugleramme-address>:8080

Add --every x after the address to fetch every x minutes (default 15). The five last rendered pages stay in ~/Pictures/Fugleramme.

A MacBook with the collage as its desktop picture

For the screen saver, open System Settings, Screen Saver, set Use Screen Saver to Custom and pick Photos under Other. Under Options, choose the Fugleramme-folder as the source. The screen saver reads the folder when it starts and will shuffle between the 5 last renders.

The screen saver settings: Photos under Other, then Options, Source and Choose Folder

It should look something like:

Not set fugleramme up yourself yet? Point it at a demo, https://fugleramme.arnegiacomo.dev or any showcase address.

Run this to disable:

sh examples/wallpaper-macos.sh --remove

The collage on the lock screen of a MacBook