Network access thanks to a cellular connection

Depending on your embedded project needs, sometimes you may want a network access. In a wireless environment, one solution is to use a cellular connection to give you Internet access.

Tested configurations

This guide was tested with redpesk OS Arz 1.1 and Batz 2.0 with the following boards:

Board Modem redpesk OS Kernel
Solidrun SolidSense N8 Compact Edge Gateway Quectel EG25 Arz 1.1 5.4.47
Solidrun Hummingboard Pulse Quectel EC25 Arz 1.1, Batz 2.0 5.10.72

Requirements

Kernel configuration

The following kernel configuration is required for all boards:

CONFIG_USB_SERIAL_OPTION=y
CONFIG_PPP=y
CONFIG_PPP_ASYNC=y 
CONFIG_PPP_SYNC_TTY=y
CONFIG_PPP_DEFLATE=y

The SolidSense seems to require more options:

CONFIG_PPP_BSDCOMP=y
CONFIG_PPP_FILTER=y
CONFIG_PPP_MPPE=y
CONFIG_PPP_MULTILINK=y
CONFIG_PPPOE=y

You can also add the following line to prevent the kernel from creating a /dev/cdc-wm device (to talk to the modem in QMI) and a wwan network interface which we won’t use since this guide uses PPP to talk to the modem.

CONFIG_USB_NET_QMI_WWAN=n

Qualcomm MSM Interface is a proprietary protocol used to interact with Qualcomm baseband processors. Point-to-Point Protocol is a protocol for two routers to communicate without any intermediary.

If more kernel configuration is required, it should be specified in your modem’s documentation.

You can use this website for information about the kernel configuration.

Packages

NetworkManager comes by default in redpesk OS, but some of its extensions are required for LTE to work:

dnf install NetworkManager-ppp NetworkManager-wwan ModemManager

Hardware

Don’t forget to plug in your SIM card and GSM antenna!

SolidSense with GSM antenna

Configure the network

There are two ways of configuring a cellular connection:

  • setup a connection which holds all the data and will automatically come up after each reboot.
  • setup a connection with minimal data, which provides more data protection since everything (the SIM’s PIN) isn’t saved to disk, but requires manual intervention after a reboot.

One-time magic command

The first step is to find which tty device corresponds to your modem. When everything is setup correctly, you should see a ttyUSB device in NetworkManager’s device list:

[root@localhost ~]# nmcli dev
DEVICE         TYPE      STATE                   CONNECTION 
lo             loopback  connected (externally)  lo         
ttyUSB2        gsm       disconnected            --         <<<<<
wlan0          wifi      disconnected            --         
p2p-dev-wlan0  wifi-p2p  disconnected            --         
enp1s0         ethernet  unavailable             --         
eth0           ethernet  unavailable             --         

You also need to know your carrier’s APN (may look like free or live.vodafone.com) and your SIM’s PIN code. If your SIM is completely unlocked, just remove the gsm.pin <PIN> option from the command.

Using this information, you can type in the following command which will create a NetworkManager “connection” object which holds all the data. Since we specify the autoconnect option, it will also setup your connection and a ppp network device automatically (and after each reboot):

[root@localhost ~]# nmcli connection add type gsm ifname ttyUSB2 autoconnect yes con-name quectel-lte apn <CARRIER APN> gsm.pin <SIM PIN>
Connection 'quectel-lte' (c8e809bb-1d68-4451-bd6e-d2b5e9a884cc) successfully added.

Manual setup

This part will detail some options of the nmcli connection add command, explain how to remove some and manually do what they do. You still need to know which ttyUSB device matches your modem by using the command given in the previous section.

PIN code

The main thing you wouldn’t want to see written in your board’s memory is the SIM’s PIN code. To remove it from persistent storage, you have to remove the gsm.pin <SIM PIN> option from the command:

[root@localhost ~]# nmcli connection add type gsm ifname ttyUSB2 autoconnect yes con-name quectel-lte apn <CARRIER APN>

To unlock the SIM manually after boot, you need to go through ModemManager directly. List all modems to find the ID of yours:

[root@localhost ~]# mmcli -L
    /org/freedesktop/ModemManager1/Modem/0 [Quectel] EC25

We are interested in the number after Modem/, here the 0. Then unlock the SIM by using its PIN code:

[root@localhost ~]# mmcli -i 0 --pin=<PIN>
successfully sent PIN code to the SIM

