heap.ovh

embedded adventures

Raspberry Pi Zero - initial impressions

I am planning to make a basic telemetry device for a RC glider. To that end, I decided to use RPi Zero as the brains of the project. I like the CSI camera connector, plenty of GPIOs, compact size and low price. However, once I got the device I found out about some deficiencies.

Power

The Pi Zero is a bit lacking in human interface devices. That is to say, it doesn't have a single button. How do I turn it on or off in the field?

The device will boot automatically once power is provided, so that's half of the problem solved. I don't want to, however, just cut power to turn it off -- filesystem corruption is a pain in the neck.

The solution is to put a button on GPIO and have a helper script listening for a press to trigger shutdown -h now. I'm not going to reinvent the wheel and instead just use the script provided here. Since I am going to use GPIO3 for i2c I will use GPIO26 instead which is conveniently next to a ground pin.

#!/usr/bin/env python
import RPi.GPIO as GPIO
import subprocess

GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.wait_for_edge(26, GPIO.FALLING)

subprocess.call(['shutdown', '-h', 'now'], shell=False)

A small init script will handle the rest.

I also need a way to signal the current status of the running script. To make matters worse I forgot to order LEDs. Fortunately, there is a green LED on RPi Zero already -- but how do I drive it?

The official documentation is not helpful. The schematic for the Zero does not indicate where the LED is hooked up. Fortunately someone figured out it is GPIO47. First, however, we need to have the kernel relinquish control over it:

echo none >/sys/class/leds/led0/trigger (I'll add it to rc.local)

Then we can easily turn it on and off programatically:

GPIO.setup(47, GPIO.OUT)
GPIO.output(47, GPIO.LOW)
sleep(2)
GPIO.output(47, GPIO.HIGH)
GPIO.cleanup()

According to the official documentation, you are meant to power the Pi with a 5.1V 1.2A power supply, preferably bought from RPi. Of course, we won't be doing that.

What is 5V even good for? The BCM2835 chip runs at 3.3V and the only peripherals that demand 5V are USB and HDMI, none of which I plan to use. And it looks like supplying 3.7V-4.2V on the +5V pin works perfectly fine. I powered off HDMI with /usr/bin/tvservice -o in /etc/rc.local and hdmi_blanking=2 in /boot/config.txt for good measure.

Personally, I think that not including LiPO charging circuitry on the Zero line is a mistake. The $5 C.H.I.P. had a built-in AXP209 IC that worked fine. With a Pi you can buy a $35.99 Pisugar2 instead, and somehow it is an acceptable solution. The whole RPi ecosystem seems very predatory. I'll just continue using my $1 TP4056-based charger instead, thanks.

UARTs

One neat thing about the Zero is that (on paper) it features two UARTs. I had thought I could use one for setting up the device and the other for a GPS module. This however turned out to be wrong, as both are wired to the same physical pins on the connector. On Zero W one of these is used for bluetooth but on the regular Zero one is just... wasted. A shame. I had thought there would be a way to remap it to another GPIO but apparently that's not the case. Oh well, time for plan B.

Fun fact: if you want to talk to something over UART from the Zero the Raspbian Lite installation provides you with nothing. No screen, dterm, minicom, cu, picoterm, nada. And without any sort of networking getting new software on it is a pain in the neck. I resorted to this little bash script from stackexchange

CDC Gadgets to the rescue

So how do you talk to the Pi if you don't have WiFi and UART is out of the question? Luckily you can get Serial over USB with the help of what's called a USB gadget driver. I know these from C.H.I.P. which was set up this way by default. You would plug it in with a single USB cable to the PC and immediately power it and get a console, no UART bridges necessary. Super convenient. Zero for some reason does not support it out of the box but you can make it work here as well.

In fact, I decided to go a step further and use the g_multi module which integrates serial, storage and ethernet gadgets.

  1. In /boot/config.txt add dtoverlay=dwc2
  2. In /boot/cmdline.txt add modules-load=dwc2,g_multi
  3. In /etc/modprobe.d/g_multi.conf add options g_multi host_addr=11:22:33:11:22:33 (change the MAC to what you want)
  4. Enable SSH service on the Pi systemctl enable ssh
  5. Disable the Getty on the UART interface and enable it on the USB Gadget systemctl enable getty@ttyGS0.service && systemctl disable getty@ttyS1.service
  6. Plug in the data USB cable into your PC and reboot the Pi

Once Pi reboots you will have a new serial device pop up in the Device Manager under COM & LPT ports. You will also get a new RNDIS device with an exclamation mark - this is the ethernet-over-usb device but you need to select a driver for it manually. Go to 'Properties' - 'Update Driver' then 'Browse my computer for drivers' then 'Let me pick from a list' - 'Network adapters' - 'Microsoft' - 'Remote NDIS Compatible Device'.

It will now appear under Network Connections. RPi runs avahi by default, so you can ssh (or sftp!) to raspberrypi.local.

So now you have two ways of accessing the Pi: Serial-over-USB and Ethernet-over-USB, simultaneously. Handy, if you mess up your networking settings.

Importantly, you can set up NAT and share network access with the Pi which makes installing software and moving files so much easier.

You might have noticed that I do not really touch upon the storage gadget but with the robust networking I hardly need it and it requires some extra hoops to jump.

Now where was I. Ah right, interfacing my GPS module.

RTC in the sky

I have a GY-NEO6MV2 GPS module with UART interface. These are dirt cheap and based on the capable u-blox chipset. Indoor reception is bad, but then I have a few other floors above me. Outside it works OK. Honestly I expected better performance with a 2.5x2.5cm patch antenna but whatever.

A quick check shows it working:

gpsmon /dev/ttyAMA0 9600
raspberrypi:/dev/ttyAMA0 9600 NMEA0183>
┌──────────────────────────────────────────────────────────────────┐
│Time: 2021-03-31T23:22:13.000Z Lat:  xx xx' 40.04015" Non:  xx xx' 22.00584" E│
└────────────────────────────── Cooked TPV ──────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ GPVTG GPGGA GPGSA GPGSV GPGLL GPRMC                                          │
└────────────────────────────── Sentences ───────────────────────────┘
┌───────────────┐┌────────────────────────┐┌────────────────────────┐
│Ch PRN  Az El S/N ││Time:      232213.00        ││Time:      232213.00        │
│ 0   1 157 47  15 ││Latitude:    xxxx.67336 N   ││Latitude:  xxxx.67336       │
│ 1   3 352 87   0 ││Longitude:  xxxx.36764 E    ││Longitude: xxxx.36764       │
│ 2   4 201 53   0 ││Speed:     0.844            ││Altitude:  66.2             │
│ 3   6 311 17   0 ││Course:    297.77           ││Quality:   1   Sats: 03     │
│ 4   9 218 21   0 ││Status:    A       FAA: A   ││HDOP:      6.80             │
│ 5  12 349  5   0 ││MagVar:                     ││Geoid:     35.8             │
│ 6  17 266 35   0 │└───────── RMC ──────────┘└────────── GGA ──────────┘
│ 7  19 294 33   0 │┌────────────────────────┐┌────────────────────────┐
│ 8  21 154 29  26 ││Mode: A2 Sats: 21 22 31     ││UTC:           RMS:         │
│ 9  22  96 62  32 ││DOP: H=6.80  V=1.00  P=6.87 ││MAJ:           MIN:         │
│10  25  26  3   0 ││TOFF:  0.139430542          ││ORI:           LAT:         │
│11  31  73 32  35 ││PPS:                        ││LON:           ALT:         │
└───── GSV ──────┘└─────── GSA + PPS ───────┘└────────── GST ──────────┘

I'm going to use it for plotting later on, but for now let's adress another deficiency of the RPi. The lack of a real-time clock.

Now, this is expected on a device like that but still a bit inconvenient. Not much of a problem on Internet-connected devices but I don't have that luxury. I could buy an external RTC module but I already have someting better - the sattelite clock.

The GPS module produces 1PPS signal on pin 3 which is unfortunately wasted on driving a blinking LED. I could solder a wire but frankly I don't need microsecond precision here, I can just use the information from NMEA messages instead.

Since I may end up having several processes trying to access GPS data I will take advantage of gpsd. It will listen on the UART port and set up a daemon on localhost:2947 which can be queried by other applications.

First, symlink the UART interface to /dev/gps0 in a way that will persist through reboots. Create /etc/udev/rules.d/99-gps.rules and add KERNEL=="ttyAMA0", SYMLINK+="gps0".

Then, include DEVICES="/dev/gps0" in /etc/default/gpsd

Finally, add the following to /etc/ntp.conf:

server 127.127.46.0 minpoll 4 maxpoll 4 prefer
fudge 127.127.46.0 refid GPS stratum 0

The strange IP addresses are ntp driver identifiers, in this case the gpsd driver. Restart the services and query ntp peers:

root@raspberrypi:~# ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 0.debian.pool.n .POOL.          16 p    -   64    0    0.000    0.000   0.002
 1.debian.pool.n .POOL.          16 p    -   64    0    0.000    0.000   0.002
 2.debian.pool.n .POOL.          16 p    -   64    0    0.000    0.000   0.002
 3.debian.pool.n .POOL.          16 p    -   64    0    0.000    0.000   0.002
*GPSD_JSON(0)    .GPS.            0 l   13   16  377    0.000   -1.465   2.353

And there we have it. This covers the "system administration and housekeeping" part, next time we'll take a look at the camera, hook up i2c barometer for precise altitude measurement and tie it all together in an ugly python script, as is tradition.