Controlling LEDs through UART
In the exercise, you will learn how to control the LEDs on your development kit through UART. We will utilize the UART controller that is connected by default to the interface MCU on your Nordic development kit to provide UART over USB.

We will set up the UART in asynchronous mode so that a callback function is called every time there is new data. We will set up the receive buffer size to 10 bytes and the receive timeout to 100 microseconds. This means when a byte is received and there is inactivity (no new data) after 100 microseconds, or 10 bytes is received, a UART_RX_RDY event is generated.
In this exercise, we will simply practice the steps covered in the UART Driver section to interact with the UART hardware in an asynchronous manner.
Note
This exercise is not supported on the Thingy (Thingy:53, Thingy:91, Thingy:91 X).
Exercise steps
1. In the GitHub repository for this course, open the base code for this exercise, found in l5/l5_e1 of whichever version directory you are using.
This code is based on the blinky sample.
2. Enable the serial driver (UART driver) by adding the following two lines in prj.conf.
CONFIG_SERIAL=y
CONFIG_UART_ASYNC_API=yAs we mentioned before, the first line is usually enabled by default through the board’s devicetree, and you should therefore see it in gray (only when a build configuration has been added to the project). However, the second line is important to enable the asynchronous API of the serial driver.

3. Include the header file of the UART driver in main.c.
#include <drivers/uart.h>
4. Get the device struct of the UART hardware. Remember, in nRF Connect SDK any peripheral is accessed as a pointer of type struct device.
const struct device *uart = device_get_binding(DT_LABEL(DT_NODELABEL(uart0)));
if (uart== NULL) {
printk("Could not find %s!\n\r",DT_LABEL(DT_NODELABEL(uart0)));
return;
}5. Since we are using UART to control the LEDs (1-3) on the development kit, we need to configure the GPIOs of these LEDs like we learned in Lesson 2.
5.1 Get C identifiers for the devicetree labels and properties’ values related to LEDs.
#define LED0_NODE DT_ALIAS(led0)
#define LED0 DT_GPIO_LABEL(LED0_NODE, gpios)
#define PIN0 DT_GPIO_PIN(LED0_NODE, gpios)
#define FLAG0 DT_GPIO_FLAGS(LED0_NODE, gpios)
#define LED1_NODE DT_ALIAS(led1)
#define LED1 DT_GPIO_LABEL(LED1_NODE, gpios)
#define PIN1 DT_GPIO_PIN(LED1_NODE, gpios)
#define FLAG1 DT_GPIO_FLAGS(LED1_NODE, gpios)
#define LED2_NODE DT_ALIAS(led2)
#define LED2 DT_GPIO_LABEL(LED2_NODE, gpios)
#define PIN2 DT_GPIO_PIN(LED2_NODE, gpios)
#define FLAG2 DT_GPIO_FLAGS(LED2_NODE, gpios)5.2 Configure the LEDs.
ret = gpio_pin_configure(leds, PIN0, GPIO_OUTPUT_ACTIVE | FLAG0);
if (ret < 0) {
return 1 ;
}
ret = gpio_pin_configure(leds, PIN1, GPIO_OUTPUT_ACTIVE | FLAG1);
if (ret < 0) {
return 1 ;
}
ret = gpio_pin_configure(leds, PIN2, GPIO_OUTPUT_ACTIVE | FLAG2);
if (ret < 0) {
return 1 ;
}6. Define the application callback function for UART. For our simple LED application, we are only interested in the UART_RX_RDY and the UART_RX_DISABLED events related to receiving data over UART. For your own application, this might differ as we discussed in the UART driver section.
static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
switch (evt->type) {
case UART_RX_RDY:
if((evt->data.rx.len) == 1){
if(evt->data.rx.buf[evt->data.rx.offset] == '1')
gpio_pin_toggle(leds,PIN0);
else if (evt->data.rx.buf[evt->data.rx.offset] == '2')
gpio_pin_toggle(leds,PIN1);
else if (evt->data.rx.buf[evt->data.rx.offset] == '3')
gpio_pin_toggle(leds,PIN2);
}
break;
case UART_RX_DISABLED:
uart_rx_enable(dev ,rx_buf,sizeof rx_buf,RECEIVE_TIMEOUT);
break;
default:
break;
}
}7. Register the UART callback function by calling the function uart_callback_set().
ret = uart_callback_set(uart, uart_cb, NULL);
if (ret) {
return 1;
}8. Now let’s send some data over UART.
8.1 Define the transmission buffer, which is a buffer to hold the data to be sent over UART.
static uint8_t tx_buf[] = {"nRF Connect SDK Fundamentals Course\n\r"
"Press 1-3 on your keyboard to toggle LEDS 1-3 on your development kit\n\r"};8.2 Send the data over UART by calling uart_tx(). Since we are not using flow control, pass SYS_FOREVER_US to disable transmission timeout. This is not related to receiving timeout.
ret = uart_tx(uart, tx_buf, sizeof(tx_buf), SYS_FOREVER_US);
if (ret) {
return 1;
}9. Let’s start receiving data!
9.1 Define the receive buffer. For this simple exercise of controlling LEDs through UART, we will define a small buffer of size 10 of type uint8_t.
9.1.1 Define the size of the receive buffer.
#define RECEIVE_BUFF_SIZE 109.1.2 Define the receive buffer and initialize its members to 0s.
static uint8_t rx_buf[RECEIVE_BUFF_SIZE] = {0};9.2 Define the receiving timeout period to be 100 ms. This means when a byte is received and there is inactivity (no new data) for 100 microseconds, a UART_RX_RDY event is generated. If you are going to use receive timeout in your application, pick a value that matches your application requirements.
#define RECEIVE_TIMEOUT 1009.3 Start receiving by calling uart_rx_enable() and pass it the address of the receive buffer.
ret = uart_rx_enable(uart, rx_buf, sizeof rx_buf, RECEIVE_TIMEOUT);
if (ret) {
return 1;
}10. Build the application and flash it on your development kit.
Testing
11. You should observe that the three LEDs on your development kit are all switched on. Using a serial terminal such as the integrated nRF Terminal or PuTTY, you should see the below output:
*** Booting Zephyr OS build v2.7.0-ncs1 ***
nRF Connect SDK Fundamentals Course
Press 1-3 on your keyboard to toggle LED 1-3 on your development kitWe are using the default baud rate (115200) and UART settings set in the devicetree. If you want to change that, you can do it using the UART API as explained earlier.
Writing a number from 1 to 3 in the serial emulator will toggle the corresponding LED on your development kit.
Note
If you are using a Thingy:91, LED1 is the red LED, LED2 is the green LED and LED3 is the blue LED.
The solution for this exercise can be found in the GitHub repository, l5/l5_e1_sol of whichever version directory you are using.