My profile picutre
Electronics Engineer

Zephyr RTOS - PPP over SIM900

Posted on

Zephyr RTOS is AWESOME

This is the first post I'm writing about Zephyr RTOS, while I've been using it for a half year, I haven't thought of posting anything yet. But after trying a GSM sample application out of curiosity, I've got impressed even more, so I thought that's going to be my first post so you can shortly get the same impression I had experienced :)

So let me tell you why Zephyr is AWESOME.

The Zephyr Project is a Linux Foundation project. It unites developers across the world to build one of the best RTOS in the world. Zephyr is an open source project developed with security in mind with a lot of features built in like connectivity technologies and standards.

Zephyr intends to provide all components needed to develop resource-constrained and embedded or microcontroller-based applications. This includes, but is not limited to:

  • A small kernel
  • A flexible configuration and build system for compile-time definition of required resources and modules
  • A set of protocol stacks (IPv4 and IPv6, Constrained Application Protocol (CoAP), LwM2M, MQTT, 802.15.4, Thread, Bluetooth Low Energy, CAN)
  • A virtual file system interface with several flash file systems for non-volatile storage (FATFS, LittleFS, NVS)
  • Management and device firmware update mechanisms

I can keep writing endlessly, so..

Let's get started

What we're going to do in this post, is to build a Zephyr application with ppp protocol over GSM. PPP stands for Point to Point Protocol. So basically, our application will have connection to the Internet through this inexpensive 10+ year old module!

First things first, download Zephyr RTOS! Zephyr docs are quite detailed and it's quite easy to setup the development environment.

You can try this small example yourself with any of the supported boards, with minimum requirements having at least 2 uart and an RNG peripheral. I'll pick my STM32L476 nucleo board.

Please note, powering the module requires a good power supply, as when the module initiates a network connection, peak currents are high!

In order to build the sample application for our board, we have to create an overlay file, so we can tell zephyr where our GSM module is connected. Overlay files, are files that "sit" on top of the dts(device tree source file) files. DTS files serve the purpose of describing to the kernel the different peripherals being utilized or different external components connected to our microcontroller. Linux kernel also uses dts.

Head to sample application directory zephyr/samples/net/gsm_modem/boards We must create a file named board.overlay , nucleo_l476rg.overlay in our case.

Contents of nucleo_l476rg.overlay

/*
 * UART 1 is configured on PA10 rx and PA9 tx
 */
&usart1 {
	status = "okay";
	current-speed = <115200>;
	gsm: gsm-modem {
		compatible = "zephyr,gsm-ppp";
		label = "gsm_ppp";
	};
};

It's time to connect our GSM module TX and RX pins to the appropriate pins on our nucleo board. Don't forget to make sure that the two boards have the same ground reference.

Now we are ready to build the sample application for our board.

west build -b nucleo_l476rg samples/net/gsm_modem/ -- -DCONFIG_MODEM_GSM_APN=\"internet\"

Woooops, looks like something went wrong.

/home/konstantinos/zephyr-sdk-0.13.2/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/10.3.0/../../../../arm-zephyr-eabi/bin/ld.bfd: zephyr/subsys/net/ip/libsubsys__net__ip.a(net_shell.c.obj): in function `sys_rand32_get':
/home/konstantinos/zephyrproject/zephyr/build/zephyr/include/generated/syscalls/rand32.h:32: undefined reference to `z_impl_sys_rand32_get'
/home/konstantinos/zephyr-sdk-0.13.2/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/10.3.0/../../../../arm-zephyr-eabi/bin/ld.bfd: zephyr/subsys/net/l2/ppp/libsubsys__net__l2__ppp.a(fsm.c.obj): in function `sys_rand32_get':
/home/konstantinos/zephyrproject/zephyr/build/zephyr/include/generated/syscalls/rand32.h:32: undefined reference to `z_impl_sys_rand32_get'
/home/konstantinos/zephyr-sdk-0.13.2/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/10.3.0/../../../../arm-zephyr-eabi/bin/ld.bfd: zephyr/subsys/net/ip/libsubsys__net__ip.a(net_context.c.obj): in function `sys_rand32_get':
/home/konstantinos/zephyrproject/zephyr/build/zephyr/include/generated/syscalls/rand32.h:32: undefined reference to `z_impl_sys_rand32_get'
/home/konstantinos/zephyr-sdk-0.13.2/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/10.3.0/../../../../arm-zephyr-eabi/bin/ld.bfd: zephyr/drivers/net/libdrivers__net.a(ppp.c.obj): in function `sys_rand32_get':
/home/konstantinos/zephyrproject/zephyr/build/zephyr/include/generated/syscalls/rand32.h:32: undefined reference to `z_impl_sys_rand32_get'
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
FATAL ERROR: command exited with status 1: /usr/bin/cmake --build /home/konstantinos/zephyrproject/zephyr/build

