RPi Startup IP and Web Display

October 28, 2017
4 min. read

This post is part of the Burn-In Cart series.

I’m using a Raspberry Pi 3 as the CPU in custom burn-in carts for cycling our Android tablets. It is hard to beat the 7” touch display using a Flask front end for quick development. The carts often have power connected and disconnected when moved around. We are also using a network inside of the carts for talking with tablets under test and need to bring this up each time we move carts and cycle power.

Getting Valid IP

If you boot your RPi connected to an already established router, you probably don’t have to worry about getting an invalid IP address. Since power is being connected to the cart and all devices come online at once, my RPi boots faster than the Ubiquiti Edgerouter-X that is running the local network.

If you boot a RPi with no network connection, you will get an APIPA (Automatic Private Internet Protocal Addressing) IP address. This is shown with 169.254.x.x IP, with last two octets randomly chosen. I’m using this to determine that my address is bad.

The best method I found to release and renew IP address from the router is using subprocess to shutdown and start up the eth0 interface. This forces a DCHP lease request. I pause 10 seconds between these and allow 10 retries.

Below is my full IP address startup script.

import netifaces as nt
from time import sleep
import subprocess


try_count = 10


def get_ip():
    """
    Use netifaces to retrieve IP address for interface eth0
    """
    ifaddresses = nt.ifaddresses('eth0')[nt.AF_INET]
    return ifaddresses[0]['addr']


def is_good_ip(ip_address):
    """
    Detect if IP starts with '169.254', which indicates that RPi started up network
    prior to a solid DHCP access and chose an internal IP.

    :return: False if IP starts with '169.254'
    """
    return '169.254' != ip_address[:8]


def cycle_eth0():
    """
    Take down and bring up eth0 network interface to force DCHP retry.
    """
    my_cmd = "sudo ifdown eth0 && sudo ifup eth0"
    proc = subprocess.Popen(my_cmd, shell=True, stdout=subprocess.PIPE)


def get_valid_ip():
    """
    Loop checking for valid IP and sleeping
    """
    for tries in range(try_count):
        try:
            if is_good_ip(get_ip()):
                break0
            cycle_eth0()
        except:
            pass
        sleep(10)
    else:
        raise Exception('IP Address did not assign correctly.')


if __name__ == '__main__':
    get_valid_ip()

But how do I run this on start of the Raspberry Pi?

Run on Boot

I created a shell script launcher.sh in the root of pi home instead of directly calling the Python script, as I might want to do more in the future.

#!/bin/sh

cd /home/pi/agapao-cart
sudo python3 boot.py

You need to make it executable in terminal:

chmod 755 launcher.sh

I made a logs directory inside pi home:

mkdir logs

Add cron task for on reboot::

sudo crontab -e

Select Nano as editor if asked. Enter the following at bottom of file:

@reboot sh /home/pi/launcher.sh >/home/pi/logs/cronlog 2>&1

This will run our launcher script and log any errors on our cronlog.

Now our RPi will boot and retry until it gets a valid IP address. If this fails for some reason, we can show this on the display using the web server.

But how do we get the web display to startup?

Run on GUI Load

To run a program when the GUI loads, I found the easiest is to add it to the ~/.config/lxsession/LXDE-pi/autostart file. I added the line below to the bottom of this file.

@/usr/bin/python3 /home/pi/agapao-cart/on_gui_load.py

Notice that I must be explicit with both the path to python3 and the full script path.

This script (on_gui_load.py) will open up our chromium browser, display in maximised full screen mode (–kiosk) and load the localhost web server. Using (–incognito) is the best way I have found to eliminate the ‘restore’ prompt if the browser is closed with a system shutdown. (Which is how I always close it.)

import subprocess


def start_web_browser_kiosk():
    my_cmd = "chromium-browser --kiosk --incognito http://127.0.0.1"
    proc = subprocess.Popen(my_cmd, shell=True)


if __name__ == '__main__':
    start_web_browser_kiosk()

The browser will load the Flask website being served as GUI for our 7” touchscreens.

Screen Sleep and Mouse Pointer

The next thing you will notice is the screen will go black after a delay and your nice embedded display will require you to tap it to find status on your process. Not ideal.

If we edit /etc/lightdm/lightdm.conf and add the following lines to the [SeatDefaults] section, we can solve the issue.

# don't sleep the screen
xserver-command=X -s 0 dpms

If you have a touch screen and do not want a mouse cursor, also add the -nocursor flag.


Part 3 of 3 in the Burn-In Cart series.

Series Start | Raspberry Pi Hardware Mocking

comments powered by Disqus