iOS 16 Lock Screen Music Player

Yesterday I noticed the nice cover art display on the lock screen of my upgraded iPhone. Today while working out I noticed it was working differently, much closer to the old style.

I couldn’t think of what might have caused the change. I scrolled up and there were some older notifications. I deleted all of them, but still no change. Then it occurred to me to tap the thumbnail of the cover art.

I have no idea why it was defaulting to the thumbnail today after defaulting to full screen yesterday.

I upgraded to iOS 16 today

I haven’t found anything that significantly annoyed me yet.

When I’m playing music and there’s cover art, it’s now displayed on the Lock Screen, which is nice.

When the cover art is missing, the controls are now at the bottom of the screen. Each time I see them there I start to rotate my phone, thinking I’ve picked it up upside down.

I’m sure I’ll get used to the new layout. I find this problem funny, at least today.

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.

Raspberry Pi ZeroW WiFi Power Management

Every Raspberry Pi Zero W I’ve had has had intermittent connection problems on Wi-Fi. I’ve been able to fix the problems by disabling power management on the Wi-Fi interface each time. This page gave me my preferred solution for taking care of the problem on each machine. I’m duplicating the information here to contribute/prevent webrot.

See the current state of power management:

sudo iw wlan0 get power_save

set power management off:

sudo iw wlan0 set power_save off

Create a systemd unit file to set Wi-Fi power management:

sudo systemctl --full --force edit wifi_powersave@.service

With this as the contents of the unit file:

[Unit]
Description=Set WiFi power save %i
After=sys-subsystem-net-devices-wlan0.device

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/iw dev wlan0 set power_save %i

[Install]
WantedBy=sys-subsystem-net-devices-wlan0.device

Then enable the unit file, setting power management to off whenever wlan0 is activated.

sudo systemctl disable wifi_powersave@on.service
sudo systemctl enable wifi_powersave@off.service

IPv6 DDWRT and Astound Broadband

My apartment has three options for high speed internet. Centurylink, Xfinity, and Astound Broadband. When I moved in I chose Wave Broadband because I’d had good experience in the past with Condointernet and it later became Wave Broadband. Now they seem to be going with the moniker Astound Broadband powered by Wave. One of the features that I’ve used in the past and am using now is their support of IPv6.

I’ve had occasional hiccups in my networking, most often recently the IPv4 networking stops working while the IPv6 network is still working. It has some interesting effects, such as my browser connected to gmail.com working fine but hitting a link on a web page in an email that goes to an IPv4 only address not working. Usually the problem can be fixed by connecting to my router and having it release its external IPv4 DHCP address and requesting a new lease. Occasionally there are problems on the ISP side of things that make things more problematic. Last week when I was having problems, I managed to mess up my IPv6 configuration and then spent a long time figuring out what the problem was. I’m writing this up mainly as a suggestion for a reliable working configuration.

I’m running DDWRT on a Netgear R7000 router. It’s somewhat strange, as it seems there’s no real release, only betas. It explicitly says go look at the directory structure and “DO NOT USE THE ROUTER DATABASE.” I’ve run it in various incarnations for well over a decade.

DD-WRT Basic Setup

The Basic IPv4 page is very standard, naming my local network and router and picking the IPv4 address range I want to use locally. I’ve generally been using the same address block since they were defined in RFC1918, though that’s been superseded by newer RFC’s. Wikipedia has a good description of private address space.

DD-WRT IPv6 Setup

I’m enabling IPv6 and seelcting DHCPv6 with Prefix Delegation. I’ve left all of the other items on this page either blank or at their defaults.

The way IPv6 hosts generally allocate global addresses in stateless mode is that they recognize router advertisements arriving on the network port and configure their own address based on information received. They then broadcast locally to make sure there are no address collisions. This may be a significant oversimplification, but it’s a useful one.

Initially I had the Router Advertisement Daemon enabled on this page but have since changed to use DNSMASQ for router advertisements since it’s already doing so much of the work on the router. Part of the reason I stopped using radvd was also based on the fact that I was trying to add two static DNS entries for IPv6 in the fields on this page. I was adding the google IPv6 DNS entries of 2001:4860:4860::8888 and 2001:4860:4860::8844. The problem I was running into was that DDWRT was creating a radvd.conf file that included the two static DNS servers I listed while also including the DNS servers inherited from the advertisement it received from my ISP router. Radvd was seeing more than three DNS servers and exiting with an error. This meant that no router advertisements were being broadcast on my local network. I’d been looking at the configuration of the radvd deamon with the command cat /tmp/radvd.conf in my terminal window. I asked questions about this over on the DDWRT forums as well as opening an issue with the radvd software itself. The slightly unhelpful answer to my question told me to use dnsmasq for router advertisements. I’d tried that by adding the enable-ra option but never succeeded in having my machines get global IPv6 addresses.

