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.
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.


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.pngreturns the page as a six-color, dithered PNG in the panel's native orientation, which can be passed straight to theinkyPython library for an Inky Impression./frame.e6returns 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,CorD(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.
- Install MicroPython on the board (e.g. for the XIAO EE02)
- Set your Wi-Fi credentials and Fugleramme host in
config.py. A frame served over HTTPS on port 443 is fetched over TLS. - Upload
main.py,boot.pyandconfig.pyto 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.

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.

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
