Zephyr Example 3: DHCP Client

In this post, a FRDM-K64F board is used. Many networking sample codes support this board. Create an empty project, run menuconfig, select:

  • NETWORKING
  • NET_IPV4, NET_ARP, NET_TCP, NET_UDP
  • NET_DHCPV4
  • NET_MGMT, NET_MGMT_EVENT,
  • LOG, NET_LOG, and
  • INIT_STACKS

You can deselect NET_IPV6 if you do not need it. If you know the CONFIG_ keyword, search feature of the menuconfig would be very handy. Type / (slash) and enter keywords.

Copy sample main.c from (Zephyr_Root)/samples/net/dhcpv4_client and build it. You should be able to see the dhcp client is working.

    1 [00:00:00.005,549] <inf> net_dhcpv4_client_sample: Run dhcpv4 client
    2 [00:00:06.027,746] <inf> net_dhcpv4: Received: 10.1.1.38
    3 [00:00:06.027,785] <inf> net_dhcpv4_client_sample: Your address: 10.1.1.38
    4 [00:00:06.027,787] <inf> net_dhcpv4_client_sample: Lease time: 43200 
      seconds
    5 [00:00:06.027,801] <inf> net_dhcpv4_client_sample: Subnet: 255.255.255.0
    6 [00:00:06.027,817] <inf> net_dhcpv4_client_sample: Router: 10.1.1.1

Zephyr has a concept of subsystem, which runs in parallel with the main thread. Shell is a subsystem, something akin to Unix shell. When used with networking, it provides very convenient ways to test and debugging.

To activates the networking shell, choose NET_SHELL. Rebuild and flash it. You will notice that now UART connection works as a shell.

For example, enter command net iface, you will get

Unfortunately this shell is not as reliable as you may wish as of this writing. I have seen some strange things, which might be related to logging features or mate terminals. It certainly will be a valuable tool for debugging in the future.

Zephyr Example 2: ToF Sensor

B-L475E-IOT01A Discovery kit for IoT from ST is equipped with a ToF sensor (VL53L0X), connected to the second I2C bus. You can find a sample code under the folder:

(Zephyr_Root)/samples/sensor/vl53l0x

where prj.conf is set to

    1 CONFIG_STDOUT_CONSOLE=y
    2 CONFIG_I2C=y
    3 CONFIG_GPIO=y
    4 CONFIG_SENSOR=y
    5 CONFIG_VL53L0X=y
    6 CONFIG_VL53L0X_PROXIMITY_THRESHOLD=100

Comparing with the default board setting defined in the file below, you will notice that all the lines except the line number 3 are new.

(Zephyr_Root)/board/arm/disco_l475_iot1/disco_l475_iot1_defconfig

Alternatively if you already have your project generated with cmake you can add the sensor feature with menuconfig. Once you created a build setting, run menuconfig (ninja menuconfig)

And select following items:

  • Device Drivers -> I2C Drivers
  • Device Drivers -> Sensor Drivers -> VL53L0X ToF sensor
  • C Library -> Send stdout to console

Then save the configuration and exit. This will replace the original project configuration stored in the file build/zephyr/.config. Corresponding changes include the settings shown at the top. But other settings will be added such as

    1 CONFIG_I2C_1 = y
    2 CONFIG_I2C_2 = y
    3 ...
    4 CONFIG_I2C_STM32 = y
    5 CONFIG_I2C_STM32_V2 = y
    6 ...
    7 CONFIG_USE_STM32_LL_I2C = y
    8 ...
    9 CONFIG_HAS_STLIB = y

Such settings are inserted automatically by the dependencies specified by the Kconfig files in the Zephyr SDK tree such as

  • (Zephyr_Root)/boards/arm/disco_l475_iot1/Kconfig.defconfig
  • (Zephyr_Root)/soc/arm/st_stm32/common/Kconfig.defconfig.series
  • (Zephyr_Root)/soc/arm/st_stm32/stm32l4/Kconfig.defconfig.series

Thus when you create your prj.conf, you do not have to specify all the CONFIG settings as they are appear in the .config file. Check corresponding Kconfig files for the CONFIG parameters.

  • (Zephyr_Root)/drivers/sensor/Kconfig
  • (Zephyr_Root)/drivers/sensor/vl53l0x/Kconfig

Now following main.c should be compiled.

    1 #include <zephyr.h>
    2 #include <device.h>
    3 #include <sensor.h>
    4 #include <gpio.h>
    5 #include <stdio.h>
    6 #include <misc/printk.h>
    7 
    8 void main(void)
    9 {
   10     struct device *dev;
   11     struct sensor_value val;
   12     int ret;
   13 
   14     dev = device_get_binding(DT_VL53L0X_NAME);
   15 
   16     while(1)
   17     {
   18         ret = sensor_sample_fetch(dev);
   19 
   20         if(ret)
   21         {
   22             printk("sensor_sample_fetch failed: %d\n", ret);
   23             return;
   24         }
   25 
   26         ret = sensor_channel_get(dev, SENSOR_CHAN_PROX, &val);
   27         printk("prox is %d\n", val.val1);
   28 
   29         ret = sensor_channel_get(dev, SENSOR_CHAN_DISTANCE, &val);
   30         printf("distance is %.3fm\n", sensor_value_to_double(&val));
   31 
   32         k_sleep(1000);
   33     }
   34 }

This is the same as the sample code provided by Zephyr SDK. Note that at line 30, printf is used instead of printk. This is because printk does not provide floating point format conversion.

Zephyr Example 1: Blinky

Setting up Zephyr Environment

Detailed description about Zephyr environment can be found here. Since most of the prerequisites are common to other development, chances are you already have them installed on your computer. Ubuntu and its derivative users may need to uninstall outdated device-tree-compiler package and install new one by building from the source.

Running a Sample Code

Grab one of the board listed as supported, clone the Zephyr source tree, go to one of the sample code directory and run:

    1 source (Zephyr_Root)/zephyr-env.sh
    2 mkdir build && cd build
    3 cmake -GNiinja DBOARD=(your_board)
    4 ninja
    5 ninja flash

Here (Zephyr_Root) is the root directory of the SDK. Board name (your_board) can be found under (Zephyr_Root)/boards/

Default CMake generator is “Unix Makefile”. However ninja is way more faster at least in Zephyr. You can specify Ninja at the command line as shown above. Or you can override it by creating a file named PreLoad.cmake with following content and put it in the same directory as CMakeLists.txt.

    1 set (CMAKE_GENERATOR "Ninja" CACHE INTERNAL "" FORCE)

Additionally you can set the board in the CMakeLists.txt so you don’t have to specify that as well in the command line.

    1 cmake_minimum_required(VERSION 3.8.2)
    2 set(BOARD disco_l475_iot1)
    3 include($(ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)

Note that this line comes before including the boilerplate setting.

Blinky

We are going to use ST Disco L475 IoT01 board in this example and examples in the posts follows. To control the GPIO, you need to have proper Kconfig settings. This is usually done by adding following line to the prj.conf file:

    1 CONFIG_GPIO=y

However it is likely done already when you specify your board above. Check the defconfig file for your board under Zephyr SDK directory. In this case, it is

(SDK_ROOT)/boards/arm/disco_l475_iot1/disco_l475_iot1_defconfig 

Write a main.c

    1 #include <zephyr.h>
    2 #include <misc/printk.h>
    3 #include <gpio.h>
    4 
    5 #define LED_PORT        DT_GPIO_STM32_GPIOC_LABEL
    6 #define LED_PIN         9
    7 
    8 void main(void)
    9 {
   10     int cnt = 0;
   11     struct device *dev;
   12 
   13     dev = device_get_binding(LED_PORT);
   14     gpio_pin_configure(dev, LED_PIN, GPIO_DIR_OUT);
   15 
   16     while (1) 
   17     {
   18         gpio_pin_write(dev, LED_PIN, cnt % 2);
   19         cnt++;
   20         k_sleep(500);
   21     }
   22 }

And run ninja again. Port name on line number 5 can be found under

(SDK_ROOT)/soc/arm/st_stm32/stm32l4/dts_fixup.h