As mentioned previously, RNG peripheral is required, but if we look in our board's dts file located at zephyr/boards/arm/nucleo_l476rg/nucleo_l476rg.dts we can see that RNG peripheral is not there. Since RNG is supported in L4 series as verified from zephyr/dts/arm/st/l4/stm32l4.dtsi. All we have to do is enable it in our overlay file or in the dts by adding the following.

&rng {
	status = "okay";
};

Before trying to rebuild again, we must clear the build directory located in zephyr, or if we use pristine build, we're mostly likely won't need to do that, as west is doing it for us. Learn more about prisine builds.

rm -rf build

As we see, now building is successful. The project is not that small, 100kB of Flash and 38kB of RAM is being used, but after all it contains a lot of stuff, full networking stack, shell and log module.

Memory region         Used Size  Region Size  %age Used
           FLASH:      100680 B         1 MB      9.60%
            SRAM:       37904 B        96 KB     38.56%
        IDT_LIST:          0 GB         2 KB      0.00%

It's time to flash the binary into our controller. Our friend west, provides a command for this as well. If you've flashed the onboard debugger with SEGGER firmware or you use an external SEGGER debugger, use west flash -r jlink otherwise just type west flash.

west flash -r jlink
-- west flash: rebuilding
ninja: no work to do.
-- west flash: using runner jlink
-- runners.jlink: JLink version: 7.60f
-- runners.jlink: Flashing file: /home/konstantinos/zephyrproject/zephyr/build/zephyr/zephyr.hex

Our project is now ready to be launched, open the serial port on the debugger with 115200 baud rate and hit the reset button and see the magic happen.

uart:~$ *** Booting Zephyr OS build zephyr-v3.0.0-1178-ge7d719b9849c  ***

[00:00:00.007,000] <inf> sample_gsm_ppp: Board 'nucleo_l476rg' APN 'internet' UART 'UART_1' device 0x80131f4 (gsm_ppp)
[00:00:02.001,000] <inf> sample_gsm_ppp: GSM modem on callback fired
[00:00:04.001,000] <inf> sample_gsm_ppp: GSM modem on callback fired
[00:00:04.303,000] <inf> modem_gsm: Manufacturer: SIMCOM_Lt
[00:00:04.366,000] <inf> modem_gsm: Model: SIMCOM_SIM900
[00:00:04.430,000] <inf> modem_gsm: Revision: Revision:1137B13SIM900M64_ST
[00:00:04.493,000] <inf> modem_gsm: IMEI: 0*************5
[00:00:04.627,000] <inf> modem_gsm: IMSI: 2*************0
[00:00:05.148,000] <inf> modem_gsm: ICCID: 8******************8
[00:00:05.174,000] <inf> modem_gsm: RSSI: -113
[00:00:11.175,000] <err> modem_cmd_handler: command ATD*99# ret:-116
[00:00:12.187,000] <inf> modem_gsm: RSSI: -91
[00:00:13.415,000] <inf> net_ppp: Initializing PPP to use UART_1
[00:00:33.495,000] <inf> sample_gsm_ppp: Network connected

