Skip to main content

How to: Project Generator

The CHESTER SDK Project Generator simplifies project initialization and configuration management by providing a structured approach to project setup, all based on a YAML configuration.

File generation

This tool can automatically generate the following files based on the provided YAML configuration:

/project-name
project.yaml

Project YAML

The project.yaml configuration file serves as the cornerstone for setting up and customizing your project.

This comprehensive guide outlines the step-by-step process to effectively configure your project using the provided YAML structure.

Project Information

Enter essential project details as outlined in the YAML configuration.

project:
variant: Variant-name
company: 2024 COMPANY a.s.
license: 'SPDX-License-Identifier: LicenseRef-COMPANY-5-Clause'
fw_name: CHESTER Example
fw_bundle: com.hardwario.chester.example
fw_version: v1.0.0

Furthermore, the variant is included in Kconfig.variant such as:

config VARIANT_<VARIANT-NAME>
bool "Enable VARIANT_<VARIANT-NAME>"
default y

It can be used in the project using:

#if defined(CONFIG_VARIANT_<VARIANT-NAME>)
// Your code
#endif /* defined(CONFIG_VARIANT_<VARIANT-NAME>) */

Feature Specification

This section outlines the available features or subsystems that can be included in the project configuration. Each feature represents a specific functionality or component that can be integrated into the project.

Subsystem features

The term subsystem- refers to a functional module or component within the software architecture. Example:

features:
- subsystem-bluetooth
Subsystem Feature Options
NameFeatureConfiguration in prj.conf
Shellsubsystem-shellCONFIG_CTR_SHELL=y
ADCsubsystem-adcCONFIG_CTR_ADC=y
Accelerometersubsystem-accelCONFIG_CTR_ACCEL=y
Batterysubsystem-battCONFIG_CTR_BATT=y
Buffersubsystem-bufCONFIG_CTR_BUF=y
BLE Tagsubsystem-ble-tagCONFIG_CTR_BLE_TAG=y
Bluetoothsubsystem-bleCONFIG_CTR_BLE=y
Buttonsubsystem-buttonCONFIG_CTR_BUTTON=y
CBPrintf FP Supportsubsystem-cbprintf-fp-supportCONFIG_CBPRINTF_FP_SUPPORT=y
Configsubsystem-configCONFIG_CTR_CONFIG=y
Cloudsubsystem-cloudCONFIG_CTR_CLOUD=y
Defaultssubsystem-defaultsCONFIG_CTR_DEFAULTS=y
DS18B20subsystem-ds18b20CONFIG_CTR_DS18B20=y
Edgesubsystem-edgeCONFIG_CTR_EDGE=y
Entropy Generatorsubsystem-entropy-generatorCONFIG_ENTROPY_GENERATOR=y
Flashsubsystem-flashCONFIG_CTR_FLASH=y
GNSSsubsystem-gnssCONFIG_CTR_GNSS=y
GPIOsubsystem-gpioCONFIG_CTR_GPIO=y
Hygrosubsystem-hygroCONFIG_CTR_HYGRO=y
Infosubsystem-infoCONFIG_CTR_INFO=y
LEDsubsystem-ledCONFIG_CTR_LED=y
Logsubsystem-logCONFIG_CTR_LOG=y
LTEsubsystem-lteCONFIG_CTR_LTE_CLKSYNC=y
LTE V2subsystem-lte-v2CONFIG_CTR_LTE_V2=y
LRWsubsystem-lrwCONFIG_CTR_LRW=y
Machine Probesubsystem-machine-probeCONFIG_CTR_MACHINE_PROBE=y
MB7066-Asubsystem-mb7066-aCONFIG_MB7066_TIMER4=y CONFIG_MB7066_SAMPLE_COUNT=1
MB7066-Bsubsystem-mb7066-bCONFIG_MB7066_TIMER4=y CONFIG_MB7066_SAMPLE_COUNT=1
RTCsubsystem-rtcCONFIG_CTR_RTC=y
RTDsubsystem-rtdCONFIG_CTR_RTD=y
Settingssubsystem-settingsCONFIG_SETTINGS=y
Signalsubsystem-signalCONFIG_CTR_SIGNAL=y
Soil Sensorsubsystem-soil-sensorCONFIG_CTR_SOIL_SENSOR=y
Testsubsystem-testCONFIG_CTR_TEST=y
Thermsubsystem-thermCONFIG_CTR_THERM=y
TinyCrypt SHA256subsystem-tinycrypt-sha256CONFIG_TINYCRYPT_SHA256=y
TinyCryptsubsystem-tinycryptCONFIG_TINYCRYPT=y
WDOGsubsystem-wdogCONFIG_CTR_WDOG=y
W1subsystem-w1CONFIG_CTR_W1=y
ZCBORsubsystem-zcborCONFIG_ZCBOR=y CONFIG_ZCBOR_STOP_ON_ERROR=y
BT Filter Accept Listsubsystem-bt-filter-accept-listCONFIG_BT_FILTER_ACCEPT_LIST=y
BT Observersubsystem-bt-observerCONFIG_BT_OBSERVER=y

Hardware Chester feature

The term hardware-chester- refers to a specific hardware component or device within the project's hardware ecosystem. Example:

features:
- hardware-chester-z

Custom Feature

Refers to a feature that a customer can add based on specific requirements. Example:

features:
- custom-x
- custom-y

These features will enable the necessary configurations on: app.overlay, Kconfig, prj.conf. Furthermore, they are also included in features.h such as:

#define FEATURE_SUBSYSTEM_BLE 1  
#define FEATURE_HARDWARE_CHESTER_Z 1
#define FEATURE_CUSTOM_X 1
#define FEATURE_CUSTOM_Y 1

Feature Type

Each feature type allows for specific parameter settings, enabling precise control and customization of the application's behavior.

  • Type: int
parameters:
- name: int-param-name # Name separated by '-'
type: int
min: 30 # Minimum value
max: 86400 # Maximum value
default: 1800 # Default value
help: 'Get/Set int-param' # Help parameter message
  • Type: float
parameters:
- name: float-param-name # Name separated by '-'
type: float
min: -40.0 # Minimum value
max: 5000.0 # Maximum value
default: 125.5 # Default value
help: 'Get/Set float-param' # Help parameter message
  • Type: string
parameters:
- name: string-param-name # Name separated by '-'
type: string
buffer_size: 35 # Buffer string size
default: "Let's connect and control ANYTHING" # Default string
help: 'Get/Set string-param-name' # Help parameter message
  • Type: bool
parameters:
- name: bool-param-name # Name separated by '-'
type: bool
default: true
help: 'Get/Set bool-param' # Help parameter message
  • Type: int array
parameters:
- name: int-array-name # Name separated by '-'
type: array[int]
len: 4 # Length of the array
min: 30 # Minimum array element value
max: 86400 # Maximum array element value
default: [31, 32, 33, 34] # or null to declare [0, 0, 0, 0]
help: 'Get/Set int-array-name' # Help parameter message
  • Type: float array
parameters:
- name: float-array-name # Name separated by '-'
type: array[float]
len: 4 # Length of the array
min: 0.0 # Minimum array element value
max: 25.0 # Maximum array element value
default: [1.0, 2.0, 3.0, 4.0] # or null to declare [0.0, 0.0, 0.0, 0.0]
help: 'Get/Set float-array-name' # Help parameter message
  • Type: bool array
parameters:
- name: bool-array-name # Name separated by '-'
type: array[bool]
len: 4 # Length of the array
default: [true, false, true, true] # or null to declare [false, false, false, false]
help: 'Get/Set bool-array-name' # Help parameter message
  • Type: enum (Standard)
parameters:
- name: enum-name-{} # Name separated by '-' and '{}' in the index location
type: enum
valueset: # Enumerators
- trigger
- counter
- voltage
- current

In app_config.c:

