3. Getting started on Zephyr RTOS

emlearn works on any platform that has C99 support. Zephyr is a modern, professional, open-source RTOS. This guide shows you how to use emlearn on that platform.

3.1. Prerequisites

Ensure that you have emlearn setup as per the Getting started on PC (Linux/MacOS/Windows).

You will need to have Zephyr installed already. Follow the Zephyr Getting Started Guide. In particular you will a functional setup that includes the Zephyr SDK, and the west build tool.

You will need to have a Zephyr project setup. If you do not already have one, either use one of the samples in Zephyr as a base, or the Zephyr example application.

No particular hardware is needed. Any board supported by Zepyr should work. In the example below, a board designed for QEMU simulator is used. Support for this is included in the standard Zephyr SDK setup. But if you have gone for a minimal setup, it may need to be installed manually. The QEMU board specifically requires the arm-zephyr-eabi compiler toolchain, and the cmsis and hal_nordic Zephyr modules.

3.2. Add emlearn to your Zephyr project

emlearn provides the neccesary metadata to be used a Zephyr module (since Feburary 2024). To include the module, add an entry to the projects section of your west.yml configuration.

projects:
  #... exiting projects
  - name: emlearn
    url: https://github.com/emlearn/emlearn
    revision: master

To make sure it worked correctly, you can run

west update emlearn

This should now pull the emlearn git repository into your workspace.

3.3. Enable emlearn in your Zephyr project

Once you have the emlearn Zephyr module, you must enable it in your application. You can add this manually to your configuration file. This file is typically called prj.conf.

CONFIG_EMLEARN=y

3.4. Create model in Python

We will train a simple model to learn the XOR function. Copy and save this as file xor_train.py.

 1import emlearn
 2import numpy
 3from sklearn.ensemble import RandomForestClassifier
 4from sklearn.metrics import get_scorer
 5
 6# Generate simple dataset
 7def make_xor(lower=0.0, upper=1.0, threshold=0.5, samples=100, seed=42):
 8    rng = numpy.random.RandomState(seed)
 9    X = rng.uniform(lower, upper, size=(samples, 2))
10    y = numpy.logical_xor(X[:, 0] > threshold, X[:, 1] > threshold)
11    return X, y
12
13X, y = make_xor()
14
15# Train a model
16estimator = RandomForestClassifier(n_estimators=3, max_depth=3, max_features=2, random_state=1)
17estimator.fit(X, y)
18score = get_scorer('f1')(estimator, X, y)
19assert score > 0.90, score # verify that we learned the function
20
21# Convert model using emlearn
22path = 'xor_model.h'
23cmodel = emlearn.convert(estimator, method='inline')
24cmodel.save(file=path, name='xor_model')
25print('Wrote model to', path)

Run the script

python xor_train.py

It will generate a file xor_model.h containing the C code for our model. Copy this file into your src/ directory.

3.5. Example application code

Here is some example code for calling the XOR model on some inputs.

 1/*
 2 * emlearn example for Zephyr RTOS
 3 */
 4
 5#include <zephyr/kernel.h>
 6
 7#include "xor_model.h"
 8
 9void xor_test(void)
10{
11    const float test_inputs[4][2] = {
12        { 0.0f, 0.0f },
13        { 1.0f, 0.0f },
14        { 0.0f, 1.0f },
15        { 1.0f, 1.0f },
16    };
17
18    for (int i=0; i<4; i++) {
19        const float *features = test_inputs[i];
20        const int out = xor_model_predict(features, 2);
21
22        printf("xor(%d,%d) = %d\n", (int)features[0], (int)features[1], out);
23    }
24}
25
26int main(void)
27{
28    xor_test();
29    return 0;
30}

3.6. Build and run in simulator

We are assuming that your code is called helloworld_xor.

west build --board qemu_cortex_m3 ./helloworld_xor/ -t run

This should result in output similar to this:

...
-- west build: running target run
...
To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: cortex-m3
...
*** Booting Zephyr OS build zephyr-v3.5.0 ***
xor(0,0) = 0
xor(1,0) = 1
xor(0,1) = 1
xor(1,1) = 0