Booting the Rasperry Pi with a splash image when using Nerves

By default when you boot an RPi running Nerves it will output a lot of log
messages to the screen. Super useful when you’re in development, but not so
pretty when used for products that have a screen attached to the RPi. We have
one such product that we might migrate to Nerves, so we decided to figure out
how to put a fancy boot splash on the RPi.

This requires two things:

  1. Suppressing log output.
  2. Outputting an image to the screen.

I will start by explaining the general steps, and then follow up with a few
tips to enabling this only in production (so we keep all the useful log
messages for development devices). I’ve also made a branch off the official
Nerves RPi3 system with the changes described. The changes required are in
the two most recent commits
here
. Go there if you need a TL;DR :slight_smile:

Note that this requires customizing the official Nerves RPi system - if
you’re not sure what that means, check this out.

Suppressing log output

Both the RPi bootloader, the Linux kernel and our Nerves application will
output stuff (log messages and images) on screen by default. This can be
disabled by setting some options in the files config.txt and cmdline.txt,
which are generated by Buildroot. The settings are:

  1. In config.txt make sure to set disable_splash=1. This will disable the
    boot rainbow. This is already set by default on the RPi3, so we actually
    don’t need to do anything here.

  2. The kernel boot parameters must be adjusted to suppress log output. The
    parameters are set in one long line in the file cmdline.txt. There’s two
    ways to suppress the logout: a) quiet and redirect the console output or b)
    disable the framebuffer console entirely.

a) Quiet and redirect console output

This is the less draconian approach. Instead of printing to tty1, we’ll
direct all output to tty3. This means the output will still be available,
but it won’t be on-screen by default. You can access it by switching to tty3
by clicking CTRL+ALT+F3. It’s not a perfect solution though. For some reason,
some of the output might still be printed on screen. Therefore, we also
minimize the amount of kernel output. To achieve this, ensure the following
options are set in cmdline.txt:

console=tty3 quiet loglevel=0 logo.nologo vt.global_cursor_default=0

The console= option is already specified as tty1 by default, so that has
to be updated. The other keys are new. Their meanings are:

  • quiet: Disables most log messages.
  • loglevel=0: This sets the loglevel of the kernel. Setting it to 0 means it
    only prints emergency level warnings.
  • logo.nologo: This disables printing of the Raspberry Pi logos.
  • vt.global_cursor_default=0: This disables the blinking cursor.

(A good reference to the kernel boot parameters can be found here:
(Linux Kernel Parameters)

We also need to make sure that the log output from our Nerves application goes
to tty3. This is done by setting -c tty3 in the erlinit.config file (by
default it is set to tty1).

All these settings will generally result in a silent boot, but I have read
that the Linux kernel may still print a few log messages to the screen. If
this is not acceptable, we must disable the framebuffer console instead.

b) Disable the framebuffer console

This is the more draconian method. Disabling the framebuffer console will
quelch all output, but it also means you can no longer interact with the
device using screen and keyboard. This is actually an added bonus for us, as
we don’t want that to be possible on production devices. So even though the
less draconian method worked for us, we chose to disable the framebuffer for
this reason.

Disabling the framebuffer console is done by setting the following to
cmdline.txt: fbcon=map:2. This will allocate the console to an
unavailable framebuffer (the RPi only has one framebuffer), effectively
disabling it. Thus, it replaces all the other options suggested above, and
you don’t need to update erlinit.config either.

Outputting an image to the screen

Buildroot has a package for a program called fbv, which can be used for
outputting an image to the screen. It does this by writing to the framebuffer
(I had no idea what that meant, but there is a nice explanation
here). It’s
not included by default by Nerves, so it must be added to the
nerves_defconfig file. You can do this through the Buildroot menuconfig
interface, or by adding the line BR2_PACKAGE_FBV=y to the file.

Once fbv has been added to the system, it must be invoked at boot. We can
do this by telling erlinit to invoke a script before starting the Erlang
VM. This is done by adding the following to the erlinit.config file:
--pre-run-exec "sh /splash.sh". The file splash.sh must be added to
the rootfs_overlay directory, and is a simple one-liner:

#! /bin/sh

# The call to fbv must be backgrounded, otherwise it blocks which means
# the Erlang VM won't boot
fbv /splash.png &

This call to fbv will render the image /splash.png to the framebuffer.
Thus, add an image with that name to the rootfs_overlay directory, and
you’re done.

Enabling it only in production

To enable this only in production, we need to have two different cmdline.txt files
available, one for production and one for development. Thus, you will need two files,
cmdline-dev.txt and cmdline-prod.txt. cmdline-dev.txt should be a copy of the
current cmdline.txt, while cmdline-prod.txt should be modified to either suppress
the kernel logs or disable the framebuffer console, as described above.

To ensure the right cmdline.txt file is used depending on the value of
MIX_ENV, we’ll use that variable in fwup.conf to select the correct file
when generating firmware. Add a fallback definition of MIX_ENV and then
update the host path for the file:

...
#
# Firmware metadata
#

define(MIX_ENV, "${MIX_ENV:-dev}") # Add this line
...
file-resource cmdline.txt {
    host-path = "${NERVES_SYSTEM}/images/cmdline-${MIX_ENV}.txt" # Update this line
}
...

Next, we’ll also add a firmware variable with the content of MIX_ENV.
This will be used to check whether the splash screen is to be rendered or not.
Add a line in the complete task in fwup.conf:

...
task complete {
   ...
   uboot_setenv(uboot-env, "nerves_fw_devpath", ${NERVES_FW_DEVPATH})
   uboot_setenv(uboot-env, "mix_env", ${MIX_ENV}) # Add this line
   uboot_setenv(uboot-env, "a.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
   ...
}
...

Then update the splash.sh script to check the mix_env variable:

#! /bin/sh

if fw_printenv | grep -q "mix_env=prod"; then
    # The call to fbv must be backgrounded, otherwise it blocks which means
    # the Erlang VM won't boot
    fbv /splash.png &
fi

And you’re done! If in doubt, you can see all the changes you need to make on
the two most recent commits here.

Extras

Of course, we’re never really done. One could also consider

  1. Rotating the splash screen to support upright screens. This can be done
    with a firmware variable.
  2. Add a requirement to firmware upgrades that their MIX_ENV variable
    matches what is stored in mix_env.
16 Likes

This is great stuff. I’ve had it in my “you should read this”-notes for a while. It did not disappoint.

Do you think this would be a reasonable feature to work into the core Nerves systems? I figure the fbv package is a very minor addition, probably not very heavy.
The support for different cmdline.txt files might be more significant as well as the details of when to actually invoke the splash and stuff and when that code-path is desirable or not.

I think a lot of people want to do this both for production and DIY fun stuff. For businesses I think it is fairly expected that you might need a custom system. I can’t quite shake the feeling that this would be slick in the default Nerves system, could even start with a nerves logo :wink:

I also imagine it straddles the line of how many batteries the base install should include, so I expect opinions would fall on both sides of this particular fence.

Have you discussed that at all?

1 Like

Thanks, I’m happy it didn’t disappoint.

I have not talked with the Nerves team about this. fbv is pretty small, and it only depends on a few other small things like libpng and libjpeg. But the standard Nerves systems are pretty bare-bones so I don’t know if they want it in there - if they add this, why not X and Y as well?

One could also implement fbv in Elixir and then it would only require adding a library :wink:

4 Likes

Thank you for this great guide.

This is how it looks for me: https://photos.app.goo.gl/zdL84EwMoqyidph66

Any idea on how to improve on the following?

  • Remove the flash at second 4-5
  • Integrate with kernel to show splash from second 1

Thanks again.

Hi @samuelventura, I’m glad you found the guide useful :slight_smile:

Unfortunately, I don’t really have any ideas regarding the flash. I don’t remember if our devices have that flash, and I don’t have one at hand for testing right now. What hardware is your system based on, and what bootloader are you using?

I also don’t have any experience with using the kernel to show a splash screen. I only know that it requires the image to be formatted a certain way, and I am not sure what changes (if any) need to be made to the kernel/Buildroot system. I did find this article which seems to have some good info: Splash Screen on the Linux Kernel | Toradex Developer Center. However, it is for a Yocto-based system, not Buildroot.

I would try enabling psplash (BR2_PACKAGE_PSPLASH_IMAGE) and get that going…

but totally don’t know if it will work - see docs here buildroot/package/psplash/Config.in at 7ab915fe4cc14583df29d27c2ebe9be602d7df74 · buildroot/buildroot · GitHub

you might need to start it:

Start the drawing process with ‘psplash -n&’ as early as
possible. Note: psplash creates a FIFO in /tmp for
communication with psplash-write.

and potentially quit it as well:

  • Terminate psplash:
    psplash-write “QUIT”

if it works then config a custom png see package/psplash: add support to psplash personalized png image · buildroot/buildroot@7ab915f · GitHub

the default png is here https://git.yoctoproject.org/psplash/plain/base-images/psplash-poky.png

I am using a customized Nerves RPI3 System on an RPI CM3+ based industrialized HMI. The nerves system customization enables wifi drivers, sync, realtime clock, among other things. I great experience so far. Nerves makes it very straight forward.

A couple of videos (Spanish audio) of the custom HMIs we are building for industrial customers.

* HMI Demo 1
* HMI Demo 2

It is booting in around 20s and half of that time is blank screen. Having an early splash would easy out that wait.

Thank you for the link about the kernel based splash. I will look into it.

Hey uotlog. I found psplash to be much more powerful and complex for what I need and hence I went for the fbv option. Also, psplash is userland as well right? So it would launch at the same time nerves erlinit currently launches my fbv solution. Thank you for your suggestion. Let me know if any of my assumtions is wrong to try it out.

Cool, thanks for sharing the videos.