Knowledge Test Score Board
— · 0%
Work through the questions below — your score updates as you go.
Keep going · grade bands mapped to DCU's honours scale
Only your first attempt at each question counts toward this score. Reload the page to reset.
This section contains 25 interactive knowledge checks designed to test your understanding of the materials covered in Chapter 12. The questions cover the deployment of Rust on the ESP32 platform, managing limited memory and storage resources, applying advanced compiler and code-level optimisations, and utilising design patterns specifically tailored for the constraints of the edge.
Please remember that many of the quizzes have multiple correct answers , and you must select all applicable options to succeed. I have carefully balanced the lengths of the answers, so take your time to evaluate each option thoroughly.
Good luck!
Derek.
Concept Match
Match the ESP32 Architectures derekmolloy.ie Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.
Definition Pool
A specialised toolchain installer required for Xtensa support in the Rust compiler.
The traditional dual-core architecture used in original ESP32 and ESP32-S3 models.
A modern toolkit for flashing and debugging embedded Rust applications over JTAG.
The modern, open-standard architecture used in newer models like the ESP32-C3 and C6.
Submit
Regarding the 'std' versus 'no_std' choice on the ESP32, which of the following statements are TRUE? derekmolloy.ie
The 'std' approach leverages the ESP-IDF (FreeRTOS-based) operating system to provide services like threads and networking.
Using 'std' results in larger binary sizes but provides a more familiar, high-level developer experience for complex apps.
The 'no_std' approach is mandatory for all ESP32 models, as they lack the hardware required to run a standard library.
The 'no_std' approach is preferred for 'bare-metal' projects where absolute control over memory and timing is required. Submit Answer
Why is the ESP32-C3 considered particularly 'Rust-friendly' compared to the original ESP32? derekmolloy.ie
Because it is the only model capable of running the Dioxus GUI framework in a bare-metal environment.
Because it features a built-in hardware accelerator designed specifically for the Rust borrow checker.
Because it lacks the complexity of dual-core synchronization, simplifying the initial development of no_std drivers.
Because its RISC-V architecture is natively supported by the standard upstream Rust compiler (rustc). Submit Answer
Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.
1 # To flash your compiled binary to an ESP32 connected via USB, use:
2 ····· flash --monitor
3
4 # To quickly scaffold a new bare-metal (no_std, esp-hal) project, use:
5 ····· -generate
Available Snippets
flash
cargo
espflash
create
probe-rs
esp
Submit
Concept Match
Match the Resource Constraints derekmolloy.ie Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.
Definition Pool
Persistent storage where the program code and constant data are stored; typically 4-16MB.
Volatile high-speed memory used for the stack and heap; often limited to 320-512KB.
A critical error occurring when deeply nested functions or large local variables exceed the allocated frame.
The buildup of small, unusable gaps in dynamic memory caused by frequent allocations and deallocations.
Submit
Read the code below, then choose the terminal output it produces and click Submit.
1 // A global static buffer
2 static BUFFER: [u8; 1024] = [0; 1024];
3
4 fn main() {
5 // Where does this large array live?
6 println!("Buffer size: {} bytes", BUFFER.len());
7 }
stdout
It is allocated on the stack every time main() is called.
stdout
It has a fixed place in the program's static memory (Flash or RAM).
stdout
It is dynamically allocated on the heap during system boot.
stdout
It is a virtual alias that consumes no physical memory at all. Submit Answer
Why is dynamic heap allocation (Box, Vec, String) often discouraged in safety-critical edge firmware? derekmolloy.ie
Because the Rust standard library cannot provide thread-safety for heap-allocated data structures.
Because frequent allocations lead to memory fragmentation, which can cause 'Out of Memory' crashes over time.
Because heap allocation is non-deterministic; it may take a variable amount of time or fail unexpectedly.
Because the ESP32 hardware requires all variables to be stored in the CPU registers for maximum performance. Submit Answer
Which of the following is a valid strategy for reducing stack usage in an ESP32 application? derekmolloy.ie
Moving large local buffers into static memory using the 'static' keyword or 'lazy_static' crate.
Allocating temporary working memory on the heap (if available) instead of using local arrays.
Replacing all recursive function calls with iterative loops to limit the number of active call frames.
Forcing the compiler to inline every function to eliminate the overhead of the 'call' instruction. Submit Answer
Concept Match
Match the Compiler Optimisations derekmolloy.ie Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.
Definition Pool
Controls parallel compilation; setting this to 1 allows for better global optimisation at the cost of speed.
A special optimisation flag that instructs the compiler to prioritise minimum binary size over speed.
Disables stack unwinding, significantly reducing the size of the final compiled binary.
Link-Time Optimisation; allows the compiler to optimise across different modules and crates.
Submit
Cargo.toml Release Profile derekmolloy.ie Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.
1 [profile.release]
2 # Optimise for size
3 opt-level = " ····· "
4
5 # Disable stack unwinding for smaller binaries
6 panic = " ····· "
7
8 # Perform global optimisations during linking
9 lto = " ····· "
Available Snippets
s
abort
true
3
none
fast
unwind
Submit
Regarding SIMD (Single Instruction, Multiple Data) on edge devices, which statement is true? derekmolloy.ie
Rust's 'core::simd' module provides a safe way to write architecture-independent parallel code.
SIMD allows a single CPU instruction to perform the same operation on multiple data points in parallel.
Using SIMD always reduces power consumption as it allows the CPU to finish tasks faster and sleep sooner.
SIMD is only available on high-end Nvidia Jetson modules and is not supported by any ESP32 chip. Submit Answer
What is the primary benefit of 'Profile-Guided Optimisation' (PGO)? derekmolloy.ie
It identifies and deletes all code paths that are never executed during the provided test runs.
It automatically compresses the binary's text section using a specialised LZMA algorithm.
It uses data from real execution runs to help the compiler make better branch prediction and inlining decisions.
It allows the developer to manually assign specific functions to the fastest CPU cores. Submit Answer
Concept Match
Match the Design Patterns derekmolloy.ie Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.
Definition Pool
A pattern where the system behaviour is defined by its current mode (e.g., Idle, Polling, Error).
A pattern ensuring that hardware peripherals (like an I2C bus) are properly released when no longer used.
Allows components to subscribe and react to events, such as a new sensor reading being available.
A simplified interface that hides the complexity of direct hardware register manipulation.
Submit
Implementing a Simple State Machine derekmolloy.ie Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.
1 enum DeviceState { Idle, Sampling, Error }
2
3 // start_timer() and reset() are hardware helpers defined elsewhere
4 fn update(state: DeviceState, ok: bool) -> DeviceState {
5 // Choose the next state from the current one
6 ····· state {
7 // Idle: start the sample timer and move on to sampling
8 DeviceState::Idle => { start_timer(); DeviceState:: ····· }
9 // Sampling: back to Idle if the reading was ok, otherwise flag a fault
10 DeviceState::Sampling => { if ok { DeviceState::Idle } else { DeviceState:: ····· } }
11 DeviceState::Error => { reset(); DeviceState::Idle }
12 }
13 }
Available Snippets
switch
Sampling
Fault
Ready
match
if
Error
Submit
How does the 'Newtype' pattern contribute to safety in embedded Rust drivers? derekmolloy.ie
It automatically handles the conversion between Big-Endian and Little-Endian network formats.
It provides a zero-cost abstraction to add domain-specific methods to primitive types like u32.
It forces the compiler to store the wrapped value in a dedicated hardware-protected memory region.
It allows the developer to create a unique type for things like 'Celsius' versus 'Fahrenheit', preventing accidental mixing. Submit Answer
Why is the 'Zero-Sized Type' (ZST) pattern often used in no_std Rust for hardware ownership? derekmolloy.ie
To represent hardware peripherals (like GPIO) as types that take up zero bytes of RAM at runtime.
To bypass the Rust borrow checker when multiple drivers need to access the same physical register.
To allow the compiler to track whether a specific hardware pin is currently 'owned' by a driver at compile time.
To enable the use of async/await on devices that lack a hardware-backed memory management unit. Submit Answer
In Zephyr, what is the primary advantage of using 'memory slabs' for allocation instead of a standard kernel heap? derekmolloy.ie
Slabs automatically encrypt data to protect sensitive sensor readings.
They are required for running Rust code, which cannot use a standard heap.
Memory slabs allow for the dynamic resizing of objects at runtime.
They provide deterministic, fixed-size allocation that prevents heap fragmentation. Submit Answer
Which Zephyr subsystem is responsible for automatically entering low-power idle or sleep states when threads are blocked? derekmolloy.ie
The Power Management (PM) subsystem Submit Answer
Concept Match
Match Zephyr AIoT Components derekmolloy.ie Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.
Definition Pool
The underlying library used for encrypted socket communication (TLS).
Provides secure boot and support for signed firmware images.
A Zephyr module providing an inference runtime for TinyML models.
The protocol used for managing over-the-air (OTA) firmware updates.
Submit
In the Zephyr development workflow, what is the role of the 'west' meta-tool? derekmolloy.ie
It is the primary compiler for Xtensa and RISC-V architectures.
It automatically translates C++ classes into Zephyr-compatible C structs.
It manages the multi-repository source tree and wraps the CMake build system.
It is a functional simulator used to model multi-node edge networks. Submit Answer
In Rust, a channel lets one thread send values to another through a queue. How is this message-passing pattern typically realised in native Zephyr C code? derekmolloy.ie
Zephyr does not support inter-thread communication patterns.
Through the use of kernel threads paired with message queues or FIFOs.
By bypassing the RTOS and writing directly to CPU registers.
By using global variables guarded by volatile keywords. Submit Answer
Concept Match
Match the Final Concepts derekmolloy.ie Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.
Definition Pool
A hardware component that resets the system if the software fails to 'kick' it within a timeout.
An advanced pattern using the type system to enforce valid sequences of operations at compile time.
A region of code that must execute without interruption, often protected by disabling interrupts.
The property where a system produces identical output for a given input in a fixed timeframe.
Submit
Which of the following is a common cause of non-deterministic behaviour in a Rust application? derekmolloy.ie
The use of 'match' expressions with too many arms, which causes the CPU's branch predictor to fail.
Relying on a global heap allocator without a real-time-aware allocation strategy (like a TLSF allocator).
Spawning background threads on a multi-core chip without using deterministic scheduling or locking.
The use of standard 'HashMap' collections, as their iteration order is deliberately randomised for security. Submit Answer
How does Rust's ownership system naturally support the 'Resource Acquisition Is Initialisation' (RAII) pattern? derekmolloy.ie
By requiring all hardware peripherals to be initialised within the main() function's global scope.
By automatically calling the Drop trait's method as soon as a variable goes out of scope, releasing its resources.
By preventing the program from compiling if a hardware resource is not used within 100 lines of its acquisition.
By ensuring that only one part of the code can 'own' and therefore manipulate a specific hardware resource at a time. Submit Answer
What is the ultimate goal of combining Rust's safety features with rigorous resource management on the edge? derekmolloy.ie
To ensure that edge nodes can be updated remotely without any risk of bricking the device due to memory errors.
To enable high-fidelity user interactions and reliable data processing on low-cost, low-power hardware.
To produce software that is 'correct by construction', minimising the requirement for expensive field debugging.
To eliminate the need for hardware-level watchdogs by guaranteeing that software can never crash. Submit Answer
Congratulations on completing the Chapter 12 Knowledge Test and the final module of the book!
You have now explored the full lifecycle of edge programming: from the fundamentals of hardware and C++, through the robust safety of Rust, to advanced networking, concurrency, and embedded optimisation. Use these principles to build the next generation of reliable, secure, and intelligent smart nodes.
Derek.