DD-WRT Services DNSMasq Additional Options

I finally came across a set of options that I needed to get dnsmasq to broadcast router advertisements that are recognized by hosts on my local network to have them configure IPv6 global addresses.

dhcp-range=::1000,::FFFF,constructor:br0,ra-stateless,5m
ra-param=br0,10,300
enable-ra

In the terminal window of DDWRT I can dump the contents of the dnsmasq configuration file with the command cat /tmp/dnsmasq.conf. In doing so, I see what DDWRT writes to the configuration followed by my own customizations. When I do that I see multiple dhcp-range= lines, which initially confused me, but seems to work properly. The first is the IPv4 range and lease time, while the second is the IPv6 information from my “Additional Options” entry.

The tool I’ve used to diagnose my router advertisment issues has been radvdump. It seems to be available on most linux distributions as well as being on ddwrt. When I was running it on my router and the router was not sending out its own advertisments, I’d get messages from my ISP that looked like this:

# radvd configuration generated by radvdump 2.19
# based on Router Advertisement from fe80::22c:c8ff:fe42:24bf
# received by interface vlan2
#

interface vlan2
{
        AdvSendAdvert on;
        # Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
        AdvManagedFlag off;
        AdvOtherConfigFlag on;
        AdvReachableTime 0;
        AdvRetransTimer 0;
        AdvCurHopLimit 64;
        AdvDefaultLifetime 1800;
        AdvHomeAgentFlag off;
        AdvDefaultPreference high;
        AdvSourceLLAddress on;
        AdvLinkMTU 1500;

        prefix 2604:4080:1304::/64
        {
                AdvValidLifetime 2592000;
                AdvPreferredLifetime 604800;
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        }; # End of prefix definition

}; # End of interface definition

When I was successfully running radvd on ddwrt, I never saw its own advertisements but could see them on other machines on my local network. Now that I’m using dnsmasq for my advertisements, I see messages on the router itself.

# radvd configuration generated by radvdump 2.19
# based on Router Advertisement from fe80::b27f:b9ff:fe83:6591
# received by interface br0
#

interface br0
{
        AdvSendAdvert on;
        # Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
        AdvManagedFlag off;
        AdvOtherConfigFlag on;
        AdvReachableTime 0;
        AdvRetransTimer 0;
        AdvCurHopLimit 64;
        AdvDefaultLifetime 300;
        AdvHomeAgentFlag off;
        AdvDefaultPreference medium;
        AdvLinkMTU 1500;
        AdvSourceLLAddress on;

        prefix 2604:4080:1304:8010::/64
        {
                AdvValidLifetime 300;
                AdvPreferredLifetime 300;
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        }; # End of prefix definition

        DNSSL wimsworld.com wimsworld.local local
        {
                AdvDNSSLLifetime 300;
        }; # End of DNSSL definition

        RDNSS fe80::b27f:b9ff:fe83:6591
        {
                AdvRDNSSLifetime 300;
        }; # End of RDNSS definition

}; # End of interface definition

I looked at the radvdump output from a friend’s location where he’s on XFinity and IPv6 just seems to be configured and works and this is what I saw. This is from a client machine since he has no access to the Xfinity supplied router itself.

# radvd configuration generated by radvdump 2.17
# based on Router Advertisement from fe80::16c0:3eff:fe4e:400c
# received by interface wlan0
#

interface wlan0
{
        AdvSendAdvert on;
        # Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
        AdvManagedFlag on;
        AdvOtherConfigFlag on;
        AdvReachableTime 0;
        AdvRetransTimer 0;
        AdvCurHopLimit 64;
        AdvDefaultLifetime 180;
        AdvHomeAgentFlag off;
        AdvDefaultPreference medium;
        AdvSourceLLAddress on;

        RDNSS 2001:558:feed::1 2001:558:feed::2
        {
                AdvRDNSSLifetime 180;
        }; # End of RDNSS definition


        prefix 2601:603:4c7f:41e0::/64
        {
                AdvValidLifetime 300;
                AdvPreferredLifetime 300;
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        }; # End of prefix definition


        route ::/0
        {
                AdvRoutePreference medium;
                AdvRouteLifetime 180;
        }; # End of route definition

}; # End of interface definition

When IPv6 is working properly on my router I have a globally scoped IPv6 address on my vlan2 interface and one on my br0 interface. I can recognize that my vlan2 interface is the outside world because the IPv4 address associated with it is a routable IPv4 address while the IPv4 address associated with br0 is 192.168.0.1.

I learned a lot about how IPv6 allocates addresses during this process, A bunch of the resources I used came from this set of locations.

Sunrise and Sunset in C++

I wanted to calculate sunrise and sunset at my location for a recent project I was working on where I wouldn’t necessarily have access to internet connectivity. I did some quick searching and found a Wikipedia page with equations and thought it would be straightforward to put those equations into code. Somehow, I was not able to get the routines working correctly.

I came across a NOAA page that used equations in a different form, and also had links to an excel spreadsheet I could download and play with. I was able to reverse engineer the spreadsheet into C++ code to get a working routine that calculates the sunrise and sunset to an accuracy that is good enough for my needs.

I’m sharing my code here. If you see anything I’ve done wrong, please let me know. If you find it useful, please let me know about that as well.

/////////////////////////////////////////////////////////////////////////////
// From NOAA Spreadsheet https://gml.noaa.gov/grad/solcalc/calcdetails.html
bool getSunriseSunset(time_t& Sunrise, time_t& Sunset, const time_t& TheTime, const double Latitude, double Longitude)
{
	bool rval = false;
	struct tm LocalTime;
	if (0 != localtime_r(&TheTime, &LocalTime))
	{
		// if we don't have a valid latitude or longitude, declare sunrise to be midnight, and sunset one second before midnight
		if ((Latitude == 0) || (Longitude == 0))
		{
			LocalTime.tm_hour = 0;
			LocalTime.tm_min = 0;
			LocalTime.tm_sec = 0;
			Sunrise = mktime(&LocalTime);
			Sunset = Sunrise + 24*60*60 - 1;
		}
		else
		{
			double JulianDay = Time2JulianDate(TheTime); // F
			double JulianCentury = (JulianDay - 2451545) / 36525;	// G
			double GeomMeanLongSun = fmod(280.46646 + JulianCentury * (36000.76983 + JulianCentury * 0.0003032), 360);	// I
			double GeomMeanAnomSun = 357.52911 + JulianCentury * (35999.05029 - 0.0001537 * JulianCentury);	// J
			double EccentEarthOrbit = 0.016708634 - JulianCentury * (0.000042037 + 0.0000001267 * JulianCentury);	// K
			double SunEqOfCtr = sin(radians(GeomMeanAnomSun)) * (1.914602 - JulianCentury * (0.004817 + 0.000014 * JulianCentury)) + sin(radians(2 * GeomMeanAnomSun)) * (0.019993 - 0.000101 * JulianCentury) + sin(radians(3 * GeomMeanAnomSun)) * 0.000289; // L
			double SunTrueLong = GeomMeanLongSun + SunEqOfCtr;	// M
			double SunAppLong = SunTrueLong - 0.00569 - 0.00478 * sin(radians(125.04 - 1934.136 * JulianCentury));	// P
			double MeanObliqEcliptic = 23 + (26 + ((21.448 - JulianCentury * (46.815 + JulianCentury * (0.00059 - JulianCentury * 0.001813)))) / 60) / 60;	// Q
			double ObliqCorr = MeanObliqEcliptic + 0.00256 * cos(radians(125.04 - 1934.136 * JulianCentury));	// R
			double SunDeclin = degrees(asin(sin(radians(ObliqCorr)) * sin(radians(SunAppLong))));	// T
			double var_y = tan(radians(ObliqCorr / 2)) * tan(radians(ObliqCorr / 2));	// U
			double EquationOfTime = 4 * degrees(var_y * sin(2 * radians(GeomMeanLongSun)) - 2 * EccentEarthOrbit * sin(radians(GeomMeanAnomSun)) + 4 * EccentEarthOrbit * var_y * sin(radians(GeomMeanAnomSun)) * sin(2 * radians(GeomMeanLongSun)) - 0.5 * var_y * var_y * sin(4 * radians(GeomMeanLongSun)) - 1.25 * EccentEarthOrbit * EccentEarthOrbit * sin(2 * radians(GeomMeanAnomSun))); // V
			double HASunriseDeg = degrees(acos(cos(radians(90.833)) / (cos(radians(Latitude)) * cos(radians(SunDeclin))) - tan(radians(Latitude)) * tan(radians(SunDeclin)))); // W
			double SolarNoon = (720 - 4 * Longitude - EquationOfTime + LocalTime.tm_gmtoff / 60) / 1440; // X
			double SunriseTime = SolarNoon - HASunriseDeg * 4 / 1440;	// Y
			double SunsetTime = SolarNoon + HASunriseDeg * 4 / 1440;	// Z
			LocalTime.tm_hour = 0;
			LocalTime.tm_min = 0;
			LocalTime.tm_sec = 0;
			time_t Midnight = mktime(&LocalTime);
			Sunrise = Midnight + SunriseTime * 86400;
			Sunset = Midnight + SunsetTime * 86400;
		}
		rval = true;
	}
	return(rval);
}

