NuttX RTOS I2C Example using ESP32 – ADXL345 Interfacing

This article is a continuation of the series on NuttX RTOS tutorials using the ESP32 Dev board and carries the discussion on NuttX RTOS and their usage. The aim of this series is to provide easy and practical examples that anyone can understand. In our previous tutorial, we saw how to use the GPIO. This is the NuttX RTOS I2C Example using ESP32 ADXL345 Interfacing.

NuttX RTOS I2C Example

ESP32 ADXL345 Interfacing

Prerequisites

To continue with this tutorial, you must have configured the ESP32 toolchain and compiled the NuttX RTOS. If you aren’t configured anything yet, we suggest you to set up the esp32 using the below-given tutorial.

Hardware Required

  • ESP32 Dev board
  • Laptop machine (Host System)
  • ADXL345 Accelerometer sensor

ADXL345 Sensor

Before we begin with the Interfacing part, we just see the basics of the ADXL345 Accelerometer.

The ADXL345 is a small, thin, ultralow power, 3-axis accelerometer with high resolution (13-bit) measurement at up to ±16 g. Digital output data is formatted as a 16-bit twos complement and is accessible through either an SPI (3- or 4-wire) or I2C digital interface.

The ADXL345 is well suited for mobile device applications. It measures the static acceleration of gravity in tilt-sensing applications, as well as dynamic acceleration resulting from motion or shock. Its high resolution (3.9 mg/LSB) enables measurement of inclination changes less than 1.0°.

Several special sensing functions are provided. Activity and inactivity sensing detect the presence or lack of motion by comparing the acceleration on any axis with user-set thresholds. Tap sensing detects single and double taps in any direction. Freefall sensing detects if the device is falling. These functions can be mapped individually to either of two interrupt output pins.

An integrated memory management system with a 32-level first-in, first-out (FIFO) buffer can be used to store data to minimize host processor activity and lower overall system power consumption. Low power modes enable intelligent motion-based power management with threshold sensing and active acceleration measurement at extremely low power dissipation.

ADXL345 Accelerometer

FEATURES

  • Ultralow power: as low as 23 µA in measurement mode and 0.1 µA in standby mode at VS = 2.5 V (typical)
  • Power consumption scales automatically with bandwidth
  • User-selectable resolution
  • Fixed 10-bit resolution
  • Full resolution, where resolution increases with g range, up to 13-bit resolution at ±16 g (maintaining 4 mg/LSB scale factor in all g ranges)
  • Embedded memory management system with FIFO technology minimizes host processor load
  • Single tap/double-tap detection
  • Activity/inactivity monitoring
  • Free-fall detection
  • Supply voltage range: 2.0 V to 3.6 V
  • I/O voltage range: 1.7 V to VS
  • SPI (3- and 4-wire) and I2C digital interfaces
  • Flexible interrupt modes mappable to either interrupt pin
  • Measurement ranges selectable via serial command
  • Bandwidth selectable via serial command
  • Wide temperature range (−40°C to +85°C)
  • 10,000 g shock survival
  • Pb free/RoHS compliant
  • Small and thin: 3 mm × 5 mm × 1 mm LGA package

APPLICATIONS

  • Handsets
  • Medical instrumentation
  • Gaming and pointing devices
  • Industrial instrumentation
  • Personal navigation devices
  • Hard disk drive (HDD) protection

For more information, you can refer to its Data Sheet.

In this tutorial, we are going to connect this ADXL345 with the ESP32 using the I2C communication.

Connection Diagram – NuttX RTOS I2C Example (ESP32 ADXL345)

In this example, we have used the I2C1. You can also use the I2C0. But make sure you are connecting to the proper SCL and SDA. Refer to the below connection diagram.

  • SDA – Pin 25
  • SCL –  Pin 26
  • Vcc –  3.3V
  • GND – Ground

ESP32 ADXL345 Connection

Concept of the Driver and Application

In this tutorial, we are not going to write any I2C driver as we did for the GPIO driver. Instead, we are going to utilize the existing, well-written I2C driver. So, we are only going to write the I2C application.

The concept is very simple. We are just going to put the ADXL345 to measurement mode and just reading the X, Y, Z axis data continuously.

Writing I2C Application

In application, I am going to create a task called “etx_i2c_accel_task” using the task_create() function. Then in that task,

  • Open the I2C driver (/dev/i2c1).
  • Initialize the ADXL345 (Put the ADXL345 into measurement mode, then turn off the interrupts).
  • Read the X, Y, Z, and print.
  • Close the driver if any error.

