ESP32 LED Blinky Example using NuttX RTOS(own app and driver)

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 inbuilt code to toggles the onboard LED. This is the ESP32 LED Blinky Example using NuttX RTOS. In this tutorial, we will write our own driver and application to control the onboard LED as well as other GPIO LEDs.

You can also read the FreeRTOS TutorialsESP32 Intro, ESP32 IDF Getting StartedESP32 Serial Communication TutorialsSTM32 RTOS Getting startedBoot sequence in Cortex-M4, and 8051 GPIO Tutorial.

ESP32 LED Blinky Example using NuttX RTOS

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 tutorials.

Hardware Required

  • ESP32 Dev board
  • Laptop machine (Host System)
  • LED with Resistor (If you don’t have onboard LED)

Introduction

As we mentioned earlier, we are going to write a code to control the LEDs. Basically, NuttX is a POSIX compliant operating system, and it likes to use the same naming convention of many of the functions and files you’d find in your standard UNIX/Linux system. And it is operating like a Linux system. In a Linux system, we have to write a kernel driver to talk to the hardware. This kernel driver will be residing in the kernel space. And the application has to be there to talk to that kernel driver. That application will reside in the userspace.

So, we are going to develop a LED driver and a LED application. If you are already familiar with the Linux kernel driver and a userspace application, then I promise you it will be very easy to understand the NuttX RTOS. If you are not familiar also fine. You take this chance and learn the NuttX RTOS. I am sure that you will start loving to work on Linux kernel drivers and an application also. We are providing the Linux device driver tutorials, that may help you to understand this concept better.

Let’s start. We are dividing the tutorial into two parts.

  1. Writing a LED driver
  2. Writing  an Application

In this demonstration, I have installed the VirtualBox in my Windows machine which contains Ubuntu 20.04 ,Linux kernel 5.8.0-50-generic and NuttX-10.1.0-RC1

Writing a LED driver

In Linux/POSIX, all things are considered as a file. So, we are going to create a file under /dev/In our example, our driver will create the /dev/etx_led file.

First, let’s see the code below.

esp32_etx_led.c

I have added this file to the nuttx/boards/xtensa/esp32/common/src/.

/****************************************************************************
 * boards/xtensa/esp32/common/src/esp32_etx_led.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 <stdint.h>
#include <stdbool.h>
#include <debug.h>
#include <assert.h>

#include <nuttx/board.h>
#include <arch/board/board.h>
#include <nuttx/arch.h>
#include "esp32-devkitc.h"
#include "esp32_gpio.h"
#include "esp32_etx_led.h"

#ifdef CONFIG_ESP32_ETX_LED

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/* Character driver methods */

static int     etx_led_open(FAR struct file *filep);
static int     etx_led_close(FAR struct file *filep);
static ssize_t etx_led_write(FAR struct file *filep, FAR const char *buffer,
                        size_t buflen);


/****************************************************************************
 * Private Data
 ****************************************************************************/

static const struct file_operations etx_led_fops =
{
  etx_led_open,   /* open   */
  etx_led_close,  /* close  */
  NULL,           /* read   */
  etx_led_write,  /* write  */
  NULL,           /* seek   */
  NULL,           /* ioctl  */
  NULL,           /* poll   */
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  NULL            /* unlink */
#endif
};

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: etx_led_open
 *
 * Description:
 * 	This function will be getting called when the application
 *	opens the "/dev/etx_led" file.
 ****************************************************************************/

static int etx_led_open(FAR struct file *filep)
{
  int ret = 0;

  return ( ret );
}

/****************************************************************************
 * Name: etx_led_close
 *
 * Description:
 * 	This function will be getting called when the application
 *	closess the "/dev/etx_led" file.
 ****************************************************************************/

static int etx_led_close(FAR struct file *filep)
{
  int ret = 0;

  return ( ret );
}

/****************************************************************************
 * Name: etx_led_write
 *
 * Description:
 * 	This function will be getting called when the application
 *	writes data the "/dev/etx_led" file.
 ****************************************************************************/

static ssize_t etx_led_write(FAR struct file *filep, FAR const char *buffer,
                             size_t len)
{
  DEBUGASSERT(buffer != NULL);
  
  //buffer[0] contains the value (ON/OFF)
  //buffer[1] contains the GPIO Number


  int32_t value     = buffer[0];
  int32_t gpio_pin  = buffer[1];
  
  
  if( gpio_pin < 0 || gpio_pin > ESP32_NGPIOS )
  {
    syslog(LOG_ERR, "Invalid GPIO Number - %d\n", gpio_pin);
    return (-1);
  }

  // Configure the GPIO as Output
  esp32_configgpio( gpio_pin, OUTPUT );

  
  //syslog(LOG_ERR, "GPIO Number - %d\n", gpio_pin);
  //syslog(LOG_ERR, "value - %d\n", value);

  // Write to the GPIO
  esp32_gpiowrite(gpio_pin, value);

  return ( len );
}

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

/****************************************************************************
 * Name: etx_led_driver_init
 *
 * Description:
 *   Initialize the EmbeTronicX LED device Driver. 
 *   This will create the device file as "/dev/etx_led"
 * 
 *   return negative number on failure.
 *   return 0 on success.
 *
 ****************************************************************************/

int etx_led_driver_init( void )
{
  int ret = 0;
  ret = register_driver("/dev/etx_led", &etx_led_fops, 0666, NULL);
  if (ret < 0)
  {
    _err("ERROR: register_driver failed : /dev/etx_led : %d\n", ret);
    ret = -1;;
  }

  return ( ret );

}

#endif /* CONFIG_ESP32_ETX_LED */

esp32_etx_led.h

I have added this file to the path nuttx/boards/xtensa/esp32/common/include/.

/****************************************************************************
 * boards/xtensa/esp32/common/include/esp32_etx_led.h
 *
 * 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.
 *
 ****************************************************************************/

#ifndef __BOARDS_XTENSA_ESP32_COMMON_INCLUDE_ETX_LED_H
#define __BOARDS_XTENSA_ESP32_COMMON_INCLUDE_ETX_LED_H

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

#include <nuttx/config.h>

#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif

/****************************************************************************
 * Name: etx_led_driver_init
 *
 * Description:
 *   Initialize the EmbeTronicX LED device Driver. 
 *   This will create the device file as "/dev/etx_led"
 *
 *   return negative number on failure.
 *   return 0 on success.
 * 
 ****************************************************************************/

#ifdef CONFIG_ESP32_ETX_LED
int etx_led_driver_init( void );
#endif



#undef EXTERN
#ifdef __cplusplus
}
#endif

#endif /* __BOARDS_XTENSA_ESP32_COMMON_INCLUDE_ETX_LED_H */

Code explanation

I will just explain to you the code that has been given above.

There is a function called etx_led_driver_init(), that is a starting point of the driver and it registers the driver and it creates the device file called /dev/etx_led.

There is one structure called struct file_operations etx_led_fops. To understand this structure, please read this Linux device driver tutorial. Using this file_operations structure, we are registering our open(), close(), and write() functions.

  • open() function will be called whenever the application opens the /dev/etx_led file.
  • close() function will be called whenever the application closes the /dev/etx_led file.
  • write() function gets called when the application writes something to the /dev/etx_led file.

In the write() function, we are configuring the GPIO as an output using the esp32_configgpio(). And set the value to that GPIO using the esp32_gpiowrite(). So, whenever an application wants to turn on or turn off the LEDs it has to write the GPIO number and the value to this driver.

As I told you already, etx_led_driver_init() is the starting point of the driver. But someone has to call this. We are calling this function form the esp32_bringup.c. I am adding the below code into the nuttx/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c‘s esp32_bringup() function.

#ifdef CONFIG_ESP32_ETX_LED
#include "esp32_etx_led.h"
#endif

