# How to use UART in custom applications

Source: https://forum.modalai.com/topic/4064/how-to-use-uart-in-custom-applications
Category: VOXL SDK and Software (https://forum.modalai.com/category/47/voxl-sdk-and-software)
Posted: 2025-01-06 23:07:46 UTC by Samuel Lehman
Replies: 7 · Views: 4684

## Samuel Lehman · 2025-01-06 23:07:46 UTC

I am currently trying to make a simple application that gets imu data and sends it out through uart. I am using the voxl-cross-template project as a base for this application. I have tried using some of the code in the libqrb5165-io project to get the uart to work however I have been unable to compile it. I believe it might be an issue with my cmake files.
```
[ 50%] Linking C executable hello-cross
CMakeFiles/hello-cross.dir/main.c.o: In function `_helper_cb':
main.c:(.text+0xb8): undefined reference to `voxl_uart_write'
main.c:(.text+0xfc): undefined reference to `ERROR'
CMakeFiles/hello-cross.dir/main.c.o: In function `main':
main.c:(.text.startup+0x3c): undefined reference to `voxl_uart_init'
main.c:(.text.startup+0xcc): undefined reference to `voxl_uart_close'
main.c:(.text.startup+0xec): undefined reference to `ERROR'
main.c:(.text.startup+0x100): undefined reference to `ERROR'
collect2: error: ld returned 1 exit status
src/CMakeFiles/hello-cross.dir/build.make:99: recipe for target 'src/hello-cross' failed
make[2]: *** [src/hello-cross] Error 1
CMakeFiles/Makefile2:97: recipe for target 'src/CMakeFiles/hello-cross.dir/all' failed
make[1]: *** [src/CMakeFiles/hello-cross.dir/all] Error 2
Makefile:135: recipe for target 'all' failed
make: *** [all] Error 2
starting building Debian Package
mkdir: cannot create directory 'pkg/DEB': No such file or directory

```
Is there a better way to send data out through uart? Is there an example of something similar to what I'm trying to do out there that I can look at?

Here is my current main function and helper_cb for reference:
```
int main(int argc, char *const argv[])
{

    enable_signal_handler();
    main_running = 1;

    int uart_res = voxl_uart_init(device, baud, 1.0, 0, 1, 0);
    if (uart_res < 0)
    {
        ERROR("Failed to open port");
        resurn 1;
    }

    pipe_client_set_simple_helper_cb(0, _helper_cb, NULL);
    pipe_client_set_disconnect_cb(0, _disconnect_cb, NULL);

    int pipe_open_res = pipe_client_open(0, imu_name, CLIENT_NAME,
                                         EN_PIPE_CLIENT_SIMPLE_HELPER,
                                         IMU_RECOMMENDED_READ_BUF_SIZE);

    if (pipe_open_res < 0)
    {
        pipe_print_error(pipe_open_res);
        printf(ENABLE_WRAP);
        return -1;
    }

    // keep going until the  signal handler sets the running flag to 0
    while (main_running)
        usleep(500000);

    // all done, signal pipe read threads to stop
    printf("\nclosing and exiting\n" RESET_FONT ENABLE_WRAP);
    pipe_client_close_all();

    int ret = voxl_uart_close(device);
    if (ret < 0)
    {
        ERROR("Failed to close UART device");
        return 1;
    }
}
static void _helper_cb(__attribute__((unused)) int ch, char *data, int bytes, __attribute__((unused)) void *context)
{

    const uint8_t MIN_WRITE_LEN = 32;
    uint8_t write_buf[MIN_WRITE_LEN];

    // validate that the data makes sense
    int n_packets;
    imu_data_t *data_array = pipe_validate_imu_data_t(data, bytes, &n_packets);
    if (data_array == NULL)
        return;

    // print everything in one go.
    if (!en_newline)
        printf("\r" CLEAR_LINE);
    printf("%7.2f|%7.2f|%7.2f|%7.2f|%7.2f|%7.2f",
           (double)data_array[n_packets - 1].accl_ms2[0],
           (double)data_array[n_packets - 1].accl_ms2[1],
           (double)data_array[n_packets - 1].accl_ms2[2],
           (double)data_array[n_packets - 1].gyro_rad[0],
           (double)data_array[n_packets - 1].gyro_rad[1],
           (double)data_array[n_packets - 1].gyro_rad[2]);

    if (en_newline)
        printf("\n");

    int write_res = voxl_uart_write(device, write_buf, MIN_WRITE_LEN);
    if (write_res < 0)
    {
        ERROR("Failed to write to UART");
        return;
    }

    fflush(stdout);
    return;
}
```

## Reply by Alex Kushleyev (ModalAI staff) · 2025-01-07 18:58:50 UTC

@Samuel-Lehman ,

In your application, you should be including `<voxl_io.h>` and linking against `voxl_io` library:

in cmake file:
```
target_link_libraries(${app_name} LINK_PUBLIC voxl-io)
```

Both the header and library (.so) are part of `libqrb5165-io` package that is built from this project: https://gitlab.com/voxl-public/voxl-sdk/core-libs/libqrb5165-io/

So, if you are building your app using cross-compiler, make sure to install the `libqrb5165-io` inside the cross compiler docker before building the application. Typically all our projects have a script for installing build deps, here it is for the `libqrb5165-io` library (which obviously does not include the library itself as a dependency, because we are actually building it here ) : https://gitlab.com/voxl-public/voxl-sdk/core-libs/libqrb5165-io/-/blob/master/install_build_deps.sh

You can also just run `apt update && apt install libqrb5165-io` after you spin up your cross compile docker.

You can also build your app directly on voxl2, as the `libqrb5165-io` library should already be installed as part of our SDK.

Alex

## Reply by Samuel Lehman · 2025-01-07 21:04:02 UTC (in reply to Alex Kushleyev)

@Alex-Kushleyev thank you for your response, however I am still unable to build the project. I have libqrb5165-io added to my install_build_deps.sh and I do have the voxl_io header included in my main file. Maybe I am placing the cmake line in the wrong spot or in the wrong cmake file altogether as I have two of them.  Here are the cmake files in my src folder and root folder respectively:
```
// ./src/CMakeLists.txt

cmake_minimum_required(VERSION 3.3)

SET(TARGET hello-cross)

# Build from all source files
file(GLOB all_src_files *.c*)

add_executable(${TARGET}
	${all_src_files}
)

include_directories(
    ../include
)

find_library(MODAL_JSON  modal_json  HINTS /usr/lib /usr/lib64)
find_library(MODAL_PIPE  modal_pipe  HINTS /usr/lib /usr/lib64)
find_library(VOXL_CUTILS voxl_cutils HINTS /usr/lib /usr/lib64)
find_library(VOXL_IO libvoxl_io HINTS /usr/lib /usr/lib64)

target_link_libraries(
    ${TARGET}
    pthread
    ${MODAL_JSON}
    ${MODAL_PIPE}
    ${VOXL_CUTILS}
    ${app_name} LINK_PUBLIC voxl-io
)

# make sure everything is installed where we want
# LIB_INSTALL_DIR comes from the parent cmake file
install(
    TARGETS			${TARGET}
	LIBRARY			DESTINATION ${LIB_INSTALL_DIR}
	RUNTIME			DESTINATION /usr/bin
	PUBLIC_HEADER	DESTINATION /usr/include
)
```
```
// ./CMakeLists.txt

cmake_minimum_required(VERSION 3.3)

project(voxl-hello-cross C)

include_directories(
	"include/"
)

# Strawson's list of standard list of default gcc flags. Yes, I treat
# warnings as errors. Warnings exist to point out sloppy code and potential
# failure points for good reason. We do not approve of sloppy code.
# set(CMAKE_C_FLAGS "-std=gnu99 -Werror -Wall -Wextra -Wuninitialized \
# 	-Wunused-variable -Wdouble-promotion -Wmissing-prototypes \
# 	-Wmissing-declarations -Werror=undef -Wno-unused-function ${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS "-std=gnu99 -Wuninitialized \
	-Wdouble-promotion -Wmissing-prototypes \
	-Wmissing-declarations -Werror=undef ${CMAKE_C_FLAGS}")

# for VOXL, install 64-bit libraries to lib64, 32-bit libs go in /usr/lib
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64")
	set(LIB_INSTALL_DIR /usr/lib64)
else()
	set(LIB_INSTALL_DIR /usr/lib)
endif()

# include each subdirectory, may have others in example/ or lib/ etc
add_subdirectory (src)
```

here is the output when I run the install and build scripts:
```
using qrb5165 sdk-1.0 debian repo
Ign:1 http://voxl-packages.modalai.com ./dists/qrb5165/sdk-1.0/binary-arm64/ InRelease
Ign:2 http://voxl-packages.modalai.com ./dists/qrb5165/sdk-1.0/binary-arm64/ Release
Get:3 http://voxl-packages.modalai.com ./dists/qrb5165/sdk-1.0/binary-arm64/ Packages [23.2 kB]
Fetched 23.2 kB in 0s (61.3 kB/s)
Reading package lists... Done
installing: 
libmodal-json
libmodal-pipe
libvoxl-cutils
libqrb5165-io

Reading package lists... Done
Building dependency tree       
Reading state information... Done
libmodal-json:arm64 is already the newest version (0.4.3).
libvoxl-cutils:arm64 is already the newest version (0.1.1).
libqrb5165-io:arm64 is already the newest version (0.4.7).
libmodal-pipe:arm64 is already the newest version (2.10.4).
0 upgraded, 0 newly installed, 0 to remove and 1 not upgraded.

Done installing dependencies

-- The C compiler identification is GNU 7.5.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/aarch64-linux-gnu-gcc-7 - skipped
-- Detecting C compile features
-- Detecting C compile features - done
CMake Error at src/CMakeLists.txt:22 (target_link_libraries):
  The LINK_PUBLIC or LINK_PRIVATE option must appear as the second argument,
  just after the target name.
```
Im new to cmake so I have no idea what I am doing.

## Reply by Alex Kushleyev (ModalAI staff) · 2025-01-07 22:17:47 UTC (in reply to Samuel Lehman)

I think you need to update the `target_link_libraries` entry to:

```
target_link_libraries(
    ${TARGET}
    pthread
    ${MODAL_JSON}
    ${MODAL_PIPE}
    ${VOXL_CUTILS}
    voxl-io
)
```

or `$(VOXL_IO)` instead of `voxl-io` since you already defined it using:

```
find_library(VOXL_IO libvoxl_io HINTS /usr/lib /usr/lib64)
```

Please try

Alex

## Reply by Samuel Lehman · 2025-01-07 22:40:42 UTC (in reply to Alex Kushleyev)

@Alex-Kushleyev after adding $(VOXL_IO) i get:
```
-- Build files have been written to: /home/root/build
[ 50%] Building C object src/CMakeFiles/hello-cross.dir/main.c.o
/home/root/src/main.c: In function '_helper_cb':
/home/root/src/main.c:88:21: warning: implicit declaration of function 'voxl_uart_write'; did you mean 'voxl_spi_write'? [-Wimplicit-function-declaration]
     int write_res = voxl_uart_write(device, write_buf, MIN_WRITE_LEN);
                     ^~~~~~~~~~~~~~~
                     voxl_spi_write
/home/root/src/main.c: In function 'main':
/home/root/src/main.c:105:20: warning: implicit declaration of function 'voxl_uart_init'; did you mean 'voxl_i2c_init'? [-Wimplicit-function-declaration]
     int uart_res = voxl_uart_init(device, baud, 1.0, 0, 1, 0);
                    ^~~~~~~~~~~~~~
                    voxl_i2c_init
/home/root/src/main.c:134:15: warning: implicit declaration of function 'voxl_uart_close'; did you mean 'voxl_i2c_close'? [-Wimplicit-function-declaration]
     int ret = voxl_uart_close(device);
               ^~~~~~~~~~~~~~~
               voxl_i2c_close
[100%] Linking C executable hello-cross
CMakeFiles/hello-cross.dir/main.c.o: In function `_helper_cb':
main.c:(.text+0xb8): undefined reference to `voxl_uart_write'
CMakeFiles/hello-cross.dir/main.c.o: In function `main':
main.c:(.text.startup+0x44): undefined reference to `voxl_uart_init'
main.c:(.text.startup+0xd0): undefined reference to `voxl_uart_close'
collect2: error: ld returned 1 exit status
src/CMakeFiles/hello-cross.dir/build.make:100: recipe for target 'src/hello-cross' failed
make[2]: *** [src/hello-cross] Error 1
CMakeFiles/Makefile2:97: recipe for target 'src/CMakeFiles/hello-cross.dir/all' failed
make[1]: *** [src/CMakeFiles/hello-cross.dir/all] Error 2
Makefile:135: recipe for target 'all' failed
make: *** [all] Error 2
Package Name:  voxl-cross-template
version Number:  0.0.1
Consolidate compiler generated dependencies of target hello-cross
[ 50%] Linking C executable hello-cross
CMakeFiles/hello-cross.dir/main.c.o: In function `_helper_cb':
main.c:(.text+0xb8): undefined reference to `voxl_uart_write'
CMakeFiles/hello-cross.dir/main.c.o: In function `main':
main.c:(.text.startup+0x44): undefined reference to `voxl_uart_init'
main.c:(.text.startup+0xd0): undefined reference to `voxl_uart_close'
collect2: error: ld returned 1 exit status
src/CMakeFiles/hello-cross.dir/build.make:100: recipe for target 'src/hello-cross' failed
make[2]: *** [src/hello-cross] Error 1
```

## Reply by Alex Kushleyev (ModalAI staff) · 2025-01-08 18:26:56 UTC (in reply to Samuel Lehman)

This is odd because the uart function definitions are here : 

https://gitlab.com/voxl-public/voxl-sdk/core-libs/libqrb5165-io/-/blob/master/apps/library/include/voxl_io/uart.h?ref_type=heads#L69

Are you sure that you are including <voxl_io.h> in your app? `implicit declaration of function` warning is a clear sign that the function declaration is not found (from voxl_io.h, well actually from `voxl_io/uart.h`, latter being included from voxl_io.h.

Please double check.

Alex

## Reply by Samuel Lehman · 2025-01-08 19:07:18 UTC (in reply to Alex Kushleyev)

@Alex-Kushleyev I have it included, heres my full main file:
```
#include <stdio.h> // for fprintf
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <modal_pipe.h>
#include <voxl_cutils.h>
#include <voxl_io.h>
#include "hello_cross.h"

#define CLIENT_NAME "hello-cross"

static char en_newline = 0;
static char imu_name[64];
static int opt = 0;
static int device = 1; // default. /dev/ttyHS1
static int baud = 115200;

void printHelloCross(void);

// called whenever we disconnect from the server
static void _disconnect_cb(__attribute__((unused)) int ch, __attribute__((unused)) void *context)
{
    fprintf(stderr, "\r" CLEAR_LINE FONT_BLINK "server disconnected" RESET_FONT);
    return;
}

// called when the simple helper has data for us
static void _helper_cb(__attribute__((unused)) int ch, char *data, int bytes, __attribute__((unused)) void *context)
{

    const uint8_t MIN_WRITE_LEN = 32;
    uint8_t write_buf[MIN_WRITE_LEN];

    // validate that the data makes sense
    int n_packets;
    imu_data_t *data_array = pipe_validate_imu_data_t(data, bytes, &n_packets);
    if (data_array == NULL)
        return;

    // print everything in one go.
    if (!en_newline)
        printf("\r" CLEAR_LINE);
    printf("%7.2f|%7.2f|%7.2f|%7.2f|%7.2f|%7.2f",
           (double)data_array[n_packets - 1].accl_ms2[0],
           (double)data_array[n_packets - 1].accl_ms2[1],
           (double)data_array[n_packets - 1].accl_ms2[2],
           (double)data_array[n_packets - 1].gyro_rad[0],
           (double)data_array[n_packets - 1].gyro_rad[1],
           (double)data_array[n_packets - 1].gyro_rad[2]);

    if (en_newline)
        printf("\n");

    int write_res = voxl_uart_write(device, write_buf, MIN_WRITE_LEN);
    if (write_res < 0)
    {
        //ERROR("Failed to write to UART");
        return;
    }

    fflush(stdout);
    return;
}

int main(int argc, char *const argv[])
{

    enable_signal_handler();
    main_running = 1;

    int uart_res = voxl_uart_init(device, baud, 1.0, 0, 1, 0);
    if (uart_res < 0)
    {
        //ERROR("Failed to open port");
        return 1;
    }

    pipe_client_set_simple_helper_cb(0, _helper_cb, NULL);
    pipe_client_set_disconnect_cb(0, _disconnect_cb, NULL);

    int pipe_open_res = pipe_client_open(0, imu_name, CLIENT_NAME,
                                         EN_PIPE_CLIENT_SIMPLE_HELPER,
                                         IMU_RECOMMENDED_READ_BUF_SIZE);

    if (pipe_open_res < 0)
    {
        pipe_print_error(pipe_open_res);
        printf(ENABLE_WRAP);
        return -1;
    }

    // keep going until the  signal handler sets the running flag to 0
    while (main_running)
        usleep(500000);

    // all done, signal pipe read threads to stop
    printf("\nclosing and exiting\n" RESET_FONT ENABLE_WRAP);
    pipe_client_close_all();

    int ret = voxl_uart_close(device);
    if (ret < 0)
    {
        //ERROR("Failed to close UART device");
        return 1;
    }
}

void printHelloCross()
{
    printf("   Imu Acceleration and Gyro\n");
    printf("   X   |   Y   |   Z   |  X   |   Y   |   Z\n");
}
```

## Reply by Alex Kushleyev (ModalAI staff) · 2025-01-08 19:40:11 UTC (in reply to Samuel Lehman)

@Samuel-Lehman , Not sure what is going on but i was able to build your example with the following command on VOXL2. I had to comment out `#include "hello_cross.h"` because i did not have it.

```
voxl2:~$ gcc -O2 -Wall voxl_io_uart_test.c -o voxl_io_uart_test -lmodal_pipe -lvoxl_io -L/usr/lib64
voxl_io_uart_test.c:14:12: warning: 'opt' defined but not used [-Wunused-variable]
 static int opt = 0;
            ^~~
voxl2:~$
```
I am not sure what's going on, you can enable verbose CMake file to see what compile command CMake is generating and compare that to the above.

Alex
