EmbeddedRelated.com
Blogs
Memfault State of IoT Report

How to use I2C devices in (Apache) NuttX: Adding support for an I2C device in your board

Alan C AssisMay 28, 2024

Previously in this EmbeddedRelated article, we saw how to find an I2C device connected to your board using the i2ctool that is very familiar for people with previous experience with embedded Linux. Today we will see how to add support to an I2C device (i.e. BMP280 sensor) in your board. So, lets to get started!

Quick Links

NuttX uses a very simple approach to interface with devices connected to the board: each board has a board bringup() function that is used to initialize the I2C bus and register/initialize an I2C device (i.e. sensor, etc) to that I2C bus. Normally each I2C device to be initialize will have an initialization function inside a file with the chip family prefix followed by the device name (i.e.: stm32_bmp180.c). This file could be located at the boards common directory (when the chip family of the board already supports common drivers) like the boards/arm/stm32/common/src/stm32_bmp180.c or could be inside the board source directory (for boards that doesn't support yet the common drivers) like boards/arm/stm32l4/nucleo-l476rg/src/stm32_bmp180.c.

This article is available in PDF format for easy printing

N.B.: The common driver directory is a solution to avoid code duplication for boards of same chip family. In the past all boards need to have their own device initialization. It means all boards using the same chip need to have their own stm32_bmp180.c, although the file was basically the same for all those boards.

In our case, we want to use the barometer BMP280 connected to I2C0 (as we saw in the previous article), but if we look at boards/arm/rp2040/common/src/ we don't find a rp2040_bmp280.c there, only a rp2040_bmp180.c. Fear not: today we will see how to add our board initialization for our BMP280!

Since the BMP180 driver (located at drivers/sensors/bmp180.c) and BMP280 driver (located at drivers/sensors/bmp280_uorb.c) has just a little different registering function signatures: bmp180_register(devpath, i2c) vs bmp280_register(devno, i2c), we can copy boards/arm/rp2040/common/src/rp2040_bmp180.c to boards/arm/rp2040/common/src/rp2040_bmp280.c and boards/arm/rp2040/common/include/rp2040_bmp180.h to boards/arm/rp2040/common/include/rp2040_bmp280.h.

$ cp boards/arm/rp2040/common/src/rp2040_bmp180.c boards/arm/rp2040/common/src/rp2040_bmp280.c
$ cp boards/arm/rp2040/common/include/rp2040_bmp180.h boards/arm/rp2040/common/include/rp2040_bmp280.h
Then all you need to do is edit both files (rp2040_bmp280.c and rp2040_bmp280.h) replacing bmp180 with bmp280. Since bmp280_register() doesn't use devpath, you can remove that "char devpath[12];" and that "snprintf(devpath, 12, "/dev/press%d", devno);" and just call bmp280_register() this way:
      ret = bmp280_register(devno, i2c);
      if (ret < 0)
        {
          snerr("ERROR: Error registering BMP280 in I2C%d\n", busno);
        }

Because you added this rp2040_bmp280.c file inside boards/arm/rp2040/common/src/ you will need to edit the Make.defs inside this directory and add:

ifeq ($(CONFIG_SENSORS_BMP280),y)
  CSRCS += rp2040_bmp280.c
endif

Also edit the bring-up file boards/arm/rp2040/common/src/rp2040_common_bringup.c adding the header files:

#ifdef CONFIG_SENSORS_BMP280
#include <nuttx/sensors/bmp280.h>
#include "rp2040_bmp280.h"
#endif

And the I2C initialization call for our BMP280 device inside rp2040_common_bringup() :

#ifdef CONFIG_SENSORS_BMP280
  /* Try to register BMP280 device in I2C0 */
  ret = board_bmp280_initialize(0);
  if (ret < 0)
    {
      syslog(LOG_ERR, "Failed to initialize BMP280 driver: %d\n", ret);
    }
#endif

These are all the necessary modifications to get BMP280 called in your board bring-up process.

You need to wire the BMP280 sensor to your RaspberryPi Pico this way:

BMP28 Raspberry Pi Pico Physical Pin
GND GND Pin 3 or 38 or ...
VCC 3V3 OUT Pin 36
SDA GP4 (I2C0 SDA) Pin 6
SCL GP5 (I2C0 SCL) Pin 7

If you don't have a breadboard you can wire directly using Dupont Wires (Femea-Femea)

Now we just need to follow similar steps to what we did in the previous article to configure NuttX to use this BMP280 sensor.

Start clearing your previous configuration:

$ make distclean

We will use the NSH over USB to avoid connecting an external USB/Serial adapter to the serial pins, so run:

$ ./tools/configure.sh raspberrypi-pico:usbnsh

Now we can select the options to get the our BMP280 working on NuttX:

$ make menuconfig
Enter inside "System Type  --->" and select "I2C Master" and "I2C0"


You can "Exit" from "System Type" and enter inside "Device Drivers  --->", as you can see "I2C Driver Support" is automatically selected (pay attention at "-*-" in front of it, instead of "[*]")


Navigate the bottom of Device Drivers and select [*] Sensor Device Support  --->


Press ENTER to enter inside "[*] Sensor Device Support  --->" and select Bosch BMP280 sensor:

You can "Exit" from "Sensor Device Support" and from "Device Driver" and move down to enter inside "Library Routines  --->" and once you are there enter inside "Standard C I/O  --->" and select "[*] Enable floating point in printf" :

Finally "Exit" from "Standard C I/O  --->" and "Library Routines  --->" and move down to enter inside "Application Configuration  --->", "Examples  --->" and select the BMP280 test application:


That is all! You can  "Exit" from "Examples  --->", "Application Configuration  --->" and Exit and Save from menuconfig:


At this point we can compile:

$ make -j

If you see this message at the end:

PICO_SDK_PATH must be specified for flash boot

It means you forgot to include the SDK in your PATH, you need to include it, in my case:

$ export PICO_SDK_PATH=/home/alan/pico-sdk

Compile again and you need to see these lines at the end:

LD: nuttx
Generating: nuttx.uf2
tools/rp2040/elf2uf2 nuttx nuttx.uf2;
Done.

As you did in previous article, press and hold the Raspberry Pi Pico BOOTSEL button and connect the USB cable. A "RPI-RP2" virtual disk should appear on your file manager, the copy nuttx.uf2 to it.

After the file is flash the green LED will turn on, you can also run "sudo dmesg" to confirm that USB CDC/ACM was detected correctly.

Run minicom (or your preferred terminal console tool) and press "Enter" three times to let the "NSH>" appears. If you type "help" or "?" you will see that "i2c" tool application appeared:

That all folk!!! I hope you have enjoyed it.

Note: this tutorial is just a reference the step I took to add support to BMP280 for RaspberryPi Pico, you don't need to do it manually because I already submitted the PR (Pull Request) to add it into NuttX mainline:
https://github.com/apache/nuttx/pull/12420

So, if you want to test your BMP280 sensor on NuttX all you need to do it is:

$ ./tools/configure.sh raspberrypi-pico:bmp280
$ export PICO_SDK_PATH=/home/yourusername/pico-sdk
$ make -j

And then copy the generated nuttx.uf2 to your board as explained above.

Anyway it is a reference that you can use to add support for other sensors or I2C devices supported by NuttX RTOS.





Memfault State of IoT Report

To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.

Please login (on the right) if you already have an account on this platform.

Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: