CarPi - Portable Wireless Media Server

I have two young children, one boy and one girl, very close in age. We bought a minivan, which is the law when you have young children. The van came with a built-in dvd player for the second row. As you can imagine, there was never any agreement over what to watch. As I was generally the driver on long trips, I had to do something before I intentionally drove into a bridge abuttment at high speed.

I decided to build a Pi based media server that I could take on trips.

I came up with the following requirements:

  • wireless, so the kids could use their own tablets as the viewing device
  • be able to run off of in car power, too obvious to explain
  • small, putting a desktop in the passenger seat didn’t seem right
  • store a fair number of vidoes, the kids were used to choice
  • cheap, again, obvious

Platform

I settled on the OrangePi Zero.

The OrangePi Zero comes with built in wireless, specifically the b/g/n variety. It’s not going to be a speed demon, but it will be fast enough. It’s small. At 46mm x 48mm, it seems less than half the size of a RaspberryPi. I also bought a case as well. It’s going to be bouncing around in the car. We don’t not keep our cars clean and it’ll need all the protection it can get. And lastly, it is supported by Armbian. Armbian is my Pi distro of choice. They offer downloads for many SBCs and their documention is stellar.

Installation

I started with the Armbian Bionic server. There is just no need for all the desktop fluff in a media server. Installation onto a microSD card is straight forward.

The three areas I worked on where wifi AP set up, media server and storage config.

WiFi Access Point

The Pi needs to be a wireless access point. That way the kids’ tablets can connect to it to accss the media server.

The OrangePi’s wireless chipset is supported by hostap, so seemed like a good choice.

apt install hostapd

Now is a good time to mention that I am assuming you know your way around the linux command line. I will not be explaining a lot of the common commands. I assume that I’m the only person that will see this.

The config file for hostapd is /etc/hostapd/hostapd.conf.

It looks like this on my CarPi

# This is the name of the WiFi interface we configured above
interface=wlan0

# Use the nl80211 driver with the brcmfmac driver
driver=nl80211

# This is the name of the network
ssid=CarPI

# Use the 2.4GHz band
hw_mode=g

# Use channel 6
channel=6

# Enable 802.11n
ieee80211n=1

# Enable WMM
wmm_enabled=1

# Enable 40MHz channels with 20ns guard interval
#ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40]

# Accept all MAC addresses
macaddr_acl=0

# Use WPA authentication
auth_algs=1

# Require clients to know the network name
ignore_broadcast_ssid=0

# Use WPA2
wpa=2

# Use a pre-shared key
wpa_key_mgmt=WPA-PSK

# The network passphrase
wpa_passphrase=<REDACTED>

# Use AES, instead of TKIP
rsn_pairwise=CCMP

I think most of them are pretty straight forward. The driver I had to look up. ignore_broadcast_ssid doesn’t work. I tried it and it won’t accept any connections. rsn_pairwise I looked up as well. I am not a 802.11 wiz. It works, I’m good with it.

DHCP

Since hostapd will be creating a new private lan, something needs to be handing out IP addresses. Installing and configuring a dhcp server is not tough. There will be no need for routing or dns. apt install isc-dhcp-server Edit the default file, /etc/default/isc-dhcp-server and bind it to the wireless lan only. INTERFACESv4="wlan0"

Next up, add the configuration for the new lan’s dhcp service. Just append the following to /etc/dhcp/dhcpd.conf.

