First Application

This guide will walk you through creating your first custom Nexus application from scratch.

What We'll Build

We'll create a simple temperature monitoring application that:

  • Reads temperature from an ADC channel

  • Displays temperature on LEDs (color-coded)

  • Logs temperature readings

  • Provides a shell command to query temperature

This application demonstrates:

  • HAL usage (GPIO, ADC, UART)

  • Framework usage (Log, Shell)

  • OSAL usage (Tasks, Delays)

  • Configuration management

Prerequisites

Before starting, ensure you have:

  • Completed Quick Start

  • Built and run the blinky example

  • Familiar with Project Structure

  • Development board (STM32F4 Discovery) or native platform

Project Setup

Create Application Directory

# Navigate to applications directory
cd nexus/applications

# Create new application
mkdir temp_monitor
cd temp_monitor

Create CMakeLists.txt

Create applications/temp_monitor/CMakeLists.txt:

##############################################################################
# Temperature Monitor Application
##############################################################################

# Create executable
add_executable(temp_monitor
    main.c
)

# Link libraries
target_link_libraries(temp_monitor PRIVATE
    nexus_hal
    nexus_osal
    nexus_log
    nexus_shell
    nexus_platform
)

# Include directories
target_include_directories(temp_monitor PRIVATE
    ${CMAKE_SOURCE_DIR}/hal/include
    ${CMAKE_SOURCE_DIR}/osal/include
    ${CMAKE_SOURCE_DIR}/framework/log/include
    ${CMAKE_SOURCE_DIR}/framework/shell/include
    ${CMAKE_SOURCE_DIR}
)

# Set output directory
set_target_properties(temp_monitor PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/applications/temp_monitor
)

Update Parent CMakeLists.txt

Edit applications/CMakeLists.txt to include your application:

# Existing applications
add_subdirectory(blinky)
add_subdirectory(shell_demo)
add_subdirectory(config_demo)
add_subdirectory(freertos_demo)

# Add your application
add_subdirectory(temp_monitor)

Application Code

Create Main File

Create applications/temp_monitor/main.c:

/**
 * \file            main.c
 * \brief           Temperature Monitor Application
 * \author          Your Name
 * \version         1.0.0
 * \date            2026-01-25
 *
 * \copyright       Copyright (c) 2026 Your Name
 *
 * \details         This application monitors temperature using ADC
 *                  and displays status on LEDs with color coding.
 */

#include "hal/hal.h"
#include "log/log.h"
#include "shell/shell.h"
#include "shell/shell_backend.h"
#include "shell/shell_command.h"
#include <stdio.h>
#include <stdlib.h>

/*---------------------------------------------------------------------------*/
/* Configuration                                                             */
/*---------------------------------------------------------------------------*/

/** LED pins on STM32F4 Discovery */
#define LED_GREEN_PORT   HAL_GPIO_PORT_D
#define LED_GREEN_PIN    12
#define LED_ORANGE_PORT  HAL_GPIO_PORT_D
#define LED_ORANGE_PIN   13
#define LED_RED_PORT     HAL_GPIO_PORT_D
#define LED_RED_PIN      14
#define LED_BLUE_PORT    HAL_GPIO_PORT_D
#define LED_BLUE_PIN     15

/** ADC configuration */
#define ADC_INSTANCE     HAL_ADC_0
#define ADC_CHANNEL      0

/** Temperature thresholds (in Celsius) */
#define TEMP_COLD        20.0f
#define TEMP_NORMAL      25.0f
#define TEMP_WARM        30.0f
#define TEMP_HOT         35.0f

/** Monitoring interval (milliseconds) */
#define MONITOR_INTERVAL 1000

/*---------------------------------------------------------------------------*/
/* Global Variables                                                          */
/*---------------------------------------------------------------------------*/

static float g_current_temp = 0.0f;
static uint32_t g_sample_count = 0;

/*---------------------------------------------------------------------------*/
/* LED Control                                                               */
/*---------------------------------------------------------------------------*/

/**
 * \brief           Initialize LEDs
 * \return          HAL_OK on success
 */