I’ve included the individual routines I created when trying to duplicate the Wikipedia equations. If anyone can point out why they didn’t work for me, I’d appreciate that as well.

/////////////////////////////////////////////////////////////////////////////
double radians(const double degrees)
{
	return((degrees * M_PI) / 180.0);
}
double degrees(const double radians)
{
	return((radians * 180.0) / M_PI);
}
double Time2JulianDate(const time_t& TheTime)
{
	double JulianDay = 0;
	struct tm UTC;
	if (0 != gmtime_r(&TheTime, &UTC))
	{
		// https://en.wikipedia.org/wiki/Julian_day
		// JDN = (1461 × (Y + 4800 + (M ? 14)/12))/4 +(367 × (M ? 2 ? 12 × ((M ? 14)/12)))/12 ? (3 × ((Y + 4900 + (M - 14)/12)/100))/4 + D ? 32075
		JulianDay = (1461 * ((UTC.tm_year + 1900) + 4800 + ((UTC.tm_mon + 1) - 14) / 12)) / 4
			+ (367 * ((UTC.tm_mon + 1) - 2 - 12 * (((UTC.tm_mon + 1) - 14) / 12))) / 12
			- (3 * (((UTC.tm_year + 1900) + 4900 + ((UTC.tm_mon + 1) - 14) / 12) / 100)) / 4
			+ (UTC.tm_mday)
			- 32075;
		// JD = JDN + (hour-12)/24 + minute/1440 + second/86400
		double partialday = (static_cast<double>((UTC.tm_hour - 12)) / 24) + (static_cast<double>(UTC.tm_min) / 1440.0) + (static_cast<double>(UTC.tm_sec) / 86400.0);
		JulianDay += partialday;
	}
	return(JulianDay);
}
time_t JulianDate2Time(const double JulianDate)
{
	time_t TheTime = (JulianDate - 2440587.5) * 86400;
	return(TheTime);
}
double JulianDate2JulianDay(const double JulianDate)
{
	double n = JulianDate - 2451545.0 + 0.0008;
	return(n);
}
/////////////////////////////////////////////////////////////////////////////
// These equations all come from https://en.wikipedia.org/wiki/Sunrise_equation
double getMeanSolarTime(const double JulianDay, const double longitude)
{
	// an approximation of mean solar time expressed as a Julian day with the day fraction.
	double MeanSolarTime = JulianDay - (longitude / 360);
	return (MeanSolarTime);
}
double getSolarMeanAnomaly(const double MeanSolarTime)
{
	double SolarMeanAnomaly = fmod(357.5291 + 0.98560028 * MeanSolarTime, 360);
	return(SolarMeanAnomaly);
}
double getEquationOfTheCenter(const double SolarMeanAnomaly)
{
	double EquationOfTheCenter = 1.9148 * sin(radians(SolarMeanAnomaly)) + 0.0200 * sin(radians(2 * SolarMeanAnomaly)) + 0.0003 * sin(radians(3 * SolarMeanAnomaly));
	return(EquationOfTheCenter);
}
double getEclipticLongitude(const double SolarMeanAnomaly, const double EquationOfTheCenter)
{
	double EclipticLongitude = fmod(SolarMeanAnomaly + EquationOfTheCenter + 180 + 102.9372, 360);
	return(EclipticLongitude);
}
double getSolarTransit(const double MeanSolarTime, const double SolarMeanAnomaly, const double EclipticLongitude)
{
	// the Julian date for the local true solar transit (or solar noon).
	double SolarTransit = 2451545.0 + MeanSolarTime + 0.0053 * sin(radians(SolarMeanAnomaly)) - 0.0069 * sin(radians(2 * EclipticLongitude));
	return(SolarTransit);
}
double getDeclinationOfTheSun(const double EclipticLongitude)
{
	double DeclinationOfTheSun = sin(radians(EclipticLongitude)) * sin(radians(23.44));
	return(DeclinationOfTheSun);
}
double getHourAngle(const double Latitude, const double DeclinationOfTheSun)
{
	double HourAngle = (sin(radians(-0.83)) - sin(radians(Latitude)) * sin(radians(DeclinationOfTheSun))) / (cos(radians(Latitude)) * cos(radians(DeclinationOfTheSun)));
	return(HourAngle);
}
double getSunrise(const double SolarTransit, const double HourAngle)
{
	double Sunrise = SolarTransit - (HourAngle / 360);
	return(Sunrise);
}
double getSunset(const double SolarTransit, const double HourAngle)
{
	double Sunset = SolarTransit + (HourAngle / 360);
	return(Sunset);
}