subnet 192.168.42.0 netmask 255.255.255.0 {
   range 192.168.42.10 192.168.42.50;
   option broadcast-address 192.168.42.255;
   option routers 192.168.42.1;
   default-lease-time 600;
   max-lease-time 7200;
   option domain-name "local";
   option domain-name-servers 192.168.5.1;

Storage

The OrangePi Zero has two USB2.0 ports. I have two kids. I decided to buy two thumb drives, one for each kid and let them choose what videos to load on them. The drives are formatted VFAT. I hate it, but it works everywhere.

I put an entry in fstab for each drive.

UUID=BC23-DD57    /mnt/thing1    vfat    auto,ro,nofail    0 0
UUID=6BD6-F51A    /mnt/thing2    vfat    auto,ro,nofail    0 0

A couple things to note. I used the UUID’s for mounting. I have no idea which drive will found first or if both will even be there. Therefore, using /dev/sdX wouldn’t work. The other things is they are mounted readonly and nofail. There’s no reason for the video storage to be written to while its in use, so ro. The nofail option lets the system keep going if the device for the mountpoint is not present. This way the system can be booted without one or both of the storage drives.

Media Server

I needed some way to server the videos. There are a number of media servers out there. Plex was suggested when I was working on this. However, given the horsepower of the OrangePi, I didn’t think Plex would work out well. I went with MiniDNLA. Just as the mini part of it’s name implies, it’s minimal. You can configure it to use coverart and other whizbang features. I just wanted it to stream videos to the angry masses.

apt install minidlna

The config file for it is /etc/minidlna.conf. There are a lot of options in the config file, I’m only going the mention the ones I’ve cared about.

# If you want to restrict a media_dir to a specific content type, you can
# prepend the directory name with a letter representing the type (A, P or V),
# followed by a comma, as so:
#   * "A" for audio    (eg. media_dir=A,/var/lib/minidlna/music)
#   * "P" for pictures (eg. media_dir=P,/var/lib/minidlna/pictures)
#   * "V" for video    (eg. media_dir=V,/var/lib/minidlna/videos)
#   * "PV" for pictures and video (eg. media_dir=PV,/var/lib/minidlna/digital_camera)
#media_dir=/var/lib/minidlna
media_dir=V,/mnt/thing1/4car
media_dir=V,/mnt/thing1/4car

I needed one entry for each drive. I put the videos in a subdirectory, 4car, just because. Actually, I did it intentionally be allowed to have other stuff on the drives if I wanted to.

# Set this to merge all media_dir base contents into the root container
# (The default is no.)
#merge_media_dirs=no
merge_media_dirs=yes

This merges the contents of the two drives into one list. Even though they never agree, they might want to watch something from the others selections.

#root_container=.
root_container=V

This just starts the list in the videos sections when a client connects. Since it doesn’t have any media, it probably isn’t necessary. However, it also doesn’t hurt, so why not.

# Network interface(s) to bind to (e.g. eth0), comma delimited.
# This option can be specified more than once.
#network_interface=
network_interface=wlan0

Most of the time, the CarPi doesn’t have any other interface connected. This just makes sure it’s listening on the wireless lan.

# Port number for HTTP traffic (descriptions, SOAP, media transfer).
# This option is mandatory (or it must be specified on the command-line using
# "-p").
port=8200

You can read, next.

Actually, not next, done.

Sometimes having no internet connection has downsides

All of this worked great. Or so I thought. The first time I added some new content to one of the drives, I got a screaming kid in the back, “I can’t find Lego Movie 2!!". It took time to figure out why minidlna wasn’t picking changes. Lets see I can relate this intelligently.

The OrangePi Zero doesn’t have a RTC. It is configured to do ntpdate as soon as its comes up. However, in the car it can’t get the date. So it’s always running a short time after the epoch. When new content is added to the drives, it’s done on another computer. They are USB drives, after all. The added content gets tagged with a current timestamp as the writing machine sees it. Minidlna doesn’t deal with such a large delta well and doesn’t update it’s cache. I decided to be totally heavyhanded and delete the minidlna’s cache each time it starts up.

Since this a Debian Buster based distro, it’s systemd. I’m not going to get into a systemd rant here. Minidlna didn’t come with a service file, it used a sysvinit script. Systemd created its own service file, /etc/systemd/system/minidlna.service. I just added to it.

[Unit]
Documentation=man:systemd-sysv-generator(8)
SourcePath=/etc/init.d/minidlna
Description=LSB: minidlna server
Before=multi-user.target
Before=multi-user.target
Before=multi-user.target
Before=graphical.target
After=network-online.target
After=remote-fs.target
Wants=network-online.target

[Service]
Type=forking
Restart=no
TimeoutSec=5min
IgnoreSIGPIPE=no
KillMode=process
GuessMainPID=no
RemainAfterExit=yes
SuccessExitStatus=5 6
ExecStart=/etc/init.d/minidlna start
ExecStop=/etc/init.d/minidlna stop
ExecStartPre=rm -f /var/cache/minidlna/files.db

[Install]
WantedBy=multi-user.target

The fix for my cache problem is the ExecStartPre=rm -f /var/cache/minidlna/files.db line. Before starting the daemon, it deletes the cache, each time. Like I said, heavy handed.

Power

The OrangePi runs fine from a 1A USB power adapter. The first time I had to get gas, both kids screamed “Hey!!” in unison. Turns out the OrangePi actually needs continous power. Turning the car off for 15 minutes to fill the tank and empty the bladder, interrupted the viewing pleasure of the afore mentioned angry masses.

Hey what about one of the USB external battery packs? I can just put one inline with the USB adapter and the OrangePi. Yeah, you know how many of those provide continuous power while switching from input power to battery and vice versa? None, that I could find. The wife appreciated the first hand-me-down battery pack. Not the 5th.

The solution came from a friend. I purchased two 18650 battries and a ‘Dual 18650 Battery Shield’. The shield takes the two batteries and has a micro-usb power input and a USB power output. It will pass through power while charging and switch batteries for output power upon loss of input power. Instant UPS. Pretty, it’s not, but it works.

alt text