static hal_status_t led_init(void) {
    hal_gpio_config_t config = {
        .direction = HAL_GPIO_DIR_OUTPUT,
        .pull = HAL_GPIO_PULL_NONE,
        .output_mode = HAL_GPIO_OUTPUT_PP,
        .speed = HAL_GPIO_SPEED_LOW,
        .init_level = HAL_GPIO_LEVEL_LOW
    };

    if (hal_gpio_init(LED_GREEN_PORT, LED_GREEN_PIN, &config) != HAL_OK) {
        return HAL_ERR_FAIL;
    }
    if (hal_gpio_init(LED_ORANGE_PORT, LED_ORANGE_PIN, &config) != HAL_OK) {
        return HAL_ERR_FAIL;
    }
    if (hal_gpio_init(LED_RED_PORT, LED_RED_PIN, &config) != HAL_OK) {
        return HAL_ERR_FAIL;
    }
    if (hal_gpio_init(LED_BLUE_PORT, LED_BLUE_PIN, &config) != HAL_OK) {
        return HAL_ERR_FAIL;
    }

    return HAL_OK;
}

/**
 * \brief           Turn off all LEDs
 */
static void led_all_off(void) {
    hal_gpio_write(LED_GREEN_PORT, LED_GREEN_PIN, HAL_GPIO_LEVEL_LOW);
    hal_gpio_write(LED_ORANGE_PORT, LED_ORANGE_PIN, HAL_GPIO_LEVEL_LOW);
    hal_gpio_write(LED_RED_PORT, LED_RED_PIN, HAL_GPIO_LEVEL_LOW);
    hal_gpio_write(LED_BLUE_PORT, LED_BLUE_PIN, HAL_GPIO_LEVEL_LOW);
}

/**
 * \brief           Update LED status based on temperature
 * \param[in]       temp: Current temperature in Celsius
 */
static void led_update_status(float temp) {
    led_all_off();

    if (temp < TEMP_COLD) {
        /* Cold: Blue LED */
        hal_gpio_write(LED_BLUE_PORT, LED_BLUE_PIN, HAL_GPIO_LEVEL_HIGH);
    } else if (temp < TEMP_NORMAL) {
        /* Normal: Green LED */
        hal_gpio_write(LED_GREEN_PORT, LED_GREEN_PIN, HAL_GPIO_LEVEL_HIGH);
    } else if (temp < TEMP_WARM) {
        /* Warm: Orange LED */
        hal_gpio_write(LED_ORANGE_PORT, LED_ORANGE_PIN, HAL_GPIO_LEVEL_HIGH);
    } else {
        /* Hot: Red LED */
        hal_gpio_write(LED_RED_PORT, LED_RED_PIN, HAL_GPIO_LEVEL_HIGH);
    }
}

/*---------------------------------------------------------------------------*/
/* Temperature Sensing                                                       */
/*---------------------------------------------------------------------------*/

/**
 * \brief           Initialize ADC for temperature sensing
 * \return          HAL_OK on success
 */
static hal_status_t adc_init_temp(void) {
    hal_adc_config_t config = {
        .resolution = HAL_ADC_RESOLUTION_12BIT,
        .sample_time = HAL_ADC_SAMPLE_TIME_480,
        .continuous = false,
        .dma_enable = false
    };

    return hal_adc_init(ADC_INSTANCE, &config);
}

/**
 * \brief           Read temperature from ADC
 * \return          Temperature in Celsius
 */
static float adc_read_temperature(void) {
    uint16_t adc_value = 0;
    float voltage = 0.0f;
    float temperature = 0.0f;

    /* Read ADC value */
    if (hal_adc_read(ADC_INSTANCE, ADC_CHANNEL, &adc_value) != HAL_OK) {
        LOG_ERROR("Failed to read ADC");
        return 0.0f;
    }

    /* Convert to voltage (assuming 3.3V reference) */
    voltage = (adc_value * 3.3f) / 4096.0f;

    /* Convert to temperature (example: LM35 sensor, 10mV/°C) */
    temperature = voltage * 100.0f;

    return temperature;
}

/*---------------------------------------------------------------------------*/
/* Shell Commands                                                            */
/*---------------------------------------------------------------------------*/

/**
 * \brief           Temperature command handler
 * \param[in]       argc: Argument count
 * \param[in]       argv: Argument array
 * \return          0 on success
 */
static int cmd_temp(int argc, char* argv[]) {
    (void)argc;
    (void)argv;

    shell_printf("Current temperature: %.2f °C\n", g_current_temp);
    shell_printf("Sample count: %lu\n", (unsigned long)g_sample_count);

    /* Temperature status */
    if (g_current_temp < TEMP_COLD) {
        shell_printf("Status: COLD (Blue LED)\n");
    } else if (g_current_temp < TEMP_NORMAL) {
        shell_printf("Status: NORMAL (Green LED)\n");
    } else if (g_current_temp < TEMP_WARM) {
        shell_printf("Status: WARM (Orange LED)\n");
    } else {
        shell_printf("Status: HOT (Red LED)\n");
    }

    return 0;
}