Here’s the code snippet I was using when trying to get the Wikipedia equations working.

double JulianDay = JulianDate2JulianDay(Time2JulianDate(LoopStartTime));
double MeanSolarTime = getMeanSolarTime(JulianDay, Longitude);
double SolarMeanAnomaly = getSolarMeanAnomaly(MeanSolarTime);
double EquationOfTheCenter = getEquationOfTheCenter(SolarMeanAnomaly);
double EclipticLongitude = getEclipticLongitude(SolarMeanAnomaly, EquationOfTheCenter);
double SolarTransit = getSolarTransit(MeanSolarTime, SolarMeanAnomaly, EclipticLongitude);
double DeclinationOfTheSun = getDeclinationOfTheSun(EclipticLongitude);
double HourAngle = getHourAngle(Latitude, DeclinationOfTheSun);
double Sunrise = getSunrise(SolarTransit, HourAngle);
double Sunset = getSunset(SolarTransit, HourAngle);
std::cout.precision(std::numeric_limits<double>::max_digits10);
std::cout << "         Julian Date: " << Time2JulianDate(LoopStartTime) << std::endl;
std::cout << "           Unix Time: " << LoopStartTime << std::endl;
std::cout << "         Julian Date: " << timeToExcelLocal(JulianDate2Time(Time2JulianDate(LoopStartTime))) << std::endl;
std::cout << "            Latitude: " << Latitude << std::endl;
std::cout << "           Longitude: " << Longitude << std::endl;
std::cout << "          Julian Day: " << JulianDay << std::endl;
std::cout << "       MeanSolarTime: " << MeanSolarTime << std::endl;
std::cout << "    SolarMeanAnomaly: " << SolarMeanAnomaly << std::endl;
std::cout << " EquationOfTheCenter: " << EquationOfTheCenter << std::endl;
std::cout << "   EclipticLongitude: " << EclipticLongitude << std::endl;
std::cout << "        SolarTransit: " << timeToExcelLocal(JulianDate2Time(SolarTransit)) << std::endl;
std::cout << " DeclinationOfTheSun: " << DeclinationOfTheSun << std::endl;
std::cout << "           HourAngle: " << HourAngle << std::endl;
std::cout << "             Sunrise: " << timeToExcelLocal(JulianDate2Time(Sunrise)) << std::endl;
std::cout << "              Sunset: " << timeToExcelLocal(JulianDate2Time(Sunset)) << std::endl;

Set default XTerm font and screen size

xrdp xterm default

I’ve been employing the interesting hack to run a single graphical program on my raspberry Pi of installing and running xrdp without having installed a full graphical window manager. When I connect to the rdp session and have my window set to 1920×1080, the terminal window that appears runs in the top left of the session window with no visible borders. If the mouse is over the terminal session the cursor works as expected. If the mouse is over the vast blackness of the rest of the screen, the cursor is a simple X cursor.

It’s never been a huge deal to me because I just run the single command of my graphical program most of the time. I had been looking for a way to configure the terminal to be a specific size based on the RDP session, but it’s not really designed for that.

