Arducam 64MP and Raspberry Pi Kernel 6 (revisited)

Over a week after my first post and I was still without a working 64mp camera. The old method of running a script to install drivers still reported the same error, and the entry that used to be in my /boot/config.txt file wasn’t helping.

[all]
dtoverlay=arducam_64mp

I went back to look at the arducam forums and came across this post and found that when they went from the custom kernel module install to the standard module install they’ve changed from using an underscore to a hyphen:

[all]
dtoverlay=arducam-64mp

Now after booting, my camera is correctly recognized. The delay and lack of obvious information on their site has been frustrating, but at least I’m up and working and shouldn’t need to do anything special with further apt updates.

wim@WimPi4-Dev:~/WimsConstructionCam $ uname -a
Linux WimPi4-Dev 6.1.19-v8+ #1637 SMP PREEMPT Tue Mar 14 11:11:47 GMT 2023 aarch64 GNU/Linux
wim@WimPi4-Dev:~/WimsConstructionCam $ libcamera-still --list-cameras
Available cameras
-----------------
0 : arducam_64mp [9248x6944] (/base/soc/i2c0mux/i2c@1/arducam_64mp@1a)
    Modes: 'SRGGB10_CSI2P' : 1280x720 [30.00 fps - (0, 0)/0x0 crop]
                             1920x1080 [30.00 fps - (0, 0)/0x0 crop]
                             2312x1736 [30.00 fps - (0, 0)/0x0 crop]
                             3840x2160 [30.00 fps - (0, 0)/0x0 crop]
                             4624x3472 [30.00 fps - (0, 0)/0x0 crop]
                             9152x6944 [30.00 fps - (0, 0)/0x0 crop]

iMessage: Sent to Email Address

I forwarded a link to a contact via iMessage yesterday and it never got to him, though it gave me a status message I’d never seen before: Sent to Email Address.

iMessage screen capture

I’d last texted this contact three years ago, and the history of that message is a blue bubble. I’ve migrated everything from an iPhone 11 to an iPhone 14 since the original message was sent, so it looks like the history made the assumption it was pure iMessage. The picture I sent this morning went through properly as a text message. The contact information knows his phone number and his email.

I’m traveling in an area where I’m going in and out of cell coverage and may be on WiFi at times, or may be completely disconnected with my phone telling me it’s in S.O.S. Mode.

I wouldn’t have minded iMessage telling me that the message could not be delivered. I wouldn’t mind the message being delivered to his email address. I don’t like being told it was delivered and it not being so.

Enable Raspberry Pi Camera Module 3 Wide after Arducam 64mp

My development pi has had an Arducam 64mp camera connected for my camera software development. I liked the quality of the camera but have at various times been frustrated with the software requirements to use the camera. It’s required both a custom kernel driver and a custom fork of the libcamera software packages. That’s meant that to use the 64mp camera I needed to reinstall the arducam suite after nearly every apt upgrade cycle, and definitely ones where the system kernel got updated.

I spent several days trying to get the remnants of the arducam64mp removed from my development system. I’d even built a fresh sd card image of Raspian Bullseye to make sure that the hardware was all connected and working properly.

In the end the fix was rather simple, if obscure.

First, remove or comment out the dtoverlay line from the /boot/config.txt file and make sure camera auto detect is enabled.

# dtoverlay=arducam-64mp
camera_auto_detect=1

Then run apt install with the –reinstall option for the libcamera packages and the raspberry kernel package.

sudo apt install --reinstall -y libcamera-apps libcamera-dev libcamera0 raspberrypi-kernel
sudo systemctl reboot

Then reboot. That should be then allow you to run libcamera-hello and verify that the new camera is working.

I’d found a reference How To Enable RP Cam V2 After Arducam 64MP that didn’t seem to work for getting the V3 wide camera working, perhaps because the focus hardware in the V3 camera made the 64mp think it was active.

I asked the question of how to remove the drivers on the Arducam Forum and then answered my own question.

On to playing with my new Camera Module 3! (I bought the Arducam cases from Amazon because I really liked the fit. The new wide camera protrudes from the front with enough clearance for the focus to function.)