/**
 * \brief           Reset statistics command
 * \param[in]       argc: Argument count
 * \param[in]       argv: Argument array
 * \return          0 on success
 */
static int cmd_reset(int argc, char* argv[]) {
    (void)argc;
    (void)argv;

    g_sample_count = 0;
    shell_printf("Statistics reset\n");

    return 0;
}

/*---------------------------------------------------------------------------*/
/* Command Definitions                                                       */
/*---------------------------------------------------------------------------*/

static const shell_command_t cmd_temp_def = {
    .name = "temp",
    .handler = cmd_temp,
    .help = "Show current temperature",
    .usage = "temp",
    .completion = NULL
};

static const shell_command_t cmd_reset_def = {
    .name = "reset",
    .handler = cmd_reset,
    .help = "Reset statistics",
    .usage = "reset",
    .completion = NULL
};

/*---------------------------------------------------------------------------*/
/* Initialization                                                            */
/*---------------------------------------------------------------------------*/

/**
 * \brief           Initialize shell
 * \return          SHELL_OK on success
 */
static shell_status_t shell_app_init(void) {
    shell_status_t status;

    /* Configure UART for shell */
    hal_uart_config_t uart_config = {
        .baudrate = 115200,
        .wordlen = HAL_UART_WORDLEN_8,
        .stopbits = HAL_UART_STOPBITS_1,
        .parity = HAL_UART_PARITY_NONE,
        .flowctrl = HAL_UART_FLOWCTRL_NONE
    };

    if (hal_uart_init(HAL_UART_1, &uart_config) != HAL_OK) {
        return SHELL_ERROR;
    }

    /* Initialize UART backend */
    status = shell_uart_backend_init(HAL_UART_1);
    if (status != SHELL_OK) {
        return status;
    }

    /* Configure shell */
    shell_config_t config = {
        .prompt = "temp> ",
        .cmd_buffer_size = 128,
        .history_depth = 16,
        .max_commands = 32
    };

    /* Initialize shell */
    status = shell_init(&config);
    if (status != SHELL_OK) {
        return status;
    }

    /* Set backend */
    status = shell_set_backend(&shell_uart_backend);
    if (status != SHELL_OK) {
        shell_deinit();
        return status;
    }

    /* Register commands */
    shell_register_builtin_commands();
    shell_register_command(&cmd_temp_def);
    shell_register_command(&cmd_reset_def);

    return SHELL_OK;
}

/*---------------------------------------------------------------------------*/
/* Main Entry Point                                                          */
/*---------------------------------------------------------------------------*/

/**
 * \brief           Main entry point
 * \return          Should never return
 */
int main(void) {
    /* Initialize HAL system */
    hal_system_init();

    /* Initialize logging */
    log_init(NULL);
    LOG_INFO("Temperature Monitor v1.0.0");

    /* Initialize LEDs */
    if (led_init() != HAL_OK) {
        LOG_ERROR("Failed to initialize LEDs");
        while (1) {
            hal_delay_ms(100);
        }
    }
    LOG_INFO("LEDs initialized");

    /* Initialize ADC */
    if (adc_init_temp() != HAL_OK) {
        LOG_ERROR("Failed to initialize ADC");
        while (1) {
            hal_gpio_toggle(LED_RED_PORT, LED_RED_PIN);
            hal_delay_ms(100);
        }
    }
    LOG_INFO("ADC initialized");

    /* Initialize shell */
    if (shell_app_init() != SHELL_OK) {
        LOG_ERROR("Failed to initialize shell");
        while (1) {
            hal_gpio_toggle(LED_ORANGE_PORT, LED_ORANGE_PIN);
            hal_delay_ms(100);
        }
    }
    LOG_INFO("Shell initialized");

    /* Print welcome message */
    shell_printf("\n");
    shell_printf("========================================\n");
    shell_printf("  Temperature Monitor v1.0.0\n");
    shell_printf("  Type 'help' for available commands\n");
    shell_printf("========================================\n");
    shell_print_prompt();

    /* Turn on green LED to indicate ready */
    hal_gpio_write(LED_GREEN_PORT, LED_GREEN_PIN, HAL_GPIO_LEVEL_HIGH);

    /* Main monitoring loop */
    uint32_t last_sample_time = hal_get_tick();

    while (1) {
        /* Process shell input */
        shell_process();

        /* Check if it's time to sample */
        uint32_t current_time = hal_get_tick();
        if (current_time - last_sample_time >= MONITOR_INTERVAL) {
            last_sample_time = current_time;

            /* Read temperature */
            g_current_temp = adc_read_temperature();
            g_sample_count++;

            /* Update LED status */
            led_update_status(g_current_temp);

            /* Log temperature */
            LOG_INFO("Temperature: %.2f °C (sample #%lu)",
                     g_current_temp, (unsigned long)g_sample_count);
        }

        /* Small delay to avoid busy-waiting */
        hal_delay_ms(10);
    }

    return 0;
}

