Skip to content

observer_header

🧩 Observer Library (MISRA-C / Safety-Oriented Ready)

A deterministic, zero-dynamic-memory, and MISRA-C:2012 compliant implementation of the Observer Pattern in pure C β€” designed for embedded, real-time, and safety-oriented applications.

⚠️ Note: This library is not certified for ISO 26262, DO-178C, or any other formal safety standard. It provides a deterministic, statically-allocated foundation and demonstrates best practices for safety-oriented software design. Formal certification requires additional safety analysis, risk assessment, and documentation.


GitHub License GitHub top language GitHub Release GitHub branch check runs CI Pipeline MISRA

🌐 Online Documentation

Looking for a better browsing experience?

πŸ“˜ Explore the full documentation at:
🌐 https://niwciu.github.io/OBSERVER
Includes Doxygen docs, examples, coverage and analysis reports.

Additional reports and analysis: - πŸ“Š GCOVR Report (Code Coverage)
- πŸ“ˆ Code Complexity Report

πŸ’‘ Recommended: The web version provides full navigation, diagrams, and integration examples with better readability.


πŸš€ Key Features

This library provides a deterministic, static-memory foundation for the Observer pattern, suitable for safety-oriented embedded systems. Its key characteristics include:

  • βœ… MISRA-C:2012 compliant – verified via Cppcheck and PC-lint
  • βœ… Zero dynamic memory allocation – all subscription tables are statically allocated
  • βœ… Deterministic execution – all operations bounded by compile-time table sizes
  • βœ… Externally synchronized thread-safety – safe if caller manages access via mutex or critical section
  • βœ… Designed for safety-oriented projects – demonstrates best practices in modularity, static memory, and defensive programming
  • βœ… Defensive argument validation – all public API functions validate input parameters
  • βœ… Full unit test coverage – 100% statement coverage verified with Unity and gcovr
  • βœ… Support for static analysis and complexity checks – integrates with Cppcheck, Lizard, clang-format, and Doxygen for maintainability

⚠️ Note: While this library demonstrates safety-oriented design practices, it is not formally certified. Formal certification requires additional safety analysis, documentation, and risk assessment.


🧠 Observer Design Pattern – Concept Overview

The Observer Pattern defines a one-to-many dependency between objects so that when one object (the Publisher) changes state, all its dependent objects (the Observers) are notified automatically.

In embedded and safety-oriented systems, this pattern enables deterministic event propagation without dynamic allocation or runtime discovery.

🧩 Conceptual Flow


flowchart TD
    A[Publisher / Event Source]
    B[Observer #1]
    C[Observer #2]
    D[Observer #3]

    %% Subscriptions
    B -->|subscribe| A
    C -->|subscribe| A
    D -->|subscribe| A

    %% Notifications
    A -->|notify| B
    A -->|notify| C
    A -->|notify| D

    style A fill:#444444,stroke:#ffffff,stroke-width:2px,color:#ffffff
    style B fill:#666666,stroke:#ffffff,stroke-width:1px,color:#ffffff
    style C fill:#666666,stroke:#ffffff,stroke-width:1px,color:#ffffff
    style D fill:#666666,stroke:#ffffff,stroke-width:1px,color:#ffffff

πŸ’‘ How it works: Observers register their callbacks in a static subscription table owned by the Publisher.
When an event occurs, the Publisher calls notify() deterministically.
No dynamic memory or recursion is used β€” all behavior is bounded at compile time.


🧩 Integration & Observer Library Examples

The library provides ready-to-run integration examples in the examples directory.
These demonstrate:

  • How to integrate the Observer library into an application.
  • Realistic usage patterns with mocked embedded hardware modules (e.g., pushbuttons, LEDs, sensors).
  • Deterministic behavior using statically allocated observer tables and MISRA-C:2012 compliant design.

πŸ’‘ Each example includes block diagrams and commented code explaining module interactions, subscription flow, and event notifications.

πŸ“˜ Available Examples

Example Description Folder
Basic Observer Simple callback example using pushbutton, LED and LCD mocks πŸ”— Open basic_observer
State Observer Observer with event_state_e argument demonstrating ENTER/EXIT states πŸ”— Open state_observer
Sensor Observer Observer with uint8_t argument demonstrating periodic sensor updates πŸ”— Open observer_u8

πŸ’‘ Each example includes block diagrams and commented code explaining module interactions, subscription flow, and event notifications.
⚠️ All examples demonstrate deterministic behavior using statically allocated tables and are not certified for safety-critical use.
ℹ️ See examples/README.md for detailed explanations and flow diagrams.


πŸ“ Directory Layout

/observer_lib/
β”œβ”€β”€ .github/   
β”‚   └── workflows/                      # Github Actions workflows                            
β”œβ”€β”€ docs/                               # Files required for deploy library webpage & publish documentation
β”œβ”€β”€ examples/
β”‚   β”œβ”€β”€ basic_observer/                 # Basic no-arg callback
β”‚   β”œβ”€β”€ state_observer/                 # Uses event_state_e argument callback
β”‚   β”œβ”€β”€ observer_u8/                    # Uses uint8_t argument callback
β”‚   └── README.md                       # Examples description file
β”‚
β”œβ”€β”€ hw/                                 # Specific hardware configurations
β”œβ”€β”€ lib/
β”‚   └── observer/
β”‚       β”œβ”€β”€ observer.c                  # Core implementation
β”‚       β”œβ”€β”€ observer.h                  # Public API
β”‚       └── observer_public_types.h     # Enums & callback typedefs
β”œβ”€β”€ test/
β”‚   β”œβ”€β”€ config_scripts/
β”‚   β”‚   β”œβ”€β”€ run_targets               
β”‚   β”‚   β”‚   └── CI.py                   # CI python script to run on local machine
β”‚   β”‚   β”‚   └── config.yaml             # CI config file for setup and customize CI workflow    
β”‚   β”‚   └── venv_setup                   
β”‚   β”‚       └── requirements.txt        # Python tools required by scripts in project
β”‚   β”‚       └── venv_setup.py           # Python script for setting up venv and install dependencies        
β”‚   β”œβ”€β”€ observer/                       # Unit tests (Unity)
β”‚   β”œβ”€β”€ template/                       # Module unit tests template
β”‚   └── unity/                          # Test framework
β”œβ”€β”€ .clang-format                       # clang-format rules
β”œβ”€β”€ LICENSE                             
β”œβ”€β”€ mkdocs.yml                          # MkDocs deploy settings
└── README.md

⚠️ Note: All examples are deterministic, statically allocated, and intended for demonstration purposes; they are not certified for safety-critical use.


πŸ”§ Core Concepts

Role Responsibility
Publisher Maintains a static table of observer callbacks and notifies them upon events.
Observer Registers a callback function to receive event notifications.
Event A deterministic trigger (button press, state change, sensor update).

βš™οΈ Why This Implementation Is Different

Unlike typical dynamic implementations, this library is:

  • Zero-dynamic-memory β€” all subscription tables are statically allocated.
  • MISRA-C:2012 compliant β€” validated through Cppcheck and PC-lint.
  • Deterministic β€” every operation bounded at compile time.
  • Safety-oriented β€” promotes traceability and modular isolation of side effects.

πŸ’‘ This implementation bridges the conceptual simplicity of the Observer Pattern with the rigorous requirements of safety-critical embedded software.

For detailed integration examples, see 🧩 Observer Library Examples.


βš™οΈ Public API Reference

1️⃣ Callbacks Without Arguments

subscribe()

subscr_status_e subscribe(observer_cb_t *subscription_table,
                          observer_cb_t cb_2_register,
                          uint8_t subscription_table_size);

Registers a callback function. Returns one of:

  • OBSERVER_OK β€” callback registered or already present
  • OBSERVER_INVALID_ARGUMENT_ERROR β€” invalid pointer or zero size
  • OBSERVER_TABLE_FULL_ERROR β€” no free slot available

unsubscribe()

subscr_status_e unsubscribe(observer_cb_t *subscription_table,
                            observer_cb_t cb_2_register,
                            uint8_t subscription_table_size);

Removes a callback and compacts the table. Returns:

  • OBSERVER_OK
  • OBSERVER_TABLE_EMPTY_ERROR
  • OBSERVER_INVALID_ARGUMENT_ERROR

notify()

subscr_status_e notify(observer_cb_t *subscription_table,
                       uint8_t subscription_table_size);

Invokes all registered callbacks sequentially. Returns:

  • OBSERVER_OK β€” at least one callback executed
  • OBSERVER_TABLE_EMPTY_ERROR β€” no callbacks registered
  • OBSERVER_INVALID_ARGUMENT_ERROR

2️⃣ Callbacks with event_state_e Argument

subscribe_state_change()

subscr_status_e subscribe_state_change(observer_cb_state_t *subscription_table,
                                       observer_cb_state_t cb_2_register,
                                       uint8_t subscription_table_size);

unsubscribe_state_change()

subscr_status_e unsubscribe_state_change(observer_cb_state_t *subscription_table,
                                         observer_cb_state_t cb_2_register,
                                         uint8_t subscription_table_size);

notify_state_change()

subscr_status_e notify_state_change(observer_cb_state_t *subscription_table,
                                    uint8_t subscription_table_size,
                                    event_state_e state);

3️⃣ Callbacks with uint8_t Argument

subscribe_u8()

subscr_status_e subscribe_u8(observer_cb_u8_arg_t *subscription_table,
                             observer_cb_u8_arg_t cb_2_register,
                             uint8_t subscription_table_size);

unsubscribe_u8()

subscr_status_e unsubscribe_u8(observer_cb_u8_arg_t *subscription_table,
                               observer_cb_u8_arg_t cb_2_register,
                               uint8_t subscription_table_size);

notify_u8()

subscr_status_e notify_u8(observer_cb_u8_arg_t *subscription_table,
                          uint8_t subscription_table_size,
                          uint8_t data);

🧩 Return Codes (subscr_status_e)

Code Meaning
OBSERVER_OK Operation successful
OBSERVER_INVALID_ARGUMENT_ERROR Invalid pointer or table size
OBSERVER_TABLE_FULL_ERROR No free slot available
OBSERVER_TABLE_EMPTY_ERROR Table contains no valid entries

βš™οΈ Running Library Targets

πŸ” Build library

To build and run any of the predefined targets, follow this sequence of commands from the main project library location:

# Navigate to the observer test folder
cd test/observer

# Configure the build with CMake
# Use "Unix Makefiles" or "Ninja" generator
cmake -S ./ -B out -G "Unix Makefiles"
# cmake -S ./ -B out -G "Ninja"

# Enter the output folder
cd out

# Build the library targets
make all        # or use: ninja

This sequence ensures the library and example targets are properly compiled and ready for execution.

After building the library, you can run any of the predefined targets, such as:

πŸ” Run Analysis and Reports generation

Task Command
πŸ§ͺ Unit Tests make run
πŸ” Static Analysis (Cppcheck) make cppcheck
πŸ“ˆ Cyclomatic Complexity make ccm
πŸ“Š Code Coverage make ccc
πŸ“ˆ Generate Cyclomatic Complexity report make ccmr
πŸ“Š Generate Code Coverage report make ccr
✨ Format library source make format
✨ Format test code make format_test

🧰 Safety-Oriented Design Summary

This library demonstrates safety-oriented design practices suitable for embedded, real-time, and safety-critical systems.

  • βœ… Static memory only β€” no dynamic allocation
  • βœ… Deterministic execution β€” bounded by compile-time table size
  • βœ… Defensive input validation β€” all public APIs validate arguments
  • βœ… Externally synchronized thread safety β€” caller manages access if needed
  • βœ… MISRA-C:2012 compliant β€” verified via static analysis
  • βœ… Portable β€” GCC, IAR, ARMCC, GHS, etc.

⚠️ Note: This summary demonstrates safety-oriented practices. Formal certification (ISO 26262, DO-178C, or similar) requires additional safety analysis, risk assessment, and documentation.


⚠️ Safety-Critical Compliance Matrix

ID Requirement Status
SC-1 No dynamic memory βœ…
SC-2 Deterministic control flow βœ…
SC-3 Defensive input validation βœ…
SC-4 MISRA-C:2012 compliance βœ…
SC-5 Full unit test coverage (100β€―% stmt) βœ…
SC-6 Externally synchronized thread safety βœ…
SC-7 Static analysis clean βœ…

⚠️ This matrix reflects safety-oriented practices. Formal compliance requires additional safety documentation.


🧠 Notes

  • Publisher owns the static subscription table.
  • Subscribers register callbacks using the publisher’s API.
  • All operations are deterministic and zero-dynamic-memory.
  • Designed following MISRA-C:2012 and safety-oriented software design practices.

βœ… The clean modular separation and static memory usage make these examples ideal for real-time, embedded, and safety-oriented applications.
⚠️ Note: This demonstrates safety-oriented practices; the library is not certified.


🧩 Practical Examples in /examples/

For more realistic use cases β€” including mocked hardware modules such as pushbuttons, LEDs, and sensors β€” check the examples/ directory.

These examples illustrate:

  • Publisher/Subscriber interaction using hardware abstraction layer (HAL) mocks.
  • Deterministic execution under embedded-like constraints.
  • Clear modular separation, suitable for unit testing and safety certification.

πŸ’‘ Each example is fully self-contained and demonstrates real embedded-like execution flow using the same static Observer core.


πŸ“œ License

Released under the MIT License β€” see LICENSE. Β© 2025 niwciu



myEmbeddedWayBanner
Part of the myEmbeddedWay safety-oriented C library collection.