const struct ctr_config_item items[] = {
CTR_CONFIG_ITEM_ENUM("enum-name-1", m_config_interim.enum_name_1, ((const char*[]){"trigger", "counter", "voltage", "current"}), "Get/Set enum-name-1", APP_CONFIG_ENUM_NAME_TRIGGER),
CTR_CONFIG_ITEM_ENUM("enum-name-2", m_config_interim.enum_name_2, ((const char*[]){"trigger", "counter", "voltage", "current"}), "Get/Set enum-name-2", APP_CONFIG_ENUM_NAME_COUNTER),
CTR_CONFIG_ITEM_ENUM("enum-name-3", m_config_interim.enum_name_3, ((const char*[]){"trigger", "counter", "voltage", "current"}), "Get/Set enum-name-3", APP_CONFIG_ENUM_NAME_VOLTAGE),
CTR_CONFIG_ITEM_ENUM("enum-name-4", m_config_interim.enum_name_4, ((const char*[]){"trigger", "counter", "voltage", "current"}), "Get/Set enum-name-4", APP_CONFIG_ENUM_NAME_CURRENT),
};

In app_config.h:

enum app_config_enum_name_ {
APP_CONFIG_ENUM_NAME_TRIGGER = 0,
APP_CONFIG_ENUM_NAME_COUNTER = 1,
APP_CONFIG_ENUM_NAME_VOLTAGE = 2,
APP_CONFIG_ENUM_NAME_CURRENT = 3,
};
struct app_config {
enum app_config_enum_name enum_name_1;
enum app_config_enum_name enum_name_2;
enum app_config_enum_name enum_name_3;
enum app_config_enum_name enum_name_4;
};
info

In the application's configuration files (app_config.c and app_config.h), the valueset is associated with enum-name indices.

  • Type: enum (Custom)
parameters:
- name: enum-name # Name separated by '-'
type: enum
valueset: # Enumerators
- npn
- pnp
related: # variable(s) of enum
- name: trigger # Name separated by '-'
default: npn # Default
help: 'Get/Set trigger-enum-name' # Help parameter message
- name: counter # Name separated by '-'
default: pnp # Default
help: 'Get/Set counter-enum-name' # Help parameter message
info

The related section specifies enum variables associated with the enum-name parameter. These variables (trigger, counter, etc.) share the same enum values (npn, pnp, etc.) and provide specific default values and help messages for each variable.

In app_config.c:

const struct ctr_config_item items[] = {
CTR_CONFIG_ITEM_ENUM("trigger-enum-name", m_config_interim.trigger_enum_name, ((const char*[]){"npn", "pnp"}), "Get/Set trigger-enum-name", APP_CONFIG_ENUM_NAME_NPN),
CTR_CONFIG_ITEM_ENUM("counter-enum-name", m_config_interim.counter_enum_name, ((const char*[]){"npn", "pnp"}), "Get/Set counter-enum-name", APP_CONFIG_ENUM_NAME_PNP),
};

In app_config.h:

struct app_config {
enum app_config_enum_name trigger_enum_name;
enum app_config_enum_name counter_enum_name;
};

Commands declaration

The commands define specific actions that can be executed within the project shell environment. Example:

commands:
- name: sample # Name separated by '-'
callback: app_work_sample() # This function should be manually created
help: 'Sample immediately.'

Features and Commands depends_on

Once the Project Generator is executed features.h and variants.h are generated based on the selected variant and features. This enables the addition of dependencies to any parameter or commands by including lines such as:

depends_on: defined(FEATURE_<feature_name>)`

or

depends_on: defined(VARIANT_<variant_name>)`
tip

Additionally, you can utilize logical operators && (AND) and || (OR) to incorporate multiple dependencies.

Example: In project.yaml:

parameters:
- name: float-param-name
type: float
min: -40.0
max: 5000.0
default: 125.5
help: 'Get/Set float-param'
depends_on: defined(FEATURE_HARDWARE_CHESTER_Z)
- name: int-array-name
type: array[int]
len: 4
min: 30
max: 86400
default: [31, 32, 33, 34]
help: 'Get/Set int-array-name'

In app_config.c:

const struct ctr_config_item items[] = {
#if defined(FEATURE_HARDWARE_CHESTER_Z)
CTR_CONFIG_ITEM_FLOAT("float-param-name", m_config_interim.float_param_name, -40.0f, 5000.0f, "Get/Set float-param.", 125.5f),
#endif /* defined(FEATURE_HARDWARE_CHESTER_Z) */

CTR_CONFIG_ITEM_FLOAT("int-array-name-1", m_config_interim.int_array_name[0], 30, 86400, "Get/Set int-array-name-1", 31),
CTR_CONFIG_ITEM_FLOAT("int-array-name-2", m_config_interim.int_array_name[1], 30, 86400, "Get/Set int-array-name-1", 32),
CTR_CONFIG_ITEM_FLOAT("int-array-name-3", m_config_interim.int_array_name[2], 30, 86400, "Get/Set int-array-name-1", 33),
CTR_CONFIG_ITEM_FLOAT("int-array-name-4", m_config_interim.int_array_name[3], 30, 86400, "Get/Set int-array-name-1", 34),
};

In app_config.h:

struct app_config {
#if defined(FEATURE_HARDWARE_CHESTER_Z)
float float_param_name;
#endif /* defined(FEATURE_HARDWARE_CHESTER_Z) */

int int_array_name[4];
};

Extras declaration

These extras are employed when non-default feature configurations are necessary in the prj.conf file.

When project requirements diverge from the default features configurations provided by underlying libraries or frameworks, these extras are utilized. They enable custumers to finely adjust the project's configuration to address specific needs not covered by default settings.

Example in prj.conf:

extras:
- CONFIG_ADC_TLA2021_INIT_PRIORITY=60
- CONFIG_ADC_NRFX_SAADC=n
- CONFIG_ADC_SHELL=n
- CONFIG_NEWLIB_LIBC_NANO=n

Preserved Code Blocks

In the context of this codebase, preserved code blocks are sections of code that are designated to remain unchanged during file generation or updates. These blocks are identified by special comment markers:

/* ### Preserved code "block-name" (begin) */
// Preserved code content
/* ^^^ Preserved code "block-name" (end) */
info

Any code enclosed between these markers will be preserved without modification, allowing developers to maintain custom or critical sections within generated files.

Directives clang-format

To control the behavior of clang-format within the codebase, developers can use special directives to exclude specific sections from automatic formatting:

/* ### Preserved code "block-name" (begin) */
/* clang-format off /
// Preserved code content excluded from formatting
/ clang-format on */
/* ^^^ Preserved code "block-name" (end) */

CBOR v2

The CHESTER SDK Project Generator utilizes cbor-decoder.yaml and cbor-encoder.yaml configuration files to automatically generate app_cbor.cand app_cbor.h files. These generated files are essential for handling CBOR (Concise Binary Object Representation) data encoding and decoding within the project. The generator ensures that all fields are correctly initialized and encoded based on the YAML configuration.

Generated CBOR Files

The generated app_cbor.c and app_cbor.h files include all the fields specified in the YAML configuration. The generator provides placeholder code for fields, which developers can later customize as needed. For example:

    /* ### Preserved code "thermometer" (begin) */

zcbor_uint32_put(zs, CODEC_KEY_E_THERMOMETER);
{
zcbor_map_start_encode(zs, ZCBOR_VALUE_IS_INDEFINITE_LENGTH);

zcbor_uint32_put(zs, CODEC_KEY_E_THERMOMETER__TEMPERATURE);

/* Filled in by Project Generator */
#if 0
if (isnan(g_app_data.thermometer_temperature)) {
zcbor_nil_put(zs, NULL);
} else {
zcbor_int32_put(zs, g_app_data._temperature * 100.f);
}
#else
zcbor_nil_put(zs, NULL);
#endif

zcbor_map_end_encode(zs, ZCBOR_VALUE_IS_INDEFINITE_LENGTH);
}

/* ^^^ Preserved code "thermometer" (end) */

Special Fields Handling

For certain fields such as TSO (Time Series Offset) and TSP (Time Series Period), the generator includes predefined encoding logic. These fields are crucial for handling time series data and are encoded with specific structures.

TSO - Time Series Offset

The TSO field is handled with a list encoding approach:

    /* ### Preserved code "trigger" (begin) */

