diff --git a/README.md b/README.md index 8de3f5a..898c852 100755 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@ creators of [Mender](https://mender.io/). This project is currently under constr primary goal of enhancing the original work while integrating it more closely with Northern.tech's suite of products. +------------------------------------------------------------------------------- + +![Mender logo](https://github.com/mendersoftware/mender/raw/master/mender_logo.png) ## Project Status @@ -28,25 +31,14 @@ The decision to fork the original mender-mcu-client was made to: * **Provide Official Support**: Ensure that the project receives the necessary attention and resources from Northern.tech to meet the needs of the community and enterprise users. +## Get started -## Get Started - -Since the project is under active development, we recommend watching the repository or checking back -regularly for updates. Detailed documentation and usage instructions will be provided as the project -progresses. - - -## Get started with Zephyr OS +For using `mender-mcu` for the first time, we strongly recommend you start with our [Zephyr reference application](https://github.com/mendersoftware/mender-mcu-integration). -For using `mender-mcu` for the first time, we strongly recommend you start with our Zephyr reference -application. - -It can be found in the repository -[mender-mcu-integration](https://github.com/mendersoftware/mender-mcu-integration). The policy is -that the reference application's `main` branch will follow the latest stable branch of `mender-mcu` +The policy is that the reference application's `main` branch will follow the latest stable branch of `mender-mcu` (this repository). -In the `mender-mcu-repository` you will find: +In the `mender-mcu-integration` repository you will find: * A reference `west` workspace for Zephyr OS builds, including `CMake` and `KConfig` configurations. * Sample application for how to use `mender-mcu` APIs. * A list of boards that we have so far tested. @@ -54,6 +46,61 @@ In the `mender-mcu-repository` you will find: We recommend starting there to understand how we have designed our solution and then coming back here for more in-depth information. +### Compatibility +| Zephyr OS version | +|-------------------| +| v3.7.0 | + +### Boards +The reference board for `mender-mcu` is the [ESP32-S3-DevKitC](https://docs.zephyrproject.org/latest/boards/espressif/esp32s3_devkitc/doc/index.html). + +### Setting up a West project with `mender-mcu` as a Zephyr module +See the [Zephyr documentation](https://docs.zephyrproject.org/latest/develop/getting_started/index.html) for more information +on getting started with Zephyr. + +In order to use `mender-mcu` as a Zephyr [module](https://docs.zephyrproject.org/latest/develop/modules.html#modules-external-projects) +in a [West project](https://docs.zephyrproject.org/latest/develop/west/basics.html#west-workspace), you will +need to add `mender-mcu` to your project's west manifest: + +```yaml +manifest: + projects: + - name: mender-mcu + url: https://github.com/mendersoftware/mender-mcu + revision: main + path: modules/mender-mcu + import: true +``` +If you already have a west workspace, you can simply add `mender-mcu` with the following command +inside the workspace after adding the project to the manifest: +``` +west update +``` +If you're starting from scratch, you can initialize a new workspace based on the manifest like so: +``` +west init workspace --manifest-url https://url/to/repository-containing-manifest +cd workspace && west update +``` + +### Configuring the client +Zephyr provides [tools](https://docs.zephyrproject.org/latest/build/kconfig/menuconfig.html) for configuring +projects, which are used to set Kconfig options. These options are used by Zephyr modules to configure their behavior. + +The options for configuring `mender-mcu` are found in the menuconfig under `Modules -> mender-mcu`, +and are prefixed with `MENDER_`. + +You can use either `west build -t menuconfig` or `west build -t guiconfig` to open +the configuration menu. + +The following option **must** be set in any configuration: +* `MENDER_SERVER_TENANT_TOKEN` which is a token that identifies which tenant a device belongs to. + +The most common options to modify are: +* `MENDER_SERVER_HOST` +* `MENDER_CLIENT_UPDATE_POLL_INTERVAL` +* `MENDER_CLIENT_INVENTORY_REFRESH_INTERVAL` + +See descriptions of the options in the menuconfig for more information. ### The Mender client API @@ -74,81 +121,211 @@ always ready. * Inventory setting. There is an explicit call to set the inventory. This will update the internal inventory of the client and sent to the Mender Server in the next inventory report. -See the source code for more. - +* User Provided Keys. The client can be configured to use a provided a keypair. If set to +`NULL`, the client will auto-generate its own keypair using ECDSA. ### Update Modules API +The Update Modules API is exposed in [mender-update-module.h](include/mender-update-module.h). + Mender MCU models the Update Modules API which we have been using in the regular Mender client for years. Refer to the [Update Modules chapter in Mender Docs](https://docs.mender.io/artifact-creation/create-a-custom-update-module) for an introduction. In Mender MCU, the modules are not "executables" but rather rather a custom C function that is integrated with the client to handle each type of update. +#### `zephyr-image` Update Module + +We provide the `zephyr-image` Update Module, which implements the update process for a Zephyr OS +update integrated with MCUboot. Its [source code](core/src/mender-zephyr-image-update-module.c) +can be inspected for inspiration and a better understanding of the expected behavior of each state. + +To use the `zephyr-image` Update Module, you need a board you need a board that supports +MCUboot. The following link will filter the officially supported boards that also support MCUboot: +* [Zephyr Project supported boards with MCU boot](https://docs.zephyrproject.org/latest/gsearch.html?q=MCUboot&check_keywords=yes&area=default#gsc.tab=0&gsc.q=MCUboot&gsc.ref=more%3Aboards&gsc.sort=) + #### Update Modules State Machine As stated above, an Update Module for Mender MCU is set of customizable C functions. Concretely, -once created, all Update Modules are identified by a C struct in -[mender-zephyr-image-update-module.h](include/mender-zephyr-image-update-module.h). The -most important part of the struct is the array of callbacks, one to be called for each state. +once created, all Update Modules are identified by a C struct. The most important part of +the struct is the array of callbacks, one to be called for each state. See [the state machine workflow diagram](https://docs.mender.io/artifact-creation/create-a-custom-update-module#the-state-machine-workflow) to learn about the flow between each state. An Update Module does not need to implement all of them, only the ones that are relevant for a particular type of update. -We provide the `zephyr-image` Update Module, which implements the update process for a Zephyr OS -update integrated with MCUboot. Its [source code](core/src/mender-zephyr-image-update-module.c) -can be inspected for inspiration and a better understanding of the expected behavior of each state. - After writing the code, you need to register the Update Module into the Mender MCU. See the register update module function in [`mender-client.h`](include/mender-client.h). -## Experimental: testing the Mender MCU Client with POSIX +### Network + +See this [example](https://github.com/mendersoftware/mender-mcu-integration/blob/main/src/utils/netup.c) from mender-mcu-integration for a demo. +This implementation uses Zephyr's native networking APIs, see [Zephyr's Networking Guide](https://docs.zephyrproject.org/latest/connectivity/networking/index.html). + +The wifi ssid is set by the `MENDER_WIFI_SSID` option, +and the password is set by the `MENDER_APP_WIFI_PSK` option. -### Dependencies -- CMake -- libcurl -- cJSON +### Certificates +See this [example](https://github.com/mendersoftware/mender-mcu-integration/blob/main/src/utils/certs.c) from mender-mcu-integration for a demo. -Example for Ubuntu/Debian: +The `mender-mcu` client can take up to two certificates; typically one for the Server API calls, and +one for downloading Mender Artifacts from the Server storage. The latter is optional. + +The certificate should be in DER format. +You will need to generate an INC file from the certificate. + +Example using CMake: +```cmake +generate_inc_file_for_target(app + "path/to/certificates/Certificate1.cer" + "${ZEPHYR_BINARY_DIR}/include/generated/Cerficiate1.cer.inc" +) ``` -sudo apt install cmake libcurl4-openssl-dev libcjson-dev pkg-config +[`generate_inc_file_for_target`](https://github.com/zephyrproject-rtos/zephyr/blob/bc42004d1be40d9b5bec2d3e8c600780b644ff6e/cmake/modules/extensions.cmake#L703) +is a Zephyr CMake extension. + +The certificates should be guarded by the `CONFIG_NET_SOCKETS_SOCKOPT_TLS` option, + +Use `CONFIG_MENDER_NET_CA_CERTIFICATE_TAG_SECONDARY_ENABLED` to check if a secondary certificate is enabled. + +The API for adding the certificates is provided by Zephyr, and can be used by including +`zephyr/include/net/tls_credentials.h` + +You can add the certificate by calling `tls_credential_add` with the following arguments: +* `tag` - The tag of the certificate - configured by setting either: + * `CONFIG_MENDER_NET_CA_CERTIFICATE_TAG_PRIMARY` - The tag for the primary certificate + * `CONFIG_MENDER_NET_CA_CERTIFICATE_TAG_SECONDARY` - The tag for the secondary certificate +* `type` - The type of the certificate - should be `TLS_CREDENTIAL_CA_CERTIFICATE` +* `data` - The certificate data - should be an array of unsigned characters +* `len` - The length of the certificate data - should be the size of the array of unsigned characters + +Example: +```c +#include + +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) +static const unsigned char ca_certificate_one[] = { +#include "Certificate1.cer.inc" +}; +#endif +int add_cert(void) { + //... + ret = tls_credential_add(CONFIG_MENDER_NET_CA_CERTIFICATE_TAG_PRIMARY, TLS_CREDENTIAL_CA_CERTIFICATE, ca_certificate_one, sizeof(ca_certificate_one)); + //... +} ``` -### Building the Client -1. Configure the build: +### Starting the client +See this [example](https://github.com/mendersoftware/mender-mcu-integration/blob/main/src/main.c) from mender-mcu-integration for a demo. + +#### Initializing the client +Before intializing the client, you must set up the network connection, certificates, and an Update Module. + +You must also define two structs: + +`mender_client_config_t` with the fields defined in [mender-client.h](include/mender-client.h). +* The `device_type` can be set to `NULL`, as it is defined at build time + +and + +`mender_client_callbacks_t` with the callbacks defined in [mender-client.h](include/mender-client.h). + +See [The Mender client API ](#the-mender-client-api). + +After implementing the necessary callbacks and creating the structs, +the client can be initialized by calling `mender_client_init`, which +is defined in [mender-client.h](include/mender-client.h): + +Example: +```c +mender_client_init(&mender_client_config, &mender_client_callbacks)); ``` -cmake -C cmake/CMake_posix_defaults.txt -B build tests/smoke + +#### Registering the Update Module +As mentioned in [Update Modules API](#update-modules-api), +you must also register the Update Module that you want to use. + +Registering the default Update Module: +```c +mender_zephyr_image_register_update_module()); ``` -2. Build the client: +#### Enabling Inventory +Inventory is enabled/disabled by setting the `MENDER_CLIENT_INVENTORY` option. + +To use inventory, you will need a `mender_keystore_t` struct, which is defined in [mender-client.h](include/mender-client.h). +This struct contains the inventory of the client, and is enabled in the code by calling `mender_inventory_set`. + +Example: +```c +mender_keystore_t inventory[] = { { .name = "demo", .value = "demo" }, { .name = "foo", .value = "bar" }, { .name = NULL, .value = NULL } }; +mender_inventory_set(inventory)); ``` -cmake --build build --parallel $(nproc --all) + +#### Activating the Client +Finally, you can activate the client by calling `mender_client_activate`: +```c +mender_client_activate(); ``` -### Running the Client -You can now run and connect the client to e.g. hosted Mender: +### Building the client + +#### ESP32-S3-DevKitC + +In order to build for ESP32-S3-DevKitC, you will need an overlay file that enables WiFi: + +```conf +&wifi { + status = "okay"; +}; ``` -export MAC_ADDRESS= -export DEVICE_TYPE= -export TENANT_TOKEN= -export ARTIFACT_NAME= -./build/mender-mcu-client.elf --mac_address=$MAC_ADDRESS --device_type=$DEVICE_TYPE --tenant_token=$TENANT_TOKEN --artifact_name=$ARTIFACT_NAME +You can also add a config fragment containing the SSID and password: + +```conf +CONFIG_WIFI=y +CONFIG_MENDER_APP_WIFI_SSID="..." +CONFIG_MENDER_APP_WIFI_PSK="..." ``` -The mac address is an arbitrary identifier. You can use anything as long as it is unique for each device. +The path to the config fragment can be provided to the build command using the `EXTRA_CONF_FILE` option. -The tenant token can be found under `My organization` in hosted Mender, where it's called `Organization token`. +To build the project, you can use the following command: +``` +west build -b esp32s3_devkitc/esp32s3/procpu -- -DEXTRA_CONF_FILE= +``` -### Creating an Artifact -Create an artifact (remember to disable compression): +You can flash the project and monitor the serial line with the following commands: ``` -./mender-artifact write module-image -T zephyr-image --compression none --artifact-name --device-type --file +west flash && west espressif monitor ``` -The `device_type` in the artifact has to match the `device_type` used when running the client. + +### Creating a Mender Artifact + +NOTE: Compression of artifacts is **not** supported, which is why we use `--compression none` + +After building the client, you can create a [Mender Artifact](https://docs.mender.io/overview/artifact). + +The update module must correspond with the one registered during initialization of the client. +The device type must match the one set at build time - if you're unsure what this is, you can +check the value of `MENDER_DEVICE_TYPE` in the menuconfig. + +The Artifact Name is arbitrary, but should be unique. + +```bash +UPDATE_MODULE= +ARTIFACT_NAME= +DEVICE_TYPE= + +mender-artifact write module-image \ + --type $UPDATE_MODULE \ + --file build/zephyr/zephyr.signed.bin \ + --artifact-name $ARTIFACT_NAME \ + --device-type $DEVICE_TYPE \ + --compression none +``` ### Deployment After creating and uploading the artifact to the server, you should be able to deploy it to the device.