What I finally found was that if I specify the font and window geometry based on the font, it will create the terminal window appropriately. A bit of trial and error and I came up with the following file in my user directory. One nice feature of this fix is that it’s not modifying any systemwide configuration.

wim@WimPi4:~ $ cat ~/.Xresources 
xterm*faceName: Luxi Mono
xterm*faceSize: 16
xterm*geometry: 147x41

Thanks to this post for giving me the solution.

Google Domains Dynamic DNS and IPv6

I’ve been wanting to use a dynamic address in my personal domain and IPv6. While I read that it should be possible, finding the exact method of configuring ddclient to do so was not obvious to me. The default installation configures everything to register an IPv4 address and finding specific configuration examples using IPv6 was hard. It turns out that finding examples was hard because the process itself is easy.

Go to domains.google.com, configure your domain to include dynamic DNS hosts, add the hostname you want to register, and retrieve the specific credentials for that hostname. There are help pages describing that process at google. It will create a record with the hostname you specify, and an A record (IPv4) with a 1 minute time to live.

Google Domains

Install ddclient using apt and go through the debconf wizard to enter the credentials. It will set up the default configuration using IPv4.

sudo apt install ddclient -y
domains.google
ddclient –force –verbose to see if errors occur
Google Domains IPv4 address registered

Manually editing /etc/ddclient.conf and changing the use= statement to usev6= and running ddclient a second time will switch to registering the IPv6 address.

wim@WimPiZeroW-Wim:~ $ sudo cat /etc/ddclient.conf
# Configuration file for ddclient generated by debconf
#
# /etc/ddclient.conf

protocol=googledomains \
usev6=if, if=wlan0 \
login=**************** \
password='***************' \
wimpizerow-wim.wimsworld.com
wim@WimPiZeroW-Wim:~ $
another ddclient –force –verbose to confirm changes didn’t create errors

running sudo systemctl confirms that ddclient.service is loaded active and running. If the address were to change, it should automatically be updated in the dynamic domain entry.

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:

LTE wireless on SIM7600G-H 4G HAT (B) for Raspberry Pi

After coming across the recent and well written article Using 4G LTE wireless modems on a Raspberry Pi, I decided to give it a try myself. I looked at his parts list, and then ended up going in a different direction by buying an all-in-one unit designed explicitly for the Raspberry Pi Zero.

What’s on the Board
  1. SIM7600G-H
  2. FE1.1S USB HUB chip
  3. NAU8810 audio decoder
  4. RT9193-33 voltage translator
  5. USB HUB input D+/D- pogo pin: for Raspberry Pi Zero/Zero W MicroUSB connector: for other Raspberry Pi boards or PC
  6. Pogo pin power supply 5V: connects to 5V pin of Zero/Zero W, up to 2A current GND: connects to GND pin of Zero/Zero W
  1. USB extended ports USB1~USB2: USB-A connectors USB3: solder pad
  2. SIM card slot supports 1.8V/3V SIM card
  3. 3.5mm earphone/mic jack
  4. MAIN antenna connector
  5. AUX auxiliary antenna connector
  6. GNSS antenna connector
  7. Power indicator
  8. Network status indicator

I’m using Google Fi Unlimited Plus as my network on my phone right now, and one of its features is the ability to have multiple data only devices using part of your data allocation. That makes experimentation with a device like this fairly easy as I was able to order a data sim from Google and put it in the device and it just worked.

I’d never heard of pogo pins before. These are spring loaded contacts that line up with pads on the Raspberry Pi Zero to make electrical contact. They worked the first time I screwed everything together, but when I took it apart and put it back together a second time the board wasn’t recognized. I removed power, pushed down the pins with some tweezers, restored power, and the board was recognized again.

wim@WimPiZeroWCamera:~ $ lsusb
Bus 001 Device 003: ID 1e0e:9001 Qualcomm / Option
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@WimPiZeroWCamera:~ $ 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=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 1, Class=Vendor Specific Class, Driver=option, 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=qmi_wwan, 480M
wim@WimPiZeroWCamera:~ $ sudo apt install libqmi-utils udhcpc ifmetric

I went through the test commands from Jeff Geerling’s post related to QMI Mode setup, then created a config file for my wwan0 interface:

auto wwan0
iface wwan0 inet manual
     pre-up ifconfig wwan0 down
     pre-up echo Y > /sys/class/net/wwan0/qmi/raw_ip
     pre-up for _ in $(seq 1 10); do /usr/bin/test -c /dev/cdc-wdm0 && break; /bin/sleep 1; done
     pre-up for _ in $(seq 1 10); do /usr/bin/qmicli -d /dev/cdc-wdm0 --nas-get-signal-strength && break; /bin/sleep 1; done
     pre-up sudo qmicli -p -d /dev/cdc-wdm0 --device-open-net='net-raw-ip|net-no-qos-header' --wds-start-network="apn='h2g2',ip-type=4" --client-no-release-cid
     pre-up udhcpc -i wwan0
     pre-up /usr/sbin/ifmetric wwan0 400
     post-down /usr/bin/qmi-network /dev/cdc-wdm0 stop
You can see where I added the ifmetric command as the last pre-up line
Route and Ping from interfaces.

You can see in the image above that my Wi-Fi network is running IPv6 and the default ping uses that interface, while specifying the wwan0 interface causes the traffic to travel a different path.

ifconfig details.
wim@WimPiZeroWCamera:~ $ 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 10482sec preferred_lft 9132sec
    inet6 2604:4080:1304:8010:8a38:1e12:3b21:5443/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 24sec preferred_lft 14sec
    inet6 fe80::ea0d:3fe8:b6c4:da64/64 scope link
       valid_lft forever preferred_lft forever
3: wwan0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    link/none
    inet 100.198.30.32/26 scope global wwan0
       valid_lft forever preferred_lft forever
wim@WimPiZeroWCamera:~ $ sudo qmicli -d /dev/cdc-wdm0 --nas-get-signal-info
-get-serving-system
sudo qmi-network /dev/cdc-wdm0 status
sudo qmicli -d /dev/cdc-wdm0  --wds-get-packet-service-status[/dev/cdc-wdm0] Successfully got signal info
LTE:
        RSSI: '-74 dBm'
        RSRQ: '-12 dB'
        RSRP: '-102 dBm'
        SNR: '4.4 dB'
wim@WimPiZeroWCamera:~ $ sudo qmicli -d /dev/cdc-wdm0 --nas-get-signal-strength
[/dev/cdc-wdm0] Successfully got signal strength
Current:
        Network 'lte': '-74 dBm'
RSSI:
        Network 'lte': '-74 dBm'
ECIO:
        Network 'lte': '-2.5 dBm'
IO: '-106 dBm'
SINR (8): '9.0 dB'
RSRQ:
        Network 'lte': '-12 dB'
SNR:
        Network 'lte': '4.4 dB'
RSRP:
        Network 'lte': '-102 dBm'
wim@WimPiZeroWCamera:~ $ sudo qmicli -d /dev/cdc-wdm0 --nas-get-home-network
[/dev/cdc-wdm0] Successfully got home network:
        Home network:
                MCC: '310'
                MNC: '260'
                Description: 'T-Mobile'
wim@WimPiZeroWCamera:~ $ sudo qmicli -d /dev/cdc-wdm0 --nas-get-serving-system
[/dev/cdc-wdm0] Successfully got serving system:
        Registration state: 'registered'
        CS: 'attached'
        PS: 'attached'
        Selected network: '3gpp'
        Radio interfaces: '1'
                [0]: 'lte'
        Roaming status: 'off'
        Data service capabilities: '1'
                [0]: 'lte'
        Current PLMN:
                MCC: '310'
                MNC: '260'
                Description: '����.��i'
        Roaming indicators: '1'
                [0]: 'off' (lte)
        3GPP time zone offset: '-420' minutes
        3GPP daylight saving time adjustment: '1' hours
        3GPP location area code: '65534'
        3GPP cell ID: '45023373'
        Detailed status:
                Status: 'available'
                Capability: 'cs-ps'
                HDR Status: 'none'
                HDR Hybrid: 'no'
                Forbidden: 'no'
        LTE tracking area code: '11316'
        Full operator code info:
                MCC: '310'
                MNC: '260'
                MNC with PCS digit: 'yes'
wim@WimPiZeroWCamera:~ $ sudo qmi-network /dev/cdc-wdm0 status
Profile at '/etc/qmi-network.conf' not found...
Getting status with 'qmicli -d /dev/cdc-wdm0 --wds-get-packet-service-status  '...
Status: connected
wim@WimPiZeroWCamera:~ $ sudo qmicli -d /dev/cdc-wdm0  --wds-get-packet-service-status
[/dev/cdc-wdm0] Connection status: 'connected'
extra information