Build and Test

Build for Native

# Configure
cmake -B build -DNEXUS_PLATFORM=native

# Build
cmake --build build

# Run
./build/applications/temp_monitor/temp_monitor

Build for STM32F4

# Configure
cmake -B build-stm32f4 \
    -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/arm-none-eabi.cmake \
    -DNEXUS_PLATFORM=stm32f4

# Build
cmake --build build-stm32f4

# Flash
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
    -c "program build-stm32f4/applications/temp_monitor/temp_monitor.elf verify reset exit"

Test the Application

  1. Connect serial terminal (115200 baud)

  2. You should see:

========================================
  Temperature Monitor v1.0.0
  Type 'help' for available commands
========================================
temp>
  1. Try commands:

temp> help
Available commands:
  help     - Show this help message
  temp     - Show current temperature
  reset    - Reset statistics

temp> temp
Current temperature: 24.50 °C
Sample count: 15
Status: NORMAL (Green LED)
  1. Observe LEDs changing color based on temperature

Enhancements

Add Configuration

Add Kconfig options for your application:

Create applications/temp_monitor/Kconfig:

config APP_TEMP_MONITOR
    bool "Temperature Monitor Application"
    default y
    select HAL_GPIO
    select HAL_ADC
    select HAL_UART
    select FRAMEWORK_LOG
    select FRAMEWORK_SHELL
    help
      Temperature monitoring application with LED indicators

if APP_TEMP_MONITOR

config APP_TEMP_MONITOR_INTERVAL
    int "Monitoring interval (ms)"
    default 1000
    range 100 10000
    help
      Temperature sampling interval in milliseconds

config APP_TEMP_MONITOR_COLD_THRESHOLD
    int "Cold temperature threshold (°C)"
    default 20
    help
      Temperature below this is considered cold

config APP_TEMP_MONITOR_HOT_THRESHOLD
    int "Hot temperature threshold (°C)"
    default 35
    help
      Temperature above this is considered hot

endif # APP_TEMP_MONITOR

Use configuration in code:

#include "nexus_config.h"

#ifdef CONFIG_APP_TEMP_MONITOR_INTERVAL
#define MONITOR_INTERVAL CONFIG_APP_TEMP_MONITOR_INTERVAL
#else
#define MONITOR_INTERVAL 1000
#endif

Add Multi-Tasking

Use OSAL for multi-tasking:

#include "osal/osal.h"

/* Temperature monitoring task */
static void temp_monitor_task(void* arg) {
    (void)arg;

    while (1) {
        /* Read temperature */
        g_current_temp = adc_read_temperature();
        g_sample_count++;

        /* Update LED status */
        led_update_status(g_current_temp);

        /* Log temperature */
        LOG_INFO("Temperature: %.2f °C", g_current_temp);

        /* Delay */
        osal_task_delay(MONITOR_INTERVAL);
    }
}

/* Shell processing task */
static void shell_task(void* arg) {
    (void)arg;

    while (1) {
        shell_process();
        osal_task_delay(10);
    }
}

int main(void) {
    /* ... initialization ... */

    /* Create tasks */
    osal_task_create(temp_monitor_task, "temp_monitor", 512, NULL, 2, NULL);
    osal_task_create(shell_task, "shell", 1024, NULL, 1, NULL);

    /* Start scheduler */
    osal_start_scheduler();

    return 0;
}

Add Data Logging

Log temperature data to storage:

#include "config/config.h"

/* Save temperature reading */
static void save_temperature(float temp) {
    char key[32];
    snprintf(key, sizeof(key), "temp.reading_%lu", g_sample_count);
    config_set_float(key, temp);
}

/* Export to JSON */
static void export_data(void) {
    char buffer[1024];
    size_t size;

    if (config_export(CONFIG_FORMAT_JSON, 0, buffer, sizeof(buffer), &size) == CONFIG_OK) {
        shell_printf("Temperature data:\n%s\n", buffer);
    }
}

Next Steps

Congratulations! You've created your first Nexus application. Next:

  1. Core Concepts - Understand Nexus architecture deeply

  2. Configuration - Master Kconfig configuration

  3. Examples Tour - Explore more complex examples

  4. 教程 - Follow step-by-step tutorials

See Also