zcbor_uint32_put(zs, CODEC_KEY_E_TRIGGER);
{
zcbor_map_start_encode(zs, ZCBOR_VALUE_IS_INDEFINITE_LENGTH);

zcbor_uint32_put(zs, CODEC_KEY_E_TRIGGER__EVENTS);
{
zcbor_list_start_encode(zs, ZCBOR_VALUE_IS_INDEFINITE_LENGTH);

/* Filled in by Project Generator */
/* TSO - Times series offset */
zcbor_uint64_put(zs, 1714731120); /* timestamp in seconds */

for (int j = 0; j < 3; j++) {
zcbor_uint32_put(zs, j); /* offset in seconds */
zcbor_uint32_put(zs, j * 100.f); /* samples min */
}
zcbor_list_end_encode(zs, ZCBOR_VALUE_IS_INDEFINITE_LENGTH);
}

zcbor_map_end_encode(zs, ZCBOR_VALUE_IS_INDEFINITE_LENGTH);
}

/* ^^^ Preserved code "trigger" (end) */

In this example, the TSO field includes a timestamp followed by multiple data points representing offsets and sample values.

TSP - Time Series Period

The TSP field is similarly handled with a list encoding approach:

    /* ### Preserved code "counter" (begin) */

zcbor_uint32_put(zs, CODEC_KEY_E_COUNTER);
{
zcbor_map_start_encode(zs, ZCBOR_VALUE_IS_INDEFINITE_LENGTH);

zcbor_uint32_put(zs, CODEC_KEY_E_COUNTER__MEASUREMENTS);
{
zcbor_list_start_encode(zs, ZCBOR_VALUE_IS_INDEFINITE_LENGTH);

/* Filled in by Project Generator */
/* TSP - Times series period */
zcbor_uint64_put(zs, 1714731120); /* timestamp in seconds */
zcbor_uint32_put(zs, 1); /* period in seconds */

for (int j = 0; j < 3; j++) {
zcbor_uint32_put(zs, j * 100.f); /* samples min */
zcbor_uint32_put(zs, j * 100.f); /* samples max */
zcbor_uint32_put(zs, j * 100.f); /* samples avg */
zcbor_uint32_put(zs, j * 100.f); /* samples mdn */
}

zcbor_list_end_encode(zs, ZCBOR_VALUE_IS_INDEFINITE_LENGTH);
}

zcbor_map_end_encode(zs, ZCBOR_VALUE_IS_INDEFINITE_LENGTH);
}

/* ^^^ Preserved code "counter" (end) */

In this example, the TSP field includes a timestamp, a period, and multiple data points representing sample statistics.

caution

Any addition changes to cbor-encoder.yaml or cbor-decoder.yaml can be updated in the project using the command. However, if the addition or removal is made in a previously generated field protected by a preserved code block, simply delete the protected section in the code and call the command again.

WEST Commands

CHESTER Init

west chester-init --list List all available templates.

west chester-init <name> Create the project folder and project.yaml (minimal).

west chester-init <name> --template <template-name> Create the project folder and project.yaml (specified template).

tip

If the project already exists, you can add a project.yaml into it calling the command.

info

If there is a /vendor folder in your directory, the project will be initialized in vendor/application/, otherwise it will be created in chester/application/.

CHESTER Create

west chester-create <name> Creates all necessary files to a buildable app.

caution

This command will overwrite all previous files. If you have already created a custom project, prefer to use chester-update.

CHESTER Update

west chester-update <name> Generate files based on project.yaml features.

west chester-update <name> --list List all available variants.

west chester-update <name> --variant <variant-name> Generate files based on project.yaml variant features.

west chester-update <name> --version <v.X.Y.Z> Generate files based on project.yaml using the specified fw_version.

Examples:

west chester-update clime --variant chester-clime-z

west chester-update clime --variant chester-clime-rtd --version v3.2.1

tip

After any change in project.yaml this command could be called to update the project with the new changes.

info

All commands can be executed without specifying <name> if you are already in the project folder, except for west chester-init.

CHESTER CBOR v2

west chester-cbor <name> Generate CBOR files.