#ifdef CONFIG_ESP32_ETX_LED
  /* Register the EmbeTronicX LED Driver */

  ret = etx_led_driver_init();
  if (ret < 0)
    {
      syslog(LOG_ERR, "ERROR: etx_led_driver_init() failed: %d\n", ret);
    }
#endif

Makefile

Now we have to build this driver. So, we have to add the below lines into the nuttx/boards/xtensa/esp32/common/src/Make.defs

ifeq ($(CONFIG_ESP32_ETX_LED),y)
  CSRCS += esp32_etx_led.c
endif

Kconfig

Till now, we are ready with the LED driver. But if we add an option to enable/disable this driver during the build, it would be very good. So, to do that, I am adding the below lines into the nuttx/boards/xtensa/esp32/common/Kconfig.

comment "Drivers added by EmbeTronicX (www.embetronicx.com)"

menu "EmbeTronicX Drivers"

config ESP32_ETX_LED
  bool "EmbeTronicX LED driver"
  default n
  ---help---
    Enables the GPIOs to control the LEDs (ESP32).
    
    This is added by the EmbeTroincX to demonstrate the
    LED control using the ESP32 Dev board with NuttX.
    
    This includes the EmbeTronicX LED driver which controls 
    the output.
    
endmenu # EmbeTronicX Drivers

That’s all about the driver. Let’s write our application to access this driver. You can refer to this commit in GitHub for the above changes. If you want the full source code, then take the code from GitHub.

Writing an Application

In application, I am going to create a task called “ETX_LED” using the task_create() function. In that task, I am writing the GPIO number and its value to the driver /dev/etx_led. Look at the below code.

etx_led_app.c

I am creating a new directory called etx_led under apps/examples

/****************************************************************************
 * examples/etx_led/etx_led_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>

#ifdef CONFIG_EXAMPLES_ETX_LED

#define ETX_LED_DRIVER_PATH "/dev/etx_led"    // LED Driver path

/****************************************************************************
 * Private Data
 ****************************************************************************/

struct gpio
{
  uint8_t gpio_val;
  uint8_t gpio_num;
};

static struct gpio etx_gpio;

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: etx_led_task
 ****************************************************************************/

static int etx_led_task(int argc, char *argv[])
{
  int ret = 0;
  
  printf("ETX_LED: Task Starting\n");
  
  int fd = open( ETX_LED_DRIVER_PATH, O_WRONLY);
  if (fd < 0)
  {
    printf("ETX_LED:ERROR - Failed to open %s: %d\n", ETX_LED_DRIVER_PATH, errno);
    ret = -1;
  }
  
  while( ret >= 0 )
  {
    printf("ETX_LED: GPIO  = %d Value %d\n", etx_gpio.gpio_num, etx_gpio.gpio_val);
    ret = write( fd, (const void*)&etx_gpio, sizeof(etx_gpio) );
    
    //toggle the gpio val
    etx_gpio.gpio_val = !etx_gpio.gpio_val;
    
    usleep(500*1000L);  
  }

  printf("ETX_LED: ERROR - Task finishing\n");
  return EXIT_FAILURE;
}

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

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

int main(int argc, FAR char *argv[])
{
  int ret;
  
  printf("ETX_LED: Starting the Application\n");
  
  if( argc < 2 )
  {
    printf("Usage : etx_led needs one argument which is GPIO number to toggle\n");
    printf("Example : To toggle the GPIO 2, please enter \"etx_led 2\"\n");
    return EXIT_FAILURE;
  }
  
  etx_gpio.gpio_val = 1;              //Initialy turn ON the LED
  etx_gpio.gpio_num = atoi(argv[1]);  //Get the GPIO Number from user
  
  ret = task_create( "ETX_LED",                         // Task Name
                     CONFIG_EXAMPLES_ETX_LED_PRIORITY,  // Task priority
                     CONFIG_EXAMPLES_ETX_LED_STACKSIZE, // Task Stack size
                     etx_led_task,                      // Task function
                     NULL
                   );
  if (ret < 0)
  {
    int errcode = errno;
    printf("ETX_LED: ERROR: Failed to start ETX LED task: %d\n", errcode);
    return EXIT_FAILURE;
  }
  
  return EXIT_SUCCESS;
}