Under redpesk OS Batz 2.0, unlocking the SIM will change the modem’s and SIM’s IDs used by ModemManager. This means “all-in-one commands” like ModemManager’s --simple-connect won’t work properly. Use mmcli -L again to see the new ID.

-i 0 does not designate the modem of ID 0, but the SIM of ID 0. You can find which SIM a modem uses in mmcli -m 0’s output, even though the modem’s ID and the SIM’s ID usually are the same.

Autoconnect

For debugging purposes you may want your device to not connect automatically to a cellular network. You can then remove the autoconnect yes option.

To manually launch the connection, you can use the following command:

[root@localhost ~]# nmcli device connect ttyUSB2
Device 'ttyUSB2' successfully activated with 'b4f9c35b-df79-49cc-b518-5a19bedc6a2b'.

NetworkManager knows which connection to use because you specified the ifname option earlier. A disconnect subcommand also exists to manually disconnect from a cellular network.

Test the connection

To validate the connection, you can try to reach 8.8.8.8 (which is a public Google DNS server):

[root@localhost ~]# ping 8.8.8.8 -c 1
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=16.6 ms

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 16.598/16.598/16.598/0.000 ms

To test the bandwidth, you can try to download a test file of a given size (wget will show you the download speed); you can find some on this OVH service.

[root@localhost ~]# wget https://proof.ovh.net/files/10Mb.dat
--2024-01-22 17:29:51--  https://proof.ovh.net/files/10Mb.dat
Resolving proof.ovh.net (proof.ovh.net)... 141.95.207.211, 2001:41d0:242:d300::
Connecting to proof.ovh.net (proof.ovh.net)|141.95.207.211|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10485760 (10M) [application/octet-stream]
Saving to: ‘10Mb.dat’

10Mb.dat            100%[===================>]  10.00M   206KB/s    in 53s     

2024-01-22 17:30:45 (193 KB/s) - ‘10Mb.dat’ saved [10485760/10485760]

Keep a ttyUSB device from being used by ModemManager (AT commands)

For some use cases you may need to manually send AT commands to your SIM card. For that you have to identify which ttyUSB devices associated with your modem can be used to send these commands:

[root@localhost ~]# mmcli -m 1
...
System |       device: /sys/devices/platform/soc@0/32f10108.usb/38200000.dwc3/xhci-hcd.3.auto/usb3/3-1/3-1.3
       |      drivers: option
       |       plugin: quectel
       | primary port: ttyUSB2
       |        ports: ttyUSB0 (qcdm), ttyUSB1 (gps), ttyUSB2 (at), ttyUSB3 (at)
...

As you can see, this modem can receive AT commands through ttyUSB2 or ttyUSB3. But them being present in the result of this command means that ModemManager already uses both these ports for its needs. We need to tell ModemManager to ignore one of these two ports and leave it for our own use. This is done through a udev rule.

Your cellular connection will still work properly with only one AT device available to ModemManager; but you won’t be able to use some ModemManager commands anymore once you are connected to the network (like the 3gpp-scan shown further down).

The udev rule needs to identify the modem (which usually is an USB device, identifiable with a vendor ID and a product ID) and the interface which provides the serial device (through an interface number). It will then apply a label to this interface which will be read by ModemManager and tell it not to use this interface.

To find the vendor and product IDs, you need to use lsusb:

[root@localhost ~]# lsusb
...
Bus 003 Device 003: ID 2c7c:0125 Quectel Wireless Solutions Co., Ltd. EC25 LTE modem
...

We see here that for our modem, the vendor ID is 2c7c and the product ID 0125.

Then we need to identify the interface number. For that we can list the /dev/serial/by-id directory:

[root@localhost ~]# ls -l /dev/serial/by-id
total 0
lrwxrwxrwx 1 root root 13 Jan 24 12:26 usb-Android_Android-if00-port0 -> ../../ttyUSB0
lrwxrwxrwx 1 root root 13 Jan 24 12:26 usb-Android_Android-if01-port0 -> ../../ttyUSB1
lrwxrwxrwx 1 root root 13 Jan 24 12:26 usb-Android_Android-if02-port0 -> ../../ttyUSB2
lrwxrwxrwx 1 root root 13 Jan 24 12:26 usb-Android_Android-if03-port0 -> ../../ttyUSB3

We would like ModemManager to ignore ttyUSB3, which corresponds to the interface number 3 (see if03 on the ttyUSB3 line).

If this method of finding the interface number does not work, you can use lsusb -v; in the section Device descriptor > Configuration descriptor you will see multiple Interface descriptor each with a bInterfaceNumber. You will need to guess and retry to find the correct interface number using this method.

We now have all our data. We can create a new udev rule: create a file in /etc/udev/rules.d which name starts with 79- and extension is .rules, according to the udev rules hierarchy (see sources). It will contain the following rule (don’t forget to replace 2c7c with your vendor ID, 0125 with your product ID and 03 with your interface number):

ACTION!="add|change|move|bind", GOTO="mm_quectel_ec25_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c7c", GOTO="mm_quectel_ec25"
GOTO="mm_quectel_ec25_end"

LABEL="mm_quectel_ec25"
# Store interface number in an env var
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", \
  ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# USB iface #4 will be ignored
ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="03", \
  SUBSYSTEM=="tty", ENV{ID_MM_PORT_IGNORE}="1"
LABEL="mm_quectel_ec25_end"

To apply the rule, you need to reboot. You should now see that the desired serial port is ignored by ModemManager:

[root@localhost ~]# mmcli -m 0
...
System   |       device: /sys/devices/platform/soc@0/32f10108.usb/38200000.dwc3/xhci-hcd.3.auto/usb3/3-1/3-1.3
         |      drivers: option
         |       plugin: quectel
         | primary port: ttyUSB3
         |        ports: ttyUSB0 (qcdm), ttyUSB1 (gps), ttyUSB2 (at), 
         |               ttyUSB3 (ignored)
...

You are now able to send AT commands to ttyUSB3 using any serial client.

Sources

More useful commands

This section regroups many commands which can be useful if your connection isn’t working or you want to do more custom things.

List all information about a modem

Find out the modem number:

[root@localhost ~]# mmcli -L
    /org/freedesktop/ModemManager1/Modem/0 [Quectel] EC25

Depending on ModemManager’s version, the modem number can change when unlocking the SIM.

Get information; for debugging purposes, this is an example where the SIM is inserted and the modem is detected as such, but no configuration has been applied and the connection is not active:

[root@localhost ~]# mmcli -m 0
  -----------------------------
  General  |              path: /org/freedesktop/ModemManager1/Modem/0
           |         device id: b8719a38bd8ffb9d6d30c788984b08ded3ce21f3
  -----------------------------
  Hardware |      manufacturer: Quectel
           |             model: EC25
           | firmware revision: EC25EFAR06A03M4G
           |         supported: gsm-umts, lte
           |           current: gsm-umts, lte
           |      equipment id: 866758042316923
  -----------------------------
  System   |            device: /sys/devices/platform/soc@0/32f10108.usb/38200000.dwc3/xhci-hcd.3.auto/usb3/3-1/3-1.3
           |           drivers: option
           |            plugin: quectel
           |      primary port: ttyUSB2
           |             ports: ttyUSB0 (qcdm), ttyUSB1 (gps), ttyUSB2 (at), ttyUSB3 (at)
  -----------------------------
  Status   |              lock: sim-pin
           |    unlock retries: sim-pin (3), sim-puk (10), sim-pin2 (3), sim-puk2 (10)
           |             state: locked
           |       power state: on
  -----------------------------
  Modes    |         supported: allowed: 2g, 3g, 4g; preferred: none
           |           current: allowed: 2g, 3g, 4g; preferred: none
  -----------------------------
  IP       |         supported: ipv4, ipv6, ipv4v6
  -----------------------------
  3GPP     |     enabled locks: sim
  -----------------------------
  SIM      |  primary sim path: /org/freedesktop/ModemManager1/SIM/0

This is an example where the connection is setup and active, and the udev rule to ignore a serial interface is active (only the sections which differ compared to previous example are shown):

[root@localhost ~]# mmcli -m 1
  --------------------------------
  General  |                 path: /org/freedesktop/ModemManager1/Modem/1
           |            device id: b8719a38bd8ffb9d6d30c788984b08ded3ce21f3