I am creating a new directory called etx_i2c under apps/examplesInside the directory, I am adding the below files.

  • etx_i2c_accel_app.c
  • etx_i2c_accel_app.h
  • Kconfig
  • Make.defs
  • Makefile

You can check the application changes that I have added as part of this tutorial in the GitHub commit. And you can get the complete source code from GitHub.

etx_i2c_accel_app.c

[Get this code from GitHub]

/****************************************************************************
 * examples/etx_i2c/etx_i2c_accel_app.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sched.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <nuttx/i2c/i2c_master.h>

#include "etx_i2c_accel_app.h"

#ifdef CONFIG_EXAMPLES_ETX_I2C_ACCEL


/****************************************************************************
 * Private Data
 ****************************************************************************/
static int fd;

/****************************************************************************
 * Private Functions
 ****************************************************************************/
/****************************************************************************
 * Name: ETX_I2C_Write
 *
 * Details : This function writes the 1byte data to the slave device
 ****************************************************************************/
static int16_t ETX_I2C_Write( uint8_t reg, uint8_t value )
{
  int16_t ex;  
  struct  i2c_msg_s       i2c_msg;
  struct  i2c_transfer_s  i2c_transfer;  
  uint8_t txbuffer[2];

  /* Setup to the data to be transferred.  Two bytes:  The ADXL345 register
   * address followed by one byte of data.
   */

  txbuffer[0] = reg;
  txbuffer[1] = value;

  /* Setup 8-bit ADXL345 address write message */

  i2c_msg.addr   = ADXL345_ADDRESS;
  i2c_msg.flags  = 0;
  i2c_msg.buffer = txbuffer;
  i2c_msg.length = 2;
  i2c_msg.frequency = 400000;  /* 400K bsp */
  
  i2c_transfer.msgv = &i2c_msg;
  i2c_transfer.msgc = 1;
  
  ex = ioctl(fd, I2CIOC_TRANSFER, (unsigned long)(uintptr_t)&i2c_transfer);
  
  if( ex < 0 )
  {
      printf("ETX_I2C:Write Error : %d\n", ex);
  }
  
  return( ex );
}

/****************************************************************************
 * Name: ETX_I2C_Read
 *
 * Details : This function reads the data from the slave device
 ****************************************************************************/
static int16_t ETX_I2C_Read( uint8_t reg, uint8_t* value, int16_t len )
{
  int16_t ex;
  struct  i2c_msg_s       i2c_msg[2];
  struct  i2c_transfer_s  i2c_transfer;
  
  // Write the register address first
  i2c_msg[0].addr   = ADXL345_ADDRESS;
  i2c_msg[0].flags  = 0;
  i2c_msg[0].buffer = &reg;
  i2c_msg[0].length = 1;
  i2c_msg[0].frequency = 400000;  /* 400K bsp */

  // Read the data
  i2c_msg[1].addr   = ADXL345_ADDRESS;
  i2c_msg[1].flags  = I2C_M_READ;
  i2c_msg[1].buffer = value;
  i2c_msg[1].length = len;
  i2c_msg[1].frequency = 400000;  /* 400K bsp */
  
  i2c_transfer.msgv = i2c_msg;
  i2c_transfer.msgc = 2;
  
  ex = ioctl(fd, I2CIOC_TRANSFER, (unsigned long)(uintptr_t)&i2c_transfer);
  
  if( ex < 0 )
  {
      printf("ETX_I2C:Read Error : %d\n", ex);
  }
  
  return( ex );
}

/****************************************************************************
 * Name: ETX_ADXL345_Init
 *
 * Details : This function Initializes the ADXL345
 ****************************************************************************/
static int16_t ETX_ADXL345_Init( void )
{
  int16_t ex = -1;
  uint8_t dev_id;
  
  // Reset all power settings
  ETX_I2C_Write( ADXL345_RA_POWER_CTL, 0);
  
  // Read the devId
  ETX_I2C_Read( ADXL345_RA_DEVID, &dev_id, 1);
  
  // Check the devId
  if( dev_id == 0xE5 )
  {
    printf("Device ID : 0x%X (Success!!!)\r\n", dev_id);
    
    //Set Output Rate to 100 Hz
    ETX_I2C_Write( ADXL345_RA_BW_RATE, 0x0A);
    
    // ADXL345 into measurement mode
    ETX_I2C_Write( ADXL345_RA_POWER_CTL, 0x08);
    
    // Disable Interrupts
    ETX_I2C_Write( ADXL345_RA_INT_ENABLE, 0x00 );
    
    ex = 0;
  }
  
  return( ex );
}

/****************************************************************************
 * Name: etx_i2c_accel_task
 ****************************************************************************/

static int etx_i2c_accel_task(int argc, char *argv[])
{
  int ret = 0;
  int16_t X,Y,Z;
  float   Xg,Yg,Zg;
    
  printf("ETX_I2C_ACCEL: Task Starting\n");
  
  fd = open( ETX_I2C_DRIVER_PATH, O_WRONLY);
  if( fd < 0 )
  {
    printf("ETX_I2C_ACCEL:ERROR - Failed to open %s: %d\n",
                                                   ETX_I2C_DRIVER_PATH, errno);
    ret = -1;
  }
  
  ret = ETX_ADXL345_Init();
  
  while( ret >= 0 )
  {
    // Read the X
    ret = ETX_I2C_Read( ADXL345_RA_DATAX0, (uint8_t*)&X, 2);
    
    // Read the Y
    ret = ETX_I2C_Read( ADXL345_RA_DATAY0, (uint8_t*)&Y, 2);
    
    // Read the Z
    ret = ETX_I2C_Read( ADXL345_RA_DATAZ0, (uint8_t*)&Z, 2);
    
    Xg = ( X * ADXL345_MG2G_MULTIPLIER * STANDARD_GRAVITY );
    Yg = ( Y * ADXL345_MG2G_MULTIPLIER * STANDARD_GRAVITY );
    Zg = ( Z * ADXL345_MG2G_MULTIPLIER * STANDARD_GRAVITY );
        
    printf("Xg: %f, Yg: %f, Zg: %f\r\n", Xg, Yg, Zg);
  }

  close(fd);
  printf("ETX_I2C_ACCEL: Task finishing\n");
  return EXIT_FAILURE;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * main
 ****************************************************************************/

int main(int argc, FAR char *argv[])
{
  int ret;
  
  printf("ETX_I2C_ACCEL: Starting the Application\n");

  
  ret = task_create( "ETX_I2C_ACCEL",                         // Task Name
                     CONFIG_EXAMPLES_ETX_I2C_ACCEL_PRIORITY,  // Task priority
                     CONFIG_EXAMPLES_ETX_I2C_ACCEL_STACKSIZE, // Task Stack size
                     etx_i2c_accel_task,                      // Task function
                     NULL
                   );
  if (ret < 0)
  {
    int errcode = errno;
    printf("ETX_I2C_ACCEL: ERROR: Failed to start ETX I2C Accel task: %d\n",
                                                                       errcode);
    return EXIT_FAILURE;
  }
  
  return EXIT_SUCCESS;
}

#endif //#ifdef CONFIG_EXAMPLES_ETX_I2C_ACCEL

Makefile

############################################################################
# apps/examples/etx_i2c/Makefile
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.  The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################

include $(APPDIR)/Make.defs

# ETX I2C built-in application info

PROGNAME  = $(CONFIG_EXAMPLES_ETX_I2C_ACCEL_PROGNAME)
PRIORITY  = $(CONFIG_EXAMPLES_ETX_I2C_ACCEL_PRIORITY)
STACKSIZE = $(CONFIG_EXAMPLES_ETX_I2C_ACCEL_STACKSIZE)
MODULE    = $(CONFIG_EXAMPLES_ETX_I2C_ACCEL)

# ETX I2C ADXL345 Accelerometer Example

MAINSRC = etx_i2c_accel_app.c

include $(APPDIR)/Application.mk

Make.defs

############################################################################
# apps/examples/etx_i2c/Make.defs
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.  The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################

ifneq ($(CONFIG_EXAMPLES_ETX_I2C_ACCEL),)
CONFIGURED_APPS += $(APPDIR)/examples/etx_i2c
endif

Kconfig

#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#

config EXAMPLES_ETX_I2C_ACCEL
  tristate "EmbeTronicX I2C app example (ADXL345 Accelerometer)"
  default n
  depends on I2C && I2C_DRIVER
  ---help---
    Enable the EmbeTronicX I2C app example (ADXL345 Accelerometer)

if EXAMPLES_ETX_I2C_ACCEL

config EXAMPLES_ETX_I2C_ACCEL_PROGNAME
  string "Program name"
  default "etx_i2c_adxl345"
  ---help---
    This is the name of the program that will be used when the NSH ELF
    program is installed.

config EXAMPLES_ETX_I2C_ACCEL_PRIORITY
  int "etx_i2c_adxl345 task priority"
  default 100

config EXAMPLES_ETX_I2C_ACCEL_STACKSIZE
  int "etx_i2c_adx345 stack size"
  default DEFAULT_TASK_STACKSIZE

endif

Building the driver and app

Let’s build our app and the I2C driver together. I have created the build.sh shell script to combine all the required steps into one file.

build.sh

This build.sh has been added to the nuttx/ directory. You can get the build.sh from GitHub.

# !/bin/bash

# A simple bash script to build the NuttX RTOS

#export ESP32 env
. $HOME/esp/esp-idf/export.sh


if [ $1 -eq 1 ]
then
  #distclean
  echo "Dist clean"
  make distclean
  
  #ESP32 configure
  echo "configuring ESP32 with NuttX RTOS"
  ./tools/configure.sh esp32-devkitc:nsh
fi

#menuconfig
make menuconfig

#make and flash
make download ESPTOOL_PORT=/dev/ttyUSB0

The above script has one argument. If you pass 1, then it does distclean and initializes the board configs. If you pass other than 1, then it does only the make menuconfig and make.

I don’t want to do the distclean. So, just building the NuttX. Run the below command in NuttX/nuttx directory.

. ./build.sh 0

When you run the above command, you should get the menuconfig window. In that menuconfig, we have to enable the i2c driver and the etx_i2c_accel_app application. Let’s do that.

Just navigate to System Type —> ESP32 Peripheral Selection —> and check (enable) the I2C1 like the below image.

I2C enable NuttX RTOS

After that, let’s enable the I2C driver by navigating to Device drivers —>enable the I2C Driver Support. And go to that menu and enable the I2C Character Driver like the below image.

I2C driver Enable NuttX

As we are going to use the floating numbers in our program, we have to enable the floating number also. To enable the floating-point number in NuttX RTOS, navigate to Library Routines —> Standard C I/O—> enable the Enable Floating point in printf like the below image.

That’s from the driver. Let’s enable the Application that we have written earlier in this tutorial. Navigate to Application Configuration —> Examples —> and check (enable) the EmbeTronicX I2C app example (ADX345 Accelerometer) like the below image.

That’s all. Let’s build the NuttX now.

Save and Exit the menuconfig and let it build. Once it is trying to connect, hold the BOOT button. Then it will start flashing.

Once you build and flashed it, you connect the device using the picocom using the picocom -b 115200 /dev/ttyUSB0 command.

Now you will get the NuttShell (nsh). Enter the ? or help command. You will get the etx_i2c_adxl345 app. Now run the etx_i2c_adxl345 app.

Output – ESP32 ADXL345 Interfacing using NuttX RTOS I2C

Once the system is booted, then run the etx_i2c_adxl345 application. You should get the X, Y, Z-axis like below continuously.

nsh> etx_i2c_adxl345
ETX_I2C_ACCEL: Starting the Application
ETX_I2C_ACCEL: Task Starting
nsh> Device ID : 0xE5 (Success!!!)
Xg: 3.373488, Yg: 1.686744, Zg: -8.786758
Xg: 3.373488, Yg: 1.686744, Zg: -8.786758
Xg: 3.373488, Yg: 1.686744, Zg: -8.786758
Xg: 3.373488, Yg: 1.686744, Zg: -8.786758
Xg: 3.373488, Yg: 1.686744, Zg: -8.786758
Xg: 3.373488, Yg: 1.686744, Zg: -8.786758
Xg: 3.373488, Yg: 1.686744, Zg: -8.786758
Xg: 3.373488, Yg: 1.686744, Zg: -8.786758

Refer to the below image.

ESP32 ADXL345 Interfacing using NuttX RTOS I2C

This is a very basic example. In this example, we just read the data and print it. In our next tutorial, we will see how to generate events using the ADXL345 and NuttX.

You can also read the below tutorials.

Linux Device Driver TutorialsC Programming Tutorials
FreeRTOS TutorialsNuttX RTOS Tutorials
RTX RTOS TutorialsInterrupts Basics
I2C Protocol – Part 1 (Basics)I2C Protocol – Part 2 (Advanced Topics)
STM32 TutorialsLPC2148 (ARM7) Tutorials
PIC16F877A Tutorials8051 Tutorials
Unit Testing in C TutorialsESP32-IDF Tutorials
Raspberry Pi TutorialsEmbedded Interview Topics
Reset Sequence in ARM Cortex-M4BLE Basics
VIC and NVIC in ARMSPI – Serial Peripheral Interface Protocol
Bootloader TutorialsRaspberry PI Pico Tutorials

Reference

0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x