#endif //#ifdef CONFIG_EXAMPLES_ETX_LED

Along with the etx_led_app.c, I am creating the three more files called Makefile, Make.defs, and Kconfig into the apps/examples/etx_led directory.

Makefile

############################################################################
# apps/examples/etx_led/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 LED built-in application info

PROGNAME  = $(CONFIG_EXAMPLES_ETX_LED_PROGNAME)
PRIORITY  = $(CONFIG_EXAMPLES_ETX_LED_PRIORITY)
STACKSIZE = $(CONFIG_EXAMPLES_ETX_LED_STACKSIZE)
MODULE    = $(CONFIG_EXAMPLES_ETX_LED)

# ETX LED Example

MAINSRC = etx_led_app.c

include $(APPDIR)/Application.mk

Make.defs

############################################################################
# apps/examples/etx_led/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_LED),)
CONFIGURED_APPS += $(APPDIR)/examples/etx_led
endif

Kconfig

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

comment "Applications added by EmbeTronicX (www.embetronicx.com)"

config EXAMPLES_ETX_LED
  tristate "EmbeTronicX LED app example"
  default n
  depends on ESP32_ETX_LED
  ---help---
    Enable the EmbeTronicX LED app example

if EXAMPLES_ETX_LED

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

config EXAMPLES_ETX_LED_PRIORITY
  int "etx_led task priority"
  default 100

config EXAMPLES_ETX_LED_STACKSIZE
  int "etx_led stack size"
  default DEFAULT_TASK_STACKSIZE

endif

If you want to see the changes regarding the application, you can look at this commit. You can also take the full code from GitHub.

Building the driver and app

All going well. Let’s build our app and the 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 into 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.

Let’s do distclean and build. Run the below command in NuttX/nuttx directory.

[email protected]:~/NuttX/nuttx$. ./build.sh 1

When you run the above command, you should get the menuconfig window. In that menuconfig, we have to enable the etx_led driver and the etx_led_app application.

Just navigate to Board Selection —> EmbeTronicX Drivers —> and check the EmbeTroincX LED driver like the below image.

Enable etx_led driver

Once after enabled this, then press Exit and go to Home. Now it is time to enable the application. To do that, please navigate to the Home —> Application Configuration —> Examples —> and check the EmbeTronicX LED app example like the below image.

enable etx led app

That’s all. 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 output like the below image.

etx_led

Our etx_led app has been added into the Builtin Apps. Let’s run the app.

ESP32 LED Blinky Example – Output

etx_led app has one argument that is the GPIO number. You can give any valid GPIO number. In this example, I am giving the onboard LED’s GPIO number 2. Run the below command.

etx_led 2

You will get an output like below.

ESP32 LED Blinky Example

If you don’t want to use the application, then you can use the below printf command. But make sure that you have enabled the printf command in the menuconfig as mentioned here.

To turn on the GPIO 2 LED try to enter the below command in nsh.

printf \x0201 > /dev/etx_led

Here, 02 is the GPIO number and 01 is the value to set.

To turn off the GPIO 2 LED try to enter the below command in nsh.

printf \x0200 > /dev/etx_led

Here, 02 is the GPIO number and 00 is the value to set.

Reset the ESP32 by pressing the DevKit EN button (RESET) to return to stop the etx_led application. If you do not have an onboard led, connect an LED to any GPIO (feed the proper GPIO number to the application) pin and associate a resistor in series (between 100 Ω to 1 k Ω).

In this ESP32 LED Blinky Example, we have controlled the output devices. In our next tutorial, we will see how to write a code to handle the button or any input devices.

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
5 1 vote
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