...
  --------------------------------
  System   |               device: /sys/devices/platform/soc@0/32f10108.usb/38200000.dwc3/xhci-hcd.3.auto/usb3/3-1/3-1.3
           |              drivers: option
           |               plugin: quectel
           |         primary port: ttyUSB2
           |                ports: ttyUSB0 (qcdm), ttyUSB1 (gps), ttyUSB2 (at), 
           |                       ttyUSB3 (ignored)
  --------------------------------
  Status   |       unlock retries: sim-pin (3), sim-puk (10), sim-pin2 (3), sim-puk2 (10)
           |                state: connected
           |          power state: on
           |          access tech: umts
           |       signal quality: 40% (recent)
...
  --------------------------------
  3GPP     |                 imei: 866758042316923
           |        enabled locks: sim
           |          operator id: 20815
           |        operator name: Free Free
           |         registration: home
           | packet service state: attached
  --------------------------------
  3GPP EPS | ue mode of operation: csps-2
  --------------------------------
  SIM      |     primary sim path: /org/freedesktop/ModemManager1/SIM/1
  --------------------------------
  Bearer   |                paths: /org/freedesktop/ModemManager1/Bearer/0

An unlocked (valid SIM PIN) but not connected yet modem shows an enabling, then a registered state (and no Bearer section).

List SIM information

[root@localhost ~]# mmcli -i 1
  -------------------------------
  General    |              path: /org/freedesktop/ModemManager1/SIM/1
  -------------------------------
  Properties |            active: yes
             |              imsi: this is a quite sensitive piece of data
             |             iccid: that too
             |       operator id: 20815
             |     operator name: Free
             |              gid1: FFFFFFFF
             |              gid2: FFFFFFFF

Scan nearby antennas

The SIM has to be unlocked for this command to work.

[root@localhost ~]# mmcli -m 1 --3gpp-scan --timeout=300
  ---------------------
  3GPP scan | networks: 20815 - Free (lte, available)
            |           20815 - Free (umts, available)
            |           20801 - Orange F (umts, available)
            |           20801 - Free (gsm, available)
            |           20820 - F-Bouygues Telecom (umts, forbidden)
            |           20810 - F SFR (umts, forbidden)
            |           20801 - Orange F (lte, available)
            |           20810 - F SFR (gsm, forbidden)
            |           20810 - F SFR (lte, forbidden)
            |           20820 - F-Bouygues Telecom (gsm, forbidden)
            |           20820 - F-Bouygues Telecom (lte, forbidden)
            |           20816 - 208 16 (lte, available)

NetworkManager device status

Most NetworkManager commands can be shortened. nmcli device and nmcli dev are equivalent, while status is the default subcommand of nmcli device so nmcli dev and nmcli device status are the same thing.

[root@localhost ~]# nmcli dev
DEVICE         TYPE      STATE                   CONNECTION 
wlan0          wifi      connected               IoTBZH     
lo             loopback  connected (externally)  lo         
ttyUSB2        gsm       disconnected            --         
p2p-dev-wlan0  wifi-p2p  disconnected            --         
enp1s0         ethernet  unavailable             --         
eth0           ethernet  unavailable             --         

From a cold start, with a fully automatic connection process, ttyUSB2’s status will go through these steps:

  • the device will be non-existent for a few seconds after boot
  • disconnected
  • connecting (prepare)
  • then it may disappear again while the SIM is unlocked because the modem’s ID changes
  • connecting (prepare)
  • connecting (configuring)
  • connected

If you chose not to save the SIM’s PIN to persistent storage with the gsm.pin option when adding a NetworkManager connection, the ttyUSB device status will at some point be connecting (need authentication). You will need to unlock the SIM with mmcli -i 0 --pin=<PIN> for NetworkManager to continue the connection process.

Modify some connection’s options

To avoid deleting and re-adding a NetworkManager every time you want to make a change, you can just modify the connection:

[root@localhost ~]# nmcli connection modify quectel-lte autoconnect no

Check the network interfaces and their IP addresses

This is the output of a successfully configured cellular connection through PPP:

[root@localhost ~]# ip a
...
5: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 3
    link/ppp 
    inet 10.13.235.249 peer 10.64.64.64/32 scope global ppp0
       valid_lft forever preferred_lft forever
    inet 10.13.235.249/32 scope global noprefixroute ppp0
       valid_lft forever preferred_lft forever
    inet6 fe80::7093:af04:2eea:ac89 peer fe80::f467:a9ea:44d9:edba/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
    inet6 fe80::e34b:4390:ac04:3e02/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

Connections configured using QMI are different, they use a wwanX interface automatically created by the kernel & udev when the QMI driver is present