Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zephyr Moving-Average Filter

A reusable moving-average filter library for Zephyr v3.7 LTS, packaged with a demo application, Ztest unit tests, and Twister-driven CI.

Build and Test Zephyr Board License

Overview

This repository is a small but complete Zephyr project built the way a production Zephyr project is structured. The feature itself (an integer moving-average filter) is intentionally simple; the value is in the scaffolding around it:

  • A reusable, dependency-free filter library shared by both the app and the tests.
  • A sample application that builds for the host simulator and for real STM32 hardware.
  • A host-runnable Ztest unit suite driven by Twister.
  • GitHub Actions CI that runs the tests and cross-builds the firmware on every push.

It uses the T2 (star) west topology: this repo is the manifest repo and imports Zephyr as a dependency. The filter logic is kept free of Zephyr headers so it can be unit-tested on native_sim in milliseconds, with the RTOS and hardware glue confined to the sample app. The app is devicetree-aware and board-portable, driving the LED through gpio_dt_spec and DT_ALIAS(led0) so it works on any board that defines an led0 alias.

The filter library

The public API lives in include/moving_average.h and is implemented in lib/moving_average.c. It uses only <stddef.h> and <stdint.h>, so it has no Zephyr or RTOS dependency.

The filter operates on int32_t samples and keeps a running sum for O(1) updates. The window is a fixed-capacity ring buffer of up to MA_MAX_WINDOW (16) samples.

#define MA_MAX_WINDOW 16U

struct moving_average {
    int32_t samples[MA_MAX_WINDOW];
    size_t  window;   /* configured window length (<= MA_MAX_WINDOW) */
    size_t  count;    /* how many samples are currently held         */
    size_t  head;     /* index of the oldest sample                  */
    int64_t sum;      /* running sum of held samples                 */
};

/* Initialise with a window length. Clamped to [1, MA_MAX_WINDOW]. */
void    ma_init(struct moving_average *ma, size_t window);

/* Add a sample; returns the current average (rounded to nearest int32). */
int32_t ma_add(struct moving_average *ma, int32_t sample);

/* Number of samples currently held. */
size_t  ma_count(const struct moving_average *ma);

Behavioural notes, all covered by the unit tests:

  • ma_init clamps the requested window into [1, MA_MAX_WINDOW]: a window of 0 becomes 1, and any value above 16 becomes 16.
  • While the window is still filling, the average is taken over the samples held so far, so the first sample is returned as its own average.
  • Once the window is full it slides: the oldest sample is evicted and ma_count saturates at the window length.
  • ma_add returns the sum divided by the current count, rounded to the nearest integer, with correct rounding for negative averages (for example, -10 and -5 average to -8).

Typical usage:

struct moving_average ma;

ma_init(&ma, 8);                       /* 8-sample window */
int32_t filtered = ma_add(&ma, raw);   /* feed a reading, get the smoothed value */

Prerequisites

  • Zephyr v3.7 LTS (pinned to v3.7.0 in west.yml).
  • The west meta-tool.
  • The Zephyr SDK (developed against SDK 0.16.8). CI installs the arm-zephyr-eabi toolchain via the official action-zephyr-setup.

Building and running

Create a west workspace from this repository's manifest:

west init -m https://github.com/ryantra/zephyr-moving-average --mr main my-workspace
cd my-workspace
west update

Build and run the demo app on the host simulator (no hardware required):

west build -b native_sim zephyr-moving-average/app
./build/zephyr/zephyr.exe

Build and flash the demo app on a Nucleo-F411RE:

west build -b nucleo_f411re zephyr-moving-average/app
west flash

The app prints raw versus filtered readings over printk and toggles the board LED each cycle.

Tests

The Ztest suite in tests/moving_average/ runs on native_sim and needs no hardware. From the workspace root, run it with Twister:

west twister -T zephyr-moving-average/tests -p native_sim --inline-logs -v

The suite (lib.moving_average) uses ZTEST_SUITE with five ZTEST cases covering the first-sample average, averaging while the window fills, the sliding window and count saturation, negative-value rounding, and window clamping.

Continuous integration

.github/workflows/build.yml runs on every push and pull request. It uses the official zephyrproject-rtos/action-zephyr-setup action to install west, the Zephyr SDK, and the workspace from this repo's west.yml, then:

  1. Runs the unit tests on native_sim via west twister -T tests -p native_sim.
  2. Cross-builds the sample firmware with west build -b nucleo_f411re app.

Project layout

west.yml                       T2 west manifest; imports Zephyr v3.7.0 and the
                               cmsis and hal_stm32 modules
include/moving_average.h       public filter API (dependency-free)
lib/moving_average.c           filter implementation (shared by app and tests)
app/                           sample application
  src/main.c                   prints raw vs. filtered readings, toggles led0
  prj.conf, CMakeLists.txt     app configuration and build
  sample.yaml                  Twister metadata (build for native_sim + nucleo_f411re)
tests/moving_average/          Ztest unit suite
  src/main.c                   ZTEST cases
  prj.conf, CMakeLists.txt     test configuration and build
  testcase.yaml                Twister metadata (run on native_sim)
.github/workflows/build.yml    CI: Twister tests + nucleo_f411re cross-build

License

Apache-2.0. See LICENSE.

About

Zephyr v3.7 LTS app: reusable filter library with Ztest unit tests and Twister CI (native_sim + nucleo_f411re)

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages