<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Royyan Dzakiy</title>
  <link rel="alternate" type="text/html" href="https://royyandzakiy.com"/>
  <link rel="self" type="application/atom+xml" href="https://royyandzakiy.com/feed/"/>
  <updated>2026-10-16T09:06:07Z</updated>
  <id>urn:uuid:060da7ea-d2f4-4736-b001-fb0e880dd589</id>

  <author>
    <name>Royyan Dzakiy</name>
    <uri>https://royyandzakiy.com/</uri>
  </author>

  <entry>
    <title>C++23 in a Blog Post: a 198-Byte WebAssembly Q15 Converter</title>
    <link rel="alternate" type="text/html" href="https://royyandzakiy.com/blog/2026-10/C-23-in-a-Blog-Post-a-198-Byte-WebAssembly-Q15-Converter/"/>
    <id>urn:uuid:73e55e2a-0314-8f5d-b327-0ff2d3dcd3a1</id>
    <updated>2026-10-12T18:38:33Z</updated>
    
    <content type="html">
      <![CDATA[<p>Type a number between -1 and 1. The conversion below runs C++23 compiled to WebAssembly, right in this page.</p>

<div class="q15-demo">
  <label>Value: <input id="q15-x" type="number" step="0.0001" value="0.3" /></label>
  <pre id="q15-out">loading…</pre>
</div>

<script type="module">
  const out = document.getElementById('q15-out');
  const input = document.getElementById('q15-x');
  const { instance: { exports: w } } =
    await WebAssembly.instantiateStreaming(fetch('/assets/demos/2026-10-cpp23-wasm-q15/q15.wasm'));
  const update = () => {
    const x = +input.value;
    const q = w.to_q15(x), back = w.from_q15(q);
    out.textContent =
      `Q15:   ${q}  (0x${w.q15_bits(q).toString(16).padStart(4, '0')})\n` +
      `back:  ${back}\n` +
      `error: ${(x - back).toExponential(3)}`;
  };
  input.oninput = update;
  update();
</script>

<h2 id="the-c">The C++</h2>

<p>TODO: walk through <code>_demos/2026-10-cpp23-wasm-q15/q15.cpp</code>: constexpr conversion, saturation, <code>static_assert</code> tests that run at compile time.</p>

<h2 id="the-build">The build</h2>

<pre class="highlight"><code>clang++ -std=c++23 --target=wasm32 -O2 -ffreestanding -nostdlib \
    -Wl,--no-entry -Wl,--strip-all -o q15.wasm q15.cpp
</code></pre>

<p>No Emscripten, no runtime, no imports: stock LLVM ships the wasm32 backend and <code>wasm-ld</code>.</p>

<h2 id="loading-it">Loading it</h2>

<p>TODO: <code>WebAssembly.instantiateStreaming</code> + calling exports, scalars only, no memory sharing yet.</p>
]]>
    </content>
  </entry>
  <entry>
    <title>Lefticus - C++ Project Template</title>
    <link rel="alternate" type="text/html" href="https://royyandzakiy.com/blog/2026-04/lefticus-cmake-project-template/"/>
    <id>urn:uuid:680e28b1-8f06-5e3a-93cd-eafac72086c7</id>
    <updated>2026-04-06T00:00:00Z</updated>
    <category term="cpp"/>
    <content type="html">
      <![CDATA[<p>I have been slowly adding more stuff for my complete <a href="https://github.com/royyandzakiy/cpp-project-template">@royyandzakiy/cpp-project-template</a>. Lately I’ve been focusing on adding things to do in the CI, and I was re-watching Jason’s video on HIS version of C++ project template.</p>

<div class="video-container">
  <iframe src="https://www.youtube.com/embed/YbgH7yat-Jo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="">
  </iframe>
</div>

<p>Based on the video, here is a list of all the tools, libraries, and features that are used, added to, or used along with the cpp_starter_project:</p>

<p>Build &amp; Configuration Tools:</p>

<ul>
  <li>CMake</li>
  <li>CTest</li>
  <li>Conan (package manager)</li>
  <li>CCache</li>
</ul>

<p>Code Quality &amp; Formatting Tools:</p>

<ul>
  <li>clang-format</li>
  <li>cmake-format</li>
  <li>clang-tidy</li>
  <li>CPPCheck</li>
</ul>

<p>Compilers &amp; Sanitizers:</p>

<ul>
  <li>GCC / Clang / MSVC support</li>
  <li>Address Sanitizer</li>
  <li>Undefined Behavior Sanitizer</li>
  <li>Thread Sanitizer</li>
  <li>Fuzzing Sanitizer (libFuzzer from Clang/LLVM)</li>
</ul>

<p>Libraries (managed via Conan):</p>

<ul>
  <li>Catch2 (testing framework)</li>
  <li>docopt (command-line option parsing)</li>
  <li>fmt (formatting library, now partly in C++20)</li>
  <li>spdlog (fast logging)</li>
  <li>imgui-sfml (for IMGUI example)</li>
  <li>SFML (pulled in by imgui-sfml)</li>
  <li>SDL (for SDL example)</li>
</ul>

<p>Documentation:</p>

<ul>
  <li>Doxygen</li>
</ul>

<p>Example GUI Libraries Shown:</p>

<ul>
  <li>Qt</li>
  <li>FLTK</li>
  <li>GTKMM</li>
  <li>IMGUI</li>
  <li>NANA</li>
  <li>SDL</li>
</ul>

<p>Editors/IDEs Mentioned:</p>

<ul>
  <li>Neovim (with Neoformat plugin)</li>
</ul>

<p>Other Features:</p>

<ul>
  <li>Inter-procedural optimization (IPO) / Link Time Optimization (LTO)</li>
  <li>Precompiled headers (PCH)</li>
  <li>compile_commands.json generation (for clang tooling)</li>
  <li>Warnings as errors (-Werror / /WX)</li>
</ul>
]]>
    </content>
  </entry>
  <entry>
    <title>Zephyr App Event Manager (app_event_manager)</title>
    <link rel="alternate" type="text/html" href="https://royyandzakiy.com/blog/2026-04/app-event-manager-nrf-connect/"/>
    <id>urn:uuid:b19bb33c-a44d-5a0a-b6a5-5cd6c8cdf4e8</id>
    <updated>2026-04-06T00:00:00Z</updated>
    <category term="zephyr"/><category term="embedded"/>
    <content type="html">
      <![CDATA[<p>Another design of event-driven architecture is the <strong>AEM (app_event_manager)</strong> in Zephyr. This subsystem/framework enforces its users to structure their code in modules. Each module connected to the event-driven backend will need to explicitly subscribe/listen to another module to get notified of any events happening.</p>

<p>To me, this architecture style really helps when I need to isolate a particular module to debug or test easily. I could simply remove it from the CMakeLists.txt, and logically, the tight coupling design will not affect other modules.</p>

<p><a href="https://docs.nordicsemi.com/bundle/ncs-3.0.2/page/nrf/libraries/others/app_event_manager.html">https://docs.nordicsemi.com/bundle/ncs-3.0.2/page/nrf/libraries/others/app_event_manager.html</a></p>

<p>Here’s a small snippet of how it looks in practice:</p>

<pre class="highlight"><code><span class="c">/* --- Event Definition --- */</span>
APP_EVENT_TYPE_DEFINE(sensor_data_event,
		  <span class="k">NULL</span>,
		  <span class="k">NULL</span>,
		  APP_EVENT_FLAGS_CREATE(
			APP_EVENT_TYPE_FLAG_INIT_LOG_ENABLE
		  ));

<span class="c">/* --- Module A: Publisher --- */</span>
<span class="k">static</span> <span class="t">void</span> <span class="f">publish_sensor_data</span>(<span class="t">int32_t</span> value) {
	<span class="k">struct</span> <span class="t">sensor_data_event</span> *evt = new_sensor_data_event();
	evt-&gt;value = value;
	APP_EVENT_SUBMIT(evt);
}

<span class="c">/* --- Module B: Subscriber --- */</span>
<span class="k">static</span> <span class="t">bool</span> <span class="f">app_event_handler</span>(<span class="k">const</span> app_event_header *aeh) {
	<span class="k">if</span> (is_sensor_data_event(aeh)) {
		<span class="k">struct</span> <span class="t">sensor_data_event</span> *evt = cast_sensor_data_event(aeh);
		LOG_INF(<span class="s">"Received sensor data: %d"</span>, evt-&gt;value);
		<span class="k">return</span> <span class="k">true</span>;
	}
	<span class="k">return</span> <span class="k">false</span>;
}

APP_EVENT_LISTENER_DEFINE(sensor_event_listener, app_event_handler);
APP_EVENT_SUBSCRIBE(sensor_event_listener, <span class="t">sensor_data_event</span>);
</code></pre>

<p>With this approach, if I want to test Module B in isolation, I can simply remove Module A from the build, and the app still runs, it simply stops sending events. Really nice for unit testing and incremental development.</p>
]]>
    </content>
  </entry>
  <entry>
    <title>Zephyr Modern C++ - State Machine Size Comparison</title>
    <link rel="alternate" type="text/html" href="https://royyandzakiy.com/blog/2026-01/zephyr-cpp-vs-c-size/"/>
    <id>urn:uuid:0d1c8e4a-824d-5071-81bd-ab33d7526662</id>
    <updated>2026-01-12T00:00:00Z</updated>
    <category term="zephyr"/><category term="cpp"/><category term="statemachine"/>
    <content type="html">
      <![CDATA[<p>Can we use the latest C++ features on microcontrollers? <strong>Yes.</strong></p>

<p>I’ve been testing out to use it on Zephyr and ESP-IDF.</p>

<p>One particular feature I tried out in this example is the use of <code>std::variant</code> for different States, and ultimately using <code>std::visit</code> to iterate through them. Here I provide implementation of a general <code>get_state_info</code> to read what the current state is using the Overloaded Lambda idiom (to enforce availability of all StateVariants), it will then return a <code>std::string_view</code> of different messages based on the <code>current_state_</code>.</p>

<p>Let’s say, a more shiny StateMachine haha. A different approach than using bare enums and switch statements (not simply saying they are less).</p>

<p>Check it out!
<a href="https://github.com/royyandzakiy/zephyr-modern-cpp">https://github.com/royyandzakiy/zephyr-modern-cpp</a></p>

<!--more-->

<p>So far, there is a <strong>540 bytes difference</strong> when using <code>std::variant</code>/<code>std::visit</code>, compared to raw enum/switch.</p>

<p><strong>First build (std::variant/visit):</strong></p>
<ul>
  <li>FLASH: 73,112 bytes (6.97% of 1MB)</li>
  <li>RAM: 53,928 bytes (11.76% of 448KB)</li>
</ul>

<p><strong>Second build (enum/switch)</strong> (code depicted in image below):</p>
<ul>
  <li>FLASH: 72,572 bytes (6.92% of 1MB)</li>
  <li>RAM: 53,928 bytes (11.76% of 448KB)</li>
</ul>

<p>I also tried comparing when adding the C++ library to the compilation, and removing it. There is a <strong>4256 byte addition</strong>.</p>

<p><strong>Third build</strong> (converting all C++ to pure C &amp; but still compiling C++ library in kconfig):</p>
<ul>
  <li>FLASH: 71,588 bytes (6.83% of 1MB)</li>
  <li>RAM: 53,952 bytes (11.76% of 448KB)</li>
</ul>

<p><strong>Fourth build</strong> (converting all C++ to pure C &amp; removing C++ library in kconfig):</p>
<ul>
  <li>FLASH: 67,332 bytes (6.42% of 1MB)</li>
  <li>RAM: 53,840 bytes (11.74% of 448KB)</li>
</ul>

<p><strong>State machine using std::variant, std::visit</strong></p>

<p><img src="/assets/images/zephyr-cpp-vs-c-size_variant.png" alt="alt text" /></p>

<p><strong>State machine using enum &amp; switch</strong></p>

<p><img src="/assets/images/zephyr-cpp-vs-c-size_switch.png" alt="alt text" /></p>

<p><strong>Removing most all CPP code, change into C</strong></p>

<p><img src="/assets/images/zephyr-cpp-vs-c-size_c_cpp.png" alt="alt text" /></p>

<p><strong>Removing CPP lib in prj.conf</strong></p>

<p><img src="/assets/images/zephyr-cpp-vs-c-size_pure_c.png" alt="alt text" /></p>

<p><strong>Code using enum &amp; switch</strong></p>

<p><img src="/assets/images/zephyr-cpp-vs-c-size_recode_pure_c.png" alt="alt text" /></p>

<hr />

<p>The variant doesn’t add too much overhead. I was expecting a lot more bytes eaten up just by using Templates itself 😄</p>

<p>cc Srdjan Stokic</p>
]]>
    </content>
  </entry>
  <entry>
    <title>Compiling latest Clang++</title>
    <link rel="alternate" type="text/html" href="https://royyandzakiy.com/blog/2025-12/latest-clang-compile/"/>
    <id>urn:uuid:cd803713-157d-58af-8199-105836dfe0f2</id>
    <updated>2025-12-29T00:00:00Z</updated>
    <category term="cpp"/>
    <content type="html">
      <![CDATA[<p>Just finished my 4 hr compile (with many many times failing) of the llvm-project to get the latest clang++ with custom features 😅</p>

<p>Compiled it in Linux WSL</p>

<p>Here Mike explained the steps very well through his youtube video “
Build Clang++20 (LLVM 18 and lldb) from source”</p>

<div class="video-container">
  <iframe src="https://www.youtube.com/embed/LbhGPiGI_Qs" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="">
  </iframe>
</div>

<p>And the guide in the Clang website is surprisingly good to get started
<a href="https://clang.llvm.org/get_started.html">LLVM - Getting Started: Building and Running Clang</a></p>
]]>
    </content>
  </entry>
  <entry>
    <title>ESP Rust Hello World!</title>
    <link rel="alternate" type="text/html" href="https://royyandzakiy.com/blog/2025-12/esp-rust-helloworld/"/>
    <id>urn:uuid:be8f086f-a11c-5d9c-8fe1-084a356583c7</id>
    <updated>2025-12-24T00:00:00Z</updated>
    <category term="rust"/>
    <content type="html">
      <![CDATA[<p>Compiled and ran my first esp rust (auto generated) firmware!</p>

<p>Planning to add a simple webserver API call, while learning Rust</p>

<p>Can’t wait to dive deeper!</p>

<p><img src="/assets/images/esp-rust-helloworld.png" alt="alt text" /></p>
]]>
    </content>
  </entry>
  <entry>
    <title>Zephyr Modern C++ - Sensors Concept</title>
    <link rel="alternate" type="text/html" href="https://royyandzakiy.com/blog/2025-12/zephyr-sensors-concept/"/>
    <id>urn:uuid:e597f402-7f2e-5098-beae-74797c9122b0</id>
    <updated>2025-12-23T00:00:00Z</updated>
    <category term="cpp"/>
    <content type="html">
      <![CDATA[<p>Can we use the latest c++ features on microcontrollers? I’ve been testing out to use it on Zephyr and ESP-IDF</p>

<p>Here is the continuation of the last post.</p>

<p>This time I try using Concepts to iterate through a bunch of “Sensors”. Here I use the generic function process_sensors that utilizes C++ Template Metaprogramming and uses the Ellipsis “…” operator to process through an arbritary amount of sensors (in this case, 3). The result is an expressive and elegant code (feels like I’m coding in javascript with the Spread Operator haha).</p>

<p>The measurements array stores results of read() of each sensors, invoked each time this process_sensors gets called. Fun fact, here the std::array automatically deduces it’s own type and size because of Class Template Argument Deduction (CTAD) enforced by the SensorLike concept (no need to define std::array&lt;type, size&gt;).</p>

<p>Is it the most efficient in the case when we need to optimize for firmware size, probably not. Is it fun? Yesh</p>

<p>Check it out!</p>

<p><a href="https://github.com/royyandzakiy/zephyr-modern-cpp">github.com/royyandzakiy/zephyr-modern-cpp</a></p>

<pre class="highlight"><code><span class="c">// --- Concepts &amp; Constraints ---</span>
<span class="k">template</span> &lt;<span class="k">typename</span> <span class="t">T</span>&gt;
<span class="k">concept</span> <span class="t">SensorLike</span> = <span class="k">requires</span>(<span class="t">T</span> t) {
	{ t.read() } -&gt; std::convertible_to&lt;<span class="t">int32_t</span>&gt;;
	{ t.get_id() } -&gt; std::convertible_to&lt;<span class="t">int</span>&gt;;
};

<span class="c">// --- Sensor Concepts Implementation ---</span>
<span class="k">class</span> <span class="t">TemperatureSensor</span> {
  <span class="k">public</span>:
	<span class="k">auto</span> read() <span class="k">const</span> -&gt; <span class="t">int32_t</span> {
		<span class="k">return</span> <span class="m">2350</span> + (sys_rand32_get() % <span class="m">100</span>); <span class="c">// 23.50 – 24.49</span>
	}
	<span class="k">auto</span> get_id() <span class="k">const</span> -&gt; <span class="t">int</span> {
		<span class="k">return</span> <span class="m">1</span>;
	}
};

<span class="k">class</span> <span class="t">HumiditySensor</span> {
  <span class="k">public</span>:
	<span class="k">auto</span> read() <span class="k">const</span> -&gt; <span class="t">int32_t</span> {
		<span class="k">return</span> <span class="m">4500</span> + (sys_rand32_get() % <span class="m">200</span>); <span class="c">// 45.00 – 46.99</span>
	}
	<span class="k">auto</span> get_id() <span class="k">const</span> -&gt; <span class="t">int</span> {
		<span class="k">return</span> <span class="m">2</span>;
	}
};

<span class="k">class</span> <span class="t">PressureSensor</span> {
  <span class="k">public</span>:
	<span class="k">auto</span> read() <span class="k">const</span> -&gt; <span class="t">int32_t</span> {
		<span class="k">return</span> <span class="m">101325</span> + (sys_rand32_get() % <span class="m">500</span>); <span class="c">// 1013.25+</span>
	}
	<span class="k">auto</span> get_id() <span class="k">const</span> -&gt; <span class="t">int</span> {
		<span class="k">return</span> <span class="m">3</span>;
	}
};

<span class="k">class</span> <span class="t">StateMachine</span> {
    <span class="c">// ...</span>
    <span class="k">template</span> &lt;<span class="t">SensorLike</span>... <span class="t">Sensors</span>&gt; <span class="k">auto</span> process_sensors(<span class="t">Sensors</span> &amp;&amp;...sensors) -&gt; <span class="t">void</span> {
		<span class="k">const</span> std::array measurements{sensors.read()...};
		<span class="t">int</span> temperatureSensorIdx = <span class="m">0</span>;

		<span class="c">// all sensor logic</span>
		{
			<span class="t">int</span> sensorIdx = <span class="m">0</span>;
			(
				[&amp;]&lt;<span class="k">typename</span> <span class="t">T</span>&gt;(<span class="t">T</span> &amp;sensor) {
					<span class="k">const</span> <span class="t">char</span> *label = <span class="s">"Unknown"</span>;
					<span class="k">if</span> <span class="k">constexpr</span> (std::is_same_v&lt;<span class="t">T</span>, <span class="t">TemperatureSensor</span>&gt;) {
						label = <span class="s">"Temp"</span>;
						temperatureSensorIdx = sensorIdx;
					}
					<span class="k">if</span> <span class="k">constexpr</span> (std::is_same_v&lt;<span class="t">T</span>, <span class="t">HumiditySensor</span>&gt;)
						label = <span class="s">"Humid"</span>;
					<span class="k">if</span> <span class="k">constexpr</span> (std::is_same_v&lt;<span class="t">T</span>, <span class="t">PressureSensor</span>&gt;)
						label = <span class="s">"Press"</span>;

					LOG_INF(<span class="s">"Sensor: %s | Value: %d"</span>, label, measurements[sensorIdx++]);
				}(sensors),
				...);
		}

		<span class="c">// TemperatureSensor value will cause system State change</span>
		{
			<span class="k">const</span> <span class="t">int32_t</span> latest_temp = measurements[<span class="m">0</span>];
			<span class="k">const</span> <span class="t">size_t</span> stored_count = std::min(buffer_index_, temp_data_circbuf_.size());

			<span class="c">// Update circular buffer</span>
			temp_data_circbuf_[buffer_index_ % temp_data_circbuf_.size()] = latest_temp;
			buffer_index_++;

			std::visit(
				[<span class="k">this</span>, latest_temp, stored_count]&lt;<span class="k">typename</span> <span class="t">Traw</span>&gt;(<span class="t">Traw</span> &amp;state) {
					<span class="k">using</span> <span class="t">T</span> = std::<span class="t">decay_t</span>&lt;<span class="t">Traw</span>&gt;; <span class="c">// cleanup any const/refs</span>

					<span class="c">// this way, enforcement to ensure all Variant types are covered by the compiler becomes optional</span>
					<span class="c">// (else, via static_assert). if I ommit the else, we can have a non-exhaustive "state machine"</span>
					<span class="k">if</span> <span class="k">constexpr</span> (std::is_same_v&lt;<span class="t">T</span>, <span class="t">IdleState</span>&gt;) {
						<span class="k">if</span> (latest_temp &gt; TEMP_THRESHOLD_START_MONITORING) {
							transition_to(<span class="t">MonitoringState</span>{latest_temp, <span class="m">1</span>});
						}
					} <span class="k">else</span> <span class="k">if</span> <span class="k">constexpr</span> (std::is_same_v&lt;<span class="t">T</span>, <span class="t">MonitoringState</span>&gt;) {
						state.sample_count++;

						<span class="k">auto</span> sum =
							std::accumulate(temp_data_circbuf_.begin(), temp_data_circbuf_.begin() + stored_count, <span class="m">0LL</span>);

						state.average_temp_value = <span class="k">static_cast</span>&lt;<span class="t">int32_t</span>&gt;(sum / stored_count);

						<span class="k">if</span> (state.average_temp_value &gt; TEMP_THRESHOLD_ALERT) {
							transition_to(<span class="t">AlertState</span>{<span class="s">"Temperature High"</span>sv, TEMP_THRESHOLD_ALERT});
						}
					} <span class="k">else</span> <span class="k">if</span> <span class="k">constexpr</span> (std::is_same_v&lt;<span class="t">T</span>, <span class="t">AlertState</span>&gt;) {
						<span class="k">if</span> (latest_temp &lt; TEMP_THRESHOLD_RECOVERY) {
							transition_to(<span class="t">CalibratingState</span>{CALIBRATION_REFERENCE_TEMP, <span class="m">1</span>});
						}
					} <span class="k">else</span> <span class="k">if</span> <span class="k">constexpr</span> (std::is_same_v&lt;<span class="t">T</span>, <span class="t">CalibratingState</span>&gt;) {
						<span class="k">if</span> (++state.calibration_step &gt; MAX_CALIBRATION_STEPS) {
							transition_to(<span class="t">IdleState</span>{});
						}
					} <span class="k">else</span> <span class="k">if</span> <span class="k">constexpr</span> (std::is_same_v&lt;<span class="t">T</span>, std::monostate&gt;) {
						transition_to(<span class="t">IdleState</span>{});
					} <span class="k">else</span> {
						<span class="c">// C++23: If a new state is added to StateVariant but not handled yet, force compile error</span>
						<span class="k">static_assert</span>(uncovered_type_exists&lt;<span class="t">T</span>&gt;,
									  <span class="s">"Non-exhaustive visitor: You forgot to handle a state!"</span>);
					}
				},
				current_state_);
		}
	}
};

<span class="k">auto</span> main() -&gt; <span class="t">int</span> {
    <span class="t">StateMachine</span> sm_;
	<span class="t">TemperatureSensor</span> t_;
	<span class="t">HumiditySensor</span> h_;
	<span class="t">PressureSensor</span> p_;

    sm_.process_sensors(t_, h_, p_);

    <span class="k">return</span> <span class="m">0</span>;
}
</code></pre>
]]>
    </content>
  </entry>
  <entry>
    <title>Zephyr Modern C++ - Statemachines using std::variant/std::visit</title>
    <link rel="alternate" type="text/html" href="https://royyandzakiy.com/blog/2025-12/zephyr-statemachine-variant/"/>
    <id>urn:uuid:b67eda4b-206a-5830-bd89-1371e2b08a7f</id>
    <updated>2025-12-22T00:00:00Z</updated>
    <category term="cpp"/>
    <content type="html">
      <![CDATA[<p>Can we use the latest C++ features on microcontrollers? Yes.</p>

<p>I’ve been testing out to use it on Zephyr and ESP-IDF</p>

<p>One particular feature I tried out in this example is the use of std::variant for different States, and ultimately using std::visit to iterate through them. Here I provide implementation of a general get_state_info to read what the current state is using the Overloaded Lambda idiom (to enforce availability of all StateVariants), it will then return a std::string_view of different messages based on the current_state_</p>

<p>Let’s say, a more shiny Statemachine haha. A different approach than using bare enums and switch statements (not simply saying they are less)</p>

<p>Check it out here!</p>

<p><a href="https://github.com/royyandzakiy/zephyr-modern-cpp">github.com/royyandzakiy/zephyr-modern-cpp</a></p>

<pre class="highlight"><code><span class="k">enum</span> <span class="k">class</span> <span class="t">StateId</span> {
	IDLE,
	MONITORING,
	ALERT,
	CALIBRATING
};
<span class="k">struct</span> <span class="t">IdleState</span> {};
<span class="k">struct</span> <span class="t">MonitoringState</span> {
	<span class="t">int32_t</span> average_temp_value;
	<span class="t">int</span> sample_count;
};

<span class="k">struct</span> <span class="t">AlertState</span> {
	std::string_view message;
	<span class="t">int32_t</span> threshold_temp_value;
};

<span class="k">struct</span> <span class="t">CalibratingState</span> {
	<span class="t">int32_t</span> reference_temp_value;
	<span class="t">int</span> calibration_step;
};

<span class="k">using</span> <span class="t">StateVariant</span> = std::variant&lt;std::monostate, <span class="t">IdleState</span>, <span class="t">MonitoringState</span>, <span class="t">AlertState</span>, <span class="t">CalibratingState</span>&gt;;

<span class="k">class</span> <span class="t">StateMachine</span> {
    <span class="c">// ...</span>
    <span class="k">auto</span> get_state_info() <span class="k">const</span> -&gt; std::string_view {
		<span class="k">return</span> std::visit(<span class="t">Overloaded</span>{
			[<span class="k">this</span>](<span class="k">const</span> <span class="t">IdleState</span>&amp;) -&gt; std::string_view {
				<span class="k">return</span> <span class="s">"Status: Idle - Awaiting for sensor trigger"</span>sv;
			},
			[<span class="k">this</span>](<span class="k">const</span> <span class="t">MonitoringState</span>&amp; s) -&gt; std::string_view {
				<span class="c">// psst: not using std::format because the zephyr SDK does not have #include &lt;format&gt;</span>
				<span class="t">int</span> len = snprintf(format_buffer_.data(), format_buffer_.size(), 
									<span class="s">"Status: Monitoring [Avg: %d.%02d°C | Samples: %d]"</span>,
									ipart(s.average_temp_value), fpart(s.average_temp_value), s.sample_count);
				<span class="k">return</span> { format_buffer_.data(), <span class="k">static_cast</span>&lt;<span class="t">size_t</span>&gt;(len) };
			},
			[<span class="k">this</span>](<span class="k">const</span> <span class="t">AlertState</span>&amp; s) -&gt; std::string_view {
				<span class="t">int</span> len = snprintf(format_buffer_.data(), format_buffer_.size(), 
								<span class="s">"Status: ALERT [%.*s | Threshold: %d.%02d°C]"</span>, 
								<span class="k">static_cast</span>&lt;<span class="t">int</span>&gt;(s.message.length()), s.message.data(),
								ipart(s.threshold_temp_value), fpart(s.threshold_temp_value));
				<span class="k">return</span> {format_buffer_.data(), <span class="k">static_cast</span>&lt;<span class="t">size_t</span>&gt;(len)};
			},
			[<span class="k">this</span>](<span class="k">const</span> <span class="t">CalibratingState</span>&amp; s) -&gt; std::string_view {
				<span class="t">int</span> len = snprintf(format_buffer_.data(), format_buffer_.size(), 
								<span class="s">"Status: Calibrating [Ref: %d.%02d°C | Step: %d/5]"</span>, 
								ipart(s.reference_temp_value), fpart(s.reference_temp_value), s.calibration_step);
				<span class="k">return</span> {format_buffer_.data(), <span class="k">static_cast</span>&lt;<span class="t">size_t</span>&gt;(len)};
			},
			[<span class="k">this</span>](<span class="k">const</span> <span class="k">auto</span>&amp;) -&gt; std::string_view {
				<span class="c">// default fallback</span>
				<span class="k">return</span> <span class="s">"Status: Unknown State! [Error]"</span>sv;
			}
		}, current_state_);
	}
    <span class="c">// ...</span>
};

<span class="k">auto</span> main() -&gt; <span class="t">int</span> {
    <span class="t">StateMachine</span> sm_;
	<span class="t">TemperatureSensor</span> t_;
	<span class="t">HumiditySensor</span> h_;
	<span class="t">PressureSensor</span> p_;
    
    sm_.process_sensors(t_, h_, p_);

    <span class="c">// ...</span>

    <span class="k">auto</span> info = sm_.get_state_info();

    <span class="k">return</span> <span class="m">0</span>;
}
</code></pre>
]]>
    </content>
  </entry>

</feed>