Horizontally flipped image of Shilshole Bay Marina

I got an email this week from the Port of Seattle Shilshole Bay Marina that concluded with an image that is reversed. At first glance it looks like it was taken from the south looking north with Puget Sound to the left. Then I realized nothing was in the right place. This image is looking south with Puget Sound (west) on the left.

In photography with slides this could be a simple mistake but in modern digital photography this requires effort to flip the image. I wonder if this was intentional to see who would comment about it, or a sign of much worse things.

More Cellular Modem Games

Testing possibilities with my cellular modem, I decided to try setting the PDP context to IPV6 instead of the IPV4V6 dual stack mode I had been running.

To do this, I sent these commands to the modem

AT+CGDCONT=1,"IPV6","h2g2"
AT+CGDCONT=6,"IPV6","h2g2"

I also had to configure dhcpc to not configure the usb0 interface with an IPv4 address. Because of the way the hardware works when I didn’t make the changes to dhcpd.conf an address IPv4 was still allocated to usb0 and a route was set up, but if I tried to ping an ipv4 address on the internet I got a Destination Net Unreachable error. I configured /etc/dhcpcd.conf so the last lines tell dhcpc to only configure the usb0 interface for ipv6.

interface usb0
ipv6only

Looking at the syslog details for usb0 after a reboot of the modem and the entire system