uart:~$ net
net - Networking commands
Subcommands:
  allocs     :Print network memory allocations.
  arp        :Print information about IPv4 ARP cache.
  capture    :Configure network packet capture.
  conn       :Print information about network connections.
  dns        :Show how DNS is configured.
  events     :Monitor network management events.
  gptp       :Print information about gPTP support.
  iface      :Print information about network interfaces.
  ipv6       :Print information about IPv6 specific information and
              configuration.
  mem        :Print information about network memory usage.
  nbr        :Print neighbor information.
  ping       :Ping a network host.
  pkt        :net_pkt information.
  ppp        :PPP information.
  resume     :Resume a network interface
  route      :Show network route.
  stacks     :Show network stacks information.
  stats      :Show network statistics.
  suspend    :Suspend a network interface
  tcp        :Connect/send/close TCP connection.
  udp        :Send/recv UDP packet
  virtual    :Show virtual network interfaces.
  vlan       :Show VLAN information.
  websocket  :Print information about WebSocket connections.

uart:~$ net ping 8.8.8.8
PING 8.8.8.8
28 bytes from 8.8.8.8 to 10.68.143.88: icmp_seq=0 ttl=57 time=536 ms
28 bytes from 8.8.8.8 to 10.68.143.88: icmp_seq=1 ttl=57 time=228 ms
28 bytes from 8.8.8.8 to 10.68.143.88: icmp_seq=2 ttl=57 time=224 ms

uart:~$ net iface

Interface 0x20000400 (PPP) [1]
==============================
Link addr : 00:00:5E:00:53:7C
MTU       : 1500
Flags     : NO_AUTO_START,IPv4
IPv4 unicast addresses (max 1):
        10.68.***.88 manual preferred infinite
IPv4 multicast addresses (max 1):
        <none>
IPv4 gateway : 0.0.0.0
IPv4 netmask : 0.0.0.0
uart:~$ net stats
Set CONFIG_NET_STATISTICS to enable statistics support.
uart:~$ net stacks 
Type "kernel stacks" to see stack information.
uart:~$ kernel stacks
0x20000be0 ppp_tx     (real size 1024): unused 736      usage 288 / 1024 (28 %)
0x20000e58 conn_mgr   (real size 512):  unused 384      usage 128 / 512 (25 %)
0x20000d98 rx_q[0]    (real size 1504): unused 928      usage 576 / 1504 (38 %)
0x20000cc8 net_mgmt   (real size 768):  unused 584      usage 184 / 768 (23 %)
0x20002358 gsm_workq  (real size 768):  unused 368      usage 400 / 768 (52 %)
0x200022a0 gsm_rx     (real size 512):  unused 160      usage 352 / 512 (68 %)
0x20002b20 sysworkq   (real size 1024): unused 584      usage 440 / 1024 (42 %)
0x200028c8 ppp_workq  (real size 768):  unused 360      usage 408 / 768 (53 %)
0x20000928 shell_uart (real size 2048): unused 1120     usage 928 / 2048 (45 %)
0x20000870 logging    (real size 768):  unused 632      usage 136 / 768 (17 %)
0x200029a8 idle 00    (real size 320):  unused 272      usage 48 / 320 (15 %)
0x20005f00 IRQ 00     (real size 2048): unused 1524     usage 524 / 2048 (25 %)

uart:~$ help
Please press the <Tab> button to see all available commands.
You can also use the <Tab> button to prompt or auto-complete all commands or its subcommands.
You can try to call commands with <-h> or <--help> parameter for more information.

Shell supports following meta-keys:
  Ctrl + (a key from: abcdefklnpuw)
  Alt  + (a key from: bf)
Please refer to shell documentation for more details.

Available commands:
  clear    :Clear screen.
  device   :Device commands
  devmem   :Read/write physical memory"devmem address [width [value]]
  help     :Prints the help message.
  history  :Command history.
  kernel   :Kernel commands
  log      :Commands for controlling logger
  modem    :Modem commands
  net      :Networking commands
  resize   :Console gets terminal screen size or assumes default in case the
            readout fails. It must be executed after each terminal width change
            to ensure correct text display.
  sample   :Sample application commands
  shell    :Useful, not Unix-like shell commands.
uart:~$

As you can see, Zephyr is not just an RTOS. It has got so many great features built in that you can enable depending on your needs, that otherwise would have taken a significant amount of development time to implement. This is the reason why I believe Zephyr is going to be the future for embedded applications.

I hope this short post makes you keen to get into the journey of learning Zephyr.

Usefull Links

Zephyr Docs
Zephyr Github
Circuit Dojo YouTube Channel
Golioth YouTube Channel

© 2022 Konstantinos Papadopoulos