====== A digital frame with the Raspberry Pi ====== **Digital frames** have become common gadgets, easily available in electronics stores or online. Unfortunately they came with proprietary software, and limited flexibility. So my choice was to purchase a nice 24 inches monitor and boundle it with a **Raspberry Pi 2**, fitted with a **64 Gb SD card**. For the software part I opted for a **web based** user interface written in **PHP**, which allow to easily browse all the **pictures organized in directories**. Each directory containing images have a **playlist.m3u** file, which lists the files to be shown in the slideshow. The playlist file contains also geometry information to re-frame the pictures to fit the screen as requested. So I can show photos taken in **4:3** ratio on the **16:9 screen**, without the **black borders**. All the magic of the slideshow is accomplished by the **[[https://github.com/RigacciOrg/photo-reframe-slideshow|photo-reframe-slideshow]]** software. {{.:raspberrypi:digital-frame.jpg?512|The Raspberry Pi Digital Frame}} In the picture above you can see the final assembly. The monitor is a **Philips 243V7QJABF, 24 inches LED IPS** capable of **1920x1080** resolution, the software runs on the **Raspberry Pi 2 Model B Rev 1.1**, using a **64 Gb SD card**. ===== Building the frame ===== The first step was to order the hand-made **wood frame** from a craftsman. The profile size is shown in the picture, the inner space to accommodate the monitor is **54.0 x 32.5 cm**. {{ .:raspberrypi:frame-profile.png?280 |Wood Frame Profile}} I made **two aluminum brackets** suitably drilled and bent to connect the frame and the monitor, using the **Vesa mount holes**. {{ .:raspberrypi:digital-frame-brackets.jpg?480 |Aluminium brackets}} {{ .:raspberrypi:digital-frame-back.jpg?480 |Raspberry Pi Digital Frame: back}} I used some **electrical wiring**, clamps and heat-shrink tubing to provide the power. The Raspberry Pi **power supply** is glued to the monitor back with a bit of **silicone adhesive**. A few pieces of **felt and a cloth** were used as padding to prevent the metal from dirtying the wall. Finally a bit of electrical cable was used to make **the hanging hook**, built so that the angle of the frame could be adjusted downwards. {{ .:raspberrypi:digital-frame-cabling.jpg?480 |Power supply for the Monitor and the Raspberry Pi}} I had to cut some of the wooden frame to **allow access to the monitor buttons**, as you can see from the photograph of the detail. The **Raspberry Pi** is mounted with wood screws, an insulating plastic sheet and spacers made with rubber tube. {{.:raspberrypi:digital-frame-wood-cut.jpg?260|Wood cut to expose the buttons}} {{.:raspberrypi:digital-frame-buttons.jpg?260|Monitor buttons}} {{.:raspberrypi:digital-frame-raspberry-mount.jpg?260|Raspberry Pi mounting}} ===== Tip to improve ===== As you can see from the final assemble, the **bottom frame** of the monitor is higher than the wood frame, so a **black stripe** remains visible at the bottom. It would have been better to make a **passepartout all around** the monitor to hide that difference. ===== photo-reframe ===== The **software core** of the digital frame is the **[[https://github.com/RigacciOrg/photo-reframe-slideshow|photo-reframe-slideshow]]** program. Its main feature is the ability to show pictures at a specific **zoom** and **pan** points, without requiring to actually cut the original pictures. You upload the original pictures along a **playlist** file, which contains the re-frame options to be applied on the fly. ===== CheatSheet ===== Here it is a **Cheat Sheet** to be printed on a 7x7 cm sticker and attached below the **wireless keyboard**. Some shortcuts are related to the **photo-reframe** program, others are related to the **XFCE** desktop (default and custom shortcuts). ^ Space | Immagine successiva | ^ Backspace | Immagine precedente | ^ P | Avvia o ferma la presentazione | ^ C | Mostra o nasconde il commento | ^ I | Mostra o nascondi le info | ^ +/- | Aumenta o diminuisce lo zoom | ^ Frecce | Panoramica | ^ Esc / Q | Esci | ^ F1 | Help | ^ F2 | Condividi via email | ^ Ctrl-Q | Poweroff, Reboot, etc. | ^ Alt-F1 | XFCE Menu | ^ Alt-F2 | Run program | ===== RaspiOS customization ===== I downloaded and installed **RaspiOS 2020-05-27 Buster**, based on Debian 10. I choosed the **full** version "with desktop and recommended software". ==== WiFi ==== I installed the **Xfce** desktop environment; on Debian 10 this is bundled with the **[[https://packages.debian.org/buster/network-manager|NetworkManager]]** service, which handles wired and wireless connections. Beware that NetworkManager does conflict with the **/etc/wpa_supplicant/wpa_supplicant.conf** configuration file: if you configure a WiFi connection in this file, you will end-up with two **wpa_supplicant** instances conflicting each other (so you shouldn't even use the trick to [[https://www.raspberrypi.org/documentation/configuration/wireless/headless.md|setup a Raspberry Pi headless]]). Use instead the NetworkManager applet from the Xfce environment to connect to your WiFi network, the network preferences will be saved into **/etc/NetworkManager/system-connections/**. ==== Desktop Autologin ==== We want the Raspberry Pi to start automatically a graphic session, so in **raspi-config** we selected **Boot Options** => **Desktop / CLI** => **Desktop Autologin**. The logged-in user will be **pi**. ==== Syslog ==== To limit the useage of the SD card, we prefer to generate system logs in RAM instead of writing them to filesystem. So we installed the **busybox-syslogd** packages, which removes the **rsyslog** one. Once installed, you can see system log using the **logread** command. ==== GL Warning ==== Running the **photo-reframe** application on the Raspberry Pi will show a warning message: libEGL warning: DRI2: failed to authenticate It turned out that we have to set the //GL Driver// to avoid that warning. Once launched **raspi-config**, select **Advanced Options** => **GL Driver** (this choiche will install the **gldriver-test** package) => **GL (Fake KMS)**. Actually we used a fake workaround, because activating the **Full KMS** option causes several crashes of the graphical system, the errors logger are something like: [drm:drm_atomic_helper_wait_for_dependencies [drm_kms_helper]] *ERROR* [PLANE:183:plane-21] flip_done timed out [drm:drm_atomic_helper_wait_for_flip_done [drm_kms_helper]] *ERROR* [CRTC:74:crtc-2] flip_done timed out [drm:drm_atomic_helper_wait_for_dependencies [drm_kms_helper]] *ERROR* [CRTC:74:crtc-2] flip_done timed out [drm:drm_atomic_helper_wait_for_dependencies [drm_kms_helper]] *ERROR* [CONNECTOR:32:HDMI-A-1] flip_done timed out ==== Hide the mouse pointer ==== The **[[https://github.com/RigacciOrg/photo-reframe-slideshow|photo-reframe]]** program used as directory browser and slideshow, uses the **setOverrideCursor()** function to **hide the mouse pointer** when run full screen. Neverthless, if you use only the keyboard to control the interface, the **mouse pointer** at the center of the screen is definitely annoying. Fortunately enough there is the Debian package **unclutter**: once installed you will find a running process when you initiate your desktop session: /usr/bin/unclutter -idle 1 -root The mouse pointer will disappear after one second of **idle time**, just to reappear if you move the mouse. If you want to customize the options, change the file **/etc/default/unclutter**. :!: Unfortunately the **unclutter** program **does not work in Wayland**, which is the graphical backend installed by the Raspberry Pi OS based on Debian 12, in replacement of the legacy X11. If you search for an hide cursor solution on Wayland, beware of this [[https://github.com/seffs/wayfire-plugins-extra-raspbian/releases|hide-cursor plugin]] which is intended for the **Wayland** + **Wayfire** environment (backend and window manager). On the Raspberry Pi 2 only the **Wayland** + **Labwc** environment is supported. For this reason I decided to use raspi-congif and [[raspberry_x_wayland|revert back to the X11]]. ==== Turning off the power LED ==== When the digital frame is in stand-by (the screen is black and turned off), there is a red light glow around the frame, caused by the **Raspberry Pi power LED** being very bright, this is particularly disturbing when the room is dark. Fortunately enough there is a way to turn off the LEDs, just add the following snippet at the end of **/boot/config.txt** and reboot: # Disable Ethernet LEDs dtparam=eth_led0=14 dtparam=eth_led1=14 # Disable the PWR LED dtparam=pwr_led_trigger=none dtparam=pwr_led_activelow=off # Disable the Activity LED dtparam=act_led_trigger=none dtparam=act_led_activelow=off This works well on my **Raspberry Pi 2** model, the matter is discussed in a **[[https://www.raspberrypi.org/forums/viewtopic.php?t=149126|post of the RaspberryPi.org forum]]**. ==== Keyboard Shortcut ==== We choosed XFCE as Desktop Environment, so we can use //Settings// => //Keyboard// => **Application Shortcuts** to associate a key combination to an action. We decided to associate **Ctrl-Q** with the applet **xfce4-session-logout**. The setting is saved into **$HOME/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-keyboard-shortcuts.xml**. ==== Screen Saver ==== The program **photo-reframe** is configured to call two external programs, one to **disable the screensaver** (at program start) and one to **re-enable the screensaver** (at program exit). The two helper programs should be named **screensaver-off** and **screensaver-on** respectively, and they are searched into the **PATH**. In this way you can leave the desktop power saving active: the screen will remain active if a slideshow is running, but it goes blank otherwise. Beware that the **xfce4-power-manager** program overloads the functionality offered by DPMS (controlled by the **xset** program); e.g. the screen blanking is perfomed even with DPMS disabled. The reccommended XFCE power manager settings (//Settings// => //Power Manager// => //Display//) are: * **Display power management**: On - This will **enable DPMS**. * **Blank after**: Never - This prevents the software screen blanking (no DPMS). * **Put sleep after**: 5 minutes - This is enforced via **DPMS Standby**. * **Switch off after**: 6 minutes - This is enforced via **DPMS Off**. With these settings display blanking is controlled by DPMS only (no software blanking); the slideshow program can simply disable DPMS to keep the screen always on. XFCE saves the Power Manager settings into the file **$HOME/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml**. === screensaver-off === #!/bin/sh # Disable software screen saver. xset s off # Disable DPMS screen saver. xset dpms force on xset -dpms === screensaver-on === #!/bin/sh # Disable software screen saver. xset s off # Enable DPMS screen saver. xset dpms 300 300 300 xset +dpms === How to use xset === With the command **xset q** it is possibile to query the status of the **[[wp>VESA_Display_Power_Management_Signaling|DPMS]]** (you must be the user owning the X display): xset q ... DPMS (Energy Star): Standby: 600 Suspend: 600 Off: 600 DPMS is Enabled Monitor is On On the Raspberry Pi, the auto-logon user is **pi**, if you are root you can set the **DISPLAY** and **XAUTHORITY** environment variables before calling xset. Check the running **Xorg** process to get such variables: ps -C Xorg ho args /usr/lib/xorg/Xorg :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch Now you can execute **xset** as user **root**: export DISPLAY=":0" XAUTHORITY="/var/run/lightdm/root/:0"; xset q If you disable the screensaver from the desktop environment (in our case XFCE //Settings// => //Power Manager// => //Display//), you will see the line **DPMS is Disabled** in ''xset q'' output. ==== Desktop Fonts ==== To have bigger fonts into XFCE menu we opened the **Settings** => **Appearance** => **Fonts** menu and increased the font size. Settings are saved into the file **$HOME/.config/xfce4/xfconf/xfce-perchannel-xml/xsettings.xml**. ===== Executing programs from the Firefox browser ===== Our digital frame will use a web page as the main user interface; the browser will start automatically at boot time and it will connect to a page located on **%%http://localhost/%%**. A local running Apache+PHP will serve the page showing thumbnails for image folders. Basically from the web page we want to be able to execute two actions: * The slideshow program **photo-reframe** to actually view the slideshow. * Shell scripts using the **bash** shell, to do actions like shutdown, reboot, etc. FIXME To be completed... ===== Firefox customization ===== **RaspiOS 2020-05-27 Buster** (based on Debian 10.4) installaed **Firefox Version 68.12.0esr**. ==== Disable the session restore page ==== If Firefox is badly closed (e.g. due a power outage, etc.), when it starts again it display the page **about:sessionrestore**. To disable this behaviour open the **about:config** and set: browser.sessionstore.resume_from_crash => false ==== Disable the status panel ==== Firefox shows a status bar in the lower left corner of the window, where it shows the URL currently focused or under the mouse. To disable this feature open the **about:config** page and set: toolkit.legacyUserProfileCustomizations.stylesheets => True then create a file **$HOME/.mozilla/firefox/.default-esr/chrome/userChrome.css** with this content: @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); #statuspanel { display: none !important; } See this [[https://support.mozilla.org/en-US/questions/1285060|support page]]. ==== Starting in fullscreen mode ==== We choosed the **[[https://github.com/tazeat/AutoFullscreen|AutoFullscreen]]** extension, by **tazeat**. Open the Firefox Menu => Addons => Extensions and search for it. Once installaed, it is saved into the directory **$HOME/.mozilla/firefox/.default-esr/extensions/{}.xpi**. With that extension loaded, Firefox will start in fullscreen automatically. Press **F11** as usual to toggle this state. ===== Programs Autostart ===== We want to run automatically two processes at startup * The **Firefox** browser, pointing to **%%http://localhost/%%**. This is the main user interface, used to browse image folders and to start the slideshow. * The **photo-reframe** program to show images from a randomly generated playlist. This is the slideshow started automatically at startup. We achieve this creating two **.desktop** files into the **.config/autostart** directory of the **pi** user. This is the **/home/pi/.config/autostart/firefox-esr.desktop** file: [Desktop Entry] Name=Firefox ESR Comment=Browse the World Wide Web GenericName=Web Browser X-GNOME-FullName=Firefox ESR Web Browser Exec=/usr/lib/firefox-esr/firefox-esr http://localhost/ Terminal=false X-MultipleArgs=false Type=Application Icon=firefox-esr Categories=Network;WebBrowser; StartupWMClass=Firefox-esr StartupNotify=false Path= This is the **/home/pi/.config/autostart/play-playlists.desktop** file: [Desktop Entry] Type=Application Version=1.0 Name=Play-Playlists Icon=image TryExec=/home/pi/Scripts/play-playlists.sh Exec=/home/pi/Scripts/play-playlists.sh --delay 16 Terminal=false ===== Fixed WiFi MAC address ===== Everytime the Raspberry Pi is rebooted, it gets a new MAC address for the **wlan0** interface. This is a //feature// provided by the **Network Manager** package. If you want to disable this behaviour, create a new file **/etc/NetworkManager/conf.d/100-disable-wifi-mac-randomization.conf** whith this content: [connection] wifi.cloned-mac-address=1 [device] wifi.scan-rand-mac-address=no See the man page for **nm-system-settings.conf**, the article **[[https://fedoramagazine.org/randomize-mac-address-nm/|Randomize your MAC address using NetworkManager]]** and this **[[https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=196348&start=25#p1524581|post]]** on how to disable it on the Raspberry Pi. Alternatively you can uninstall the **NetworkManager** package and use **dhcpcd5** for all the network settings. ===== Web References ===== * **[[https://www.raspberrypi.org/documentation/configuration/config-txt/video.md|Video options in config.txt]]** * **[[https://wiki.archlinux.org/index.php/Xfce#Display_blanking|XFCE, DPMS and display blanking]]**