Nov 12 11:56:20 WimPiZeroW-Wim kernel: [   35.599281] rndis_host 1-1.1:1.0 usb0: register 'rndis_host' at usb-20980000.usb-1.1, RNDIS device, 0a:93:cc:8e:31:51
Nov 12 11:56:27 WimPiZeroW-Wim dhcpcd[252]: usb0: waiting for carrier
Nov 12 11:56:27 WimPiZeroW-Wim dhcpcd[252]: usb0: carrier acquired
Nov 12 11:56:27 WimPiZeroW-Wim dhcpcd[252]: usb0: IAID cc:8e:31:51
Nov 12 11:56:27 WimPiZeroW-Wim dhcpcd[252]: usb0: adding address fe80::64a:adc8:ebbe:d0ed
Nov 12 11:56:27 WimPiZeroW-Wim avahi-daemon[240]: Joining mDNS multicast group on interface usb0.IPv6 with address fe80::64a:adc8:ebbe:d0ed.
Nov 12 11:56:27 WimPiZeroW-Wim avahi-daemon[240]: New relevant interface usb0.IPv6 for mDNS.
Nov 12 11:56:27 WimPiZeroW-Wim avahi-daemon[240]: Registering new address record for fe80::64a:adc8:ebbe:d0ed on usb0.*.
Nov 12 11:56:27 WimPiZeroW-Wim dhcpcd[252]: usb0: soliciting an IPv6 router
Nov 12 11:56:28 WimPiZeroW-Wim dhcpcd[252]: usb0: Router Advertisement from fe80::7976:1565:680f:9a36
Nov 12 11:56:28 WimPiZeroW-Wim dhcpcd[252]: usb0: adding address 2607:fb90:8060:79e:d1b6:2c95:f26d:2e0c/64
Nov 12 11:56:28 WimPiZeroW-Wim kernel: [   43.860443] ICMPv6: process `dhcpcd' is using deprecated sysctl (syscall) net.ipv6.neigh.usb0.retrans_time - use net.ipv6.neigh.usb0.retrans_time_ms instead
Nov 12 11:56:28 WimPiZeroW-Wim avahi-daemon[240]: Leaving mDNS multicast group on interface usb0.IPv6 with address fe80::64a:adc8:ebbe:d0ed.
Nov 12 11:56:28 WimPiZeroW-Wim avahi-daemon[240]: Joining mDNS multicast group on interface usb0.IPv6 with address 2607:fb90:8060:79e:d1b6:2c95:f26d:2e0c.
Nov 12 11:56:28 WimPiZeroW-Wim avahi-daemon[240]: Registering new address record for 2607:fb90:8060:79e:d1b6:2c95:f26d:2e0c on usb0.*.
Nov 12 11:56:28 WimPiZeroW-Wim avahi-daemon[240]: Withdrawing address record for fe80::64a:adc8:ebbe:d0ed on usb0.
Nov 12 11:56:28 WimPiZeroW-Wim dhcpcd[252]: usb0: adding route to 2607:fb90:8060:79e::/64
Nov 12 11:56:28 WimPiZeroW-Wim dhcpcd[252]: usb0: requesting DHCPv6 information
Nov 12 11:56:28 WimPiZeroW-Wim dhcpcd[252]: usb0: adding default route via fe80::7976:1565:680f:9a36

An interesting side effect of only having IPv6 on the usb0 interface while allowing it elsewhere is that while my machine is also connected to my home network it automatically routes IPv4 traffic over the wlan0 interface.

wim@WimPiZeroW-Wim:~ $ ip -o a
1: lo    inet 127.0.0.1/8 scope host lo\       valid_lft forever preferred_lft forever
1: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft forever
2: wlan0    inet 192.168.0.63/24 brd 192.168.0.255 scope global dynamic noprefixroute wlan0\       valid_lft 4230sec preferred_lft 3555sec
2: wlan0    inet6 2604:4080:1304:8010:57c0:7b33:ef3:3f35/64 scope global dynamic mngtmpaddr noprefixroute \       valid_lft 1789sec preferred_lft 1789sec
2: wlan0    inet6 fe80::2f9e:ceef:76a0:1efa/64 scope link \       valid_lft forever preferred_lft forever
3: usb0    inet6 2607:fb90:8060:79e:d1b6:2c95:f26d:2e0c/64 scope global mngtmpaddr noprefixroute \       valid_lft forever preferred_lft forever
3: usb0    inet6 fe80::64a:adc8:ebbe:d0ed/64 scope link \       valid_lft forever preferred_lft forever

iOS 16 Volume Control Change

I hate change for change sake. Apple seems to have modified the volume control for music playing on the Lock Screen and I can’t see a good reason for the change.

Music playing via AirPods

While listening to music via AirPods it seems the only way to adjust the volume is to press the physical up/down buttons on the phone.

Music playing via AirPlay on speakers

When music is playing on external speakers the volume control line is visible below the play control.

My cardio workout has me using AirPods with the phone resting horizontally on the machine in front of me. In the past if I needed to adjust the volume I could tap the screen to wake it up, then adjust the volume with the slider. I also had visual feedback as to where the volume was set. With the new layout I have to pick up the phone, press the volume button, and return the phone to its resting location. It may sound like a minimal change but has to do a lot with balance and cadence, plus a much larger chance of dropping the phone.

Arducam Red Tint Issue on Raspberry Pi

no tuning file

After writing up my issue yesterday I posted the question on the amazon product details. I received an answer with a link to this page overnight. It references a page with several custom tuning files for their lenses. There did not seem to be a tuning file that matched the 175° FOV lens on the package I got, but there was both a 160° and a 200° version. I downloaded both files and ran a test capture with each. On initial viewing, either will work for my solution. I need to focus the lens to see if there are any noticeable differences.

–tuning-file imx219_160.json
–tuning-file imx219_200.json

The commands I used to download and test the tuning files are

wget https://www.arducam.com/wp-content/uploads/2022/05/imx219_160.json
wget https://www.arducam.com/wp-content/uploads/2022/05/imx219_200.json
libcamera-still -v 2 --nopreview --hflip --vflip --thumb none --tuning-file imx219_160.json --output `hostname`-160.jpg
libcamera-still -v 2 --nopreview --hflip --vflip --thumb none --tuning-file imx219_200.json --output `hostname`-200.jpg

I got a message about the tuning file being an older version, with pointers on how to convert it to the updated version.

WARN RPiController controller.cpp:43 This format of the tuning file will be deprecated soon! Please use the convert_tuning.py utility to update to version 2.0.

I still need to decide how I want to handle this in my automated photo processing, but having the information is hugely useful.

Links in one place:

Arducam 8MP IMX219 175 Degree Ultra Wide Angle Raspberry Pi Camera Module

I’ve been playing with several cameras attached to raspberry pi recently. Getting cameras is easy right now while getting Raspberry Pi is not. I purchased this model from Amazon and have it connected to a Raspberry Pi4. I had to upgrade the unit from running Raspian Buster to Raspian Bullseye to get support for this particular camera.

Amazon Listing

I was doing some searching for red tint problems and fixing them, and it seems to be something that should be able to fix in software. Most of the fixes refer to Jetson Nano, which is not the platform I’m working with, so the fixes don’t align with my platform.

Red Tint Problem on IMX219

I still need to manually focus the lens on this camera. It’s always a frustrating process on a small lens like this because my fingers obscure the view. I’m running with camera_auto_detect=1 while https://www.arducam.com/docs/cameras-for-raspberry-pi/native-raspberry-pi-cameras/8mp-imx219-standard-camera-modules/ recommends modifying /boot/config.txt and setting both camera_auto_detect=0 and dtoverlay=imx219. exiftool reports Camera Model Name : /base/soc/i2c0mux/i2c@1/imx219@10 with the current settings. I’ve verified that using the explicit camera overlay produces the same red tint as well as having exactly the same Model Name in the exif data.

I was able to find the file /usr/share/libcamera/ipa/raspberrypi/imx219.json which may be similar to the ISP files the Jetson platforms were using. I’m still working on the red tint.

Sharing GPS between multiple Raspberry Pi

I have several Raspberry Pi around my apartment in various states of development projects running different versions of Raspian. I’ve been playing with GPSD recently and want the services to be transparently available on each Pi without having to connect directly via a USB port. It turns out that making it work is easy, but obscure.

On my machine that has the GPS plugged into the USB port I need to change the /lib/systemd/system/gpsd.socket file to allow the socket to be visible on the network. On my Raspian Buster machine, it originally looked like this

[Unit]
Description=GPS (Global Positioning System) Daemon Sockets

[Socket]
ListenStream=/var/run/gpsd.sock
ListenStream=[::1]:2947
ListenStream=127.0.0.1:2947
SocketMode=0600

[Install]
WantedBy=sockets.target

I changed it to look like this

[Unit]
Description=GPS (Global Positioning System) Daemon Sockets

[Socket]
ListenStream=/var/run/gpsd.sock
ListenStream=[::]:2947
ListenStream=0.0.0.0:2947
SocketMode=0600

[Install]
WantedBy=sockets.target

I was able to test that it was working properly by running the command gpsmon WimPi4:2497 on the remote host. WimPi4 is the hostname of my machine with the GPS installed.

On the machines I wanted access to the GPS, I modified the file /etc/default/gpsd by adding an entry in the DEVICES section to make the file below.

# Devices gpsd should collect to at boot time.
# They need to be read/writeable, either by user gpsd or the group dialout.
DEVICES="gpsd://WimPi4"

# Other options you want to pass to gpsd
GPSD_OPTIONS=""

# Automatically hot add/remove USB GPS devices via gpsdctl
USBAUTO="true"

With the changes to the gpsd configuration I was able to run cgps or gpsmon on the client machines without requiring an argument. The client programs are connecting the local daemon, which is then connecting to the machine with the gps receiver running gpsd.

More Networking with SIM7600G-H 4G HAT (B) for Raspberry Pi

I initially set up my Waveshare SIM7600G-H 4G HAT (B) for Raspberry Pi as described in the post last week.

Partly because the original web article I was following mentioned different modes of connecting to the internet, and partly because of my infatuation with IPv6, I decided to try to see the performance of different modes I might be able to set up with the device I have.

The support wiki for the device has four pages related to networking setup with the Raspberry Pi, RNDIS, NDIS, SIM868 PPP, and “3G Router“. I’d looked up definitions of NDIS and RNDIS on Wikipedia and didn’t really understand the differences, but the SIM868 page is titled “SIM7600X ECM dial-up Internet” and the ECM led me to believe it was the same as the second type of networking defined in the original article I’d been following. (Choosing QMI or ECM)

I’d been able to issue commands to the board by echoing the correct AT command to /dev/sttyUSB2. I was using the command echo AT+CGPS=1 >/dev/ttyUSB2 to enable the GPS on each boot. Following the Waveshare RNDIS instructions I blithely issued the command echo AT+CUSBPIDSWITCH=9011,1,1 >/dev/sttyUSB2 on my raspberry. The wwan0 device that I’d had set up and running went away and was replaced by a usb0 network device that Raspbian automatically enabled and acquired a valid internet address for. It had a routing metric that was lower than my wlan0 interface, but I figured it might be easier to organize this later without adding udhcpc into the mix of networking software on my raspberry. It also acquired an IPv6 address, which I’d not figured out how to do with the QMI setup and qmicli tools in my initial configuration. The original article mentioned possibly a lower networking latency using ECM vs QMI, so because one of the Waveshare pages had ECM in the title, I tried echo AT+CUSBPIDSWITCH=9018,1,1 /dev/sttyUSB2.

The latency seemed to be slightly different, and I wanted to switch back and do some real testing. That’s when I realized that I no longer had any /dev/ttyUSB devices to be able to send commands to. Completely power cycling the device made no difference. This is when I realized there are no reset jumpers on the device at all, and it had been able to remember the carrier APN that I’d initially set up before switching modes. Looking at the support web page, the only way I could figure out to recover the device was to connect it to a windows machine and install their drivers. I was not interested in downloading drivers from an unknown source and installing them on my primary workstation but realized my old Windows7 machine that hadn’t been turned on in close to a year was a good sacrifice if I could get things working.

First, I had to download the SIM7600X Driver, then I had to get their software.

Initial look at software

It took me a while to figure out that I had to hit the “Tools->STC/IAP15 ISP Programmer” menu, followed by the “Send Multi Char” tab on the resulting window pane to get the list of AT commands it could send.

Simcom Software

It also took me a while to recognize that while I could type commands into the primary window on the left, when I pasted commands that I’d typed in notepad, they were displayed but not executed by the card. The program also didn’t let me easily copy the contents of the main window so I could keep track of what commands I’d used and gotten as a result. After a while, I realized that this was essentially just a specialized serial terminal, and I had serial terminals I’m much more familiar with on my machine already. The Simcom HS-USB ports are visible in the windows device manager as COM12, COM13, COM14, and COM16. The purpose of each port is visible in the naming of the port in the current mode, NMEA, AT PORT, Diagnostics, and Audio.

AT+CRESET appeared to be a good command to get me back to a reasonable working state, but it only seemed to reboot the card, without changing any of the settings that had been stored. I was finally able to figure out that the AT&F command would set all the settings back to the factory defaults. After doing that I was no longer automatically connecting to Google FI, which at least meant I was no longer using up my data allowance while I was figuring things out.

I’d originally gotten into trouble by issuing the command AT+CUSBPIDSWITCH=9018,1,1. I came across the list of all available commands, which is much larger than what was listed in the Simcom Software itself. Issuing the command AT+CUSBPIDSWITCH? returns the current mode, and AT+CUSBPIDSWITCH=? lists the possible modes.

After the factory reset and switch back to 9011 mode, I got some of these results:

AT&F
OK
ATI
Manufacturer: SIMCOM INCORPORATED
Model: SIMCOM_SIM7600G-H
Revision: SIM7600M22_V2.0.1
SVN: 01
IMEI: 868822042540193
+GCAP: +CGSM

AT+CGDCONT?
+CGDCONT: 1,"IPV4V6","Telstra.internet","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 2,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 3,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 4,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 5,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 6,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0

OK
AT+CGDCONT=?
+CGDCONT: (1-24),"IP",,,(0-2),(0-4),(0-1),(0-1)
+CGDCONT: (1-24),"PPP",,,(0-2),(0-4),(0-1),(0-1)
+CGDCONT: (1-24),"IPV6",,,(0-2),(0-4),(0-1),(0-1)
+CGDCONT: (1-24),"IPV4V6",,,(0-2),(0-4),(0-1),(0-1)

OK
AT+CUSBPIDSWITCH?
+CUSBPIDSWITCH: 9011

OK
AT+CUSBPIDSWITCH=?
+CUSBPIDSWITCH: (9000,9001,9002,9003,9004,9005,9006,9007,9011,9016,9018,9019,901A,901B,9020,9021,9022,9023,9024,9025,9026,9027,9028,9029,902A,902B),(0-1),(0-1)

Following some instructions I found on a post written by Mathieu Leguey, I was able to reconfigure the APN to connect to Google Fi. That page is also where I found the link to the complete list of commands. This other page is what led me to believe I need to set the APN twice to get IPv6 operating properly.

AT+CGDCONT?
+CGDCONT: 1,"IPV4V6","h2g2","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 2,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 3,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 4,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 5,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 6,"IPV4V6","h2g2","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0

I later realized I could configure the GPS to automatically start with the command AT+CGPSAUTO=1. The final configuration commands I decided to use that seem to work for my purposes are as follows:

AT&F
AT+CUSBPIDSWITCH=9011,1,1
AT+CGDCONT=1,"IPV4V6","h2g2"
AT+CGDCONT=6,"IPV4V6","h2g2"
AT+CGPSAUTO=1
SecureCRT Display

By doing this, I was able to avoid installing any networking drivers or configuration on Raspian that wasn’t automatically installed with the minimal system image. I should have been able to issue all of the commands via the echo command and the /dev/sttyUSB2 port if I’d not initially put the unit into 9018 mode and removed the USB control port.

ip a
ifconfig -a
route -v -n
wim@WimPiZeroW-Hope:~ $ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether b8:27:eb:7c:6a:80 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.53/24 brd 192.168.0.255 scope global dynamic noprefixroute wlan0
       valid_lft 10481sec preferred_lft 9131sec
    inet6 2604:4080:1304:8010:347f:5b74:9cac:5a2/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 27sec preferred_lft 17sec
    inet6 fe80::7a98:f2b1:147d:36d0/64 scope link
       valid_lft forever preferred_lft forever
3: usb0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    link/ether fe:5c:c9:12:f4:3a brd ff:ff:ff:ff:ff:ff
    inet 192.168.225.31/24 brd 192.168.225.255 scope global dynamic noprefixroute usb0
       valid_lft 42884sec preferred_lft 37484sec
    inet6 2607:fb90:8062:ac89:b44d:ec7e:82c0:b337/64 scope global mngtmpaddr noprefixroute
       valid_lft forever preferred_lft forever
    inet6 fe80::5ade:a00c:113f:dfcd/64 scope link
       valid_lft forever preferred_lft forever
wim@WimPiZeroW-Hope:~ $ ifconfig -a
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 14  bytes 1876 (1.8 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 14  bytes 1876 (1.8 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

usb0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.225.31  netmask 255.255.255.0  broadcast 192.168.225.255
        inet6 2607:fb90:8062:ac89:b44d:ec7e:82c0:b337  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::5ade:a00c:113f:dfcd  prefixlen 64  scopeid 0x20<link>
        ether fe:5c:c9:12:f4:3a  txqueuelen 1000  (Ethernet)
        RX packets 74  bytes 6149 (6.0 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 100  bytes 14461 (14.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.53  netmask 255.255.255.0  broadcast 192.168.0.255
        inet6 fe80::7a98:f2b1:147d:36d0  prefixlen 64  scopeid 0x20<link>
        inet6 2604:4080:1304:8010:347f:5b74:9cac:5a2  prefixlen 64  scopeid 0x0<global>
        ether b8:27:eb:7c:6a:80  txqueuelen 1000  (Ethernet)
        RX packets 3068  bytes 458727 (447.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 956  bytes 151187 (147.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wim@WimPiZeroW-Hope:~ $ route -v
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         mobileap.qualco 0.0.0.0         UG    203    0        0 usb0
default         192.168.0.1     0.0.0.0         UG    302    0        0 wlan0
192.168.0.0     0.0.0.0         255.255.255.0   U     302    0        0 wlan0
192.168.225.0   0.0.0.0         255.255.255.0   U     203    0        0 usb0
wim@WimPiZeroW-Hope:~ $ route -v -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.225.1   0.0.0.0         UG    203    0        0 usb0
0.0.0.0         192.168.0.1     0.0.0.0         UG    302    0        0 wlan0
192.168.0.0     0.0.0.0         255.255.255.0   U     302    0        0 wlan0
192.168.225.0   0.0.0.0         255.255.255.0   U     203    0        0 usb0
wim@WimPiZeroW-Hope:~ $ ping -I usb0 www.google.com -c 4
PING www.google.com(nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004)) from 2607:fb90:8062:ac89:b44d:ec7e:82c0:b337 usb0: 56 data bytes
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=1 ttl=116 time=68.0 ms
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=2 ttl=116 time=67.1 ms
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=3 ttl=116 time=65.2 ms
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=4 ttl=116 time=65.5 ms

--- www.google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 65.217/66.449/68.012/1.159 ms
wim@WimPiZeroW-Hope:~ $ ping -I wlan0 www.google.com -c 4
PING www.google.com(nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004)) from 2604:4080:1304:8010:347f:5b74:9cac:5a2 wlan0: 56 data bytes
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=1 ttl=50 time=68.0 ms
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=2 ttl=50 time=27.3 ms
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=3 ttl=50 time=30.4 ms
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=4 ttl=50 time=44.7 ms

--- www.google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3008ms
rtt min/avg/max/mdev = 27.330/42.629/68.048/16.072 ms
wim@WimPiZeroW-Hope:~ $ lsusb
Bus 001 Device 003: ID 1e0e:9011 Qualcomm / Option SimTech, Incorporated
Bus 001 Device 002: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
wim@WimPiZeroW-Hope:~ $ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
        |__ Port 1: Dev 3, If 0, Class=Communications, Driver=rndis_host, 480M
        |__ Port 1: Dev 3, If 1, Class=CDC Data, Driver=rndis_host, 480M
        |__ Port 1: Dev 3, If 2, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 3, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 4, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 5, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 6, Class=Vendor Specific Class, Driver=option, 480M
ping results

One thing that jumped out when using the USB interface is that the default gateway is an internal Qualcomm name, mobileap.qualco.

lsusb results

I still need to figure out how to change the metric of usb0 to be higher than wlan0 probably installing the ifmetric program, following directions similar to this answer.

Update 7/17/2020

Adding an interface and metric line to the end of /etc/dhcpd.conf got me the routing I wanted without having to add any new programs. It also means the IPv6 stuff is taken care of automatically.

wim@WimPiZeroW-Hope:~ $ tail -5 /etc/dhcpcd.conf
#interface eth0
#fallback static_eth0

interface usb0
metric 400
wim@WimPiZeroW-Hope:~ $ route -n -v
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    302    0        0 wlan0
0.0.0.0         192.168.225.1   0.0.0.0         UG    400    0        0 usb0
192.168.0.0     0.0.0.0         255.255.255.0   U     302    0        0 wlan0
192.168.225.0   0.0.0.0         255.255.255.0   U     400    0        0 usb0
wim@WimPiZeroW-Hope:~ $ route -n -v -6
Kernel IPv6 routing table
Destination                    Next Hop                   Flag Met Ref Use If
::1/128                        ::                         U    256 2     0 lo
2604:4080:1304:8010::/64       ::                         U    302 1     0 wlan0
2607:fb90:80c7:b02e::/64       ::                         U    400 2     0 usb0
fe80::/64                      ::                         U    256 1     0 usb0
fe80::/64                      ::                         U    256 1     0 wlan0
::/0                           fe80::b27f:b9ff:fe83:6591  UG   302 1     0 wlan0
::/0                           fe80::6c86:c4b4:1f1:c09    UG   400 2     0 usb0
::1/128                        ::                         Un   0   4     0 lo
2604:4080:1304:8010:347f:5b74:9cac:5a2/128 ::                         Un   0   2     0 wlan0
2607:fb90:80c7:b02e:b86b:f626:1fa9:3a62/128 ::                         Un   0   4     0 usb0
fe80::7a98:f2b1:147d:36d0/128  ::                         Un   0   3     0 wlan0
fe80::88be:29ff:2c84:e5a0/128  ::                         Un   0   5     0 usb0
ff00::/8                       ::                         U    256 2     0 usb0
ff00::/8                       ::                         U    256 2     0 wlan0
::/0                           ::                         !n   -1  1     0 lo
usb0 routing metric fixed

References: