<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[The Embedded Rustacean Blog]]></title><description><![CDATA[Streamlining the Embedded Rust Learning Curve 🦀


For collaboration: https://www.passionfroot.me/theembeddedrustacean]]></description><link>https://blog.theembeddedrustacean.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1768831157127/472ef434-394e-43a5-9438-4b2eab2ce78b.png</url><title>The Embedded Rustacean Blog</title><link>https://blog.theembeddedrustacean.com</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 12 Apr 2026 02:04:57 GMT</lastBuildDate><atom:link href="https://blog.theembeddedrustacean.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Embedded Rust Education: A Look Forward to 2026]]></title><description><![CDATA[Introduction
Another year comes to an end; as always, it is an opportunity to reflect on the achievements of the past year and look forward to the next. It's been over 3.5 years since I embarked on my embedded Rust journey, and it only gets more exci...]]></description><link>https://blog.theembeddedrustacean.com/embedded-rust-education-a-look-forward-to-2026</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embedded-rust-education-a-look-forward-to-2026</guid><category><![CDATA[Rust]]></category><category><![CDATA[rust lang]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[embedded]]></category><category><![CDATA[iot]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Wed, 31 Dec 2025 10:18:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767100717693/6849b9dd-e67d-4e94-ac0d-59b59d9ffe5c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Another year comes to an end; as always, it is an opportunity to reflect on the achievements of the past year and look forward to the next. It's been over 3.5 years since I embarked on my embedded Rust journey, and it only gets more exciting. This post will be a round-up of some of the main milestones in 2025 and what is planned for 2026.</p>
<blockquote>
<p>I am always grateful for all the feedback and notes I keep receiving from the embedded &amp; Rust community. If you have anything to add at any time please feel free to reach out on any social account or just email <a target="_blank" href="mailto:hi@theembeddedrustacean.com">hi@theembeddedrustacean.com</a>.</p>
</blockquote>
<h2 id="heading-2025-reflections">2025 Reflections 🪞</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://tenor.com/view/starry-night-van-gogh-peace-water-reflection-gif-15646447">https://tenor.com/view/starry-night-van-gogh-peace-water-reflection-gif-15646447</a></div>
<p> </p>
<p>The goal remains to address more gaps in the embedded Rust educational ecosystem. A big part of it is attracting first-time embedded, curious users who are into Rust. More parts of the ecosystem are becoming stable, but there are still many moving parts. Here are some of the milestones in 2025:</p>
<ul>
<li><p>⬆️ <strong>Simplified Embedded Rust Updates</strong>: Both the <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-no-std">core library edition</a> and <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-std">standard library edition</a> of <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-std">Simplified Embedded Rust</a> were updated twice in 2025. The changes accommodated Wokwi changes and the release of the no-std esp-hal 1.0.0, among others. The most recent update introduced a whole new project chapter that mimics a real-world product. Additionally, in May, <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-stm32">the STM32 Blog Collection Edition</a> was added to the book series. The new edition was created to collect all the STM32 blog posts from <a target="_blank" href="https://blog.theembeddedrustacean.com/"><em>The Embedded Rustacean Blog</em></a> in a single resource.</p>
</li>
<li><p>💵 <strong>Simplified Embedded Rust Pricing Model Update</strong>: Now that the <code>esp-hal</code> project is becoming more stable, the updates for the book are going to be less frequent, which warrants a change. As such, the subscription model was discontinued in September. The Simplified Embedded Rust adopted a pay once, receive updates forever model. This excludes the STM32 Blog Collection Edition, which can be obtained for free by subscribing to and sharing <a target="_blank" href="https://www.theembeddedrustacean.com/subscribe">The Embedded Rustacean Newsletter</a>.</p>
</li>
<li><p>📚 <strong>900+ Copies Sold:</strong> Since its launch in May 2024, <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-std">Simplified Embedded Rust</a> has sold over 900 copies in digital and print formats. This is around a 500-copy increase from the end of 2024.</p>
</li>
<li><p>🗞️ <strong>61 Newsletter Editions:</strong> <a target="_blank" href="https://www.theembeddedrustacean.com/subscribe">The Embedded Rustacean</a> newsletter was launched in 2023 as a source for everything embedded Rust and is still going strong. While 2023 ended with nine editions, 2024 ended with 35, and now the newsletter is closing 2025 with 61 editions. The newsletter continued to operate twice a month throughout 2025.</p>
</li>
<li><p>🤓 <strong>5k+ Subscribers:</strong> The Embedded Rustacean Newsletter’s growth has been phenomenal. We ended 2024 with a little over 3k subscribers and now end 2025 with more than 5k. Maintaining the newsletter has also become much more challenging than the previous year. With the emergence of AI and LLMs, the internet has been flooded with technically inaccurate content, making it more time-consuming to filter through. The Embedded Rustacean thankfully maintained its high engagement rates, positioning itself as a trusted source.</p>
</li>
<li><p>🦀 <strong>uFerris Hardware Announcement:</strong> I've probably mentioned in the past that many Rustaceans have reached out with interest in content about real hardware. However, I was always wondering what the best format is to deliver more content with physical hardware. As such, in September of 2025, the uFerris hardware initiative was announced to The Embedded Rustacean subscribers. uFerris is meant to serve as companion hardware for <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-std">Simplified Embedded Rust</a> and as a beginner-friendly development board that supports multiple controllers. Subscribers were polled on whether they would be interested in uFerris, and more than 70% agreed it was a good fit for them. Ever since, the effort to create the uFerris hardware has been launched. Prototype boards were manufactured and sent to early access users for evaluation. You can learn more about uFerris <a target="_blank" href="https://www.theembeddedrustacean.com/c/uferris">here</a>.</p>
</li>
</ul>
<h2 id="heading-a-look-forward-to-2026">A Look Forward to 2026 🔮</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://tenor.com/view/rdj-robert-downey-jr-hehe-can't-wait-non-vedo-l'ora-gif-7612716849186797618">https://tenor.com/view/rdj-robert-downey-jr-hehe-can't-wait-non-vedo-l'ora-gif-7612716849186797618</a></div>
<p> </p>
<h3 id="heading-whats-in-plan">What’s in Plan</h3>
<p>Just like before, I do much of this work in my spare time. As a result, to continue offering the best-quality material, I try to focus on what would benefit the community most by listening to the feedback I receive.</p>
<ul>
<li><p>📖 <strong>Simplified Embedded Rust Roadmap</strong>: While the <code>esp-hal</code> project is stabilizing, there are still a few planned updates in the book's short-term roadmap. One update involves adding support for <code>esp-radio</code> (formerly <code>esp-wifi</code>) in the <code>no-std</code> edition of the book. This ultimately depends on when <code>esp-radio</code> reaches stability, but hopefully an update will be out on or around Q2 of 2026. Another update would involve accommodating the uFerris hardware by complementing the existing book examples for uFerris.</p>
</li>
<li><p>🦀 <strong>uFerris Hardware:</strong> uFerris hardware is currently being evaluated, and another hardware iteration is planned before becoming available for pre-order. Pre-orders are expected to start around Q2 of 2026. To stay up to date with what’s happening on uFerris, make sure to subscribe to <a target="_blank" href="https://www.theembeddedrustacean.com/subscribe">The Embedded Rustacean</a>.</p>
</li>
<li><p>📖 <strong>Additional</strong> <strong>Educational Material?</strong> Beyond the material in the books, I reckon there should be additional material to help learners expand their knowledge into more advanced (or even different) concepts. This could take the form of another book, microcourses, blog posts, or similar content. The challenge stems from the ecosystem's dynamic nature, which makes it harder to maintain the material consistently. What I have in mind going forward is creating more uFerris-centric material, maybe even driving community efforts toward it. uFerris is meant to serve as a reference platform that can accommodate multiple controllers.</p>
</li>
<li><p>🌲 <strong>Expand Embedded Rust Reach &amp; Continue Growing Newsletter:</strong> The newsletter has been a core part of giving back to the community. The efforts to grow it will continue, hopefully enabling me to provide high-quality content continuously.</p>
</li>
<li><p>🏟️ <strong>Conferences:</strong> In 2024, I was fortunate to talk at the Rust in Paris conference. You can find the <a target="_blank" href="https://www.youtube.com/watch?v=UIqchWfD35I&amp;t=428s">video here</a>. There is more I have planned for 2026, but it is yet to be confirmed. Stay tuned!</p>
</li>
</ul>
<h2 id="heading-acknowledgments">Acknowledgments 🙏</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://tenor.com/view/thanks-thanks-alot-thanks-a-lot-thanks-so-much-thank-you-gif-14580681502774806591">https://tenor.com/view/thanks-thanks-alot-thanks-a-lot-thanks-so-much-thank-you-gif-14580681502774806591</a></div>
<p> </p>
<p>Finally, I would like to thank everybody who contributed to these accomplishments; without their effort, none of the above would have been possible.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>2025 has been nothing less than spectacular for embedded Rust. Embedded educational material is in a much better place now, but there is still much to catch up on. There is still a gap between the ecosystem's updates and the supporting educational material. The dynamic nature of many projects is probably the primary contributor to that gap. However, we seem to be inching toward more stability, at least in some of the more significant projects. Hopefully, 2026 is the year when many of these gaps are closed.</p>
]]></content:encoded></item><item><title><![CDATA[Embedded Rust Education: 2024 Reflections & 2025 Visions]]></title><description><![CDATA[Introduction
Another year comes to an end, as such, this is an opportunity to reflect on the achievements of 2024 and look forward to 2025. It's been over 2.5 years since I embarked on my embedded Rust journey, and it remains as exciting as ever. If ...]]></description><link>https://blog.theembeddedrustacean.com/embedded-rust-education-2024-reflections-2025-visions</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embedded-rust-education-2024-reflections-2025-visions</guid><category><![CDATA[Rust]]></category><category><![CDATA[embedded systems]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[iot]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Fri, 20 Dec 2024 07:00:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1734622460394/f3c8f1ae-7ef1-4a6f-971b-d98c17d6928c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Another year comes to an end, as such, this is an opportunity to reflect on the achievements of 2024 and look forward to 2025. It's been over 2.5 years since I embarked on my embedded Rust journey, and it remains as exciting as ever. If you’re interested in how the journey started, refer to the <a target="_blank" href="https://blog.theembeddedrustacean.com/embedded-rust-education-2023-reflections-2024-visions">post from last year</a>. This post will be a round-up of everything that happened in 2024 and what to expect in 2025.</p>
<blockquote>
<p>I am forever grateful for all the feedback and notes I keep receiving from the embedded &amp; Rust community. It has been the guiding light and fuel for all my efforts. If you have anything to note after reading this post or have any ideas to share, please feel free to reach out on any social account or just email <a target="_blank" href="mailto:hi@theembeddedrustacean.com">hi@theembeddedrustacean.com</a></p>
</blockquote>
<h2 id="heading-2024-reflections">2024 Reflections 🪞</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://tenor.com/view/sun-rise-sun-set-good-morning-good-night-reflections-gif-15821186">https://tenor.com/view/sun-rise-sun-set-good-morning-good-night-reflections-gif-15821186</a></div>
<p> </p>
<p>The goal was and remains to address the gaps in the embedded Rust educational ecosystem. A big part of it is to attract first-time embedded curious users who are into Rust. There was, and still is, a lot of ground to cover. In that regard, there were a lot of interesting and significant milestones in 2024, here they are:</p>
<ul>
<li><p>📖 <strong>Simplified Embedded Rust Published</strong>: In May 2024, I successfully published <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-std">Simplified Embedded Rust</a>. Also, although what I had in plan was one book covering ESP devices, I ended up with a series including two editions: a <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-no-std">core library edition</a> and a <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-std">standard library edition</a>. The editions covered both frameworks that the Espressif ecosystem supports for embedded Rust.</p>
</li>
<li><p>📚 <strong>400+ Copies Sold:</strong> Since its launch in May 2024, <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-std">Simplified Embedded Rust</a> has sold over 400 copies in digital and print formats. The book's reception was beyond my expectations. I was overwhelmed by the messages of support and thanks I received.</p>
</li>
<li><p><strong>✍️ 100+ Blog Posts</strong>: I started with blog posts in the Rust-embedded educational space, and it was an amazing journey. Rustaceans continuously ping me about how they find the blog posts useful and how they helped them with their own journeys. In 2024, I reached the milestone of 100 blog posts. However, my focus has shifted somewhat since. I will discuss this further below.</p>
</li>
<li><p>🛍️ <strong>Rebranding:</strong> In 2024, an effort was made to consolidate all activities under one brand: The Embedded Rustacean. For those that have been around for a bit, there was the former Apollo Labs brand that the blog was operating under and is no longer used. There are still some github repos that are yet to be consolidated though.</p>
</li>
<li><p>🗞️ <strong>35 Newsletter Editions:</strong> <a target="_blank" href="https://www.theembeddedrustacean.com/subscribe">The Embedded Rustacean</a> newsletter was launched in 2023 as a source for everything embedded Rust. 2023 ended with nine editions. The newsletter continued to operate bi-monthly throughout 2024. In 2024, 26 newsletter editions regularly highlighted news, educational material, and jobs in the embedded and Rust ecosystem. Throughout the year, the ecosystem had many exciting advancements, with several new companies joining the adoption list. From how things look, we can expect a very exciting 2025 for embedded Rust.</p>
</li>
<li><p>🤓 <strong>3.7k+ Subscribers:</strong> The Embedded Rustacean’s growth has been phenomenal. From less than 900 subscribers at the end of 2023, it has now crossed the 3700 subscriber mark! Further, the newsletter has had high engagement rates, exceeding a 40% open rate and a 30% click-through rate. These numbers testify to the usefulness of the material it has been delivering.</p>
</li>
<li><p>🏟️ <strong>2 Conferences:</strong> In 2024, I had the pleasure to participate in 2 conferences to talk about embedded Rust. In July, I participated in the <a target="_blank" href="https://www.uarust.com/">UA Rust Conference</a> with my talk “Standard vs. Core Library: An Embedded Perspective.“ Additionally, I participated in the <a target="_blank" href="https://devcon.espressif.com/">Espressif DevCon</a> in September with my talk “<a target="_blank" href="https://www.youtube.com/watch?v=8yWw9GrzwAo&amp;t=1s">Redefining Embedded Learning with ESP &amp; Rust Bridging the Gap Between Complexity</a>.“ While the Espressif talk is currently public on YouTube, the UARust talk has not yet been published publicly.</p>
</li>
</ul>
<h2 id="heading-a-look-forward-to-2025">A Look Forward to 2025 🔮</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://tenor.com/view/chris-pratt-andy-dwyer-jimmy-kimmel-interview-excited-gif-22198805">https://tenor.com/view/chris-pratt-andy-dwyer-jimmy-kimmel-interview-excited-gif-22198805</a></div>
<p> </p>
<h3 id="heading-what-i-have-in-plan">What I Have in Plan</h3>
<p>In 2024, I switched jobs to one that required more of my time. Also, just like before, I am still doing much of this work in my spare time. As a result, to continue offering the best quality material, I will switch to an alternate strategy where I can deliver more efficiently.</p>
<ul>
<li><p><strong>⬇️ ✍️ ⬆️ 📖 Less Blog Posting, More Book Updating</strong>: It has been challenging to find the time to maintain <a target="_blank" href="https://blog.theembeddedrustacean.com/">The Embedded Rustacean Blog</a>, the <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-std">Simplified Embedded Rust</a> book, and <a target="_blank" href="https://www.theembeddedrustacean.com/subscribe">The Embedded Rustacean</a> newsletter. Especially as the number of blog posts has been piling up, many have become outdated due to the dynamic ecosystem. This makes the posts quite challenging to maintain individually with each update. Consequently, I’ll be shifting most of my focus toward maintaining Simplified Embedded Rust to stay dated with the ecosystem changes as it is more central and self-contained. Blog posts will be less frequent but probably reserved for supplementing concepts not covered by the book.</p>
</li>
<li><p><strong>🏗️ Projects</strong>: Some comments I have received regarding Simplified Embedded Rust suggest that it is similar to other embedded Rust educational resources in that it lacks a project (or projects) for practice. I will work on addressing this early in 2025.</p>
</li>
<li><p>🌲 <strong>Expand Embedded Rust Reach &amp; Continue Growing Newsletter:</strong> The growth experienced by The Embedded Rustacean newsletter in 2024 was exceptional. In 2025, I will be investing more effort in growing the newsletter and expanding its reach further to encompass more embedded Rust enthusiasts.</p>
</li>
<li><p>🏟️ <strong>Conferences:</strong> I plan to continue attending Rust conferences as circumstances allow. For now, I plan to attend Rust in Paris in March 2025.</p>
</li>
<li><p><strong>📚 Another Book?</strong> This is a bit of a long shot, but ever since publishing Simplified Embedded Rust, I’ve had several inquiries about whether I would write a more hardware-oriented book with advanced concepts, essentially expanding the Simplified Embedded Rust series. Additionally, I have been pinged before about expanding into different devices like the Raspberry Pi Pico or the nRFs. While I am still inclined to venture in that direction, it really depends on how much time I can dedicate. Ultimately, my plan is to create a streamlined learning flow, not necessarily in the form of a book.</p>
</li>
</ul>
<h2 id="heading-acknowledgments">Acknowledgments 🙏</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://tenor.com/view/thank-you-thanks-thankyou-thankyougif-animated-gif-17513220857764046810">https://tenor.com/view/thank-you-thanks-thankyou-thankyougif-animated-gif-17513220857764046810</a></div>
<p> </p>
<p>Finally, I would like to thank everybody who contributed to these accomplishments; without their effort, none of the above would have been possible. In particular:</p>
<ul>
<li><p>All the book reviewers who dedicated the time to proofread Simplified Embedded Rust in their spare time and provide their comments.</p>
</li>
<li><p>The Espressif DevCon and the UARust Conference organizers, thank you for your time revising and preparing the conference recordings.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The embedded Rust ecosystem is growing spectacularly. Last year, I highlighted a scarcity of embedded Rust educational material, though, at the end of this year, we are in a different place. A lot of new material has emerged, and even existing material has been updated and enhanced. Hopefully, this trend is expected to continue growing. Now that there is a material that addresses folks coming from a non-embedded background, maybe 2025 is the year to shift gears to introduce more intermediate to advanced material. While there are many “pure” Rust topics to tackle, there are also integration topics that might be of increased interest. For example, in 2024, we saw Rust integrated into the Zephyr framework, in addition to other efforts attempting to integrate Rust into existing codebases. 2025 might be the year where we see more effort for resources pouring into that direction.</p>
]]></content:encoded></item><item><title><![CDATA[From Zero to Rust: Simplified Embedded Systems Programming]]></title><description><![CDATA[Introduction 🚀
On Friday, May 17th, 2024, the release of Simplified Embedded Rust was announced marking a new kind of book in the embedded Rust learning space. I'm happy to say that this book, designed to address the needs of learners, has been rece...]]></description><link>https://blog.theembeddedrustacean.com/from-zero-to-rust-simplified-embedded-systems-programming</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/from-zero-to-rust-simplified-embedded-systems-programming</guid><category><![CDATA[Rust]]></category><category><![CDATA[learning]]></category><category><![CDATA[beginner]]></category><category><![CDATA[embedded]]></category><category><![CDATA[iot]]></category><category><![CDATA[books]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Mon, 20 May 2024 20:54:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716237290838/39404d58-13c2-4e30-b877-a1ecad9c67b7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction 🚀</h2>
<p>On Friday, May 17th, 2024, the release of <em>Simplified Embedded Rust</em> was announced marking a new kind of book in the embedded Rust learning space. I'm happy to say that this book, designed to address the needs of learners, has been received with great excitement and enthusiasm. Following its release, I received an overwhelming number of responses and questions. Common queries included:</p>
<ul>
<li><p>📚 When is the paperback going to be available?</p>
</li>
<li><p>💳 Why the subscription model?</p>
</li>
<li><p>📖 Which edition should I choose?</p>
</li>
<li><p>🦀 What's Rust?</p>
</li>
<li><p>🤔 How different is the book from <a target="_blank" href="http://blog.theembeddedrustacean.com/"><em>The Embedded Rustacean</em></a> blog posts?</p>
</li>
</ul>
<p>In this post, I aim to address these questions and provide further insights into the motivations and goals behind the book.</p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h2 id="heading-motivation-behind-the-book">Motivation Behind the Book 🎓</h2>
<p>Curiosity is a vital component of learning 🐈. As educators, we strive to nurture this curiosity to help students discover their passion. However, without engaging and exciting material, we risk losing their interest and attention. Hands-on learning is crucial, but what is required to get started can vary significantly across different fields. Beginners are in some cases expected to deal with complex tools they encounter for the first time. For instance, coding used to require relatively involved setups, whereas now, it can be done in seconds on several web-based platforms. Embedded adds a twist to this -&gt; hardware 😱.</p>
<p><em>Simplified Embedded Rust</em> is as much about transforming the approach to embedded systems learning as it is about embedded Rust. Learning embedded systems can be daunting due to the complexities of toolchains and setups, especially for beginners. Rust, often seen as a challenging language, adds another layer of difficulty. This book rises to the challenge by simplifying Rust's introduction within the context of embedded systems.</p>
<h2 id="heading-goals">Goals 🎯</h2>
<p>The primary goal of <em>Simplified Embedded Rust</em> is to make embedded Rust accessible and straightforward. By lowering the barrier to entry, the book aims to increase accessibility and provide a coherent overview of embedded Rust, minimizing the challenges many of us faced early on. The book gradually builds up the necessary knowledge, making it easier for readers to grasp more complex concepts. Practical material for each peripheral is included, allowing readers to apply their learning effectively without needing physical hardware. Thanks to modern tools like <a target="_blank" href="https://wokwi.com/">Wokwi</a> we can replicate the software learning experience in embedded more effectively. The book also supports programming any supported ESP device in Rust, leveraging the powerful Espressif crates.</p>
<h2 id="heading-why-rust">Why Rust? 🦀</h2>
<p>Rust is a modern systems programming language designed for performance, safety, and concurrency. Unlike many traditional languages, Rust provides memory safety without requiring a garbage collector, making it a great fit for low-level programming where direct hardware access and real-time constraints are crucial.</p>
<p>Key features of Rust include:</p>
<ul>
<li><p><strong>Memory Safety</strong>: Rust ensures memory safety through its infamous borrow checker preventing common bugs such as null pointer dereferencing and buffer overflows by using a strict ownership system. 🔒</p>
</li>
<li><p><strong>Concurrency</strong>: Rust's concurrency model ensures that data races are caught at compile time, making it easier to write safe, concurrent code. 🏎️</p>
</li>
<li><p><strong>Performance</strong>: Rust's performance is comparable to C and C++, making it suitable for systems programming and other performance-critical applications. 🚀</p>
</li>
<li><p><strong>Tooling</strong>: Rust comes with a rich set of tools, including a package manager (Cargo), a build system, and integrated testing support. 🛠️</p>
</li>
</ul>
<p>Rust's unique combination of memory-safety, performance, and modern tooling makes it an excellent choice for embedded systems programming, where reliability and efficiency are paramount.</p>
<h2 id="heading-why-esp">Why ESP? 🌐</h2>
<p>The choice of ESP was deliberate. ESP offers the lowest entry barrier and has official Rust support, making it an ideal starting point. Espressif was one of the first system-on-chip (SoC) hardware companies to announce official support for Rust. Espressif has also a dedicated Rust team that contributes to their open source project to support embedded crates for ESPs. Through this effort, and among many other things, espressif also supports Rust on Wokwi. The espressif Rust team was also instrumental in helping me bring this book to life.</p>
<h2 id="heading-the-review-process">The Review Process 🔍</h2>
<p><em>Simplified Embedded Rust</em> is self-published, but ensuring its technical accuracy and effectiveness was paramount. To achieve this, both editions of the book was sent for review by a diverse group of 20 reviewers, who provided invaluable feedback. The group included both experienced and beginner developers. Their insights were invaluable in helping ensure the book meets its goals and maintains technical correctness.</p>
<h2 id="heading-how-is-this-book-different-from-the-blog"><strong>How is This Book Different from The Blog?</strong> ✍️</h2>
<p>A question I received is how the book differs from the content on <a target="_blank" href="https://blog.theembeddedrustacean.com/"><em>The Embedded Rustacean</em></a> blog (formerly apollolabs tech blog). While <em>Simplified Embedded Rust</em> builds on several examples from the blog, it offers core differences that make it a more comprehensive learning resource. Unlike the blog, the book is self-contained with exercises, aims to be ESP agnostic, offers a clear flow, and provides a conceptual background on embedded systems and the Rust ecosystem. The book is also regularly updated and includes pre-wired exercises, ensuring readers have a seamless learning experience from start to finish.</p>
<h2 id="heading-what-the-book-covers">What the Book Covers 📘</h2>
<p>The book starts with a generic explanation of microcontroller systems that drive embedded systems, providing essential background knowledge for programming embedded systems. This is followed by an in-depth explanation of the ESP and Rust embedded ecosystems. The remainder of the book is programming-oriented, covering all core peripherals and how to code each in Rust on ESP. Each peripheral has its own chapter that includes:</p>
<ol>
<li><p><strong>Conceptual Background</strong>: An overview of the peripheral and how it operates.</p>
</li>
<li><p><strong>Configuration and Coding Steps</strong>: Detailed steps to configure and program the peripheral.</p>
</li>
<li><p><strong>Application Example(s)</strong>: A practical example (or examples) to demonstrate the peripheral in action.</p>
</li>
<li><p><strong>Exercises</strong>: Exercises to reinforce learning and provide hands-on experience.</p>
</li>
</ol>
<h2 id="heading-who-is-this-book-for"><strong>Who is This Book For?</strong> 🌟</h2>
<p><em>Simplified Embedded Rust</em> is targeted at individuals familiar with Rust who are eager to explore embedded systems for the first time, as well as embedded developers at all experience levels who are new to Rust. Whether you're looking to expand your skills or embark on a new learning journey, this book aims to meet your needs and offer valuable insights into the world of embedded Rust.</p>
<h2 id="heading-choosing-the-right-edition">Choosing the Right Edition 📚</h2>
<p>Espressif offers two approaches for embedded development on their SoCs; <code>std</code> and <code>no-std</code>. <code>std,</code> also known as the standard library, which is based on the ESP-IDF framework. <code>no-std</code>, also known as the core library, which is for bare-metal development. For those seeking more details, please check a more detailed overview <a target="_blank" href="https://docs.esp-rs.org/book/overview/index.html">here</a>. As such, the book is available in two editions supporting each approach: the <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-std">standard library edition</a> and the <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-no-std">core library edition</a>. The choice depends on the depth of knowledge you seek. The standard library edition is easier to start with especially if coming from a Rust background, while the core library edition delves a tad deeper. If still confused, the following table might help you decide:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Embedded Systems Knowledge</td><td>Rust Programming Knowledge</td><td>Recommended Edition</td></tr>
</thead>
<tbody>
<tr>
<td>Intermediate/Advanced</td><td>Intermediate/Advanced</td><td>Core</td></tr>
<tr>
<td>Intermediate/Advanced</td><td>Beginner</td><td>Core or Standard</td></tr>
<tr>
<td>Beginner</td><td>Intermediate/Advanced</td><td>Standard</td></tr>
<tr>
<td>Beginner</td><td>Beginner</td><td>Standard</td></tr>
</tbody>
</table>
</div><p>To acquire the Core Edition <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-std">click here</a>.</p>
<p>To acquire the Standard Edition <a target="_blank" href="https://www.theembeddedrustacean.com/c/ser-std">click here</a>.</p>
<h2 id="heading-why-a-subscription-model">Why a Subscription Model? 💡</h2>
<p>A frequent question is why the book is subscription-based. While this model might not be ideal for everyone, it reflects the ongoing effort required to keep the book current in a dynamic ecosystem. There is a significant amount of work that would be needed to keep the book up to date. For example, during the review process, significant changes occurred to the <code>esp-hal</code> which necessitated updates, delaying the publication to ensure that the book is up to date.</p>
<p>Still, for those who prefer a one-time purchase, there are two options:</p>
<ol>
<li><p>Keep the subscription only if you want updates beyond the minimum duration. You can still purchase once and cancel anytime before renewal, maintaining access to the latest material until the end of the billing cycle. If you need an updated copy at any time later, simply resubscribe.</p>
</li>
<li><p>Wait for the paperback version, which will be available on Amazon within a month.</p>
</li>
</ol>
<h2 id="heading-upcoming-updates">Upcoming Updates 🛠️</h2>
<p>Although the book is published, I view this as a start rather than an end to an exciting journey. The ecosystem, as mentioned before, is evolving quickly. There are still a lot of exciting things to come that I hope to incorporate. Some short-term milestones include:</p>
<ul>
<li><p>Publishing additional formats. This includes Mobi, Epub, and Paperback. These formats should be available within a month.</p>
</li>
<li><p>Since the book is meant to be ESP agnostic, pre-wired examples and templates for all other ESPs will be provided. Also, if necessary, the content would be modified to make it more overarching.</p>
</li>
<li><p>Added projects. The idea is to incorporate projects for readers so that they can apply their knowledge from all peripherals.</p>
</li>
<li><p>Added examples. Some peripherals might benefit from more examples. Also, some more peripherals might be added, notably SPI.</p>
</li>
<li><p>Incorporate community crates. Some might notice that the core edition does not have a connectivity/networking chapter. This is because I am waiting on the crate involved, <code>esp-wifi</code>, to become more stable</p>
</li>
</ul>
<p>If you have any ideas or suggestions, please feel free to submit a feature request to either of the book's GitHub repositories. You can submit <a target="_blank" href="https://github.com/theembeddedrustacean/ser-std/issues/new?assignees=&amp;labels=enhancement&amp;projects=&amp;template=feature_request.md&amp;title=">here</a> for the Standard edition and <a target="_blank" href="https://github.com/theembeddedrustacean/ser-no-std/issues/new?assignees=&amp;labels=enhancement&amp;projects=&amp;template=feature_request.md&amp;title=">here</a> for the Core edition.</p>
<h2 id="heading-acknowledgments">Acknowledgments 🙏</h2>
<p>Finally, this work would not have been possible without all the support that I have received. I extend my heartfelt thanks to many individuals and groups. This experience has underscored how special the Rust community is. I am grateful to the reviewers, the Espressif Rust team, and all my students who have been a constant source of inspiration. Thank you for your support and contributions to making <em>Simplified Embedded Rust</em> a reality.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong> ✨</h2>
<p><em>Simplified Embedded Rust</em> is more than just a book—it's a gateway to a transformative journey to becoming an embedded Rust developer. Whether you're a seasoned developer or just starting your journey, this book aims to make embedded Rust accessible, engaging, and practical. I invite you to dive in, explore the world of embedded Rust, and start your transformation.</p>
<p>Thank you for your support, and I look forward to your feedback and contributions!</p>
]]></content:encoded></item><item><title><![CDATA[Embedded Rust Bluetooth on ESP: Secure BLE Server]]></title><description><![CDATA[This post is the sixth of a multi-part series where I'm exploring the use of Bluetooth Low Energy along embedded Rust on the ESP32. Information in this post might rely on knowledge presented in past posts.


Embedded Rust Bluetooth on ESP: BLE Scanne...]]></description><link>https://blog.theembeddedrustacean.com/embedded-rust-bluetooth-on-esp-secure-ble-server</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embedded-rust-bluetooth-on-esp-secure-ble-server</guid><category><![CDATA[Rust]]></category><category><![CDATA[ble]]></category><category><![CDATA[embedded systems]]></category><category><![CDATA[iot]]></category><category><![CDATA[tutorials]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Mon, 15 Apr 2024 21:06:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1713211624171/000ab454-b047-467c-b613-c60b20e5b322.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong><em>This post is the sixth of a multi-part series where I'm exploring the use of Bluetooth Low Energy along embedded Rust on the ESP32. Information in this post might rely on knowledge presented in past posts.</em></strong></p>
</blockquote>
<ol>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-scanner"><strong>Embedded Rust Bluetooth on ESP: BLE Scanner</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-advertiser"><strong>Embedded Rust Bluetooth on ESP: BLE Advertiser</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-server"><strong>Embedded Rust Bluetooth on ESP: BLE Server</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-client"><strong>Embedded Rust Bluetooth on ESP: BLE Client</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-secure-ble-client">Embedded Rust Bluetooth on ESP: Secure BLE Client</a></p>
</li>
</ol>
<h2 id="heading-introduction"><strong>Introduction</strong></h2>
<p>In this post, we're going to build on the <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-server">BLE Server post</a> to instead create a secure BLE server. We're going to create a <strong>peripheral</strong> device that will assume the role of a <strong>server</strong> upon connection establishment. Afterward, the connection will be secured. Similar to past posts, the code will be built using the <a target="_blank" href="https://crates.io/crates/esp32-nimble/0.6.0"><strong>esp32-nimble</strong> crate.</a></p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Familiarity with standard library development in Rust with the ESP.</p>
</li>
<li><p>Basic knowledge of networking layering concepts/stacks (ex. OSI model).</p>
</li>
<li><p>Basic knowledge of Bluetooth.</p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3 git repo</strong></a>. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
<h4 id="heading-connections"><strong>🔌 Connections</strong></h4>
<p>  No connections are required for this example.</p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨 Software Design</strong></h2>
<p>The steps in securing a channel were presented in <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-secure-ble-client">last week's post</a> where we secured the connection of a BLE Client. It was demonstrated how after a connection is established, it can be secured by encrypting the connection. The exact same idea applies whether the device is a server or a client. The terms <strong>pairing</strong> and <strong>bonding</strong> were also introduced. security information to quickly establish a secure connection.</p>
<p>We mentioned that after a connection is secured, we further have the option to secure particular attributes. This is captured through the attribute properties. The properties available related to security include a choice of authentication, authorization, or encryption. These all apply to both read and write operations. As such, a server may define permissions independently for each characteristic. The server may allow some characteristics to be accessed by any client, while limiting access to other characteristics to only authenticated or authorized clients.</p>
<p>Authentication guarantees that a message has originated from a trusted party while authorization is a confirmation by the user to continue with the procedure. Characteristics that require authentication cannot be accessed until the client has gone through an authenticated <strong>pairing</strong> method. Authorization, on the other hand, is typically handled by the application. The BLE stack would forward requests to the application to complete the process.</p>
<p>In this post, we'll be appending the code in the <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-server">peripheral server post</a> to make it's connection secure. As such, we'll be creating a <strong>secure peripheral server</strong> with one <strong>characteristic</strong>. In that context, the code will take the following steps:</p>
<ol>
<li><p><strong>(Added Step)</strong> Configure device security capabilities and connection method for pairing</p>
</li>
<li><p><strong>(Modified Step)</strong> Create Secure Service &amp; Characteristic</p>
</li>
<li><p>Configure Advertising Data &amp; Start Advertising</p>
</li>
<li><p>Establish a connection</p>
</li>
<li><p>Read a characteristic value every second</p>
</li>
</ol>
<h2 id="heading-code-implementation"><strong>👨‍💻 Code Implementation</strong></h2>
<h3 id="heading-crate-imports"><strong>📥 Crate Imports</strong></h3>
<p>In this implementation, the following crates are required:</p>
<ul>
<li><p>The <code>esp_idf_hal</code> crate to import delays.</p>
</li>
<li><p>The <code>esp_idf_sys</code> crate since its needed.</p>
</li>
<li><p>The <code>esp32_nimble</code> crate for the BLE abstractions.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32_nimble::{enums::*, uuid128, BLEAdvertisementData, BLEDevice, NimbleProperties};
<span class="hljs-keyword">use</span> esp_idf_hal::delay::FreeRtos;
<span class="hljs-keyword">use</span> esp_idf_sys <span class="hljs-keyword">as</span> _;
</code></pre>
<h3 id="heading-initializationconfiguration-code"><strong>🎛 Initialization/Configuration Code</strong></h3>
<p><strong>1️⃣ Obtain a handle for the BLE device</strong>: Similar to the pattern we've seen in embedded Rust with peripherals, as part of the singleton design pattern, we first have to take ownership of the device peripherals. In this context, its the <code>BLEDevice</code> that we need to take ownership of. This is done using the <code>take()</code> associated method. Here I create a BLE device handler named <code>ble_device</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ble_device = BLEDevice::take();
</code></pre>
<p><strong>2️⃣ Configure Device Security:</strong> In this step we need to configure the device I/O capabilities for pairing and set the authorization mode. The authorization mode would determine the pairing method and whether bonding is required or not. The authorization mode is captured through several flags mentioned earlier configurable through the <code>esp32_nimble::enums::AuthReq</code> enum. To match the client from last week, we will choose the passkey entry method.</p>
<p>To configure security parameters, we first need to call the <code>security</code> method on the <code>BLEDevice</code> instance. This would return a <code>BLESecurity</code> type that renders access to the security configuration methods. The authorization mode is configured through the <code>set_auth</code> method where we pass a <code>AuthReq</code> enum choice. <code>AuthReq::all</code> means setting all the flags and implies the passkey entry method and bonding. We also need to set the IO capabilities through the <code>set_io_cap</code> method. For that, we need to pass a <code>SecurityIOCap</code> enum option which we pass <code>DisplayOnly</code>. Here's the code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Configure Device Security</span>
ble_device
    .security()
    .set_auth(AuthReq::all())
    .set_passkey(<span class="hljs-number">123456</span>)
    .set_io_cap(SecurityIOCap::DisplayOnly)
    .resolve_rpa();
</code></pre>
<p>Note the <code>resolve_rpa</code> method utilized. RPA stands for resolvable private address. RPA is required to establish a higher level of security which generates what is referred to as a Long Term Key (LTK). Without going into too much detail, this would be required if you want to test with certain devices, especially iOS ones.</p>
<p><strong>3️⃣ Create an Advertiser Instance</strong>: After initializing the NimBLE stack we create an advertiser instance by calling <code>get_advertising</code>, this will create a <code>&amp;Mutex&lt;BLEAdvertising&gt;</code> instance. Heres the code:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ble_advertiser = ble_device.get_advertising();
</code></pre>
<p><strong>4️⃣ Obtain Handle for Server</strong>: We create a <code>server</code> instance by calling <code>get_server</code>, this will create a <code>BLEServer</code> instance. Heres the code:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> server = ble_device.get_server();
</code></pre>
<p>5️⃣ <strong>Define Server Connect &amp; Disconnect Behaviour:</strong> using the <code>server</code> instance there exists a <code>on_connect</code> method for <code>BLEServer</code>. <code>on_connect</code> has one argument which is a closure that passes a handle for a <code>BLEServer</code> and a <code>BLEConnDesc</code> that contains connection information. In the closure body, upon connect, we'll print the connection data to the console then update the connection parameters. To update connection parameters, the <code>BLEServerupdate_conn_params</code> method is used. Here's the code:</p>
<pre><code class="lang-rust">server.on_connect(|server, clntdesc| {
    <span class="hljs-comment">// Print connected client data</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{:?}"</span>, clntdesc);
    <span class="hljs-comment">// Update connection parameters</span>
    server
        .update_conn_params(clntdesc.conn_handle(), <span class="hljs-number">24</span>, <span class="hljs-number">48</span>, <span class="hljs-number">0</span>, <span class="hljs-number">60</span>)
        .unwrap();
});
</code></pre>
<p>Similar to <code>on_connect</code> theres an <code>on_disconnect</code> method. <code>on_disconnect</code> also has one argument which is a closure that passes a handle for a <code>BLEConnDesc</code> and a <code>Result</code> that contains the reason for disconnection. All were going to do is to print a message that our device disconnected.</p>
<pre><code class="lang-rust">server.on_disconnect(|_desc, _reason| {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Disconnected, back to advertising"</span>);
});
</code></pre>
<p>6️⃣ <strong>Define Services &amp; Characteristics:</strong> In this example, only one <strong>characteristic</strong> will be defined with a read and notify properties. However, every <strong>characteristic</strong> has to be associated with (listed under) a <strong>service</strong>. To create a service, within <code>BLEServer</code> there exists a <code>create_service</code> method that takes a single <code>BleUuid</code> argument. <code>BleUuid</code> is an enum representing a Bluetooth <strong>UUID</strong>. To make things easier, the nimble crate provides a <code>uuid128</code> macro to parse a 128 UUID from string literals at compile time. We only need to pass a <strong>UUID</strong> string literal and the macro would take care of the rest. We create a service as follows:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Create a service with custom UUID</span>
<span class="hljs-keyword">let</span> my_service = server.create_service(uuid128!(<span class="hljs-string">"9b574847-f706-436c-bed7-fc01eb0965c1"</span>));
</code></pre>
<p>Next, we need to create a <strong>characteristic</strong>. This is done using the <code>BLEServicecreate_characteristic</code> method which has two arguments; <code>BlueUuid</code> and <code>NimbleProperties</code>. <code>NimbleProperties</code> is a struct containing a collection of associated constants representing the different supported operations and required security levels. For this <strong>characteristic</strong> we will support encrypted <code>READ</code> through the <code>NimbleProperties::READ_ENC</code> property. <code>create_characteristic</code> will return a <code>Arc&lt;Mutex&lt;BLECharacteristic&gt;&gt;</code>. As such, we can set a starting value for the <strong>characteristic</strong> that we created using the <code>BLECharacteristic</code> <code>set_value</code> method. <code>set_value</code> takes a single <code>&amp;[u8]</code> parameter. Here's the code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Create a characteristic to associate with created service</span>
<span class="hljs-keyword">let</span> my_service_characteristic = my_service.lock().create_characteristic(
    uuid128!(<span class="hljs-string">"681285a6-247f-48c6-80ad-68c3dce18585"</span>),
    NimbleProperties::READ | NimbleProperties::READ_ENC,
);

<span class="hljs-comment">// Set a starting value for the characteristic</span>
my_service_characteristic.lock().set_value(<span class="hljs-string">b"Start Value"</span>);
</code></pre>
<p>6️⃣ <strong>Configure Advertiser Data:</strong> This step is similar to what was done in the <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-advertiser">BLE Advertiser post</a>. A small difference is that here we are also attaching the <strong>service</strong> to the advertisement data. This is done using the <code>add_service_uuid</code> method. Note that the UUID being advertised is the same <strong>service</strong> UUID created earlier.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Configure Advertiser Data</span>
ble_advertiser
    .lock()
    .set_data(
        BLEAdvertisementData::new()
            .name(<span class="hljs-string">"ESP32 Server"</span>)
            .add_service_uuid(uuid128!(<span class="hljs-string">"9b574847-f706-436c-bed7-fc01eb0965c1"</span>)),
    )
    .unwrap();
</code></pre>
<p>That's it for configuration!</p>
<h3 id="heading-application-code"><strong>📱 Application Code</strong></h3>
<p><strong>Start Advertising</strong>: Now we have to start the advertising process. This is done by calling the <code>BLEAdvertising</code> <code>start</code> method.</p>
<pre><code class="lang-rust">ble_advertiser.lock().start().unwrap();
</code></pre>
<p><strong>Update Characteristic</strong>: All we have to do now is keep updating the <strong>characteristic</strong> by calling the <code>set_value</code> method again. Here, the <code>my_service_characteristic</code> value keeps getting updated every one every second by incrementing its contents.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> val = <span class="hljs-number">0</span>;

<span class="hljs-keyword">loop</span> {
    FreeRtos::delay_ms(<span class="hljs-number">1000</span>);
    my_service_characteristic.lock().set_value(&amp;[val]).notify();
    val = val.wrapping_add(<span class="hljs-number">1</span>);
}
</code></pre>
<h2 id="heading-testing">🧪 Testing</h2>
<p>In order to test this code, you can use <a target="_blank" href="https://www.nordicsemi.com/Products/Development-tools/nRF-Connect-for-mobile">nRF connect</a> mobile app or the <a target="_blank" href="https://learn.adafruit.com/bluefruit-le-connect/ios-setup">bluefruit connect</a> mobile app. In either app you would be able to connect to the ESP and poll the server data.</p>
<h2 id="heading-full-application-code"><strong>📱Full Application Code</strong></h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32_nimble::{enums::*, uuid128, BLEAdvertisementData, BLEDevice, NimbleProperties};
<span class="hljs-keyword">use</span> esp_idf_hal::delay::FreeRtos;
<span class="hljs-keyword">use</span> esp_idf_sys <span class="hljs-keyword">as</span> _;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    esp_idf_sys::link_patches();

    <span class="hljs-comment">// Take ownership of device</span>
    <span class="hljs-keyword">let</span> ble_device = BLEDevice::take();

    <span class="hljs-comment">// Obtain handle for peripheral advertiser</span>
    <span class="hljs-keyword">let</span> ble_advertiser = ble_device.get_advertising();

    <span class="hljs-comment">// Configure Device Security</span>
    ble_device
        .security()
        .set_auth(AuthReq::all())
        .set_passkey(<span class="hljs-number">123456</span>)
        .set_io_cap(SecurityIOCap::DisplayOnly)
        .resolve_rpa();

    <span class="hljs-comment">// Obtain handle for server</span>
    <span class="hljs-keyword">let</span> server = ble_device.get_server();

    <span class="hljs-comment">// Define server connect behaviour</span>
    server.on_connect(|server, clntdesc| {
        <span class="hljs-comment">// Print connected client data</span>
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{:?}"</span>, clntdesc);
        <span class="hljs-comment">// Update connection parameters</span>
        server
            .update_conn_params(clntdesc.conn_handle(), <span class="hljs-number">24</span>, <span class="hljs-number">48</span>, <span class="hljs-number">0</span>, <span class="hljs-number">60</span>)
            .unwrap();
    });

    <span class="hljs-comment">// Define server disconnect behaviour</span>
    server.on_disconnect(|_desc, _reason| {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Disconnected, back to advertising"</span>);
    });

    <span class="hljs-comment">// Create a service with custom UUID</span>
    <span class="hljs-keyword">let</span> my_service = server.create_service(uuid128!(<span class="hljs-string">"9b574847-f706-436c-bed7-fc01eb0965c1"</span>));

    <span class="hljs-comment">// Create a characteristic to associate with created service</span>
    <span class="hljs-keyword">let</span> my_service_characteristic = my_service.lock().create_characteristic(
        uuid128!(<span class="hljs-string">"681285a6-247f-48c6-80ad-68c3dce18585"</span>),
        NimbleProperties::READ | NimbleProperties::READ_ENC ,
    );

    <span class="hljs-comment">// Modify characteristic value</span>
    my_service_characteristic.lock().set_value(<span class="hljs-string">b"Start Value"</span>);

    <span class="hljs-comment">// Configure Advertiser Data</span>
    ble_advertiser
        .lock()
        .set_data(
            BLEAdvertisementData::new()
                .name(<span class="hljs-string">"ESP32 Server"</span>)
                .add_service_uuid(uuid128!(<span class="hljs-string">"9b574847-f706-436c-bed7-fc01eb0965c1"</span>)),
        )
        .unwrap();

    <span class="hljs-comment">// Start Advertising</span>
    ble_advertiser.lock().start().unwrap();

    <span class="hljs-comment">// (Optional) Print dump of local GATT table</span>
    <span class="hljs-comment">// server.ble_gatts_show_local();</span>

    <span class="hljs-comment">// Init a value to pass to characteristic</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> val = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">loop</span> {
        FreeRtos::delay_ms(<span class="hljs-number">1000</span>);
        my_service_characteristic.lock().set_value(&amp;[val]).notify();
        val = val.wrapping_add(<span class="hljs-number">1</span>);
    }
}
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>This post introduced how to create a secure BLE server on the ESP32-C3 with Rust. This was by using the <code>esp32-nimble</code> crate in a standard library development environment using the <code>esp-idf-hal</code> . In this post, the ESP32-C3 was configured as a secure peripheral device advertising a service. The device also assumes a server role after a connection is established and secures the connection with a passkey. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[Embedded Rust Bluetooth on ESP: Secure BLE Client]]></title><description><![CDATA[This post is the fourth of a multi-part series where I'm exploring the use of Bluetooth Low Energy along embedded Rust on the ESP32. Information in this post might rely on knowledge presented in past posts.


Embedded Rust Bluetooth on ESP: BLE Scann...]]></description><link>https://blog.theembeddedrustacean.com/embedded-rust-bluetooth-on-esp-secure-ble-client</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embedded-rust-bluetooth-on-esp-secure-ble-client</guid><category><![CDATA[Rust]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[iot]]></category><category><![CDATA[embedded systems]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Tue, 09 Apr 2024 05:59:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1712585919445/eb243d8d-2f3d-400d-add3-eb1ebad4a793.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong><em>This post is the fourth of a multi-part series where I'm exploring the use of Bluetooth Low Energy along embedded Rust on the ESP32. Information in this post might rely on knowledge presented in past posts.</em></strong></p>
</blockquote>
<ol>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-scanner"><strong>Embedded Rust Bluetooth on ESP: BLE Scanner</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-advertiser"><strong>Embedded Rust Bluetooth on ESP: BLE Advertiser</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-server"><strong>Embedded Rust Bluetooth on ESP: BLE Server</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-client"><strong>Embedded Rust Bluetooth on ESP: BLE Client</strong></a></p>
</li>
</ol>
<h2 id="heading-introduction"><strong>Introduction</strong></h2>
<p>In this post, we're going to build on the <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-client">previous post</a> to instead create a secure BLE client. We're going to create a <strong>central</strong> device that will assume the role of a <strong>client</strong> upon connection establishment. Afterward, the connection will be secured. Similar to past posts, the code will be built using the <a target="_blank" href="https://crates.io/crates/esp32-nimble/0.6.0"><strong>esp32-nimble</strong> crate.</a></p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Familiarity with standard library development in Rust with the ESP.</p>
</li>
<li><p>Basic knowledge of networking layering concepts/stacks (ex. OSI model).</p>
</li>
<li><p>Basic knowledge of Bluetooth.</p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3 git repo</strong></a>. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
<h4 id="heading-connections"><strong>🔌 Connections</strong></h4>
<p>  No connections are required for this example.</p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨 Software Design</strong></h2>
<p>After a connection is established is can be secured by encrypting the connection. This is such that data exchanged is not in plain text. Encryption requires that keys are exchanged as part of the process in securing a connection. There are two terms involved in this process, <strong>pairing</strong> and <strong>bonding</strong>. <strong>Pairing</strong> refers to the process of exchanging and keys for encryption purposes. <strong>Bonding</strong> on the other hand is in reference to encryption for future reconnections. Additionally, <strong>bonding</strong> always happens after <strong>pairing</strong> is completed.</p>
<p>There are several steps that a BLE connection goes through to secure a channel:</p>
<ol>
<li><p><strong>Establish I/O Capabilities:</strong> As part of the <strong>pairing</strong> process both devices have to establish what their I/O capabilities are. In addition, among other things, devices exchange supported security features and whether or not bonding is requested. I/O capabilites include the following options:</p>
<ul>
<li><p><strong>DisplayOnly</strong>: The peer only has a display</p>
</li>
<li><p><strong>DisplayYesNo</strong>: The peer has a display and the option to select “yes” or “no”</p>
</li>
<li><p><strong>KeyboardOnly</strong>: The peer has keyboard only</p>
</li>
<li><p><strong>NoInputNoOutput</strong>: The peer has no input and no output capabilities</p>
</li>
<li><p><strong>KeyboardDisplay</strong>: The peer has keyboard and display capabilities</p>
</li>
</ul>
</li>
<li><p><strong>Choose Pairing Method:</strong> In order to complete <strong>pairing</strong>, devices need to agree on a pairing method to exchange keys. The <strong>pairing</strong> method chosen is decided based on a Out of Bound (OOB) flag, the Man-In-The-Middle (MITM) flag, and the I/O capability chosen earlier. Pairing methods include (least secure to most secure):</p>
<ul>
<li><p><strong>Just Works</strong>: This method is the simplest and most convenient, where devices automatically exchange keys without any user interaction. However, it provides the lowest level of security and is vulnerable to man-in-the-middle attacks.</p>
</li>
<li><p><strong>Passkey Entry</strong>: In this method, one device displays a randomly generated passkey, and the user must enter the same passkey on the other device. This ensures that the devices are physically present and aware of each other during the <strong>pairing</strong> process.</p>
</li>
<li><p><strong>Out-of-Band (OOB)</strong>: OOB pairing involves using an external channel, such as NFC or QR codes, to exchange <strong>pairing</strong> information securely. This method provides a high level of security by leveraging an additional communication channel.</p>
</li>
<li><p><strong>Numeric Comparison</strong>: Numeric Comparison pairing provides a high level of security by ensuring that both devices are aware of each other's identities. This method also makes sure that there is no interception or tampering during the <strong>pairing</strong> process. It requires active participation from the users to verify the displayed numeric values, which adds an extra layer of security against unauthorized access. In this method, the devices display a 6-digit number and the user selects “yes” or “no” to confirm the display.</p>
</li>
</ul>
</li>
<li><p><strong>Bonding (Optional)</strong>: After successful pairing, the devices have the option to enter into a <strong>bonding</strong> relationship. This involves securely storing the security information exchanged during <strong>pairing</strong>, such as the encryption keys and device identities, in persistent memory. As such, when the devices come into proximity again, they can use the stored security information to quickly establish a secure connection.</p>
</li>
</ol>
<p>Now that the connection is secured, we further have the option to secure particular attributes. In the <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-server">BLE Server post</a>, GATT operations were explained in which the supported operations by an attribute were defined through its properties. As such, there are additional properties available related to security for choice of authentication, authorization, and encryption. Authentication guarantees that a message has originated from a trusted party while authorization is a confirmation by the user to continue with the procedure. These attribute properties, however, are set by a server as the client would be accessing the attributes remotely.</p>
<p>Note that attributes can be associated with different levels of security. The level of security associated with an attribute, like GATT operations, is captured through its properties. This is something we'll be demonstrating in a secure server example in next week's post. This means that there could be several attributes available applying different security levels. A client in turn would only be able to access the attributes matching (or less than) the level of security is achieves.</p>
<p>In this post, we'll be appending the code in the <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-client">central client post</a> to make it's connection secure. As such, we'll be creating a <strong>secure central client</strong> with one <strong>characteristic</strong>. In that context, the code will take the following steps:</p>
<ol>
<li><p><strong>(Added Step)</strong> Configure device security capabilities and connection method for pairing</p>
</li>
<li><p>Scan and find a particular advertiser</p>
</li>
<li><p>Establish a connection</p>
</li>
<li><p><strong>(Added Step)</strong> Secure the connection</p>
</li>
<li><p>Read a characteristic value every second</p>
</li>
</ol>
<h2 id="heading-code-implementation"><strong>👨‍💻 Code Implementation</strong></h2>
<h3 id="heading-crate-imports"><strong>📥 Crate Imports</strong></h3>
<p>In this implementation, the following crates are required:</p>
<ul>
<li><p>The <code>esp_idf_hal</code> crate to import delays and blocking abstractions.</p>
</li>
<li><p>The <code>esp_idf_sys</code> crate since its needed.</p>
</li>
<li><p>The <code>esp32_nimble</code> crate for the BLE abstractions.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32_nimble::{enums::*, uuid128, BLEClient, BLEDevice};
<span class="hljs-keyword">use</span> esp_idf_hal::delay::FreeRtos;
<span class="hljs-keyword">use</span> esp_idf_hal::task::block_on;
<span class="hljs-keyword">use</span> esp_idf_sys <span class="hljs-keyword">as</span> _;
</code></pre>
<h3 id="heading-initializationconfiguration-code"><strong>🎛 Initialization/Configuration Code</strong></h3>
<p><strong>1️⃣ Obtain a handle for the BLE device</strong>: Similar to the pattern we've seen in embedded Rust with peripherals, as part of the singleton design pattern, we first have to take ownership of the device peripherals. In this context, its the <code>BLEDevice</code> that we need to take ownership of. This is done using the <code>take()</code> associated method. Here I create a BLE device handler named <code>ble_device</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ble_device = BLEDevice::take();
</code></pre>
<p><strong>2️⃣ Configure Device Security:</strong> In this step we need to configure the device I/O capabilities for pairing and set the authorization mode. The authorization mode would determine the pairing method and whether bonding is required or not. The authorization mode is captured through several flags mentioned earlier configurable through the <code>esp32_nimble::enums::AuthReq</code> enum. From what it seems, the NimBLE crate supports at most the passkey entry method.</p>
<p>To configure security parameters, we first need to call the <code>security</code> method on the <code>BLEDevice</code> instance. This would return a <code>BLESecurity</code> type that renders access to the security configuration methods. The authorization mode is configured through the <code>set_auth</code> method where we pass a the <code>AuthReq</code> enum choice. <code>AuthReq::all</code> means setting all the flags and implies the passkey entry method and and bonding. We also need to set the IO capabilities through the <code>set_io_cap</code> method. For that, we need to pass a <code>SecurityIOCap</code> enum option which we pass <code>KeyboardOnly</code>. Now although we do not have a keyboard, there exists a method we'll see later where we can pass a passkey that we will hardcode. Here's the code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Configure Device Security</span>
ble_device
    .security()
    .set_auth(AuthReq::all())
    .set_io_cap(SecurityIOCap::KeyboardOnly);
</code></pre>
<p><strong>3️⃣ Create a Scan Instance</strong>: After initializing the NimBLE stack we create a scan instance by calling <a target="_blank" href="https://taks.github.io/esp32-nimble/esp32_nimble/struct.BLEDevice.html#method.get_scan"><code>get_scan</code></a>, this will create a <a target="_blank" href="https://taks.github.io/esp32-nimble/esp32_nimble/struct.BLEScan.html"><code>BLEScan</code></a> instance. This instance would allow us to start looking for advertising servers. Heres the code:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ble_scan = ble_device.get_scan();
</code></pre>
<p><strong>4️⃣ Configure Scan Parameters and Callback</strong>: Now that we have a scan instance we can configure the scan parameters as done previously in the past <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-scanner">BLE scanner post</a>. One difference here is that the <code>on_result</code> method has been replaced by a <code>find_device</code> <code>async</code> method. In this case, to make things easier, rather than printing all advertising devices, we're going to look for and connect one particular peripheral device. <code>find_device</code> has two parameters; a <code>ms</code> duration defining how long to look for a particular device, and a closure passing a <code>BLEAdvertisedDevice</code> as a token. In this closure, we are extracting the advertising device names to see if they match the <code>name</code> we're looking for. <code>DEVICE_NAME</code> is a <code>const</code> defined earlier in the code reflecting the <code>&amp;str</code> name.</p>
<p>Upon completion of the scan process, note that the device would be a handle for a <code>BLEAdvertisedDevice</code> type wrapped in an <code>Option</code>. Note also how the <strong>scan interval</strong> chosen is 100ms and the <strong>scan window</strong> is 99ms.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> device = ble_scan
    .active_scan(<span class="hljs-literal">true</span>)
    .interval(<span class="hljs-number">100</span>)
    .window(<span class="hljs-number">99</span>)
    .find_device(<span class="hljs-number">10000</span>, |device| device.name() == DEVICE_NAME)
    .<span class="hljs-keyword">await</span>
    .unwrap();
</code></pre>
<p>5️⃣ <strong>Instantiate Client and Define Behaviour On Connection to a Peripheral:</strong> Upon identifying a device to connect to the <code>Option</code> should return a <code>Some</code> wrapping a <code>BLEAdvertisedDevice</code> . For the obtained device, we need to instantiate the <code>Client</code> and define connection behavior. We instantiate a <code>client</code> using the <code>BLEClientnew</code> method.</p>
<p>Using the <code>client</code> instance there exists a <code>on_connect</code> method for <code>BLEClient</code>. <code>on_connect</code> has one argument which is a closure that passes a handle for a <code>BLEClient</code> that contains the connected client information. In the closure body, upon connect, we'll print that we are connected and update the connection parameters. To update connection parameters, the <code>BLEClient</code> <code>update_conn_params</code> method is used. Here's the code:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(device) = device {
    <span class="hljs-comment">// Create Client Handle</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> client = BLEClient::new();

    <span class="hljs-comment">// Define Connect Behaviour</span>
    client.on_connect(|client| {    
    <span class="hljs-comment">// Update Connect Parameters on Connect</span>
        client.update_conn_params(<span class="hljs-number">120</span>, <span class="hljs-number">250</span>, <span class="hljs-number">0</span>, <span class="hljs-number">60</span>).unwrap();
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Connected to {}"</span>, DEVICE_NAME);
    });

   <span class="hljs-comment">// Remainder of code</span>
}
</code></pre>
<p>6️⃣ <strong>Establish a Connection:</strong> Now that we've identified the device we want to connect to and the connection behavior, we can establish the connection. This is done by calling the <code>connect</code> method for the identified <code>device</code>. All we need to do is pass the device address as an argument which can be accessed using the <code>BLEAdvertisedDeviceaddr</code> method.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(device) = device {
    <span class="hljs-comment">// Prior code from step 4</span>

   <span class="hljs-comment">// Connect to advertising device using its address</span>
   client.connect(device.addr()).<span class="hljs-keyword">await</span>.unwrap();

   <span class="hljs-comment">// Remainder of code</span>
}
</code></pre>
<p><strong>7️⃣ Secure the Connection:</strong> After connection establishment, we need to secure the connection. To secure the connection we need to provide the passkey that will be entered in the process. This is done through the <code>on_passkey_request</code> method where a 6-digit passkey needs to be provided. This passkey should match the key entered at the other peer. Afterward, <code>secure_connection</code> an <code>async</code> method is called to establish a secure connection:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Enter 123456 passkey on request</span>
client.on_passkey_request(|| <span class="hljs-number">123456</span>);

<span class="hljs-comment">// Secure Connection</span>
client.secure_connection().<span class="hljs-keyword">await</span>.unwrap();
</code></pre>
<p>That's it for configuration!</p>
<h3 id="heading-application-code"><strong>📱 Application Code</strong></h3>
<p>The code in this section is identical to what has been developed in the BLE Client example. However, note what differs is that the remote characteristic security level can be different depending on its properties. To be able to access the characteristic, our client would need to have a matching security level.</p>
<p><strong>Identify Services and Characteristics available:</strong> Since we are operating as a <strong>Client</strong>, we need to get the "remote" <strong>services</strong> and <strong>characteristics</strong> that are available at the <strong>server</strong>. The UUIDs for these services should be known beforehand. Meaning, the <strong>client</strong> should be familiar with the <strong>services</strong> the <strong>server</strong> offers and their UUIDs to identify them.</p>
<p>In the first step, we need to obtain a <strong>service</strong> and then follow it by obtaining the <strong>characteristic(s)</strong> corresponding to that <strong>service</strong>. For the connected <code>client</code> of <code>BLEClient</code> type, there exists a <code>get_service</code> method that takes a UUID as a parameter. <code>get_service</code> is an <code>async</code> method returning a <code>Result</code> wrapping a <code>BLERemoteService</code>. In turn, <code>BLERemoteService</code> has a <code>get_characteristic</code> method with a similar signature that returns a <code>BLERemoteCharacteristic</code>. Heres the corresponding code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Seek and Create Handle for the BLE Remote Server Service corresponding to the UUID</span>
<span class="hljs-keyword">let</span> service = client
        .get_service(uuid128!(<span class="hljs-string">"9b574847-f706-436c-bed7-fc01eb0965c1"</span>))
        .<span class="hljs-keyword">await</span>
        .unwrap();

<span class="hljs-comment">// Seek and Create Handle for the BLE Remote Server Characteristic corresponding to the UUID</span>
<span class="hljs-keyword">let</span> uuid = uuid128!(<span class="hljs-string">"681285a6-247f-48c6-80ad-68c3dce18585"</span>);
<span class="hljs-keyword">let</span> characteristic = service.get_characteristic(uuid).<span class="hljs-keyword">await</span>.unwrap();
</code></pre>
<p><strong>Read From Characteristic(s):</strong> Now that we have access to the remote <strong>characteristic</strong> through the <code>characteristic</code> handle, we can perform any supported GATT operations. For this case, for a server we create we know that the characteristic would at least support a read operation. Using the <code>BLERemoteCharacteristicread_value</code> method we receive a <code>Result</code> wrapping a <code>Vec&lt;u8&gt;</code>. As indicated earlier we mentioned that we want to update the read value every 1 sec as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">loop</span> {
    <span class="hljs-comment">// Read &amp; Print Characteristic Value</span>
    <span class="hljs-keyword">let</span> value = characteristic.read_value().<span class="hljs-keyword">await</span>.unwrap();
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Read Value: {:?}"</span>, value);

    <span class="hljs-comment">// Wait 1 second before reading again</span>
    FreeRtos::delay_ms(<span class="hljs-number">1000</span>);
}
</code></pre>
<blockquote>
<p>📝 <strong>Note</strong>: While not incorporated in this code, it would be beneficial to identify the supported GATT operations for a characteristic beforehand. For that, there exists <code>BLERemoteCharacteristic</code> methods like <code>can_read</code>, <code>can_notify</code>, <code>can_write</code>, and so on that return a <code>bool</code> indicating if the operation is supported.</p>
</blockquote>
<h2 id="heading-testing">🧪 Testing</h2>
<p>In order to test this code, you can use <a target="_blank" href="https://www.nordicsemi.com/Products/Development-tools/nRF-Connect-for-mobile">nRF connect</a> mobile app or the <a target="_blank" href="https://learn.adafruit.com/bluefruit-le-connect/ios-setup">bluefruit connect</a> mobile app. In either app, you will be able to create an advertising <strong>peripheral</strong> as a <strong>server</strong> and define <strong>services</strong> and <strong>characteristics</strong>. Be mindful that the services you create need to have the same UUID defined in our code.</p>
<blockquote>
<p>📝 <strong>Note</strong>: When testing, make sure to change <code>DEVICE_NAME</code> accordingly.</p>
</blockquote>
<h2 id="heading-full-application-code"><strong>📱Full Application Code</strong></h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32_nimble::{enums::*, uuid128, BLEClient, BLEDevice};
<span class="hljs-keyword">use</span> esp_idf_hal::delay::FreeRtos;
<span class="hljs-keyword">use</span> esp_idf_hal::task::block_on;
<span class="hljs-keyword">use</span> esp_idf_sys <span class="hljs-keyword">as</span> _;

<span class="hljs-keyword">const</span> DEVICE_NAME: &amp;<span class="hljs-built_in">str</span> = <span class="hljs-string">"iPhone"</span>;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    esp_idf_sys::link_patches();

    block_on(<span class="hljs-keyword">async</span> {
        <span class="hljs-comment">// Acquire BLE Device Handle</span>
        <span class="hljs-keyword">let</span> ble_device = BLEDevice::take();

        <span class="hljs-comment">// Configure Device Security</span>
        ble_device
            .security()
            .set_auth(AuthReq::all())
            .set_io_cap(SecurityIOCap::KeyboardOnly);

        <span class="hljs-comment">// Acquire Scan Handle</span>
        <span class="hljs-keyword">let</span> ble_scan = ble_device.get_scan();

        <span class="hljs-comment">// Scan and Find Device/Server</span>
        <span class="hljs-keyword">let</span> device = ble_scan
            .active_scan(<span class="hljs-literal">true</span>)
            .interval(<span class="hljs-number">100</span>)
            .window(<span class="hljs-number">99</span>)
            .find_device(<span class="hljs-number">10000</span>, |device| device.name() == DEVICE_NAME)
            .<span class="hljs-keyword">await</span>
            .unwrap();

        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(device) = device {
            <span class="hljs-comment">// Create Client Handle</span>
            <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> client = BLEClient::new();

            <span class="hljs-comment">// Define Connect Behaviour</span>
            client.on_connect(|client| {
                <span class="hljs-comment">// Update Connect Parameters on Connect</span>
                client.update_conn_params(<span class="hljs-number">120</span>, <span class="hljs-number">250</span>, <span class="hljs-number">0</span>, <span class="hljs-number">60</span>).unwrap();
                <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Connected to {}"</span>, DEVICE_NAME);
            });

            <span class="hljs-comment">// Connect to advertising device using its address</span>
            client.connect(device.addr()).<span class="hljs-keyword">await</span>.unwrap();

            <span class="hljs-comment">// Enter 123456 passkey on request</span>
            client.on_passkey_request(|| <span class="hljs-number">123456</span>);

            <span class="hljs-comment">// Secure Connection</span>
            client.secure_connection().<span class="hljs-keyword">await</span>.unwrap();

            <span class="hljs-comment">// Seek and Create Handle for the BLE Remote Server Service corresponding to the UUID</span>
            <span class="hljs-keyword">let</span> service = client
                .get_service(uuid128!(<span class="hljs-string">"9b574847-f706-436c-bed7-fc01eb0965c1"</span>))
                .<span class="hljs-keyword">await</span>
                .unwrap();

            <span class="hljs-comment">// Seek and Create Handle for the BLE Remote Server Characteristic corresponding to the UUID</span>
            <span class="hljs-keyword">let</span> uuid = uuid128!(<span class="hljs-string">"681285a6-247f-48c6-80ad-68c3dce18585"</span>);
            <span class="hljs-keyword">let</span> characteristic = service.get_characteristic(uuid).<span class="hljs-keyword">await</span>.unwrap();

            <span class="hljs-keyword">loop</span> {
                <span class="hljs-comment">// Read &amp; Print Characteristic Value</span>
                <span class="hljs-keyword">let</span> value = characteristic.read_value().<span class="hljs-keyword">await</span>.unwrap();
                <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Read Value: {:?}"</span>, value);

                <span class="hljs-comment">// Wait 1 second before reading again</span>
                FreeRtos::delay_ms(<span class="hljs-number">1000</span>);
            }
        }
    });
}
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>This post introduced how to create a secure BLE client on the ESP32-C3 with Rust. This was by using the <code>esp32-nimble</code> crate in a standard library development environment using the <code>esp-idf-hal</code> . In this post, the ESP32-C3 was configured as a secure central device scanning for a peripheral server device containing a service. The device also assumes a client role after a connection is established and secures the connection with a passkey. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[Embedded Rust Bluetooth on ESP: BLE Client]]></title><description><![CDATA[This post is the fourth of a multi-part series where I'm exploring the use of Bluetooth Low Energy along embedded Rust on the ESP32. Information in this post might rely on knowledge presented in past posts.


Embedded Rust Bluetooth on ESP: BLE Scann...]]></description><link>https://blog.theembeddedrustacean.com/embedded-rust-bluetooth-on-esp-ble-client</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embedded-rust-bluetooth-on-esp-ble-client</guid><category><![CDATA[Rust]]></category><category><![CDATA[bluetooth]]></category><category><![CDATA[embedded]]></category><category><![CDATA[iot]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Mon, 01 Apr 2024 09:00:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1711909287026/046a366b-cbda-40cb-affc-d637bdedfd6a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong><em>This post is the fourth of a multi-part series where I'm exploring the use of Bluetooth Low Energy along embedded Rust on the ESP32. Information in this post might rely on knowledge presented in past posts.</em></strong></p>
</blockquote>
<ol>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-scanner"><strong>Embedded Rust Bluetooth on ESP: BLE Scanner</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-advertiser"><strong>Embedded Rust Bluetooth on ESP: BLE Advertiser</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-server"><strong>Embedded Rust Bluetooth on ESP: BLE Server</strong></a></p>
</li>
</ol>
<h2 id="heading-introduction"><strong>Introduction</strong></h2>
<p>In this post, we're going to build on the <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-server">previous post</a> to instead create a BLE client. We're going to create a <strong>central</strong> device that will assume the role of a <strong>client</strong> upon connection establishment. Similar to past posts, the code will be built using the <a target="_blank" href="https://crates.io/crates/esp32-nimble/0.6.0"><strong>esp32-nimble</strong> crate.</a></p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Familiarity with standard library development in Rust with the ESP.</p>
</li>
<li><p>Basic knowledge of networking layering concepts/stacks (ex. OSI model).</p>
</li>
<li><p>Basic knowledge of Bluetooth.</p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3 git repo</strong></a>. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
<h4 id="heading-connections"><strong>🔌 Connections</strong></h4>
<p>  No connections are required for this example.</p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨 Software Design</strong></h2>
<p>In our example, we'll be creating a <strong>central client</strong> with one <strong>characteristic</strong> that can be <strong>read</strong> from. In that context, after configuring our device, the code will take the following steps:</p>
<ol>
<li><p>Scan and find a particular advertiser</p>
</li>
<li><p>Establish a connection</p>
</li>
<li><p>Read the characteristic value every second</p>
</li>
</ol>
<h2 id="heading-code-implementation"><strong>👨‍💻 Code Implementation</strong></h2>
<h3 id="heading-crate-imports"><strong>📥 Crate Imports</strong></h3>
<p>In this implementation, the following crates are required:</p>
<ul>
<li><p>The <code>esp_idf_hal</code> crate to import delays and blocking abstractions.</p>
</li>
<li><p>The <code>esp_idf_sys</code> crate since its needed.</p>
</li>
<li><p>The <code>esp32_nimble</code> crate for the BLE abstractions.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32_nimble::{uuid128, BLEClient, BLEDevice};
<span class="hljs-keyword">use</span> esp_idf_hal::delay::FreeRtos;
<span class="hljs-keyword">use</span> esp_idf_hal::task::block_on;
<span class="hljs-keyword">use</span> esp_idf_sys <span class="hljs-keyword">as</span> _;
</code></pre>
<h3 id="heading-initializationconfiguration-code"><strong>🎛 Initialization/Configuration Code</strong></h3>
<p><strong>1️⃣ Obtain a handle for the BLE device</strong>: Similar to the pattern we've seen in embedded Rust with peripherals, as part of the singleton design pattern, we first have to take ownership of the device peripherals. In this context, its the <code>BLEDevice</code> that we need to take ownership of. This is done using the <code>take()</code> associated method. Here I create a BLE device handler named <code>ble_device</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ble_device = BLEDevice::take();
</code></pre>
<p><strong>2️⃣ Create a Scan Instance</strong>: After initializing the NimBLE stack we create a scan instance by calling <a target="_blank" href="https://taks.github.io/esp32-nimble/esp32_nimble/struct.BLEDevice.html#method.get_scan"><code>get_scan</code></a>, this wi<a target="_blank" href="https://taks.github.io/esp32-nimble/esp32_nimble/struct.BLEDevice.html#method.get_scan">ll creat</a>e a <a target="_blank" href="https://taks.github.io/esp32-nimble/esp32_nimble/struct.BLEScan.html"><code>BLEScan</code></a> instance<a target="_blank" href="https://taks.github.io/esp32-nimble/esp32_nimble/struct.BLEScan.html">. This</a> instance would allow us to start looking for advertising servers. Heres the code:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ble_scan = ble_device.get_scan();
</code></pre>
<p><strong>3️⃣ Configure Scan Parameters and Callback</strong>: Now that we have a scan instance we can configure the scan parameters as done previously in the past <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-scanner">BLE scanner post</a>. One difference here is that the <code>on_result</code> method has been replaced by a <code>find_device</code> <code>async</code> method. In this case, to make things easier, rather than printing all advertising devices, we're going to look for and connect one particular peripheral device. <code>find_device</code> has two parameters; a <code>ms</code> duration defining how long to look for a particular device, and a closure passing a <code>BLEAdvertisedDevice</code> as a token. In this closure, we are extracting the advertising device names to see if they match the <code>name</code> we're looking for. <code>DEVICE_NAME</code> is a <code>const</code> defined earlier in the code reflecting the <code>&amp;str</code> name.</p>
<p>Upon completion of the scan process, note that the device would be a handle for a <code>BLEAdvertisedDevice</code> type wrapped in an <code>Option</code>. Note also how the <strong>scan interval</strong> chosen is 100ms and the <strong>scan window</strong> is 99ms.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> device = ble_scan
    .active_scan(<span class="hljs-literal">true</span>)
    .interval(<span class="hljs-number">100</span>)
    .window(<span class="hljs-number">99</span>)
    .find_device(<span class="hljs-number">10000</span>, |device| device.name() == DEVICE_NAME)
    .<span class="hljs-keyword">await</span>
    .unwrap();
</code></pre>
<p>4️⃣ <strong>Instantiate Client and Define Behaviour On Connection to a Peripheral:</strong> Upon identifying a device to connect to the <code>Option</code> should return a <code>Some</code> wrapping a <code>BLEAdvertisedDevice</code> . For the obtained device, we need to instantiate the <code>Client</code> and define connection behavior. We instantiate a <code>client</code> using the <code>BLEClient</code> <code>new</code> method.</p>
<p>Using the <code>client</code> instance there exists a <code>on_connect</code> method for <code>BLEClient</code>. <code>on_connect</code> has one argument which is a closure that passes a handle for a <code>BLEClient</code> that contains the connected client information. In the closure body, upon connect, we'll print that we are connected and update the connection parameters. To update connection parameters, the <code>BLEClient</code> <code>update_conn_params</code> method is used and has the following signature:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">update_conn_params</span></span>(
    &amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>,
    min_interval: <span class="hljs-built_in">u16</span>,
    max_interval: <span class="hljs-built_in">u16</span>,
    latency: <span class="hljs-built_in">u16</span>,
    timeout: <span class="hljs-built_in">u16</span>
) -&gt; <span class="hljs-built_in">Result</span>&lt;(), BLEError&gt;
</code></pre>
<p><code>min_interval</code> is a value for the minimum <strong>connection interval</strong> in ms, <code>max_interval</code> is a value for the maximum <strong>connection interval</strong> in ms, <code>latency</code> is expressed as the number of intervals to skip if theres no data to transmit, and <code>timeout</code> is the <strong>supervision timeout</strong> time in 10ms units. Here's the code:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(device) = device {
    <span class="hljs-comment">// Create Client Handle</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> client = BLEClient::new();

    <span class="hljs-comment">// Define Connect Behaviour</span>
    client.on_connect(|client| {    
    <span class="hljs-comment">// Update Connect Parameters on Connect</span>
        client.update_conn_params(<span class="hljs-number">120</span>, <span class="hljs-number">250</span>, <span class="hljs-number">0</span>, <span class="hljs-number">60</span>).unwrap();
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Connected to {}"</span>, DEVICE_NAME);
    });

   <span class="hljs-comment">// Remainder of code</span>
}
</code></pre>
<p>5️⃣ <strong>Establish a Connection:</strong> Now that we've identified the device we want to connect to and the connection behavior, we can establish the connection. This is done by calling the <code>connect</code> method for the identified <code>device</code>. All we need to do is pass the device address as an argument which can be accessed using the <code>BLEAdvertisedDevice</code> <code>addr</code> method.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(device) = device {
    <span class="hljs-comment">// Prior code from step 4</span>

   <span class="hljs-comment">// Connect to advertising device using its address</span>
   client.connect(device.addr()).<span class="hljs-keyword">await</span>.unwrap();

   <span class="hljs-comment">// Remainder of code</span>
}
</code></pre>
<p>That's it for configuration!</p>
<h3 id="heading-application-code"><strong>📱 Application Code</strong></h3>
<p><strong>Identify Services and Characteristics available:</strong> Since we are operating as a <strong>Client</strong>, we need to get the "remote" <strong>services</strong> and <strong>characteristics</strong> that are available at the <strong>server</strong>. The UUIDs for these services should be known beforehand. Meaning, the <strong>client</strong> should be familiar with the <strong>services</strong> the <strong>server</strong> offers and their UUIDs to identify them.</p>
<p>In the first step, we need to obtain a <strong>service</strong> and then follow it by obtaining the <strong>characteristic(s)</strong> corresponding to that <strong>service</strong>. For the connected <code>client</code> of <code>BLEClient</code> type, there exists a <code>get_service</code> method that takes a UUID as a parameter. <code>get_service</code> is an <code>async</code> method returning a <code>Result</code> wrapping a <code>BLERemoteService</code>. In turn, <code>BLERemoteService</code> has a <code>get_characteristic</code> method with a similar signature that returns a <code>BLERemoteCharacteristic</code>. Heres the corresponding code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Seek and Create Handle for the BLE Remote Server Service corresponding to the UUID</span>
<span class="hljs-keyword">let</span> service = client
        .get_service(uuid128!(<span class="hljs-string">"9b574847-f706-436c-bed7-fc01eb0965c1"</span>))
        .<span class="hljs-keyword">await</span>
        .unwrap();

<span class="hljs-comment">// Seek and Create Handle for the BLE Remote Server Characteristic corresponding to the UUID</span>
<span class="hljs-keyword">let</span> uuid = uuid128!(<span class="hljs-string">"681285a6-247f-48c6-80ad-68c3dce18585"</span>);
<span class="hljs-keyword">let</span> characteristic = service.get_characteristic(uuid).<span class="hljs-keyword">await</span>.unwrap();
</code></pre>
<p><strong>Read From Characteristic(s):</strong> Now that we have access to the remote <strong>characteristic</strong> through the <code>characteristic</code> handle, we can perform any supported GATT operations. For this case, for a server we create we know that the characteristic would at least support a read operation. Using the <code>BLERemoteCharacteristic</code> <code>read_value</code> method we receive a <code>Result</code> wrapping a <code>Vec&lt;u8&gt;</code>. As indicated earlier we mentioned that we want to update the read value every 1 sec as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">loop</span> {
    <span class="hljs-comment">// Read &amp; Print Characteristic Value</span>
    <span class="hljs-keyword">let</span> value = characteristic.read_value().<span class="hljs-keyword">await</span>.unwrap();
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Read Value: {:?}"</span>, value);

    <span class="hljs-comment">// Wait 1 second before reading again</span>
    FreeRtos::delay_ms(<span class="hljs-number">1000</span>);
}
</code></pre>
<blockquote>
<p>📝 <strong>Note</strong>: While not incorporated in this code, it would be beneficial to identify the supported GATT operations for a characteristic beforehand. For that, there exists <code>BLERemoteCharacteristic</code> methods like <code>can_read</code>, <code>can_notify</code>, <code>can_write</code>, and so on that return a <code>bool</code> indicating if the operation is supported.</p>
</blockquote>
<h2 id="heading-testing">🧪 Testing</h2>
<p>In order to test this code, you can use <a target="_blank" href="https://www.nordicsemi.com/Products/Development-tools/nRF-Connect-for-mobile">nRF connect</a> mobile app or the <a target="_blank" href="https://learn.adafruit.com/bluefruit-le-connect/ios-setup">bluefruit connect</a> mobile app. In either app, you will be able to create an advertising <strong>peripheral</strong> as a <strong>server</strong> and define <strong>services</strong> and <strong>characteristics</strong>. Be mindful that the services you create need to have the same UUID defined in our code.</p>
<blockquote>
<p>📝 <strong>Note</strong>: When testing, make sure to change <code>DEVICE_NAME</code> accordingly.</p>
</blockquote>
<h2 id="heading-full-application-code"><strong>📱Full Application Code</strong></h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32_nimble::{uuid128, BLEClient, BLEDevice};
<span class="hljs-keyword">use</span> esp_idf_hal::delay::FreeRtos;
<span class="hljs-keyword">use</span> esp_idf_hal::task::block_on;
<span class="hljs-keyword">use</span> esp_idf_sys <span class="hljs-keyword">as</span> _;

<span class="hljs-keyword">const</span> DEVICE_NAME: &amp;<span class="hljs-built_in">str</span> = <span class="hljs-string">"Device Name"</span>;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    esp_idf_sys::link_patches();

    block_on(<span class="hljs-keyword">async</span> {
        <span class="hljs-comment">// Acquire BLE Device Handle</span>
        <span class="hljs-keyword">let</span> ble_device = BLEDevice::take();
        <span class="hljs-comment">// Acquire Scan Handle</span>
        <span class="hljs-keyword">let</span> ble_scan = ble_device.get_scan();
        <span class="hljs-comment">// Scan and Find Device/Server</span>
        <span class="hljs-keyword">let</span> device = ble_scan
            .active_scan(<span class="hljs-literal">true</span>)
            .interval(<span class="hljs-number">100</span>)
            .window(<span class="hljs-number">99</span>)
            .find_device(<span class="hljs-number">10000</span>, |device| device.name() == DEVICE_NAME)
            .<span class="hljs-keyword">await</span>
            .unwrap();

        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(device) = device {
            <span class="hljs-comment">// Create Client Handle</span>
            <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> client = BLEClient::new();

            <span class="hljs-comment">// Define Connect Behaviour</span>
            client.on_connect(|client| {
                <span class="hljs-comment">// Update Connect Parameters on Connect</span>
                client.update_conn_params(<span class="hljs-number">120</span>, <span class="hljs-number">250</span>, <span class="hljs-number">0</span>, <span class="hljs-number">60</span>).unwrap();
                <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Connected to {}"</span>, DEVICE_NAME);
            });

            <span class="hljs-comment">// Connect to advertising device using its address</span>
            client.connect(device.addr()).<span class="hljs-keyword">await</span>.unwrap();

            <span class="hljs-comment">// Seek and Create Handle for the BLE Remote Server Service corresponding to the UUID</span>
            <span class="hljs-keyword">let</span> service = client
                .get_service(uuid128!(<span class="hljs-string">"9b574847-f706-436c-bed7-fc01eb0965c1"</span>))
                .<span class="hljs-keyword">await</span>
                .unwrap();

            <span class="hljs-comment">// Seek and Create Handle for the BLE Remote Server Characteristic corresponding to the UUID</span>
            <span class="hljs-keyword">let</span> uuid = uuid128!(<span class="hljs-string">"681285a6-247f-48c6-80ad-68c3dce18585"</span>);
            <span class="hljs-keyword">let</span> characteristic = service.get_characteristic(uuid).<span class="hljs-keyword">await</span>.unwrap();

            <span class="hljs-keyword">loop</span> {
                <span class="hljs-comment">// Read &amp; Print Characteristic Value</span>
                <span class="hljs-keyword">let</span> value = characteristic.read_value().<span class="hljs-keyword">await</span>.unwrap();
                <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Read Value: {:?}"</span>, value);

                <span class="hljs-comment">// Wait 1 second before reading again</span>
                FreeRtos::delay_ms(<span class="hljs-number">1000</span>);
            }
        }
    });
}
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>This post introduced how to create a BLE client on the ESP32-C3 with Rust. This was by using the <code>esp32-nimble</code> crate in a standard library development environment using the <code>esp-idf-hal</code> . In this post, the ESP32-C3 was configured as a central device scanning for a peripheral server device containing a service. The device also assumes a client role after a connection is established. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[Embedded Rust Bluetooth on ESP: BLE Server]]></title><description><![CDATA[This post is the third of a multi-part series where I'm exploring the use of Bluetooth Low Energy along embedded Rust on the ESP32. Information in this post might rely on knowledge presented in past posts.


Embedded Rust Bluetooth on ESP: BLE Scanne...]]></description><link>https://blog.theembeddedrustacean.com/embedded-rust-bluetooth-on-esp-ble-server</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embedded-rust-bluetooth-on-esp-ble-server</guid><category><![CDATA[Bluetooth Low Energy]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Rust programming]]></category><category><![CDATA[iot]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Mon, 25 Mar 2024 09:00:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1711232405795/cce73d61-c637-4ad4-b594-ef8dba0a9b71.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong><em>This post is the third of a multi-part series where I'm exploring the use of Bluetooth Low Energy along embedded Rust on the ESP32. Information in this post might rely on knowledge presented in past posts.</em></strong></p>
</blockquote>
<ol>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-scanner"><strong>Embedded Rust Bluetooth on ESP: BLE Scanner</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-advertiser"><strong>Embedded Rust Bluetooth on ESP: BLE Advertiser</strong></a></p>
</li>
</ol>
<h2 id="heading-introduction"><strong>Introduction</strong></h2>
<p>In the first two posts of this series, we've dealt with activities conducted pre-connection establishment. This included a <strong>central</strong> device scanning for advertising <strong>peripheral</strong> devices (<a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-scanner">BLE Scanner</a>) and a <strong>peripheral</strong> device advertising its presence to <strong>central</strong> devices (<a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-advertiser">BLE Advertiser</a>). In this post, we want to transition to the post-connection phase. This means that a connection needs to be established first, and then the connected devices assume the roles of <strong>server</strong> and <strong>client</strong> when exchanging data. In this case the devices can start exchanging data in the form of what is known as <strong>attributes</strong>.</p>
<p>In this post, we're going to create a <strong>peripheral</strong> device that will assume the role of a <strong>server</strong> upon connection establishment. Similar to past posts, the code will be built using the <a target="_blank" href="https://crates.io/crates/esp32-nimble/0.6.0"><strong>esp32-nimble</strong> crate.</a></p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Familiarity with standard library development in Rust with the ESP.</p>
</li>
<li><p>Basic knowledge of networking layering concepts/stacks (ex. OSI model).</p>
</li>
<li><p>Basic knowledge of Bluetooth.</p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3 git repo</strong></a>. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
<h4 id="heading-connections"><strong>🔌 Connections</strong></h4>
<p>  No connections are required for this example.</p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨 Software Design</strong></h2>
<h3 id="heading-connection-establishment-amp-management">🔗 <strong>Connection Establishment &amp; Management</strong></h3>
<p>Once a <strong>central</strong> decides to connect to a <strong>peripheral</strong>, it would send a connection request to the <strong>peripheral</strong>. The <strong>peripheral</strong> would in turn accept the connection and establish a bi-directional communication channel between the two devices. Once a new connection is established, new connection settings/parameters are involved to manage the connection. Notice how in terms of connection management, we are still using the <strong>peripheral</strong> and <strong>central</strong> roles. Some important settings include the following:</p>
<ol>
<li><p><strong>Connection Interval:</strong> Recall scan intervals in the pre-connection phase. This is a similar concept, albeit post-connection. The <strong>connection interval</strong> defines how often the <strong>central</strong> device communicates with the <strong>peripheral</strong> device. A shorter <strong>connection interval</strong> allows for more frequent communication, resulting in lower latency but higher power consumption. Conversely, a longer <strong>connection interval</strong> reduces the frequency of communication, resulting in higher latency but lower power consumption. The lower power consumption is a result of the devices going to sleep after all needed packets are exchanged.</p>
<p> The <strong>connection interval</strong> is typically negotiated during the establishment of a connection, but can also be updated during a connection. It is specified in units of 1.25 milliseconds, with allowable values ranging from 7.5 ms to 4,000 ms (or 7.5 ms to 4 seconds).</p>
<p> Within a <strong>connection interval</strong>, there also exists <strong>connection events</strong>. A <strong>connection event</strong>, triggered by a <strong>central</strong> sending a packet, marks the exchange of data between devices to either synchronize their clocks and/or communicate data.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1711004362325/30723ed1-9482-4085-ae70-e6c4974dbef8.png" alt class="image--center mx-auto" /></p>
<ol start="2">
<li><p><strong>Peripheral Latency:</strong> This allows the <strong>peripheral</strong> device to skip a certain number of connection events (intervals) if it has no data to transmit. This also allows the <strong>peripheral</strong> to conserve power by entering a low-power mode between connection events. One way of viewing this is extending the <strong>connection interval</strong> when theres no data to share.</p>
</li>
<li><p><strong>Supervision Timeout:</strong> This limits the duration of inactivity before the connection is considered lost. If the <strong>peripheral</strong> has no data to transmit for a prolonged period exceeding the supervision timeout, the connection may be terminated by either the <strong>peripheral</strong> or the <strong>central</strong> device.</p>
</li>
</ol>
<h3 id="heading-data-exchange">🔃 <strong>Data Exchange</strong></h3>
<p>This is the phase where the devices assume <strong>client</strong> and <strong>server</strong> roles. The meaning of these roles is similar to the context of regular networking. A <strong>server</strong> is a data provider and a <strong>client</strong> is a data requestor. While a <strong>central</strong> role often aligns with a <strong>client</strong> role and a <strong>peripheral</strong> roles often aligns with a <strong>server</strong> role, thats not always the case. Pre-connection roles are not tied to post-connection data exchange roles. Meaning that a <strong>peripheral</strong> or <strong>central</strong> device can dynamically switch between data exchange roles, allowing a device to act either as a <strong>client</strong> or a <strong>server</strong> as needed.</p>
<p>In a BLE connection, all data is exchanged through <strong>attributes</strong>. An <strong>attribute</strong> itself is a data structure and is the basic building block forming larger data structures defined by the GATT layer like <strong>services</strong> and <strong>characteristics</strong> (more next). An <strong>attribute</strong> can simply be viewed as a shared variable between two devices that both can modify. Why is this necessary? one might wonder. Meaning that, we could have exchanged data purely through attributes. Interoperability is one of the main reasons. By defining standardized <strong>services</strong> and <strong>characteristics</strong>, BLE devices from different manufacturers can communicate and interoperate seamlessly. This promotes compatibility and allows for the creation of diverse ecosystems of BLE devices and applications.</p>
<p>There are several GATT layer operations that can modify attributes. These operations are split into <strong>client</strong>-<strong>initiated</strong> and <strong>server</strong>-<strong>initiated</strong>, depending on the source triggering a data exchange operation. Here are the main GATT operations:</p>
<ol>
<li><p><strong>Read</strong>: This is a <strong>client</strong>-<strong>initiated</strong> operation. This operation allows a <strong>client</strong> device to retrieve the value of a <strong>characteristic</strong> from a <strong>server</strong> device. The <strong>client</strong> sends a read request to the <strong>server</strong>, and the <strong>server</strong> responds with the current value of the <strong>characteristic</strong>.</p>
</li>
<li><p><strong>Write</strong>: This is a <strong>client</strong>-<strong>initiated</strong> operation. This operation allows a <strong>client</strong> device to set the value of a <strong>characteristic</strong> on a <strong>server</strong> device. The <strong>client</strong> sends a write request containing the new value, and the <strong>server</strong> updates the <strong>characteristic</strong> value accordingly.</p>
</li>
<li><p><strong>Write Without Response</strong>: This is a <strong>client</strong>-<strong>initiated</strong> operation. Similar to the write operation, this operation allows a <strong>client</strong> device to set the value of a <strong>characteristic</strong> on a <strong>server</strong> device. However, unlike the write operation, the write without response operation does not require an acknowledgment from the <strong>server</strong>. This can be used for faster data transmission when acknowledgment is not necessary.</p>
</li>
<li><p><strong>Notify</strong>: This is a <strong>server</strong>-<strong>initiated</strong> operation. This operation allows a <strong>server</strong> device to send notifications to a <strong>client</strong> device when the value of a <strong>characteristic</strong> changes. The <strong>client</strong> subscribes to notifications for a specific <strong>characteristic</strong>, and the <strong>server</strong> sends notifications whenever the <strong>characteristic</strong> value is updated.</p>
</li>
<li><p><strong>Indicate</strong>: This is a <strong>server</strong>-<strong>initiated</strong> operation. Similar to notifications, this operation allows a <strong>server</strong> device to send indications to a <strong>client</strong> device when the value of a <strong>characteristic</strong> changes. However, indications require acknowledgment from the <strong>client</strong>, ensuring reliable delivery of data.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1711010675893/2e1fca16-8c11-4ba2-afff-0de1788aac63.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-services-amp-characteristics">🛎️ <strong>Services &amp; Characteristics</strong></h3>
<p>As mentioned earlier, the basic building block in BLE is an <strong>attribute</strong>. As such, both <strong>services</strong> and <strong>characteristics</strong> are special types of <strong>attributes</strong> reserved to provide some structure in the data exchange process.</p>
<p><strong>Services</strong> are a type of attribute that represent a collection of related data or functionalities offered by a BLE device. Each <strong>service</strong> is identified by a unique 16-bit or 128-bit Universally Unique Identifier (<strong>UUID</strong>). <strong>Services</strong> typically contain one or more <strong>characteristics</strong> and define the capabilities of the device. <strong>Services</strong> act as containers for <strong>characteristics</strong> and provide a logical grouping for related functionality.</p>
<p><strong>Characteristics</strong> are another type of <strong>attribute</strong> that represent individual data elements within a <strong>service</strong>. Each <strong>characteristic</strong> has its own unique 16-bit or 128-bit <strong>UUID</strong> as well within the context of its parent <strong>service</strong>. <strong>Characteristics</strong> define specific pieces of data or operations that can be performed, such as reading a sensor value or writing a configuration parameter. <strong>Characteristics</strong> have properties that define how they can be accessed and manipulated, such as <strong>read</strong>, <strong>write</strong>, <strong>notify</strong>, and <strong>indicate</strong> (GATT Operations).</p>
<p>There also exists <strong>descriptors</strong> which are additional <strong>attributes</strong> that provide metadata or additional information about a <strong>characteristic</strong>. <strong>Descriptors</strong> are optional and can be used to specify <strong>characteristics'</strong> properties, permissions, and user-friendly descriptions. <strong>Descriptors</strong> like all other <strong>attributes</strong> also have their own unique 16-bit or 128-bit <strong>UUID. Descriptors</strong> provide context and additional details about <strong>characteristics</strong>, enhancing their usability and interoperability.</p>
<p>Lets take an example. Say we want to create a BLE-enabled fitness tracker which part of it tracks the heart rate. One <strong>service</strong> could be a heart rate <strong>service</strong> that would encompass heart rate-related functionalities. This type of <strong>service</strong> could have several <strong>characteristics</strong>, one could be a heart rate measurement <strong>characteristic</strong> and another would be a body sensor location <strong>characteristic</strong>.</p>
<p>The BLE standard provides a lot of standard <strong>services</strong> for common applications to leverage in designs. However, we are not mandated to use any of them. We can actually create our own <strong>services</strong> and <strong>characteristics</strong> to exchange data which is what we'll be doing in our example. To exchange data though, a device should have at least one <strong>service</strong> that encompasses at least one <strong>characteristic</strong>.</p>
<p>In our example we'll be creating a <strong>peripheral server</strong> with one <strong>characteristic</strong> that can be <strong>read</strong> from or <strong>written</strong> to.</p>
<h2 id="heading-code-implementation"><strong>👨‍💻 Code Implementation</strong></h2>
<h3 id="heading-crate-imports"><strong>📥 Crate Imports</strong></h3>
<p>In this implementation, the following crates are required:</p>
<ul>
<li><p>The <code>esp_idf_hal</code> crate to import delays.</p>
</li>
<li><p>The <code>esp_idf_sys</code> crate since its needed.</p>
</li>
<li><p>The <code>esp32_nimble</code> crate for the BLE abstractions.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32_nimble::{uuid128, BLEAdvertisementData, BLEDevice, NimbleProperties};
<span class="hljs-keyword">use</span> esp_idf_hal::delay::FreeRtos;
<span class="hljs-keyword">use</span> esp_idf_sys <span class="hljs-keyword">as</span> _;
</code></pre>
<h3 id="heading-initializationconfiguration-code"><strong>🎛 Initialization/Configuration Code</strong></h3>
<p><strong>1️⃣ Obtain a handle for the BLE device</strong>: Similar to the pattern we've seen in embedded Rust with peripherals, as part of the singleton design pattern, we first have to take ownership of the device peripherals. In this context, its the <code>BLEDevice</code> that we need to take ownership of. This is done using the <code>take()</code> associated method. Here I create a BLE device handler named <code>ble_device</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ble_device = BLEDevice::take();
</code></pre>
<p><strong>2️⃣ Create an Advertiser Instance</strong>: After initializing the NimBLE stack we create an advertiser instance by calling <code>get_advertising</code>, this will create a <code>&amp;Mutex&lt;BLEAdvertising&gt;</code> instance. Heres the code:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ble_advertiser = ble_device.get_advertising();
</code></pre>
<p><strong>3️⃣ Obtain Handle for Server</strong>: We create a <code>server</code> instance by calling <code>get_server</code>, this will create a <code>BLEServer</code> instance. Heres the code:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> server = ble_device.get_server();
</code></pre>
<p>4️⃣ <strong>Define Server Connect &amp; Disconnect Behaviour:</strong> using the <code>server</code> instance there exists a <code>on_connect</code> method for <code>BLEServer</code>. <code>on_connect</code> has one argument which is a closure that passes a handle for a <code>BLEServer</code> and a <code>BLEConnDesc</code> that contains connection information. In the closure body, upon connect, we'll print the connection data to the console then update the connection parameters. To update connection parameters, the <code>BLEServerupdate_conn_params</code> method is used and has the following signature:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">update_conn_params</span></span>(
    &amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>,
    conn_handle: <span class="hljs-built_in">u16</span>,
    min_interval: <span class="hljs-built_in">u16</span>,
    max_interval: <span class="hljs-built_in">u16</span>,
    latency: <span class="hljs-built_in">u16</span>,
    timeout: <span class="hljs-built_in">u16</span>
) -&gt; <span class="hljs-built_in">Result</span>&lt;(), BLEError&gt;
</code></pre>
<p><code>conn_handle</code> is the connection handle of the client, <code>min_interval</code> is a value for the minimum <strong>connection interval</strong> in ms, <code>max_interval</code> is a value for the maximum <strong>connection interval</strong> in ms, <code>latency</code> is expressed as the number of intervals to skip if theres no data to transmit, and <code>timeout</code> is the <strong>supervisiontimeout</strong> time in 10ms units. Here's the code:</p>
<pre><code class="lang-rust">server.on_connect(|server, clntdesc| {
    <span class="hljs-comment">// Print connected client data</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{:?}"</span>, clntdesc);
    <span class="hljs-comment">// Update connection parameters</span>
    server
        .update_conn_params(clntdesc.conn_handle(), <span class="hljs-number">24</span>, <span class="hljs-number">48</span>, <span class="hljs-number">0</span>, <span class="hljs-number">60</span>)
        .unwrap();
});
</code></pre>
<p>Similar to <code>on_connect</code> theres an <code>on_disconnect</code> method. <code>on_disconnect</code> also has one argument which is a closure that passes a handle for a <code>BLEConnDesc</code> and a <code>Result</code> that contains the reason for disconnection. All were going to do is to print a message that our device disconnected.</p>
<pre><code class="lang-rust">server.on_disconnect(|_desc, _reason| {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Disconnected, back to advertising"</span>);
});
</code></pre>
<p>5️⃣ <strong>Define Services &amp; Characteristics:</strong> In this example, only one <strong>characteristic</strong> will be defined with a read and notify properties. However, every <strong>characteristic</strong> has to be associated with (listed under) a <strong>service</strong>. To create a service, within <code>BLEServer</code> there exists a <code>create_service</code> method that takes a single <code>BleUuid</code> argument. <code>BleUuid</code> is an enum representing a Bluetooth <strong>UUID</strong>. To make things easier, the nimble crate provides a <code>uuid128</code> macro to parse a 128 UUID from string literals at compile time. We only need to pass a <strong>UUID</strong> string literal and the macro would take care of the rest. We create a service as follows:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Create a service with custom UUID</span>
<span class="hljs-keyword">let</span> my_service = server.create_service(uuid128!(<span class="hljs-string">"9b574847-f706-436c-bed7-fc01eb0965c1"</span>));
</code></pre>
<p>The UUID used is a custom UUID generated using an <a target="_blank" href="https://www.uuidgenerator.net/">online generator</a> tool. <code>create_service</code> also returns a <code>Arc&lt;Mutex&lt;BLEService&gt;&gt;</code>.</p>
<p>Next, we need to create a <strong>characteristic</strong>. This is done using the <code>BLEServicecreate_characteristic</code> method which has two arguments; <code>BlueUuid</code> and <code>NimbleProperties</code>. <code>NimbleProperties</code> is a struct containing a collection of associated constants representing the different GATT operations. These constants can be or'ed together to define the operations that can modify the <strong>characteristic</strong>. Our <strong>characteristic</strong> will support <code>READ</code> and <code>NOTIFY</code> operations. <code>create_characteristic</code> will return a <code>Arc&lt;Mutex&lt;BLECharacteristic&gt;&gt;</code>. As such, we can set a starting value for the <strong>characteristic</strong> that we created using the <code>BLECharacteristicset_value</code> method. <code>set_value</code> takes a single <code>&amp;[u8]</code> parameter. Here's the code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Create a characteristic to associate with created service</span>
<span class="hljs-keyword">let</span> my_service_characteristic = my_service.lock().create_characteristic(
    uuid128!(<span class="hljs-string">"681285a6-247f-48c6-80ad-68c3dce18585"</span>),
    NimbleProperties::READ | NimbleProperties::NOTIFY,
);

<span class="hljs-comment">// Set a starting value for the characteristic</span>
my_service_characteristic.lock().set_value(<span class="hljs-string">b"Start Value"</span>);
</code></pre>
<p>6️⃣ <strong>Configure Advertiser Data:</strong> This step is similar to what was done in the <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-advertiser">BLE Advertiser post</a>. A small difference is that here we are also attaching the <strong>service</strong> to the advertisement data. This is done using the <code>add_service_uuid</code> method. Note that the UUID being advertised is the same <strong>service</strong> UUID created earlier.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Configure Advertiser Data</span>
ble_advertiser
    .lock()
    .set_data(
        BLEAdvertisementData::new()
            .name(<span class="hljs-string">"ESP32 Server"</span>)
            .add_service_uuid(uuid128!(<span class="hljs-string">"9b574847-f706-436c-bed7-fc01eb0965c1"</span>)),
    )
    .unwrap();
</code></pre>
<p>That's it for configuration!</p>
<h3 id="heading-application-code"><strong>📱 Application Code</strong></h3>
<p><strong>Start Advertising</strong>: Now we have to start the advertising process. This is done by calling the <code>BLEAdvertisingstart</code> method.</p>
<pre><code class="lang-rust">ble_advertiser.lock().start().unwrap();
</code></pre>
<p><strong>Update Characteristic</strong>: All we have to do now is keep updating the <strong>characteristic</strong> by calling the <code>set_value</code> method again. Here, the <code>my_service_characteristic</code> value keeps getting updated every one every second by incrementing its contents.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> val = <span class="hljs-number">0</span>;

<span class="hljs-keyword">loop</span> {
    FreeRtos::delay_ms(<span class="hljs-number">1000</span>);
    my_service_characteristic.lock().set_value(&amp;[val]).notify();
    val = val.wrapping_add(<span class="hljs-number">1</span>);
}
</code></pre>
<h2 id="heading-testing">🧪 Testing</h2>
<p>In order to test this code, you can use <a target="_blank" href="https://www.nordicsemi.com/Products/Development-tools/nRF-Connect-for-mobile">nRF connect</a> mobile app or the <a target="_blank" href="https://learn.adafruit.com/bluefruit-le-connect/ios-setup">bluefruit connect</a> mobile app. In either app you would be able to connect to the ESP and poll the server data.</p>
<h2 id="heading-full-application-code"><strong>📱Full Application Code</strong></h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32_nimble::{uuid128, BLEAdvertisementData, BLEDevice, NimbleProperties};
<span class="hljs-keyword">use</span> esp_idf_hal::delay::FreeRtos;
<span class="hljs-keyword">use</span> esp_idf_sys <span class="hljs-keyword">as</span> _;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    esp_idf_sys::link_patches();

    <span class="hljs-comment">// Take ownership of device</span>
    <span class="hljs-keyword">let</span> ble_device = BLEDevice::take();

    <span class="hljs-comment">// Obtain handle for peripheral advertiser</span>
    <span class="hljs-keyword">let</span> ble_advertiser = ble_device.get_advertising();

    <span class="hljs-comment">// Obtain handle for server</span>
    <span class="hljs-keyword">let</span> server = ble_device.get_server();

    <span class="hljs-comment">// Define server connect behaviour</span>
    server.on_connect(|server, clntdesc| {
        <span class="hljs-comment">// Print connected client data</span>
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{:?}"</span>, clntdesc);
        <span class="hljs-comment">// Update connection parameters</span>
        server
            .update_conn_params(clntdesc.conn_handle(), <span class="hljs-number">24</span>, <span class="hljs-number">48</span>, <span class="hljs-number">0</span>, <span class="hljs-number">60</span>)
            .unwrap();
    });

    <span class="hljs-comment">// Define server disconnect behaviour</span>
    server.on_disconnect(|_desc, _reason| {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Disconnected, back to advertising"</span>);
    });

    <span class="hljs-comment">// Create a service with custom UUID</span>
    <span class="hljs-keyword">let</span> my_service = server.create_service(uuid128!(<span class="hljs-string">"9b574847-f706-436c-bed7-fc01eb0965c1"</span>));

    <span class="hljs-comment">// Create a characteristic to associate with created service</span>
    <span class="hljs-keyword">let</span> my_service_characteristic = my_service.lock().create_characteristic(
        uuid128!(<span class="hljs-string">"681285a6-247f-48c6-80ad-68c3dce18585"</span>),
        NimbleProperties::READ | NimbleProperties::NOTIFY,
    );

    <span class="hljs-comment">// Modify characteristic value</span>
    my_service_characteristic.lock().set_value(<span class="hljs-string">b"Start Value"</span>);

    <span class="hljs-comment">// Configure Advertiser Data</span>
    ble_advertiser
        .lock()
        .set_data(
            BLEAdvertisementData::new()
                .name(<span class="hljs-string">"ESP32 Server"</span>)
                .add_service_uuid(uuid128!(<span class="hljs-string">"9b574847-f706-436c-bed7-fc01eb0965c1"</span>)),
        )
        .unwrap();

    <span class="hljs-comment">// Start Advertising</span>
    ble_advertiser.lock().start().unwrap();

    <span class="hljs-comment">// (Optional) Print dump of local GATT table</span>
    <span class="hljs-comment">// server.ble_gatts_show_local();</span>

    <span class="hljs-comment">// Init a value to pass to characteristic</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> val = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">loop</span> {
        FreeRtos::delay_ms(<span class="hljs-number">1000</span>);
        my_service_characteristic.lock().set_value(&amp;[val]).notify();
        val = val.wrapping_add(<span class="hljs-number">1</span>);
    }
}
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>This post introduced how to create a BLE server on the ESP32-C3 with Rust. This was by using the <code>esp32-nimble</code> crate in a standard library development environment using the <code>esp-idf-hal</code> . In this post, the ESP32-C3 was configured as a peripheral device advertising a service. The device also assumes a server role after a connection is established. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[Embedded Rust Bluetooth on ESP: BLE Advertiser]]></title><description><![CDATA[This post is the second of a multi-part series where I'm exploring the use of Bluetooth Low Energy along embedded Rust on the ESP32. Information in this post might rely on knowledge presented in past posts.


Embedded Rust Bluetooth on ESP: BLE Scann...]]></description><link>https://blog.theembeddedrustacean.com/embedded-rust-bluetooth-on-esp-ble-advertiser</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embedded-rust-bluetooth-on-esp-ble-advertiser</guid><category><![CDATA[Bluetooth Low Energy]]></category><category><![CDATA[bluetooth]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[iot]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Sat, 16 Mar 2024 06:07:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1710568925151/b5b81f22-f099-419c-8889-06ec07f07a86.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This post is the second of a multi-part series where I'm exploring the use of Bluetooth Low Energy along embedded Rust on the ESP32. Information in this post might rely on knowledge presented in past posts.</p>
</blockquote>
<ol>
<li><a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-scanner">Embedded Rust Bluetooth on ESP: BLE Scanner</a></li>
</ol>
<h2 id="heading-introduction">Introduction</h2>
<p>In <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-scanner">last week's post</a>, some BLE foundations were introduced where the difference between <strong>central</strong> and <strong>peripheral</strong> devices was explained. <strong>Central</strong> devices initiate connections and request data or services from <strong>peripheral</strong> devices. <strong>Peripheral</strong> devices, on the other end, were ones that advertise their presence and provide services to <strong>central</strong> devices. Additionally, these terms were associated with devices before a connection was established. In that context, the ESP32-C3 was configured to run as a <strong>central</strong> device to perform a scan for advertising BLE <strong>peripheral</strong> devices.</p>
<p>In this post, we're going to remain in the pre-connection phase but do the opposite. We're going to configure the ESP32-C3 to run as an advertising <strong>peripheral</strong> device. The code will be built using the <a target="_blank" href="https://crates.io/crates/esp32-nimble/0.6.0"><strong>esp32-nimble</strong></a> crate.</p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Familiarity with standard library development in Rust with the ESP.</p>
</li>
<li><p>Basic knowledge of networking layering concepts/stacks (ex. OSI model).</p>
</li>
<li><p>Basic knowledge of Bluetooth.</p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3 git repo</strong></a>. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
<h4 id="heading-connections"><strong>🔌 Connections</strong></h4>
<p>  No connections are required for this example.</p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨 Software Design</strong></h2>
<p>The idea of advertising is for a <strong>peripheral</strong> device to "advertise" its presence and services. However, a <strong>peripheral</strong> device does not necessarily have to establish a connection with a <strong>central</strong> device to provide data. An application example is beacons. Beacons are <strong>peripheral</strong> devices that broadcast data but do not establish connections with <strong>central</strong> devices. In this post, we are going to program the ESP as an advertising <strong>peripheral</strong> device. Key settings include the following:</p>
<h3 id="heading-advertisment-type">Advertisment Type</h3>
<p>There are several advertisement types including the following:</p>
<ul>
<li><p><strong>Not connectable:</strong> This means that a <strong>central</strong> device cannot connect to the <strong>peripheral</strong> device.</p>
</li>
<li><p><strong>Directed connectable:</strong> This means that the <strong>peripheral</strong> device advertisement packets are targeted to a specific central scanner. This type is meant for cases where the <strong>peripheral</strong> already knows the <strong>central</strong> and wants to allow for a quick reconnection.</p>
</li>
<li><p><strong>Undirected connectable:</strong> This means that the <strong>peripheral</strong> device advertisement packets are not targeted to a specific scanner.</p>
</li>
</ul>
<h3 id="heading-scan-response">Scan Response</h3>
<p>Advertising <strong>peripherals</strong> can choose to accept what is referred to as <strong>scan requests</strong>. <strong>Central</strong> devices can choose to send <strong>scan requests</strong> to advertisers. If a <strong>scan request</strong> is accepted, the <strong>peripheral</strong> device would respond with additional information in a <strong>scan response</strong>. In that context, a <strong>peripheral</strong> can be either <strong>scannable</strong> or <strong>non-scannable</strong>. A <strong>scannable</strong> <strong>peripheral</strong> is one that accepts <strong>scan requests</strong> from a <strong>central</strong> scanner.</p>
<p>Recall from <a target="_blank" href="https://apollolabsblog.hashnode.dev/embedded-rust-bluetooth-on-esp-ble-scanner">last week's post</a>, in the <strong>central</strong> device, <strong>scan requests</strong> were defined through the <strong>scan type</strong>. The <strong>scan type</strong> could be configured as active or passive. Active scanning meant that the central would send <strong>scan requests</strong>. Additionally, the <strong>scan type</strong> was configured using the <code>active_scan</code> method.</p>
<h3 id="heading-discoverable-mode">Discoverable Mode</h3>
<p>The discoverable mode refers to the state in which a BLE device actively broadcasts its presence to nearby devices. There are two main discoverable modes:</p>
<ol>
<li><p><strong>General Discoverable Mode</strong>: In this mode, the BLE device continuously broadcasts advertising packets to advertise its presence to nearby devices.</p>
</li>
<li><p><strong>Limited Discoverable Mode</strong>: Limited discoverable mode is similar to general discoverable mode but with a limited duration. This mode is typically used when the device only needs to be discoverable for a short time to establish connections with nearby devices. This mode is useful in cases relative to conserving power.</p>
</li>
</ol>
<p>The <strong>peripheral</strong> that will be configured in this post, will only advertise its presence. As such, it will not advertise any services, will be <strong>non-connectable</strong> and <strong>non-scannable</strong> (doesn't support scan requests).</p>
<h2 id="heading-code-implementation"><strong>👨‍💻 Code Implementation</strong></h2>
<h3 id="heading-crate-imports"><strong>📥 Crate Imports</strong></h3>
<p>In this implementation, the following crates are required:</p>
<ul>
<li><p>The <code>esp_idf_hal</code> crate to import <code>delay</code> abstractions.</p>
</li>
<li><p>The <code>esp32_nimble</code> crate for the necessary BLE abstractions.</p>
</li>
<li><p>The <code>esp_idf_sys</code> crate since its needed by the <code>esp32_nimble</code> crate.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32_nimble::{
    enums::{ConnMode, DiscMode},
    BLEAdvertisementData, BLEDevice,
};
<span class="hljs-keyword">use</span> esp_idf_hal::delay::FreeRtos;
<span class="hljs-keyword">use</span> esp_idf_sys <span class="hljs-keyword">as</span> _;
</code></pre>
<h3 id="heading-initializationconfiguration-code"><strong>🎛 Initialization/Configuration Code</strong></h3>
<p><strong>1️⃣ Obtain a handle for the BLE device</strong>: Similar to the pattern we've seen in embedded Rust with peripherals, as part of the singleton design pattern, we first have to take ownership of the device peripherals. In this context, its the <code>BLEDevice</code> that we need to take ownership of. This is done using the <code>take()</code> associated method. Here I create a BLE device handler named <code>ble_device</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ble_device = BLEDevice::take();
</code></pre>
<p>Although not obvious, note that <code>take</code> not only provides ownership, but behind the scenes also initializes the NimBLE stack.</p>
<p><strong>2️⃣ Create an Advertiser Instance</strong>: After initializing the NimBLE stack we create an advertiser instance by calling <code>get_advertising</code>, this will create a <code>&amp;Mutex&lt;BLEAdvertising&gt;</code> instance. Heres the code:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ble_advertiser = ble_device.get_advertising();
</code></pre>
<p>Note how <code>BLEAdvertising</code> is wrapped in a <code>Mutex</code>. This means that every time we want to access it, we need to obtain a <code>lock</code>.</p>
<p><strong>3️⃣ Configure Device Advertising Data:</strong> In this example, we are not going to advertise any specific data. However, at minimum we need to define what our advertiser name is. To do this, there exists the <code>set_data</code> method for <code>BLEAdvertising</code> that takes a single argument of <code>BLEAdvertisementData</code>. For that we need to create an instance of <code>BLEAdvertisementData</code> using it's <code>new</code> method, then specify a name using the <code>name</code> method. The name method takes a single <code>&amp;str</code> argument.</p>
<pre><code class="lang-rust"> <span class="hljs-comment">// Specify Advertiser Name</span>
 <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ad_data = BLEAdvertisementData::new();
 ad_data.name(<span class="hljs-string">"ESP32-C3 Peripheral"</span>);

 <span class="hljs-comment">// Configure Advertiser with Specified Data</span>
 ble_advertiser.lock().set_data(&amp;<span class="hljs-keyword">mut</span> ad_data).unwrap();
</code></pre>
<p><strong>4️⃣ Configure Advertiser Settings</strong>: With advertiser instance we created we can now configure the advertiser parameters discussed earlier; <strong>advertisement type</strong>, <strong>discoverable mode</strong>, and <strong>scan response</strong>. These parameters can all be configured by calling <code>BLEAdvertising</code> methods on the <code>ble_advertiser</code> instance we created.</p>
<p>To set the <strong>advertisement type,</strong> there exists the <code>advertisement_type</code> method that takes a single <code>ConnMode</code> enum argument that specifies the connection mode. We are going to choose the <code>Non</code> option which means that our device is <strong>non-connectable</strong>. This is because we don't want any <strong>central</strong> devices to connect to us.</p>
<p>To set the <strong>discoverable mode</strong> there exists the <code>disc_mode</code> method that takes a single <code>DiscMode</code> enum argument specifying the discoverable mode. We are going to choose the <code>Gen</code> option which means that our device is in <strong>general discoverable</strong> mode. This means our device will continuously broadcasts advertising packets to advertise its presence to nearby devices.</p>
<p>Finally, we can specify if we will allow <strong>scan responses</strong>. This is done using the <code>scan_response</code> method which takes a single <code>bool</code> argument. We'll simply set this to <code>false</code>. Note that if we were to allow <strong>scan responses</strong> we would have needed to specify the data that would result from a <strong>scan response</strong>. For that we could have used the <code>set_raw_scan_response_data</code> method that is part of <code>BLEAdvertising</code><strong>.</strong> Here's the code for our configuration:</p>
<pre><code class="lang-rust">ble_advertiser
    .lock()
    .advertisement_type(ConnMode::Non)
    .disc_mode(DiscMode::Gen)
    .scan_response(<span class="hljs-literal">false</span>);
</code></pre>
<p>That's it for configuration!</p>
<h3 id="heading-application-code"><strong>📱 Application Code</strong></h3>
<p><strong>Start Advertising</strong>: All we have to do now is start the advertising process. This is done by calling the <code>BLEAdvertising</code> <code>start</code> method. <code>start</code> doesn't take any parameters. After that we enter a <code>loop</code> to keep the application running. A delay is included in the <code>loop</code> to avoid the watchdog from triggering due to lack of activity.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Start Advertising</span>
ble_advertiser.lock().start().unwrap();
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Advertisement Started"</span>);
<span class="hljs-keyword">loop</span> {
    <span class="hljs-comment">// Keep Advertising</span>
    <span class="hljs-comment">// Add delay to prevent watchdog from triggering</span>
    FreeRtos::delay_ms(<span class="hljs-number">10</span>);
}
</code></pre>
<h2 id="heading-full-application-code"><strong>📱Full Application Code</strong></h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32_nimble::{
    enums::{ConnMode, DiscMode},
    BLEAdvertisementData, BLEDevice,
};
<span class="hljs-keyword">use</span> esp_idf_hal::delay::FreeRtos;
<span class="hljs-keyword">use</span> esp_idf_sys <span class="hljs-keyword">as</span> _;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    esp_idf_svc::sys::link_patches();

    <span class="hljs-comment">// Take ownership of device</span>
    <span class="hljs-keyword">let</span> ble_device = BLEDevice::take();
    <span class="hljs-comment">// Obtain handle for advertiser</span>
    <span class="hljs-keyword">let</span> ble_advertiser = ble_device.get_advertising();

    <span class="hljs-comment">// Specify Advertiser Name</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ad_data = BLEAdvertisementData::new();
    ad_data.name(<span class="hljs-string">"ESP32-C3 Peripheral"</span>);

    <span class="hljs-comment">// Configure Advertiser with Specified Data</span>
    ble_advertiser.lock().set_data(&amp;<span class="hljs-keyword">mut</span> ad_data).unwrap();

    <span class="hljs-comment">// Make Advertiser Non-Connectable</span>
    <span class="hljs-comment">// Set Discovery Mode to General</span>
    <span class="hljs-comment">// Deactivate Scan Responses</span>
    ble_advertiser
        .lock()
        .advertisement_type(ConnMode::Non)
        .disc_mode(DiscMode::Gen)
        .scan_response(<span class="hljs-literal">false</span>);

    <span class="hljs-comment">// Start Advertising</span>
    ble_advertiser.lock().start().unwrap();
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Advertisement Started"</span>);
    <span class="hljs-keyword">loop</span> {
        <span class="hljs-comment">// Keep Advertising</span>
        <span class="hljs-comment">// Add delay to prevent watchdog from triggering</span>
        FreeRtos::delay_ms(<span class="hljs-number">10</span>);
    }
}
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>This post introduced how to start working with BLE on the ESP32-C3 with Rust. This was by using the <code>esp32-nimble</code> crate in a standard library development environment using the <code>esp-idf-hal</code> . In this post, the ESP32-C3 was configured as a peripheral device as an advertising BLE device. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[Embedded Rust Bluetooth on ESP: BLE Scanner]]></title><description><![CDATA[This post is a start of a new series where I'll be exploring the use of Bluetooth Low Energy along embedded Rust on the ESP32.

Introduction
Bluetooth is a wireless communication technology that enables data exchange over short distances between devi...]]></description><link>https://blog.theembeddedrustacean.com/embedded-rust-bluetooth-on-esp-ble-scanner</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embedded-rust-bluetooth-on-esp-ble-scanner</guid><category><![CDATA[Rust]]></category><category><![CDATA[bluetooth]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[iot]]></category><category><![CDATA[embedded systems]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Sun, 10 Mar 2024 14:46:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1710081098784/91fe5c28-0179-4a8d-a786-4130320ae1e5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This post is a start of a new series where I'll be exploring the use of Bluetooth Low Energy along embedded Rust on the ESP32.</p>
</blockquote>
<h2 id="heading-introduction">Introduction</h2>
<p>Bluetooth is a wireless communication technology that enables data exchange over short distances between devices, allowing for convenient connectivity in various applications such as audio streaming, file transfer, and device synchronization. Bluetooth can take a while to wrap your head around. Other than understanding the protocol and its stack, there's more than one flavor. There's Bluetooth Classic and there's Bluetooth Low Energy or what is commonly known as BLE. Bluetooth Classic and BLE share some common components but are not compatible. This means that a BLE radio can't connect to a Bluetooth radio unless that Bluetooth radio supports BLE. This is referred to as dual mode. The focus of this post is BLE.</p>
<p>BLE, introduced as part of Bluetooth 4.0, extends the capabilities of traditional Bluetooth by providing energy-efficient communication for devices with low-power requirements, making it ideal for applications like wearable technology, IoT devices, and wireless sensors. BLE enables long battery life and reduced power consumption while maintaining compatibility with existing Bluetooth devices, opening up new possibilities for connected devices in diverse industries.</p>
<p>BLE incorporates several terms and quite an involved stack. However, to work with BLE one does not need to intimately dig into each layer, however, some terms are necessary to understand. In this post, I'll try to simplify some of these concepts and explain the necessary terms. In this code, I'm going to demonstrate how to perform a <strong>scan</strong> using the ESP as a <strong>central</strong> device. What these terms mean will be explained in more detail soon.</p>
<p>The code will be built using the <a target="_blank" href="https://crates.io/crates/esp32-nimble/0.6.0"><strong>esp32-nimble</strong></a> crate. The esp32-nimble crate is a wrapper for the ESP32 NimBLE Bluetooth stack. The crate is inspired by the <a target="_blank" href="https://github.com/h2zero/NimBLE-Arduino"><strong>NimBLE-Arduino</strong></a> project. NimBLE is an open source BLE stack fully compliant with the Bluetooth specification providing both <strong>host</strong> and <strong>controller</strong> functionalities. NimBLE is also part of the Apache MyNewt project. The ESP-IDF supports only a port of the NimBLE <strong>host</strong> stack and provides a different <strong>controller</strong> implementation.</p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Familiarity with standard library development in Rust with the ESP.</p>
</li>
<li><p>Basic knowledge of networking layering concepts/stacks (ex. OSI model).</p>
</li>
<li><p>Basic knowledge of Bluetooth.</p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong> git repo</a>. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
<h4 id="heading-connections"><strong>🔌 Connections</strong></h4>
<p>  No connections are required for this example.</p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨 Software Design</strong></h2>
<p>Its always confused me how Bluetooth seems to have two parallel stacks and several roles. Though looking at the stack and roles from device connection state would help simplify understanding. In that context, lets consider two main states; pre-connection and post-connection. Let's keep that in mind as we inspect the layers and device roles.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710013456904/1fb28812-64c2-428a-b93c-2fcbbf094434.png" alt class="image--center mx-auto" /></p>
<p>The figure below shows the BLE stack. Note how there is a host part and controller part. The <strong>controller</strong> layers lie in the hardware and handle the low level physical aspects of communication, such as radio frequencies, modulation, packet encoding, packet transmission, and packet reception. The <strong>host</strong> layers lie in the software and are responsible for higher-level protocol handling, including connection management, security, and data processing. The <strong>host</strong> and the <strong>controller</strong> interact with each other through an interface often referred to as the <strong>host</strong> <strong>controller</strong> interface of (HCI). This can be simply a serial interface like UART. You can potentially think of the <strong>controller</strong> and the <strong>host</strong> as two different ICs that exchange data with each other.</p>
<p>There are parallels to be drawn from computer network systems to help understand. You've probably seen network interface controllers (NICs), or network cards. Some NICs for example would enable connecting to an ethernet network or WiFi from one end and a computer mother board from another end. The motherboard connection could be something like a PCI interface. This is equivalent to the HCI in BLE context.</p>
<p>The BLE protocol stack is composed of several layers, each serving distinct functions in facilitating communication between BLE-enabled devices. These layers include:</p>
<ol>
<li><p><strong>Physical Layer (PHY)</strong>: This layer of is responsible for converting digital data into radio waves and vice versa for transmission.</p>
</li>
<li><p><strong>Link Layer (LL)</strong>: This layer among several tasks, manages the connection establishment, data packet format, and error handling.</p>
</li>
<li><p><strong>Host Controller Interface (HCI)</strong>: The HCI layer provides an interface between the Host and the Controller. It standardizes communication between the Host (typically a microprocessor running higher-level protocols) and the Controller (responsible for low-level radio operations).</p>
</li>
<li><p><strong>Logical Link Control and Adaptation Protocol (L2CAP)</strong>: L2CAP is a protocol layer that provides segmentation and reassembly of data packets, multiplexing of multiple logical connections, and quality of service (QoS) negotiation.</p>
</li>
<li><p><strong>Security Manager (SM)</strong>: This layer is responsible for establishing and managing security features in BLE connections.</p>
</li>
<li><p><strong>Attribute Profile (ATT) and Generic Attribute Profile (GATT)</strong>: These layers define a hierarchical data structure used to organize data exchanged between BLE devices. They enables devices to expose services, characteristics, and descriptors, allowing for standardized communication and interoperability.</p>
</li>
<li><p><strong>Generic Access Profile (GAP):</strong> This layer is responsible for managing the basic aspects of device interaction in a BLE network. Things like discovery, advertising, and connection establishment.</p>
</li>
</ol>
<p>Note how in the figure, the layers on the left hand side are pre connection layers (SM and GAP). This is because the tasks they perform are pre connection tasks; advertising, authenticating, discovery...etc.. The right hand side, on the other hand, includes the post connection layers (ATT and GATT) performing post connection tasks mainly to do with data exchange.</p>
<h3 id="heading-device-roles">🤹‍♂️ Device Roles</h3>
<p>There are four main terms that float around with device roles in BLE; <strong>Central</strong>, <strong>Peripheral</strong>, <strong>Server</strong>, and <strong>Client.</strong> Just like the stack layers, we can look at the roles from a pre/post connection perspective.</p>
<ol>
<li><p><strong>Pre Connection Roles</strong>:</p>
<ul>
<li><p><strong>Central</strong>: The "central" is a device that typically initiates connections and requests data or services from peripheral devices. Central devices can scan for nearby peripherals, establish connections, and exchange data with them.</p>
</li>
<li><p><strong>Peripheral</strong>: The "peripheral" is a device that advertises its presence and provides services to central devices.</p>
</li>
</ul>
</li>
<li><p><strong>Post Connection Roles</strong> :</p>
<ul>
<li><p><strong>Client</strong>: The "client" is a device that consumes data or services provided by a "server."</p>
</li>
<li><p><strong>Server</strong>: The "server" is a device that provides data or services to clients.</p>
</li>
</ul>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710013484442/2a909782-84e1-4877-80ff-753a7010aa19.png" alt class="image--center mx-auto" /></p>
<p>In this post, we are going to program the ESP as a <strong>central</strong> device scanning for other devices. When scanning there are several scan settings available and depend on what the application desires. Factors considered include device discovery frequency, power consumption, and connection reliability. In this post we're not going to use them all, but its beneficial to know what is available. Key settings include the following:</p>
<ol>
<li><p><strong>Scan Interval</strong>: As shown in the figure below, the scan interval determines the time interval between successive scans performed by the BLE device. It affects how frequently the device scans for nearby devices. Shorter scan intervals result in more frequent scanning but may consume more power. A good practice is to set a relatively short scan interval, so that the scanning process is more likely to receive the advertising packets. The scanning interval can go up to seconds but is typically specified in milliseconds.</p>
</li>
<li><p><strong>Scan Window</strong>: The <strong>scan</strong> <strong>window</strong> specifies the duration within each <strong>scan</strong> <strong>interval</strong> during which the device actively listens for advertising packets. It affects the duration of each scanning cycle and impacts the likelihood of discovering nearby devices. Adjusting the <strong>scan</strong> <strong>window</strong> allows balancing between power consumption and device discovery frequency.</p>
</li>
<li><p><strong>Scan Duration</strong>: The scan duration determines the total duration of a single scanning cycle, including both the <strong>scanning</strong> <strong>interval</strong> and <strong>scan</strong> <strong>window</strong>. It affects how long the device spends actively scanning for nearby devices before entering an idle state. Longer scan durations increase the likelihood of discovering nearby devices but may consume more power.</p>
</li>
<li><p><strong>Scan Type</strong>: The scan type specifies whether the scanning process is <strong>passive</strong> or <strong>active</strong>. In <strong>passive</strong> scanning, the device only listens for advertising packets from nearby devices. In <strong>active</strong> scanning, the device sends out scan requests to nearby devices, prompting them to respond with advertising packets.</p>
</li>
<li><p><strong>Filter Policies</strong>: Filter policies define which advertising packets the device filters or ignores during the scanning process. They can filter devices based on specific criteria such as device address, advertising data, or signal strength. Filter policies help optimize the scanning process by focusing on relevant advertising packets.</p>
</li>
</ol>
<p>Understanding the above terms would make our job quite straight forward using the <a target="_blank" href="https://crates.io/crates/esp32-nimble/0.6.0"><strong>esp32-nimble</strong></a> crate.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710057070695/d86eb170-a92f-4864-867e-188e71e31237.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-code-implementation"><strong>👨‍💻 Code Implementation</strong></h2>
<h3 id="heading-crate-imports"><strong>📥 Crate Imports</strong></h3>
<p>In this implementation, the following crates are required:</p>
<ul>
<li><p>The <code>esp_idf_hal</code> crate to import the <code>task::block_on</code> function.</p>
</li>
<li><p>The <code>esp_idf_sys</code> crate since its needed.</p>
</li>
<li><p>The <code>esp32_nimble</code> crate for the BLE abstractions.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32_nimble::BLEDevice;
<span class="hljs-keyword">use</span> esp_idf_hal::task::block_on;
<span class="hljs-keyword">use</span> esp_idf_sys <span class="hljs-keyword">as</span> _;
</code></pre>
<h3 id="heading-initializationconfiguration-code"><strong>🎛 Initialization/Configuration Code</strong></h3>
<p><strong>1️⃣ Obtain a handle for the BLE device</strong>: Similar to the pattern we've seen in embedded Rust with peripherals, as part of the singleton design pattern, we first have to take ownership of the device peripherals. In this context, its the <code>BLEDevice</code> that we need to take ownership of. You might have guessed it already, this is done using the <code>take()</code> associated method. Here I create a BLE device handler named <code>ble_device</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ble_device = BLEDevice::take();
</code></pre>
<p>Although not obvious, note that <code>take</code> not only provides ownership, but behind the scenes also initializes the NimBLE stack.</p>
<p><strong>2️⃣ Create a Scan Instance</strong>: After initializing the NimBLE stack we create a scan instance by calling <a target="_blank" href="https://taks.github.io/esp32-nimble/esp32_nimble/struct.BLEDevice.html#method.get_scan"><code>get_scan</code></a>, this will create a <a target="_blank" href="https://taks.github.io/esp32-nimble/esp32_nimble/struct.BLEScan.html"><code>BLEScan</code></a> instance. This instance would allow us to start looking for advertising servers. Heres the code:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ble_scan = ble_device.get_scan();
</code></pre>
<p><strong>3️⃣ Configure Scan Parameters and Callback</strong>: Now that we have a scan instance we can configure the scan parameters discussed earlier; <strong>scan type</strong>, <strong>interval</strong>, and <strong>window</strong>. Additionally, we need to configure the behaviour on the return of a scan result. These parameters can all be configured by calling <code>BLEScan</code> methods on the <code>ble_scan</code> instance we created.</p>
<p>To set the <strong>scan type,</strong> there exists the <code>active_scan</code> method that takes a single <code>bool</code> type argument. To set the <strong>interval</strong> there exists the <code>interval</code> method that takes a single <code>u32</code> type argument representing the <strong>interval</strong> in ms. To set the <strong>window</strong> there exists the <code>window</code> method that takes a single <code>u32</code> type argument representing the <strong>window</strong> in ms.</p>
<p>Finally, whenever our central device detects an advertising device, the behaviour needs to be identified. This is done using the <code>on_result</code> method. The <code>on_result</code> parameter is a callback that is called when a new scan result is detected. The first parameter is a reference to <code>ble_scan</code> instance itself, and the second is a reference to a detected device of <a target="_blank" href="https://taks.github.io/esp32-nimble/esp32_nimble/struct.BLEAdvertisedDevice.html"><code>BLEAdvertisedDevice</code></a> type. <code>BLEAdvertisedDevice</code> contains alot of data about the advertiser device obtained by various methods. At a minimum, we're going to retrieve the name, address and rssi of a advertising device. This is demonstrated in the following code:</p>
<pre><code class="lang-rust">ble_scan
    .active_scan(<span class="hljs-literal">true</span>)
    .interval(<span class="hljs-number">100</span>)
    .window(<span class="hljs-number">50</span>)
    .on_result(|_scan, param| {
        <span class="hljs-built_in">println!</span>(
            <span class="hljs-string">"Advertised Device Name: {:?}, Address: {:?} dB, RSSI: {:?}"</span>,
            param.name(),
            param.addr(),
            param.rssi()
        );
    });
</code></pre>
<p>Note that the <strong>scan interval</strong> chosen is 100ms and the <strong>scan window</strong> is 50ms.</p>
<p>That's it for configuration!</p>
<h3 id="heading-application-code"><strong>📱 Application Code</strong></h3>
<p><strong>Start the Scan</strong>: All we have to do now is start the scan process. This is done by calling the <code>BLEScan</code> <code>start</code> method. <code>start</code> takes one parameter which specifies a <strong>scan duration</strong> in milliseconds. Note though how <code>start</code> is an <code>async</code> function that returns a <code>Future</code>. This means that it would defer execution until the scan is completed. For that, note in the full application how the code is wrapped in a <code>async</code> block inside a <code>block_on</code> function. This serves to block execution until the scan process is completed.</p>
<pre><code class="lang-rust">ble_scan.start(<span class="hljs-number">5000</span>).<span class="hljs-keyword">await</span>.unwrap();
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Scan finished"</span>);
</code></pre>
<h2 id="heading-full-application-code"><strong>📱Full Application Code</strong></h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32_nimble::BLEDevice;
<span class="hljs-keyword">use</span> esp_idf_hal::task::block_on;
<span class="hljs-keyword">use</span> esp_idf_sys <span class="hljs-keyword">as</span> _;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    esp_idf_svc::sys::link_patches();

    block_on(<span class="hljs-keyword">async</span> {
        <span class="hljs-keyword">let</span> ble_device = BLEDevice::take();
        <span class="hljs-keyword">let</span> ble_scan = ble_device.get_scan();
        ble_scan
            .active_scan(<span class="hljs-literal">true</span>)
            .interval(<span class="hljs-number">100</span>)
            .window(<span class="hljs-number">50</span>)
            .on_result(|_scan, param| {
                <span class="hljs-built_in">println!</span>(
                    <span class="hljs-string">"Advertised Device Name: {:?}, Address: {:?} dB, RSSI: {:?}"</span>,
                    param.name(),
                    param.addr(),
                    param.rssi()
                );
            });
        ble_scan.start(<span class="hljs-number">5000</span>).<span class="hljs-keyword">await</span>.unwrap();
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Scan finished"</span>);
    });
}
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>This post introduced how to start working with BLE on the ESP32-C3 with Rust. This was by using the <code>esp32-nimble</code> crate in a standard library development environment using the <code>esp-idf-hal</code> . In this post, the ESP32-C3 was configured as a central device to perform a scan for advertising BLE devices. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[Edge IoT with Rust on ESP: WiFi Revisited]]></title><description><![CDATA[Introduction
Ever since creating the WiFi post, I received several inquiries about using a custom SSID and password. In that past post, I had hardcoded the WiFi SSID and password. I figured its a sign for updating the code to demonstrate how to enter...]]></description><link>https://blog.theembeddedrustacean.com/edge-iot-with-rust-on-esp-wifi-revisited</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/edge-iot-with-rust-on-esp-wifi-revisited</guid><category><![CDATA[Rust]]></category><category><![CDATA[iot]]></category><category><![CDATA[embedded systems]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[ESP32]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Sat, 02 Mar 2024 20:33:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1709410739267/ffbd5275-ddf0-480f-8044-56900c1e81a0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Ever since creating the <a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-connecting-wifi">WiFi post</a>, I received several inquiries about using a custom SSID and password. In that past post, I had hardcoded the WiFi SSID and password. I figured its a sign for updating the code to demonstrate how to enter a custom access point SSID and password.</p>
<p>In this post, I will be updating the past <a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-connecting-wifi">WiFi post</a> application code to accommodate custom network SSID entry. UART will be used to acquire user entry from the terminal.</p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-connecting-wifi">WiFi Blog Post</a>.</p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embassy-on-esp-uart-echo">UART Blog Post</a>.</p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<p>Additionally, the full project (code and simulation) is available on Wokwi <a target="_blank" href="https://wokwi.com/projects/391058600462186497"><strong>here</strong></a>.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨 Software Design</strong></h2>
<p>In the past example, the ESP32 was configured in station mode in the following steps:</p>
<ol>
<li><p>Configure WiFi</p>
</li>
<li><p>Start WiFi</p>
</li>
<li><p>Connect WiFi</p>
</li>
<li><p>(Optional) Confirm Connection and Check Connection Configuration</p>
</li>
</ol>
<p>Ahead of these steps we'll need to capture the user entry and use it in the WiFi configuration. These are the additional steps that need to be taken ahead of connecting to WiFi:</p>
<ol>
<li><p>Instantiate and Configure UART</p>
</li>
<li><p>Ask user for SSID</p>
</li>
<li><p>Read and store SSID</p>
</li>
<li><p>Ask user for password</p>
</li>
<li><p>Read and store password</p>
</li>
</ol>
<p>After that we can proceed to configure WiFi with the stored entries.</p>
<h2 id="heading-code-implementation"><strong>👨‍💻 Code Implementation</strong></h2>
<h3 id="heading-crate-imports"><strong>📥 Crate Imports</strong></h3>
<p>In this implementation, the following crates are required:</p>
<ul>
<li><p>The <code>anyhow</code> crate for error handling.</p>
</li>
<li><p>The <code>esp_idf_hal</code> crate to import the peripherals.</p>
</li>
<li><p>The <code>esp_idf_svc</code> crate to import the device services necessary for WiFi.</p>
</li>
<li><p>The <code>heapless</code> crate for the heapless <code>String</code> and <code>Vec</code> types.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp_idf_hal::delay::BLOCK;
<span class="hljs-keyword">use</span> esp_idf_hal::gpio;
<span class="hljs-keyword">use</span> esp_idf_hal::prelude::*;
<span class="hljs-keyword">use</span> esp_idf_hal::uart::*;
<span class="hljs-keyword">use</span> esp_idf_svc::eventloop::EspSystemEventLoop;
<span class="hljs-keyword">use</span> esp_idf_svc::nvs::EspDefaultNvsPartition;
<span class="hljs-keyword">use</span> esp_idf_svc::wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi};
<span class="hljs-keyword">use</span> heapless::{<span class="hljs-built_in">String</span>, <span class="hljs-built_in">Vec</span>};
<span class="hljs-keyword">use</span> std::fmt::Write;
</code></pre>
<h3 id="heading-initializationconfiguration-code"><strong>🎛 Initialization/Configuration Code</strong></h3>
<p>1️⃣ <strong>Obtain a handle for the device peripherals</strong>: Similar to all past blog posts, in embedded Rust, as part of the singleton design pattern, we first have to take the device peripherals. This is done using the <code>take()</code> method. Here I create a device peripheral handler named <code>peripherals</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> peripherals = Peripherals::take().unwrap();
</code></pre>
<p>2️⃣ <strong>Configure &amp; Instantiate UART</strong>: UART needs to be instantiated to use the same pins used for terminal logging. These are pins 20 and 21. This is similar to how UART was configured in the <a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-standard-library-embedded-rust-uart-communication">UART post</a>:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Configure UART</span>
<span class="hljs-comment">// Create handle for UART config struct</span>
<span class="hljs-keyword">let</span> config = config::Config::default().baudrate(Hertz(<span class="hljs-number">115_200</span>));

<span class="hljs-comment">// Instantiate UART</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> uart = UartDriver::new(
    peripherals.uart0,
    peripherals.pins.gpio21,
    peripherals.pins.gpio20,
    <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio0&gt;::<span class="hljs-literal">None</span>,
    <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio1&gt;::<span class="hljs-literal">None</span>,
    &amp;config,
)
.unwrap();
</code></pre>
<p>3️⃣ <strong>Acquire User Input</strong>: Following the UART configuration, the user is prompted to enter the SSID as shown below. Following that, the SSID is captured by entering a loop where a character is read one at a time using the UART driver <code>read</code> method. Note the following:</p>
<ul>
<li><p>Each character entered is echoed to the console using the <code>write</code> method. This is so that the user has visual confirmation that the intended character is entered. In the case of password entry, an asterisk (ascii value 42) is echoed instead of the actual entry.</p>
</li>
<li><p>Each character that is acquired is appended/buffered in the <code>ssid</code> and <code>password</code> vectors using the <code>extend_from_slice</code> <code>Vec</code> method.</p>
</li>
<li><p>Every time a character is read, the code checks if its a carriage return (ascii value 13). If it is then the code breaks out of the loop.</p>
</li>
<li><p>Both <code>ssid</code> and <code>password</code> are <code>heapless::Vec</code> types.</p>
</li>
</ul>
<pre><code class="lang-rust">uart.write_str(<span class="hljs-string">"Enter Network SSID: "</span>).unwrap();

<span class="hljs-comment">// Read and Buffer SSID</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ssid = <span class="hljs-built_in">Vec</span>::&lt;<span class="hljs-built_in">u8</span>, <span class="hljs-number">32</span>&gt;::new();
<span class="hljs-keyword">loop</span> {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buf = [<span class="hljs-number">0_u8</span>; <span class="hljs-number">1</span>];
    uart.read(&amp;<span class="hljs-keyword">mut</span> buf, BLOCK).unwrap();
    uart.write(&amp;buf).unwrap();
    <span class="hljs-keyword">if</span> buf[<span class="hljs-number">0</span>] == <span class="hljs-number">13</span> {
        <span class="hljs-keyword">break</span>;
    }
    ssid.extend_from_slice(&amp;buf).unwrap();
}

uart.write_str(<span class="hljs-string">"\nEnter Network Password: "</span>).unwrap();

<span class="hljs-comment">// Read and Buffer Password</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> password = <span class="hljs-built_in">Vec</span>::&lt;<span class="hljs-built_in">u8</span>, <span class="hljs-number">64</span>&gt;::new();
<span class="hljs-keyword">loop</span> {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buf = [<span class="hljs-number">0_u8</span>; <span class="hljs-number">1</span>];
    uart.read(&amp;<span class="hljs-keyword">mut</span> buf, BLOCK).unwrap();
    uart.write(&amp;[<span class="hljs-number">42</span>]).unwrap();
    <span class="hljs-keyword">if</span> buf[<span class="hljs-number">0</span>] == <span class="hljs-number">13</span> {
        <span class="hljs-keyword">break</span>;
    }
    password.extend_from_slice(&amp;buf).unwrap();
}
</code></pre>
<p>4️⃣ <strong>Adjust Buffered Types</strong>: Both <code>ssid</code> and <code>password</code> are <code>Vec</code> types. The WiFi configuration however accepts a <code>heapless::String</code> type. As such, the acquired values need to be adjusted such that the types are compatible as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ssid: <span class="hljs-built_in">String</span>&lt;<span class="hljs-number">32</span>&gt; = <span class="hljs-built_in">String</span>::from_utf8(ssid).unwrap();
<span class="hljs-keyword">let</span> password: <span class="hljs-built_in">String</span>&lt;<span class="hljs-number">64</span>&gt; = <span class="hljs-built_in">String</span>::from_utf8(password).unwrap();
</code></pre>
<p>5️⃣ <strong>Obtain handle for WiFi</strong>: this involves the same steps that were done in the WiFi <a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-connecting-wifi"><strong>post</strong>.</a></p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> sysloop = EspSystemEventLoop::take()?;
<span class="hljs-keyword">let</span> nvs = EspDefaultNvsPartition::take()?;
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> wifi = EspWifi::new(peripherals.modem, sysloop, <span class="hljs-literal">Some</span>(nvs))?;
</code></pre>
<p>6️⃣ <strong>Configure the WiFi Driver</strong>: now that we have the ssid and password we can proceed to configure the <code>wifi</code> driver as follows:</p>
<pre><code class="lang-rust">wifi.set_configuration(&amp;Configuration::Client(ClientConfiguration {
    ssid: ssid,
    bssid: <span class="hljs-literal">None</span>,
    auth_method: AuthMethod::<span class="hljs-literal">None</span>,
    password: password,
    channel: <span class="hljs-literal">None</span>,
}))?;
</code></pre>
<p>This is it for configuration! Let's now jump into the application code.</p>
<h3 id="heading-application-code"><strong>📱 Application Code</strong></h3>
<p><strong>Start and Connect Wifi</strong>: Now that wifi is configured, all we need to do is <code>start</code> it and then <code>connect</code> to a network:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Start Wifi</span>
wifi.start()?;

<span class="hljs-comment">// Connect Wifi</span>
wifi.connect()?;

<span class="hljs-comment">// Wait until the network interface is up</span>
wifi.wait_netif_up()?;

<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Wifi Connected"</span>);

<span class="hljs-keyword">loop</span> {}
</code></pre>
<p>This is it!</p>
<h2 id="heading-full-application-code"><strong>📱Full Application Code</strong></h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Also, the Wokwi project can be accessed <a target="_blank" href="https://wokwi.com/projects/391058600462186497"><strong>here</strong></a>.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp_idf_hal::delay::BLOCK;
<span class="hljs-keyword">use</span> esp_idf_hal::gpio;
<span class="hljs-keyword">use</span> esp_idf_hal::prelude::*;
<span class="hljs-keyword">use</span> esp_idf_hal::uart::*;
<span class="hljs-keyword">use</span> esp_idf_svc::eventloop::EspSystemEventLoop;
<span class="hljs-keyword">use</span> esp_idf_svc::nvs::EspDefaultNvsPartition;
<span class="hljs-keyword">use</span> esp_idf_svc::wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi};
<span class="hljs-keyword">use</span> heapless::{<span class="hljs-built_in">String</span>, <span class="hljs-built_in">Vec</span>};
<span class="hljs-keyword">use</span> std::fmt::Write;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() -&gt; anyhow::<span class="hljs-built_in">Result</span>&lt;()&gt; {
    <span class="hljs-comment">// Take Peripherals</span>
    <span class="hljs-keyword">let</span> peripherals = Peripherals::take().unwrap();
    <span class="hljs-keyword">let</span> sysloop = EspSystemEventLoop::take()?;
    <span class="hljs-keyword">let</span> nvs = EspDefaultNvsPartition::take()?;

    <span class="hljs-comment">// Configure UART</span>
    <span class="hljs-comment">// Create handle for UART config struct</span>
    <span class="hljs-keyword">let</span> config = config::Config::default().baudrate(Hertz(<span class="hljs-number">115_200</span>));

    <span class="hljs-comment">// Instantiate UART</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> uart = UartDriver::new(
        peripherals.uart0,
        peripherals.pins.gpio21,
        peripherals.pins.gpio20,
        <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio0&gt;::<span class="hljs-literal">None</span>,
        <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio1&gt;::<span class="hljs-literal">None</span>,
        &amp;config,
    )
    .unwrap();

    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> wifi = BlockingWifi::wrap(
        EspWifi::new(peripherals.modem, sysloop.clone(), <span class="hljs-literal">Some</span>(nvs))?,
        sysloop,
    )?;

    <span class="hljs-comment">// This line is for Wokwi only so that the console output is formatted correctly</span>
    uart.write_str(<span class="hljs-string">"\x1b[20h"</span>).unwrap();

    uart.write_str(<span class="hljs-string">"Enter Network SSID: "</span>).unwrap();

    <span class="hljs-comment">// Read and Buffer SSID</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ssid = <span class="hljs-built_in">Vec</span>::&lt;<span class="hljs-built_in">u8</span>, <span class="hljs-number">32</span>&gt;::new();
    <span class="hljs-keyword">loop</span> {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buf = [<span class="hljs-number">0_u8</span>; <span class="hljs-number">1</span>];
        uart.read(&amp;<span class="hljs-keyword">mut</span> buf, BLOCK).unwrap();
        uart.write(&amp;buf).unwrap();
        <span class="hljs-keyword">if</span> buf[<span class="hljs-number">0</span>] == <span class="hljs-number">13</span> {
            <span class="hljs-keyword">break</span>;
        }
        ssid.extend_from_slice(&amp;buf).unwrap();
    }

    uart.write_str(<span class="hljs-string">"\nEnter Network Password: "</span>).unwrap();

    <span class="hljs-comment">// Read and Buffer Password</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> password = <span class="hljs-built_in">Vec</span>::&lt;<span class="hljs-built_in">u8</span>, <span class="hljs-number">64</span>&gt;::new();
    <span class="hljs-keyword">loop</span> {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buf = [<span class="hljs-number">0_u8</span>; <span class="hljs-number">1</span>];
        uart.read(&amp;<span class="hljs-keyword">mut</span> buf, BLOCK).unwrap();
        uart.write(&amp;[<span class="hljs-number">42</span>]).unwrap();
        <span class="hljs-keyword">if</span> buf[<span class="hljs-number">0</span>] == <span class="hljs-number">13</span> {
            <span class="hljs-keyword">break</span>;
        }
        password.extend_from_slice(&amp;buf).unwrap();
    }

    <span class="hljs-keyword">let</span> ssid: <span class="hljs-built_in">String</span>&lt;<span class="hljs-number">32</span>&gt; = <span class="hljs-built_in">String</span>::from_utf8(ssid).unwrap();
    <span class="hljs-keyword">let</span> password: <span class="hljs-built_in">String</span>&lt;<span class="hljs-number">64</span>&gt; = <span class="hljs-built_in">String</span>::from_utf8(password).unwrap();

    wifi.set_configuration(&amp;Configuration::Client(ClientConfiguration {
        ssid: ssid,
        bssid: <span class="hljs-literal">None</span>,
        auth_method: AuthMethod::<span class="hljs-literal">None</span>,
        password: password,
        channel: <span class="hljs-literal">None</span>,
    }))?;

    <span class="hljs-comment">// Start Wifi</span>
    wifi.start()?;

    <span class="hljs-comment">// Connect Wifi</span>
    wifi.connect()?;

    <span class="hljs-comment">// Wait until the network interface is up</span>
    wifi.wait_netif_up()?;

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Wifi Connected"</span>);

    <span class="hljs-keyword">loop</span> {}
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This post introduced how to configure and connect ESP Wifi in station mode using Rust and the <code>esp_idf_svc</code> crate. This code is modified from a past WiFi example allowing a user to enter an SSID and password instead of hardcoding them. This avoids having to recompile the code every time the WiFi station needs to be changed. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[ESP Embedded Rust: Ping CLI App Part 2]]></title><description><![CDATA[Introduction
In this week's post, we're going to add the remainder of the features to the CLI app last week. This includes capturing options from users, supporting host names, and printing statistics.
 
📚 Knowledge Pre-requisites
To understand the c...]]></description><link>https://blog.theembeddedrustacean.com/esp-embedded-rust-ping-cli-app-part-2</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/esp-embedded-rust-ping-cli-app-part-2</guid><category><![CDATA[Rust]]></category><category><![CDATA[cli]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[embedded systems]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Sat, 24 Feb 2024 11:10:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708772692067/088c97c1-52ce-4f1d-8584-3d936b18f5c8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>In this week's post, we're going to add the remainder of the features to the <a target="_blank" href="https://apollolabsblog.hashnode.dev/esp-embedded-rust-ping-cli-app-part-1">CLI app last week</a>. This includes capturing options from users, supporting host names, and printing statistics.</p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Knowledge in DNS is helpful.</p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/esp-embedded-rust-ping-cli-app-part-1">ESP Embedded Rust: Ping CLI Part 1</a></p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the apollolabs <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3">ESP32C3 git repo</a>. Note that if the code on the git repo is slightly different, it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<p>Additionally, the full project (code and simulation) is available on Wokwi <a target="_blank" href="https://wokwi.com/projects/389975860067518465"><strong>here</strong></a>.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨</strong> Software Design</h2>
<p>I'm going to make a small modification to the program options to ease the development a bit. Here's the modified version of the <code>help</code>:</p>
<pre><code class="lang-rust">Ping is a utility that sends ICMP Echo Request packets to a specified network host 
(either identified by its IP address or hostname) to test connectivity and measure round-trip time.

Usage: ping [options] &lt;hostname/IP&gt;

Options:
  --count=&lt;number&gt;     Number of ICMP Echo Request packets to send (default is <span class="hljs-number">4</span>).
  --interval=&lt;seconds&gt; Set the interval between successive ping packets <span class="hljs-keyword">in</span> seconds.
  --timeout=&lt;seconds&gt;  Specify a timeout value <span class="hljs-keyword">for</span> each ping attempt.
  --size=&lt;bytes&gt;       Set the size of the ICMP packets.

Examples:
  ping <span class="hljs-number">192.168</span>.<span class="hljs-number">1.1</span>          # Ping the IP address <span class="hljs-number">192.168</span>.<span class="hljs-number">1.1</span>
  ping example.com          # Ping the hostname <span class="hljs-symbol">'example</span>.com'
  ping --count=<span class="hljs-number">10</span> google.com     # <span class="hljs-built_in">Send</span> <span class="hljs-number">10</span> ping requests to google.com
  ping --interval=<span class="hljs-number">0.5</span> --size=<span class="hljs-number">100</span> example.com  # Ping with interval of <span class="hljs-number">0.5</span> seconds and packet size of <span class="hljs-number">100</span> bytes to <span class="hljs-symbol">'example</span>.com'
</code></pre>
<p>In last week's code there were three steps:</p>
<ol>
<li><p>Setup CLI Root Menu &amp; Callback</p>
</li>
<li><p>CLI Start-Up</p>
</li>
<li><p>Create Ping App Logic</p>
</li>
</ol>
<p>Here are the areas that need to be updated:</p>
<ul>
<li><p>The root menu in step 1 needs to be updated with items to incorporate the command options.</p>
</li>
<li><p>The app logic in step 3 needs to incorporate the logic to acquire the optional parameters, process the parameters, and adjust the printed output.</p>
</li>
</ul>
<h2 id="heading-code-implementation"><strong>👨‍💻</strong> Code Implementation</h2>
<h3 id="heading-crate-imports"><strong>📥 Crate Imports</strong></h3>
<p>In this implementation the crates required are as follows:</p>
<ul>
<li><p>The <code>esp_idf_hal</code> crate to import the needed device hardware abstractions.</p>
</li>
<li><p><code>menu</code> to provide the menu CLI structs and abstractions.</p>
</li>
<li><p><code>std::fmt</code> to import the <code>Write</code> trait.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp_idf_hal::delay::BLOCK;
<span class="hljs-keyword">use</span> esp_idf_hal::gpio;
<span class="hljs-keyword">use</span> esp_idf_hal::peripherals::Peripherals;
<span class="hljs-keyword">use</span> esp_idf_hal::prelude::*;
<span class="hljs-keyword">use</span> esp_idf_hal::uart::*;
<span class="hljs-keyword">use</span> menu::*;
<span class="hljs-keyword">use</span> std::fmt::Write;
</code></pre>
<h3 id="heading-updating-the-root-menu">📝 Updating the Root Menu</h3>
<p>We need to update the <code>ROOT_MENU</code> ping command <code>Item</code> to accommodate the options shown in the help output earlier. This includes <code>count</code>, <code>interval</code>, <code>timeout</code>, and <code>size</code>. In the parameter list, these all need to be of type <code>Parameter::NamedValue</code> since they are all named parameters with an argument (<a target="_blank" href="https://docs.rs/menu/latest/menu/enum.Parameter.html">link to menu crate documentation</a>). Here's the updated <code>Item</code> :</p>
<pre><code class="lang-rust">&amp;Item {
    item_type: ItemType::Callback {
        function: ping_app,
        parameters: &amp;[Parameter::Mandatory {
            parameter_name: <span class="hljs-string">"hostname/IP"</span>,
            help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"IP address or hostname"</span>),
        },
        Parameter::NamedValue {
            parameter_name: <span class="hljs-string">"count"</span>,
            argument_name: <span class="hljs-string">"cnt"</span>,
            help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"Packet count"</span>),
        },
        Parameter::NamedValue {
            parameter_name: <span class="hljs-string">"interval"</span>,
            argument_name: <span class="hljs-string">"int"</span>,
            help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"Interval between counts"</span>),
        },
        Parameter::NamedValue {
            parameter_name: <span class="hljs-string">"timeout"</span>,
            argument_name: <span class="hljs-string">"to"</span>,
            help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"timeout for each ping attempt"</span>),
        },
        Parameter::NamedValue {
            parameter_name: <span class="hljs-string">"size"</span>,
            argument_name: <span class="hljs-string">"sz"</span>,
            help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"Set the size of the packet"</span>),
        },],
    },
    command: <span class="hljs-string">"ping"</span>,
    help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"
    Ping is a utility that sends ICMP Echo Request packets to a specified network host 
    (either identified by its IP address or hostname) to test connectivity and measure round-trip time.

    Usage: ping [options] &lt;hostname/IP&gt;

    Options:
      --count=&lt;number&gt;     Number of ICMP Echo Request packets to send (default is 4).
      --interval=&lt;seconds&gt; Set the interval between successive ping packets in seconds.
      --timeout=&lt;seconds&gt;  Specify a timeout value for each ping attempt.
      --size=&lt;bytes&gt;       Set the size of the ICMP packets.
      --help               Display this help message and exit.

    Examples:
      ping 192.168.1.1          # Ping the IP address 192.168.1.1
      ping example.com          # Ping the hostname 'example.com'
      ping -count=10 google.com     # Send 10 ping requests to google.com
      ping -interval=0.5 -size=100 example.com  # Ping with interval of 0.5 seconds and packet size of 100 bytes to 'example.com'
    "</span>),
},
</code></pre>
<h3 id="heading-acquire-and-process-parameters">👨‍💻 Acquire and Process Parameters</h3>
<p><strong>🌐 Acquiring and Processing a Hostname</strong></p>
<p>In the <a target="_blank" href="https://apollolabsblog.hashnode.dev/esp-embedded-rust-ping-cli-app-part-1">last post</a>, in the ping app CLI <code>ping_app</code> callback function, we supported pinging only raw IP addresses using the <code>Ipv4Addrfrom_str</code> method. Meaning if we wanted to ping a hostname like www.google.com, we wouldn't be able to. To support hostnames, we'd need an abstraction that can parse the acquired string and resolve the hostname to an IP. <code>std::net::ToSocketAddrs</code> provides such abstractions. Heres the updated code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Read terminal input</span>
<span class="hljs-keyword">let</span> ip_str = argument_finder(item, args, <span class="hljs-string">"hostname/IP"</span>).unwrap().unwrap();

<span class="hljs-comment">// Resolve IP Address</span>
<span class="hljs-keyword">let</span> addresses = (ip_str, <span class="hljs-number">0</span>)
    .to_socket_addrs()
    .expect(<span class="hljs-string">"Unable to resolve domain"</span>)
    .next()
    .unwrap();

<span class="hljs-comment">// Convert to IP v4 type address</span>
<span class="hljs-keyword">let</span> addr = <span class="hljs-keyword">match</span> addresses {
    std::net::SocketAddr::V4(a) =&gt; *a.ip(),
    std::net::SocketAddr::V6(_) =&gt; {
        <span class="hljs-built_in">writeln!</span>(context, <span class="hljs-string">"Address not compatible, try again"</span>).unwrap();
        <span class="hljs-keyword">return</span>;
    }
};
</code></pre>
<p>I'm going to broadly explain what is happening here and would recommend referring to the <code>std::net::ToSocketAddrs</code> documentation for detail. The first part of the code is similar to before where we read the terminal input. In the second part, <code>to_socket_addrs</code> is being used to parse and resolve an address from the user entry. <code>to_socket_addrs</code> returns a collection of possible addresses in which only the first one is being used.</p>
<p>The addresses returned by <code>to_socket_addrs</code> are of IpAddr type that could be either a IPv4 or IPv6 address. As such, the second part of the code makes sure that its an IPv4 address that is being used since thats what <code>EspPing</code> supports.</p>
<p><strong>📝 Acquiring and Processing Options</strong></p>
<p>We earlier added four command options the user can enter. These options would need to update the <code>ping_configEspPing</code> configuration struct. As such, a <code>default</code> instance of <code>ping_config</code> is created first such that if the user doesn't enter any options to update a member then we opt for a <code>default</code> configuration. Here's the code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Setup Default Ping Config</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ping_config = PingConfiguration::default();

<span class="hljs-comment">// Obtain CLI Options and Modify Default Configuration Accordingly</span>
ping_config.count = <span class="hljs-number">1</span>;
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ping_attempts = <span class="hljs-number">4</span>;

<span class="hljs-keyword">match</span> argument_finder(item, args, <span class="hljs-string">"count"</span>) {
    <span class="hljs-literal">Ok</span>(arg) =&gt; <span class="hljs-keyword">match</span> arg {
        <span class="hljs-literal">Some</span>(cnt) =&gt; ping_attempts = FromStr::from_str(cnt).unwrap(),
        <span class="hljs-literal">None</span> =&gt; (),
    },
    <span class="hljs-literal">Err</span>(_) =&gt; (),
}

<span class="hljs-keyword">match</span> argument_finder(item, args, <span class="hljs-string">"interval"</span>) {
    <span class="hljs-literal">Ok</span>(arg) =&gt; <span class="hljs-keyword">match</span> arg {
        <span class="hljs-literal">Some</span>(inter) =&gt; {
            ping_config.interval = Duration::from_secs(FromStr::from_str(inter).unwrap())
        }
        <span class="hljs-literal">None</span> =&gt; (),
    },
    <span class="hljs-literal">Err</span>(_) =&gt; (),
}

<span class="hljs-keyword">match</span> argument_finder(item, args, <span class="hljs-string">"timeout"</span>) {
    <span class="hljs-literal">Ok</span>(arg) =&gt; <span class="hljs-keyword">match</span> arg {
        <span class="hljs-literal">Some</span>(to) =&gt; ping_config.timeout = Duration::from_secs(FromStr::from_str(to).unwrap()),
        <span class="hljs-literal">None</span> =&gt; (),
    },
    <span class="hljs-literal">Err</span>(_) =&gt; (),
}

<span class="hljs-keyword">match</span> argument_finder(item, args, <span class="hljs-string">"size"</span>) {
    <span class="hljs-literal">Ok</span>(arg) =&gt; <span class="hljs-keyword">match</span> arg {
        <span class="hljs-literal">Some</span>(sz) =&gt; ping_config.data_size = FromStr::from_str(sz).unwrap(),
        <span class="hljs-literal">None</span> =&gt; (),
    },
    <span class="hljs-literal">Err</span>(_) =&gt; (),
}
</code></pre>
<p>Note how <code>ping_config.count</code> is set to 1. This is because if it's anything other than 1 then it would send multiple <code>byte_size</code> packets per attempt. Additionally, we'd want to print each <code>ping</code> attempt individually a number of times that the user defines. For that, the <code>ping_attempts</code> variable is introduced with a default of <code>4</code>.</p>
<blockquote>
<p>📝 <strong>Note:</strong><code>count</code> is used in two different contexts. <code>count</code> that is provided as a user option is essentially what we are calling the number of ping attempts. On the other hand, while <code>ping_config.count</code> also refers to the number of attempts, it refers to attempts done in one ping operation. This means we wouldn't be able to print every attempt individually. To circumvent this, <code>ping_config.count</code> is always set to 1 and <code>ping_attempts</code> controls how many times an <code>EspPing</code> is performed.</p>
</blockquote>
<h3 id="heading-adjust-the-printed-output">👨‍💻 Adjust the Printed Output</h3>
<p>In the first part of the printed output, we need to adjust it to reflect the input the user provided. Additionally, we need to introduce some variables to collect information for printing the statistics later. These are <code>summary</code>, <code>times</code>, and <code>rx_count</code>. Here is the adjusted output:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Update CLI</span>
<span class="hljs-comment">// Pinging {IP} with {x} bytes of data</span>
<span class="hljs-built_in">writeln!</span>(
    context,
    <span class="hljs-string">"Pinging {} [{:?}] with {} bytes of data\n"</span>,
    ip_str, addr, ping_config.data_size
)
.unwrap();

<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> summary = Summary::default();
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> times: <span class="hljs-built_in">Vec</span>&lt;<span class="hljs-built_in">u128</span>&gt; = <span class="hljs-built_in">Vec</span>::new();
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> rx_count = <span class="hljs-number">0</span>;

<span class="hljs-comment">// Ping 4 times and print results in following format:</span>
<span class="hljs-comment">// Reply from {IP}: bytes={summary.recieved} time={summary.time} TTL={summary.timeout}</span>
<span class="hljs-keyword">for</span> _n <span class="hljs-keyword">in</span> <span class="hljs-number">1</span>..=ping_attempts {
    summary = ping.ping(addr, &amp;ping_config).unwrap();

    <span class="hljs-built_in">writeln!</span>(
        context,
        <span class="hljs-string">"Reply from {:?}: bytes = {}, time = {:?}, TTL = {:?}"</span>,
        addr, ping_config.data_size, summary.time, ping_config.timeout
    )
    .unwrap();

    <span class="hljs-comment">// Update values for statistics</span>
    times.push(summary.time.as_millis());
    <span class="hljs-keyword">if</span> summary.transmitted == summary.received {
        rx_count += <span class="hljs-number">1</span>;
    }
}
</code></pre>
<p>Afterward, the ping stats are appended with the following commands:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Print ping statistics in following format:</span>
<span class="hljs-comment">// Ping statistics for {IP}:</span>
<span class="hljs-comment">//      Packets: Sent = {sent}, Received  = {rec}, Lost = {loss} &lt;{per}% Loss&gt;</span>
<span class="hljs-comment">// Approximate round trip times in milliseconds:</span>
<span class="hljs-comment">//      Minimum = {min}ms, Maximum = {max}ms, Average = {avg}ms</span>
<span class="hljs-built_in">writeln!</span>(context, <span class="hljs-string">"\nPing Statistics for {:?}"</span>, addr).unwrap();
<span class="hljs-built_in">writeln!</span>(
    context,
    <span class="hljs-string">"     Packets: Sent = {}, Received  = {}, Lost = {} &lt;{}% loss&gt;"</span>,
    ping_attempts,
    rx_count,
    ping_attempts - rx_count,
    ((ping_attempts - rx_count) / ping_attempts) * <span class="hljs-number">100</span>
)
.unwrap();
<span class="hljs-built_in">writeln!</span>(context, <span class="hljs-string">"Approximate round trip times in milliseconds:"</span>).unwrap();
<span class="hljs-built_in">writeln!</span>(
    context,
    <span class="hljs-string">"     Minimum = {} ms, Maximum = {} ms, Average = {} ms"</span>,
    times.iter().min().unwrap(),
    times.iter().max().unwrap(),
    times.iter().sum::&lt;<span class="hljs-built_in">u128</span>&gt;() / times.len() <span class="hljs-keyword">as</span> <span class="hljs-built_in">u128</span>,
)
.unwrap();
</code></pre>
<p>That's it for code!</p>
<h2 id="heading-full-application-code">📱Full Application Code</h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Also, the Wokwi project can be accessed <a target="_blank" href="https://wokwi.com/projects/389975860067518465"><strong>here</strong></a>.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp_idf_hal::delay::BLOCK;
<span class="hljs-keyword">use</span> esp_idf_hal::gpio;
<span class="hljs-keyword">use</span> esp_idf_hal::prelude::*;
<span class="hljs-keyword">use</span> esp_idf_hal::uart::*;
<span class="hljs-keyword">use</span> esp_idf_svc::eventloop::EspSystemEventLoop;
<span class="hljs-keyword">use</span> esp_idf_svc::nvs::EspDefaultNvsPartition;
<span class="hljs-keyword">use</span> esp_idf_svc::ping::{Configuration <span class="hljs-keyword">as</span> PingConfiguration, EspPing, Summary};
<span class="hljs-keyword">use</span> esp_idf_svc::wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi};
<span class="hljs-keyword">use</span> menu::*;
<span class="hljs-keyword">use</span> std::fmt::Write;
<span class="hljs-keyword">use</span> std::net::ToSocketAddrs;
<span class="hljs-keyword">use</span> std::<span class="hljs-built_in">str</span>::FromStr;
<span class="hljs-keyword">use</span> std::time::Duration;

<span class="hljs-comment">// CLI Root Menu Struct Initialization</span>
<span class="hljs-keyword">const</span> ROOT_MENU: Menu&lt;UartDriver&gt; = Menu {
    label: <span class="hljs-string">"root"</span>,
    items: &amp;[
        &amp;Item {
            item_type: ItemType::Callback {
                function: hello_name,
                parameters: &amp;[Parameter::Mandatory {
                    parameter_name: <span class="hljs-string">"name"</span>,
                    help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"Enter your name"</span>),
                }],
            },
            command: <span class="hljs-string">"hw"</span>,
            help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"This is the help for the hello, name hw command!"</span>),
        },
        &amp;Item {
            item_type: ItemType::Callback {
                function: ping_app,
                parameters: &amp;[Parameter::Mandatory {
                    parameter_name: <span class="hljs-string">"hostname/IP"</span>,
                    help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"IP address or hostname"</span>),
                },
                Parameter::NamedValue {
                    parameter_name: <span class="hljs-string">"count"</span>,
                    argument_name: <span class="hljs-string">"cnt"</span>,
                    help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"Packet count"</span>),
                },
                Parameter::NamedValue {
                    parameter_name: <span class="hljs-string">"interval"</span>,
                    argument_name: <span class="hljs-string">"int"</span>,
                    help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"Interval between counts"</span>),
                },
                Parameter::NamedValue {
                    parameter_name: <span class="hljs-string">"timeout"</span>,
                    argument_name: <span class="hljs-string">"to"</span>,
                    help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"timeout for each ping attempt"</span>),
                },
                Parameter::NamedValue {
                    parameter_name: <span class="hljs-string">"size"</span>,
                    argument_name: <span class="hljs-string">"sz"</span>,
                    help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"Set the size of the packet"</span>),
                },],
            },
            command: <span class="hljs-string">"ping"</span>,
            help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"
            Ping is a utility that sends ICMP Echo Request packets to a specified network host 
            (either identified by its IP address or hostname) to test connectivity and measure round-trip time.

            Usage: ping [options] &lt;hostname/IP&gt;

            Options:
              --count=&lt;number&gt;     Number of ICMP Echo Request packets to send (default is 4).
              --interval=&lt;seconds&gt; Set the interval between successive ping packets in seconds.
              --timeout=&lt;seconds&gt;  Specify a timeout value for each ping attempt.
              --size=&lt;bytes&gt;       Set the size of the ICMP packets.
              --help               Display this help message and exit.

            Examples:
              ping 192.168.1.1          # Ping the IP address 192.168.1.1
              ping example.com          # Ping the hostname 'example.com'
              ping -count=10 google.com     # Send 10 ping requests to google.com
              ping -interval=0.5 -size=100 example.com  # Ping with interval of 0.5 seconds and packet size of 100 bytes to 'example.com'
            "</span>),
        },
    ],
    entry: <span class="hljs-literal">None</span>,
    exit: <span class="hljs-literal">None</span>,
};

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() -&gt; anyhow::<span class="hljs-built_in">Result</span>&lt;()&gt; {
    <span class="hljs-comment">// Take Peripherals</span>
    <span class="hljs-keyword">let</span> peripherals = Peripherals::take().unwrap();
    <span class="hljs-keyword">let</span> sysloop = EspSystemEventLoop::take()?;
    <span class="hljs-keyword">let</span> nvs = EspDefaultNvsPartition::take()?;

    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> wifi = BlockingWifi::wrap(
        EspWifi::new(peripherals.modem, sysloop.clone(), <span class="hljs-literal">Some</span>(nvs))?,
        sysloop,
    )?;

    wifi.set_configuration(&amp;Configuration::Client(ClientConfiguration {
        ssid: <span class="hljs-string">"Wokwi-GUEST"</span>.try_into().unwrap(),
        bssid: <span class="hljs-literal">None</span>,
        auth_method: AuthMethod::<span class="hljs-literal">None</span>,
        password: <span class="hljs-string">""</span>.try_into().unwrap(),
        channel: <span class="hljs-literal">None</span>,
    }))?;

    <span class="hljs-comment">// Start Wifi</span>
    wifi.start()?;

    <span class="hljs-comment">// Connect Wifi</span>
    wifi.connect()?;

    <span class="hljs-comment">// Wait until the network interface is up</span>
    wifi.wait_netif_up()?;

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Wifi Connected"</span>);

    <span class="hljs-comment">// Configure UART</span>
    <span class="hljs-comment">// Create handle for UART config struct</span>
    <span class="hljs-keyword">let</span> config = config::Config::default().baudrate(Hertz(<span class="hljs-number">115_200</span>));

    <span class="hljs-comment">// Instantiate UART</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> uart = UartDriver::new(
        peripherals.uart0,
        peripherals.pins.gpio21,
        peripherals.pins.gpio20,
        <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio0&gt;::<span class="hljs-literal">None</span>,
        <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio1&gt;::<span class="hljs-literal">None</span>,
        &amp;config,
    )
    .unwrap();

    <span class="hljs-comment">// This line is for Wokwi only so that the console output is formatted correctly</span>
    uart.write_str(<span class="hljs-string">"\x1b[20h"</span>).unwrap();

    <span class="hljs-comment">// Create a buffer to store CLI input</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> clibuf = [<span class="hljs-number">0u8</span>; <span class="hljs-number">64</span>];
    <span class="hljs-comment">// Instantiate CLI runner with root menu, buffer, and uart</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> r = Runner::new(ROOT_MENU, &amp;<span class="hljs-keyword">mut</span> clibuf, uart);

    <span class="hljs-keyword">loop</span> {
        <span class="hljs-comment">// Create single element buffer for UART characters</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buf = [<span class="hljs-number">0_u8</span>; <span class="hljs-number">1</span>];
        <span class="hljs-comment">// Read single byte from UART</span>
        r.context.read(&amp;<span class="hljs-keyword">mut</span> buf, BLOCK).unwrap();
        <span class="hljs-comment">// Pass read byte to CLI runner for processing</span>
        r.input_byte(buf[<span class="hljs-number">0</span>]);
    }
}

<span class="hljs-comment">// Callback function for hw command</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">hello_name</span></span>&lt;<span class="hljs-symbol">'a</span>&gt;(
    _menu: &amp;Menu&lt;UartDriver&gt;,
    item: &amp;Item&lt;UartDriver&gt;,
    args: &amp;[&amp;<span class="hljs-built_in">str</span>],
    context: &amp;<span class="hljs-keyword">mut</span> UartDriver,
) {
    <span class="hljs-comment">// Print to console passed "name" argument</span>
    <span class="hljs-built_in">writeln!</span>(
        context,
        <span class="hljs-string">"Hello, {}!"</span>,
        argument_finder(item, args, <span class="hljs-string">"name"</span>).unwrap().unwrap()
    )
    .unwrap();
}

<span class="hljs-comment">// Callback function for ping command</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">ping_app</span></span>&lt;<span class="hljs-symbol">'a</span>&gt;(
    _menu: &amp;Menu&lt;UartDriver&gt;,
    item: &amp;Item&lt;UartDriver&gt;,
    args: &amp;[&amp;<span class="hljs-built_in">str</span>],
    context: &amp;<span class="hljs-keyword">mut</span> UartDriver,
) {
    <span class="hljs-comment">// Retreieve CLI Input</span>
    <span class="hljs-keyword">let</span> ip_str = argument_finder(item, args, <span class="hljs-string">"hostname/IP"</span>).unwrap().unwrap();

    <span class="hljs-comment">// Resolve IP Address</span>
    <span class="hljs-keyword">let</span> addresses = (ip_str, <span class="hljs-number">0</span>)
        .to_socket_addrs()
        .expect(<span class="hljs-string">"Unable to resolve domain"</span>)
        .next()
        .unwrap();

    <span class="hljs-comment">// Convert to IP v4 type address</span>
    <span class="hljs-keyword">let</span> addr = <span class="hljs-keyword">match</span> addresses {
        std::net::SocketAddr::V4(a) =&gt; *a.ip(),
        std::net::SocketAddr::V6(_) =&gt; {
            <span class="hljs-built_in">writeln!</span>(context, <span class="hljs-string">"Address not compatible, try again"</span>).unwrap();
            <span class="hljs-keyword">return</span>;
        }
    };

    <span class="hljs-comment">// Create EspPing instance</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ping = EspPing::new(<span class="hljs-number">0_u32</span>);

    <span class="hljs-comment">// Setup Default Ping Config</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ping_config = PingConfiguration::default();

    <span class="hljs-comment">// Obtain CLI Options and Modify Default Configuration Accordingly</span>
    ping_config.count = <span class="hljs-number">1</span>;
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ping_attempts = <span class="hljs-number">4</span>;

    <span class="hljs-keyword">match</span> argument_finder(item, args, <span class="hljs-string">"count"</span>) {
        <span class="hljs-literal">Ok</span>(arg) =&gt; <span class="hljs-keyword">match</span> arg {
            <span class="hljs-literal">Some</span>(cnt) =&gt; ping_attempts = FromStr::from_str(cnt).unwrap(),
            <span class="hljs-literal">None</span> =&gt; (),
        },
        <span class="hljs-literal">Err</span>(_) =&gt; (),
    }

    <span class="hljs-keyword">match</span> argument_finder(item, args, <span class="hljs-string">"interval"</span>) {
        <span class="hljs-literal">Ok</span>(arg) =&gt; <span class="hljs-keyword">match</span> arg {
            <span class="hljs-literal">Some</span>(inter) =&gt; {
                ping_config.interval = Duration::from_secs(FromStr::from_str(inter).unwrap())
            }
            <span class="hljs-literal">None</span> =&gt; (),
        },
        <span class="hljs-literal">Err</span>(_) =&gt; (),
    }

    <span class="hljs-keyword">match</span> argument_finder(item, args, <span class="hljs-string">"timeout"</span>) {
        <span class="hljs-literal">Ok</span>(arg) =&gt; <span class="hljs-keyword">match</span> arg {
            <span class="hljs-literal">Some</span>(to) =&gt; ping_config.timeout = Duration::from_secs(FromStr::from_str(to).unwrap()),
            <span class="hljs-literal">None</span> =&gt; (),
        },
        <span class="hljs-literal">Err</span>(_) =&gt; (),
    }

    <span class="hljs-keyword">match</span> argument_finder(item, args, <span class="hljs-string">"size"</span>) {
        <span class="hljs-literal">Ok</span>(arg) =&gt; <span class="hljs-keyword">match</span> arg {
            <span class="hljs-literal">Some</span>(sz) =&gt; ping_config.data_size = FromStr::from_str(sz).unwrap(),
            <span class="hljs-literal">None</span> =&gt; (),
        },
        <span class="hljs-literal">Err</span>(_) =&gt; (),
    }

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{:?}"</span>, ping_config);

    <span class="hljs-comment">// Update CLI</span>
    <span class="hljs-comment">// Pinging {IP} with {x} bytes of data</span>
    <span class="hljs-built_in">writeln!</span>(
        context,
        <span class="hljs-string">"Pinging {} [{:?}] with {} bytes of data\n"</span>,
        ip_str, addr, ping_config.data_size
    )
    .unwrap();

    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> summary = Summary::default();
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> times: <span class="hljs-built_in">Vec</span>&lt;<span class="hljs-built_in">u128</span>&gt; = <span class="hljs-built_in">Vec</span>::new();
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> rx_count = <span class="hljs-number">0</span>;

    <span class="hljs-comment">// Ping 4 times and print results in following format:</span>
    <span class="hljs-comment">// Reply from {IP}: bytes={summary.recieved} time={summary.time} TTL={summary.timeout}</span>
    <span class="hljs-keyword">for</span> _n <span class="hljs-keyword">in</span> <span class="hljs-number">1</span>..=ping_attempts {
        summary = ping.ping(addr, &amp;ping_config).unwrap();

        <span class="hljs-built_in">writeln!</span>(
            context,
            <span class="hljs-string">"Reply from {:?}: bytes = {}, time = {:?}, TTL = {:?}"</span>,
            addr, ping_config.data_size, summary.time, ping_config.timeout
        )
        .unwrap();

        <span class="hljs-comment">// Update values for statistics</span>
        times.push(summary.time.as_millis());
        <span class="hljs-keyword">if</span> summary.transmitted == summary.received {
            rx_count += <span class="hljs-number">1</span>;
        }
    }

    <span class="hljs-comment">// Print ping statstics in following format:</span>
    <span class="hljs-comment">// Ping statistics for {IP}:</span>
    <span class="hljs-comment">//      Packets: Sent = {sent}, Recieved  = {rec}, Lost = {loss} &lt;{per}% Loss&gt;</span>
    <span class="hljs-comment">// Approximate round trip times in milliseconds:</span>
    <span class="hljs-comment">//      Minimum = {min}ms, Maximum = {max}ms, Average = {avg}ms</span>
    <span class="hljs-built_in">writeln!</span>(context, <span class="hljs-string">"\nPing Statistics for {:?}"</span>, addr).unwrap();
    <span class="hljs-built_in">writeln!</span>(
        context,
        <span class="hljs-string">"     Packets: Sent = {}, Recieved  = {}, Lost = {} &lt;{}% loss&gt;"</span>,
        ping_attempts,
        rx_count,
        ping_attempts - rx_count,
        ((ping_attempts - rx_count) / ping_attempts) * <span class="hljs-number">100</span>
    )
    .unwrap();
    <span class="hljs-built_in">writeln!</span>(context, <span class="hljs-string">"Approximate round trip times in milliseconds:"</span>).unwrap();
    <span class="hljs-built_in">writeln!</span>(
        context,
        <span class="hljs-string">"     Minimum = {} ms, Maximum = {} ms, Average = {} ms"</span>,
        times.iter().min().unwrap(),
        times.iter().max().unwrap(),
        times.iter().sum::&lt;<span class="hljs-built_in">u128</span>&gt;() / times.len() <span class="hljs-keyword">as</span> <span class="hljs-built_in">u128</span>,
    )
    .unwrap();
}
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>In this post, a ping CLI application replica was built on an ESP32C3 using Rust and the supporting <code>std</code> library crates. The current version is an updated version of a CLI presented in a previous post. In the application in this post, hostnames and options are supported. Additionally, ping statistics are reported as part of the output. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[ESP Embedded Rust: Ping CLI App Part  1]]></title><description><![CDATA[Introduction
In the last blog post, I demonstrated the basic usage of ping::EspPing. Also in the post of the week before that, I went through the process of creating a command line interface over UART. I figured, why not combine both to create a Ping...]]></description><link>https://blog.theembeddedrustacean.com/esp-embedded-rust-ping-cli-app-part-1</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/esp-embedded-rust-ping-cli-app-part-1</guid><category><![CDATA[Rust]]></category><category><![CDATA[cli]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[embedded systems]]></category><category><![CDATA[iot]]></category><category><![CDATA[ESP32]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Sat, 17 Feb 2024 11:38:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708169105333/537be5cb-9b1c-4998-96fb-ef4decbe9621.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>In the last blog post, I demonstrated the basic usage of <code>ping::EspPing</code>. Also in the <a target="_blank" href="https://apollolabsblog.hashnode.dev/esp-embedded-rust-command-line-interface">post</a> of the week before that, I went through the process of creating a command line interface over UART. I figured, why not combine both to create a Ping CLI app replica?! As a result, in this post, a replica of a CLI ping application will be created. So that it's not overwhelming, the process is going to be broken up into two posts. In the first post, the basic framework of the app will be created. Next week, in the second post, more features will be added.</p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>The content of this post is heavily dependent on the following past posts:</p>
<ul>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/esp-embedded-rust-command-line-interface">ESP Embedded Rust: Command Line Interface</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-ping">Edge IoT with Rust on ESP: Ping!</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-connecting-wifi">Edge IoT with Rust on ESP: Connecting WiFi</a></p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<p>Additionally, the full project (code and simulation) is available on Wokwi <a target="_blank" href="https://wokwi.com/projects/389975860067518465"><strong>here</strong></a>.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨 Software Design</strong></h2>
<p>This is a description of the app we're going to build:</p>
<pre><code class="lang-plaintext">Ping is a utility that sends ICMP Echo Request packets to a specified network host 
(either identified by its IP address or hostname) to test connectivity and measure round-trip time.

Usage: ping [options] &lt;hostname/IP&gt;

Options:
  -c, --count &lt;number&gt;     Number of ICMP Echo Request packets to send (default is 4).
  -i, --interval &lt;seconds&gt; Set the interval between successive ping packets in seconds.
  -t, --timeout &lt;seconds&gt;  Specify a timeout value for each ping attempt.
  -s, --size &lt;bytes&gt;       Set the size of the ICMP packets.

Examples:
  ping 192.168.1.1          # Ping the IP address 192.168.1.1
  ping example.com          # Ping the hostname 'example.com'
  ping -c 10 google.com     # Send 10 ping requests to google.com
  ping -i 0.5 -s 100 example.com  # Ping with interval of 0.5 seconds and packet size of 100 bytes to 'example.com'
</code></pre>
<p>This description is what will appear when <code>help ping</code> is entered. Also, the following is the type of output we want to recreate:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1708101110294/048fd335-3691-49ed-a4c4-0ac01a57a045.png" alt class="image--center mx-auto" /></p>
<p>To create the application, the following steps are followed:</p>
<h3 id="heading-step-1-setup-cli-root-menu-amp-callback">🐾 Step 1: Setup CLI Root Menu &amp; Callback</h3>
<p>In this first step, the following tasks will be accomplished:</p>
<ol>
<li><p>The <code>ping</code> command <code>Item</code> needs to be added to the root menu <code>Menu</code> struct.</p>
</li>
<li><p>The callback function for <code>ping</code> setup.</p>
</li>
</ol>
<h3 id="heading-step-2-cli-start-up">🐾 Step 2: CLI Start-Up</h3>
<p>The following are the actions that the application needs to take before spawning the CLI interface and invoking any commands:</p>
<ol>
<li><p>Configure, Instantiate, and Connect to WiFi.</p>
</li>
<li><p>Configure and Instantiate UART</p>
</li>
<li><p>Instantiate and run the CLI runner with the root menu.</p>
</li>
</ol>
<h3 id="heading-step-3-create-ping-app-logic">🐾 Step 3: Create Ping App Logic</h3>
<p>The app logic will be contained in the <code>ping</code> command callback function. This is the logic that will be executed when the <code>ping</code> command is invoked. The following are the app logic steps:</p>
<ol>
<li><p>Retrieve &amp; Process CLI input</p>
</li>
<li><p>Instantiate <code>EspPing</code></p>
</li>
<li><p>Setup <code>EspPing</code> Configuration</p>
</li>
<li><p>Perform <code>ping</code> and update CLI</p>
</li>
</ol>
<p>For this week's post, in step 1, the only input that will be processed is the ip address. Hostname and option processing capability will be added in the next post. Additionally, <code>ping</code> stats printing will be added next week.</p>
<h2 id="heading-code-implementation"><strong>👨‍💻 Code Implementation</strong></h2>
<h3 id="heading-crate-imports"><strong>📥 Crate Imports</strong></h3>
<p>In this implementation, the following crates are required:</p>
<ul>
<li><p>The <code>esp_idf_hal</code> crate to import the peripherals needed for <code>uart</code>.</p>
</li>
<li><p>The <code>esp_idf_svc</code> crate to import the device services needed for <code>wifi</code> and <code>ping</code>.</p>
</li>
<li><p>The <code>menu</code> crate for creating the CLI.</p>
</li>
<li><p>The <code>std::str</code> crate to import the <code>FromStr</code> abstraction.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp_idf_hal::delay::BLOCK;
<span class="hljs-keyword">use</span> esp_idf_hal::gpio;
<span class="hljs-keyword">use</span> esp_idf_hal::peripherals::Peripherals;
<span class="hljs-keyword">use</span> esp_idf_hal::prelude::*;
<span class="hljs-keyword">use</span> esp_idf_hal::uart::*;
<span class="hljs-keyword">use</span> esp_idf_svc::eventloop::EspSystemEventLoop;
<span class="hljs-keyword">use</span> esp_idf_svc::ipv4::Ipv4Addr;
<span class="hljs-keyword">use</span> esp_idf_svc::nvs::EspDefaultNvsPartition;
<span class="hljs-keyword">use</span> esp_idf_svc::ping::{Configuration <span class="hljs-keyword">as</span> PingConfiguration, EspPing};
<span class="hljs-keyword">use</span> esp_idf_svc::wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi};
<span class="hljs-keyword">use</span> menu::*;
<span class="hljs-keyword">use</span> std::fmt::Write;
<span class="hljs-keyword">use</span> std::<span class="hljs-built_in">str</span>::FromStr;
</code></pre>
<h3 id="heading-step-1-setup-cli-root-menu-amp-callback-1">🐾 Step 1: Setup CLI Root Menu &amp; Callback</h3>
<p><strong>1️⃣ Add the</strong><code>ping</code><strong>command</strong><code>Item</code><strong>to root menu:</strong> similar to what was done in the CLI post with the <code>hw</code> command for the hello app, a new <code>&amp;Item</code> is added for <code>ping</code> in the <code>ROOT_MENU</code>. Note that the callback function name is <code>ping_app</code> also there's only one <code>parameter_name</code> that will be recognized which is <code>"hostname/IP"</code> . Also, the <code>help</code> message details how the command will work. Be mindful that the options are included in the description although not supported yet.</p>
<pre><code class="lang-rust">&amp;Item {
    item_type: ItemType::Callback {
        function: ping_app,
        parameters: &amp;[Parameter::Mandatory {
            parameter_name: <span class="hljs-string">"hostname/IP"</span>,
            help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"IP address or hostname"</span>),
     }],
    },
    command: <span class="hljs-string">"ping"</span>,
    help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"
    Ping is a utility that sends ICMP Echo Request packets to a specified network host 
    (either identified by its IP address or hostname) to test connectivity and measure round-trip time.

    Usage: ping [options] &lt;hostname/IP&gt;

    Options:
      -c, --count &lt;number&gt;     Number of ICMP Echo Request packets to send (default is 4).
      -i, --interval &lt;seconds&gt; Set the interval between successive ping packets in seconds.
      -t, --timeout &lt;seconds&gt;  Specify a timeout value for each ping attempt.
      -s, --size &lt;bytes&gt;       Set the size of the ICMP packets.
      -h, --help               Display this help message and exit.

    Examples:
      ping 192.168.1.1          # Ping the IP address 192.168.1.1
      ping example.com          # Ping the hostname 'example.com'
      ping -c 10 google.com     # Send 10 ping requests to google.com
      ping -i 0.5 -s 100 example.com  # Ping with interval of 0.5 seconds and packet size of 100 bytes to 'example.com'
     "</span>),
}
</code></pre>
<p><strong>2️⃣ Create the callback function:</strong> The callback function named <code>ping_app</code> for the <code>ping</code> command specified in the <code>ROOT_MENUItem</code> is implemented as follows:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">ping_app</span></span>&lt;<span class="hljs-symbol">'a</span>&gt;(
    _menu: &amp;Menu&lt;UartDriver&gt;,
    item: &amp;Item&lt;UartDriver&gt;,
    args: &amp;[&amp;<span class="hljs-built_in">str</span>],
    context: &amp;<span class="hljs-keyword">mut</span> UartDriver,
) {
    <span class="hljs-comment">// App code goes here</span>
}
</code></pre>
<p>We're going to keep it empty for now, and fill in the logic implementation in the last step.</p>
<h3 id="heading-step-2-cli-start-up-1">🐾 Step 2: CLI Start-Up</h3>
<p>1️⃣ <strong>Configure, Instantiate, and Connect to WiFi:</strong> This involves the same steps taken in the <a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-connecting-wifi">wifi post</a> to connect to WiFi. Inside the <code>main</code> function, the following code is added:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> peripherals = Peripherals::take().unwrap();
<span class="hljs-keyword">let</span> sysloop = EspSystemEventLoop::take()?;
<span class="hljs-keyword">let</span> nvs = EspDefaultNvsPartition::take()?;

<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> wifi = BlockingWifi::wrap(
    EspWifi::new(peripherals.modem, sysloop.clone(), <span class="hljs-literal">Some</span>(nvs))?,
    sysloop,
)?;

wifi.set_configuration(&amp;Configuration::Client(ClientConfiguration {
    ssid: <span class="hljs-string">"Wokwi-GUEST"</span>.try_into().unwrap(),
    bssid: <span class="hljs-literal">None</span>,
    auth_method: AuthMethod::<span class="hljs-literal">None</span>,
    password: <span class="hljs-string">""</span>.try_into().unwrap(),
    channel: <span class="hljs-literal">None</span>,
}))?;

<span class="hljs-comment">// Start Wifi</span>
wifi.start()?;

<span class="hljs-comment">// Connect Wifi</span>
wifi.connect()?;

<span class="hljs-comment">// Wait until the network interface is up</span>
wifi.wait_netif_up()?;

<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Wifi Connected"</span>);
</code></pre>
<p><strong>2️⃣ Configure and Instantiate UART:</strong> This and the following step are identical to what was accomplished in the <a target="_blank" href="https://apollolabsblog.hashnode.dev/esp-embedded-rust-command-line-interface">CLI post</a>. Here is the code associated with this step:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Configure UART</span>
<span class="hljs-comment">// Create handle for UART config struct</span>
<span class="hljs-keyword">let</span> config = config::Config::default().baudrate(Hertz(<span class="hljs-number">115_200</span>));

<span class="hljs-comment">// Instantiate UART</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> uart = UartDriver::new(
    peripherals.uart0,
    peripherals.pins.gpio21,
    peripherals.pins.gpio20,
    <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio0&gt;::<span class="hljs-literal">None</span>,
    <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio1&gt;::<span class="hljs-literal">None</span>,
    &amp;config,
)
.unwrap();
</code></pre>
<p><strong>3️⃣ Instantiate and run the CLI runner with the root menu</strong>: Again, following the the <a target="_blank" href="https://apollolabsblog.hashnode.dev/esp-embedded-rust-command-line-interface">CLI post</a>, this is the associated code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Create a buffer to store CLI input</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> clibuf = [<span class="hljs-number">0u8</span>; <span class="hljs-number">64</span>];
<span class="hljs-comment">// Instantiate CLI runner with root menu, buffer, and uart</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> r = Runner::new(ROOT_MENU, &amp;<span class="hljs-keyword">mut</span> clibuf, uart);

<span class="hljs-keyword">loop</span> {
    <span class="hljs-comment">// Create single element buffer for UART characters</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buf = [<span class="hljs-number">0_u8</span>; <span class="hljs-number">1</span>];
    <span class="hljs-comment">// Read single byte from UART</span>
    r.context.read(&amp;<span class="hljs-keyword">mut</span> buf, BLOCK).unwrap();
    <span class="hljs-comment">// Pass read byte to CLI runner for processing</span>
    r.input_byte(buf[<span class="hljs-number">0</span>]);
}
</code></pre>
<h3 id="heading-step-3-create-ping-app-logic-1">🐾 Step 3: Create Ping App Logic</h3>
<p><strong>1️⃣ Retrieve &amp; Process CLI input:</strong> In the <code>ping_app</code> callback function, the first order of action will be to recover the user input. This is done using the <code>argument_finder</code> function in the <code>menu</code> crate. For now, the only entry supported is an IP address. Given that the retrieved IP address entry is a <code>&amp;str</code> type, it needs to be converted to an <code>EspPing</code> compatible <code>Ipv4Addr</code> type using the <code>from_str</code> associated method which returns a <code>Result</code>. Finally, in case the user enters incorrect input, this can be handled by doing a pattern match on the <code>from_strResult</code>. Here is the code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Retreieve CLI Input</span>
<span class="hljs-keyword">let</span> ip_str = argument_finder(item, args, <span class="hljs-string">"hostname/IP"</span>).unwrap().unwrap();

<span class="hljs-comment">// Process Input - Convert &amp;str type to Ipv4Addr</span>
<span class="hljs-keyword">let</span> ip = Ipv4Addr::from_str(ip_str);

<span class="hljs-comment">// Process Input - Make sure address formant is correct</span>
<span class="hljs-keyword">let</span> addr = <span class="hljs-keyword">match</span> ip {
    <span class="hljs-literal">Ok</span>(addr) =&gt; addr,
    <span class="hljs-literal">Err</span>(_) =&gt; {
        <span class="hljs-built_in">writeln!</span>(context, <span class="hljs-string">"Address error, try again"</span>).unwrap();
        <span class="hljs-keyword">return</span>;
    }
};
</code></pre>
<p><strong>2️⃣ Instantiate</strong><code>EspPing</code>: This is a single-line action same to what was done before:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ping = EspPing::new(<span class="hljs-number">0_u32</span>);
</code></pre>
<p><strong>3️⃣ Setup</strong><code>EspPing</code><strong>Configuration</strong>: for this post, a default configuration is going to be used. In next week's post, the configuration will be modified to accommodate any user-entered options.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ping_config = &amp;PingConfiguration::default();
</code></pre>
<p><strong>4️⃣ Perform</strong><code>ping</code><strong>and update CLI</strong>: This is the part where the output of the ping app is replicated. First, we need to print <code>Pinging [IP address] with [bytes sent] bytes of data</code> . The IP address is the one entered by the user and the number of bytes sent is inside the <code>ping_config</code> struct.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Update CLI</span>
<span class="hljs-comment">// Pinging {IP} with {x} bytes of data</span>
<span class="hljs-built_in">writeln!</span>(
    context,
    <span class="hljs-string">"Pinging {} with {} bytes of data\n"</span>,
    ip_str, ping_config.data_size
)
.unwrap();
</code></pre>
<p>Afterward, a ping needs to be performed 4 times and the output of each ping is reported in the format <code>Reply from [IP Address]: bytes=[bytes received] time=[response duration] TTL=[timeout duration]</code>. Here's the associated code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Ping 4 times and print results</span>
<span class="hljs-keyword">for</span> _n <span class="hljs-keyword">in</span> <span class="hljs-number">1</span>..=<span class="hljs-number">4</span> {
    <span class="hljs-keyword">let</span> summary = ping.ping(addr, ping_config).unwrap();

    <span class="hljs-built_in">writeln!</span>(
        context,
        <span class="hljs-string">"Reply from {}: bytes = {}, time = {:?}, TTL = {:?}"</span>,
        ip_str, summary.received, summary.time, ping_config.timeout
    )
    .unwrap();
}
</code></pre>
<p>Thats it!</p>
<h2 id="heading-testing"><strong>🧪 Testing</strong></h2>
<p>Since the current version supports only IP addresses, for the sake of testing, local network addresses can be pinged if using physical hardware. If you desire to test with internet addresses or on Wokwi some possible addresses to ping include the following:</p>
<ul>
<li><p><strong>OpenDNS</strong>: 208.67.222.222 and 208.67.220.220</p>
</li>
<li><p><strong>Cloudflare</strong>: 1.1.1.1 and 1.0.0.1</p>
</li>
<li><p><strong>Google DNS</strong>: 8.8.8.8 and 8.8.4.4</p>
</li>
</ul>
<h2 id="heading-full-application-code"><strong>📱Full Application Code</strong></h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Also, the Wokwi project can be accessed <a target="_blank" href="https://wokwi.com/projects/389975860067518465"><strong>here</strong></a>.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp_idf_hal::delay::BLOCK;
<span class="hljs-keyword">use</span> esp_idf_hal::gpio;
<span class="hljs-keyword">use</span> esp_idf_hal::peripherals::Peripherals;
<span class="hljs-keyword">use</span> esp_idf_hal::prelude::*;
<span class="hljs-keyword">use</span> esp_idf_hal::uart::*;
<span class="hljs-keyword">use</span> esp_idf_svc::eventloop::EspSystemEventLoop;
<span class="hljs-keyword">use</span> esp_idf_svc::ipv4::Ipv4Addr;
<span class="hljs-keyword">use</span> esp_idf_svc::nvs::EspDefaultNvsPartition;
<span class="hljs-keyword">use</span> esp_idf_svc::ping::{Configuration <span class="hljs-keyword">as</span> PingConfiguration, EspPing};
<span class="hljs-keyword">use</span> esp_idf_svc::wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi};
<span class="hljs-keyword">use</span> menu::*;
<span class="hljs-keyword">use</span> std::fmt::Write;
<span class="hljs-keyword">use</span> std::<span class="hljs-built_in">str</span>::FromStr;

<span class="hljs-comment">// CLI Root Menu Struct Initialization</span>
<span class="hljs-keyword">const</span> ROOT_MENU: Menu&lt;UartDriver&gt; = Menu {
    label: <span class="hljs-string">"root"</span>,
    items: &amp;[
        &amp;Item {
            item_type: ItemType::Callback {
                function: hello_name,
                parameters: &amp;[Parameter::Mandatory {
                    parameter_name: <span class="hljs-string">"name"</span>,
                    help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"Enter your name"</span>),
                }],
            },
            command: <span class="hljs-string">"hw"</span>,
            help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"This is the help for the hello, name hw command!"</span>),
        },
        &amp;Item {
            item_type: ItemType::Callback {
                function: ping_app,
                parameters: &amp;[Parameter::Mandatory {
                    parameter_name: <span class="hljs-string">"hostname/IP"</span>,
                    help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"IP address or hostname"</span>),
                }],
            },
            command: <span class="hljs-string">"ping"</span>,
            help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"
            Ping is a utility that sends ICMP Echo Request packets to a specified network host 
            (either identified by its IP address or hostname) to test connectivity and measure round-trip time.

            Usage: ping [options] &lt;hostname/IP&gt;

            Options:
              -c, --count &lt;number&gt;     Number of ICMP Echo Request packets to send (default is 4).
              -i, --interval &lt;seconds&gt; Set the interval between successive ping packets in seconds.
              -t, --timeout &lt;seconds&gt;  Specify a timeout value for each ping attempt.
              -s, --size &lt;bytes&gt;       Set the size of the ICMP packets.
              -h, --help               Display this help message and exit.

            Examples:
              ping 192.168.1.1          # Ping the IP address 192.168.1.1
              ping example.com          # Ping the hostname 'example.com'
              ping -c 10 google.com     # Send 10 ping requests to google.com
              ping -i 0.5 -s 100 example.com  # Ping with interval of 0.5 seconds and packet size of 100 bytes to 'example.com'
            "</span>),
        },
    ],
    entry: <span class="hljs-literal">None</span>,
    exit: <span class="hljs-literal">None</span>,
};

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() -&gt; anyhow::<span class="hljs-built_in">Result</span>&lt;()&gt; {
    <span class="hljs-comment">// Take Peripherals</span>
    <span class="hljs-keyword">let</span> peripherals = Peripherals::take().unwrap();
    <span class="hljs-keyword">let</span> sysloop = EspSystemEventLoop::take()?;
    <span class="hljs-keyword">let</span> nvs = EspDefaultNvsPartition::take()?;

    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> wifi = BlockingWifi::wrap(
        EspWifi::new(peripherals.modem, sysloop.clone(), <span class="hljs-literal">Some</span>(nvs))?,
        sysloop,
    )?;

    wifi.set_configuration(&amp;Configuration::Client(ClientConfiguration {
        ssid: <span class="hljs-string">"Wokwi-GUEST"</span>.try_into().unwrap(),
        bssid: <span class="hljs-literal">None</span>,
        auth_method: AuthMethod::<span class="hljs-literal">None</span>,
        password: <span class="hljs-string">""</span>.try_into().unwrap(),
        channel: <span class="hljs-literal">None</span>,
    }))?;

    <span class="hljs-comment">// Start Wifi</span>
    wifi.start()?;

    <span class="hljs-comment">// Connect Wifi</span>
    wifi.connect()?;

    <span class="hljs-comment">// Wait until the network interface is up</span>
    wifi.wait_netif_up()?;

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Wifi Connected"</span>);

    <span class="hljs-comment">// Configure UART</span>
    <span class="hljs-comment">// Create handle for UART config struct</span>
    <span class="hljs-keyword">let</span> config = config::Config::default().baudrate(Hertz(<span class="hljs-number">115_200</span>));

    <span class="hljs-comment">// Instantiate UART</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> uart = UartDriver::new(
        peripherals.uart0,
        peripherals.pins.gpio21,
        peripherals.pins.gpio20,
        <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio0&gt;::<span class="hljs-literal">None</span>,
        <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio1&gt;::<span class="hljs-literal">None</span>,
        &amp;config,
    )
    .unwrap();

    <span class="hljs-comment">// This line is for Wokwi only so that the console output is formatted correctly</span>
    uart.write_str(<span class="hljs-string">"\x1b[20h"</span>).unwrap();

    <span class="hljs-comment">// Create a buffer to store CLI input</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> clibuf = [<span class="hljs-number">0u8</span>; <span class="hljs-number">64</span>];
    <span class="hljs-comment">// Instantiate CLI runner with root menu, buffer, and uart</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> r = Runner::new(ROOT_MENU, &amp;<span class="hljs-keyword">mut</span> clibuf, uart);

    <span class="hljs-keyword">loop</span> {
        <span class="hljs-comment">// Create single element buffer for UART characters</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buf = [<span class="hljs-number">0_u8</span>; <span class="hljs-number">1</span>];
        <span class="hljs-comment">// Read single byte from UART</span>
        r.context.read(&amp;<span class="hljs-keyword">mut</span> buf, BLOCK).unwrap();
        <span class="hljs-comment">// Pass read byte to CLI runner for processing</span>
        r.input_byte(buf[<span class="hljs-number">0</span>]);
    }
}

<span class="hljs-comment">// Callback function for hw command</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">hello_name</span></span>&lt;<span class="hljs-symbol">'a</span>&gt;(
    _menu: &amp;Menu&lt;UartDriver&gt;,
    item: &amp;Item&lt;UartDriver&gt;,
    args: &amp;[&amp;<span class="hljs-built_in">str</span>],
    context: &amp;<span class="hljs-keyword">mut</span> UartDriver,
) {
    <span class="hljs-comment">// Print to console passed "name" argument</span>
    <span class="hljs-built_in">writeln!</span>(
        context,
        <span class="hljs-string">"Hello, {}!"</span>,
        argument_finder(item, args, <span class="hljs-string">"name"</span>).unwrap().unwrap()
    )
    .unwrap();
}

<span class="hljs-comment">// Callback function for ping command</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">ping_app</span></span>&lt;<span class="hljs-symbol">'a</span>&gt;(
    _menu: &amp;Menu&lt;UartDriver&gt;,
    item: &amp;Item&lt;UartDriver&gt;,
    args: &amp;[&amp;<span class="hljs-built_in">str</span>],
    context: &amp;<span class="hljs-keyword">mut</span> UartDriver,
) {
    <span class="hljs-comment">// Retreieve CLI Input</span>
    <span class="hljs-keyword">let</span> ip_str = argument_finder(item, args, <span class="hljs-string">"hostname/IP"</span>).unwrap().unwrap();

    <span class="hljs-comment">// Process Input - Convert &amp;str type to Ipv4Addr</span>
    <span class="hljs-keyword">let</span> ip = Ipv4Addr::from_str(ip_str);

    <span class="hljs-comment">// Process Input - Make sure address formant is correct</span>
    <span class="hljs-keyword">let</span> addr = <span class="hljs-keyword">match</span> ip {
        <span class="hljs-literal">Ok</span>(addr) =&gt; addr,
        <span class="hljs-literal">Err</span>(_) =&gt; {
            <span class="hljs-built_in">writeln!</span>(context, <span class="hljs-string">"Address error, try again"</span>).unwrap();
            <span class="hljs-keyword">return</span>;
        }
    };

    <span class="hljs-comment">// Create EspPing instance</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ping = EspPing::new(<span class="hljs-number">0_u32</span>);

    <span class="hljs-comment">// Setup Ping Config</span>
    <span class="hljs-keyword">let</span> ping_config = &amp;PingConfiguration::default();

    <span class="hljs-comment">// Update CLI</span>
    <span class="hljs-comment">// Pinging {IP} with {x} bytes of data</span>
    <span class="hljs-built_in">writeln!</span>(
        context,
        <span class="hljs-string">"Pinging {} with {} bytes of data\n"</span>,
        ip_str, ping_config.data_size
    )
    .unwrap();

    <span class="hljs-comment">// Ping 4 times and print results</span>
    <span class="hljs-comment">// Reply from {IP}: bytes={summary.recieved} time={summary.time} TTL={summary.timeout}</span>
    <span class="hljs-keyword">for</span> _n <span class="hljs-keyword">in</span> <span class="hljs-number">1</span>..=<span class="hljs-number">4</span> {
        <span class="hljs-keyword">let</span> summary = ping.ping(addr, ping_config).unwrap();

        <span class="hljs-built_in">writeln!</span>(
            context,
            <span class="hljs-string">"Reply from {}: bytes = {}, time = {:?}, TTL = {:?}"</span>,
            ip_str, summary.received, summary.time, ping_config.timeout
        )
        .unwrap();
    }
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this post, a ping CLI application replica was built on a ESP32C3 using Rust and the supporting <code>std</code> library crates. The current version is limited to pinging IP addresses only. In the next blog post, hostnames and options support will be added. Additionally, ping statistics will be reported as part of the output. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="substart"></div>]]></content:encoded></item><item><title><![CDATA[Edge IoT with Rust on ESP: Ping!]]></title><description><![CDATA[This blog post is the seventh of a multi-part series of posts where I explore various peripherals in the ESP32C3 using standard library embedded Rust and the esp-idf-svc. Please be aware that certain concepts in newer posts could depend on concepts i...]]></description><link>https://blog.theembeddedrustacean.com/edge-iot-with-rust-on-esp-ping</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/edge-iot-with-rust-on-esp-ping</guid><category><![CDATA[Rust]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[embedded systems]]></category><category><![CDATA[iot]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Sat, 10 Feb 2024 18:10:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1707588150485/1592ad68-4596-463e-b1ab-5843eaa149ba.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong><em>This blog post is the seventh of a multi-part series of posts where I explore various peripherals in the ESP32C3 using standard library embedded Rust and the esp-idf-svc. Please be aware that certain concepts in newer posts could depend on concepts in prior posts.</em></strong></p>
</blockquote>
<p>Prior posts include (in order of publishing):</p>
<ol>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-connecting-wifi"><strong>Edge IoT with Rust on ESP: Connecting WiFi</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-http-client"><strong>Edge IoT with Rust on ESP: HTTP Client</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-http-server"><strong>Edge IoT with Rust on ESP: HTTP Server</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-ntp">Edge IoT with Rust on ESP: NTP</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-mqtt-subscriber">Edge IoT with Rust on ESP: MQTT Subscriber</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-mqtt-publisher">Edge IoT with Rust on ESP: MQTT Publisher</a></p>
</li>
</ol>
<h2 id="heading-introduction">Introduction</h2>
<p>Ping is a networking utility used to test the reachability of a host on an Internet Protocol (IP) network and to measure the round-trip time for messages sent from the originating host to a destination computer and back. Essentially, it sends a small packet of data to a specified address and waits for a response. If the destination host is reachable, it sends a response back, allowing the sender to determine the status and latency of the connection. Ping is widely used for troubleshooting network connectivity issues and measuring network performance.</p>
<p>The <code>esp-idf-svc</code> provides an <code>EspPing</code> abstraction to perform ping operations. In this post, a simple ping application will be created using <code>ping::EspPing</code>. I'll be pinging the Google DNS IP.</p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Basic familiarity with the network stack.</p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<p>Additionally, the full project (code and simulation) is available on Wokwi <a target="_blank" href="https://wokwi.com/projects/389360760159060993"><strong>here</strong></a>.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨 Software Design</strong></h2>
<p>Ping utilizes the Internet Control Message Protocol (ICMP) in the network layer of the network stack. ICMP is a protocol used by network devices, like routers and servers, to communicate error messages and operational information back to the sender. Ping uses ICMP Echo Request and Echo Reply messages to determine the reachability and latency of a target host.</p>
<p>When you initiate a ping from your device, it constructs an ICMP Echo Request packet containing a small amount of data and sends it to the destination IP address. The destination device, if reachable, receives the packet and responds with an ICMP Echo Reply packet, indicating that it has received the ping request. This exchange of ICMP messages allows ping to determine if the destination host is reachable and measure the round-trip time (RTT) it takes for the packet to travel to the destination and back.</p>
<p>In this post, we are going to configure the ESP to ping the Google DNS IP (8.8.8.8). The steps include the following:</p>
<ol>
<li><p>Configure and Connect to WiFi.</p>
</li>
<li><p>Configure and perform a Ping.</p>
</li>
<li><p>Print Results.</p>
</li>
</ol>
<h2 id="heading-code-implementation"><strong>👨‍💻 Code Implementation</strong></h2>
<h3 id="heading-crate-imports"><strong>📥 Crate Imports</strong></h3>
<p>In this implementation, the following crates are required:</p>
<ul>
<li><p>The <code>esp_idf_hal</code> crate to import the peripherals.</p>
</li>
<li><p>The <code>esp_idf_svc</code> crate to import the device services (wifi in particular).</p>
</li>
<li><p>The <code>std::str</code> crate to import the <code>FromStr</code> abstraction.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::<span class="hljs-built_in">str</span>::FromStr;
<span class="hljs-keyword">use</span> esp_idf_hal::peripherals::Peripherals;
<span class="hljs-keyword">use</span> esp_idf_svc::eventloop::EspSystemEventLoop;
<span class="hljs-keyword">use</span> esp_idf_svc::ipv4::Ipv4Addr;
<span class="hljs-keyword">use</span> esp_idf_svc::nvs::EspDefaultNvsPartition;
<span class="hljs-keyword">use</span> esp_idf_svc::ping::{Configuration <span class="hljs-keyword">as</span> PingConfiguration, EspPing};
<span class="hljs-keyword">use</span> esp_idf_svc::wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi};
</code></pre>
<h3 id="heading-initializationconfiguration-code"><strong>🎛 Initialization/Configuration Code</strong></h3>
<p>1️⃣ <strong>Obtain a handle for the device peripherals</strong>: Similar to all past blog posts, in embedded Rust, as part of the singleton design pattern, we first have to take the device peripherals. This is done using the <code>take()</code> method. Here I create a device peripheral handler named <code>peripherals</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> peripherals = Peripherals::take().unwrap();
</code></pre>
<p>2️⃣ <strong>Configure and Connect to WiFi</strong>: this involves the same steps that were done in the <a target="_blank" href="https://apollolabsblog.hashnode.dev/edge-iot-with-rust-on-esp-connecting-wifi">wifi post</a>.</p>
<p>3️⃣ <strong>Create Handle and Configure Ping:</strong> Within <code>esp_idf_svc::ping::EspPing</code> there exists an <code>new</code> abstraction. <code>new</code> takes a single <code>interface_index</code> parameter that is a <code>u32</code> type. <code>interface_index</code> represents the Netif index. Passing a value of 0 means that is no interface index (click <a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_netif.html">here</a> for more about ESP Netif). Following that we create an <code>ping</code> handle as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ping = EspPing::new(<span class="hljs-number">0_u32</span>);
</code></pre>
<p>That's it for Configuration!</p>
<h3 id="heading-application-code"><strong>📱 Application Code</strong></h3>
<p>1️⃣ <strong>Perform a Ping:</strong> Within the <code>EspPing</code> abstraction, there is a <code>ping</code> method with the following signature:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">ping</span></span>(
    &amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>,
    ip: Ipv4Addr,
    conf: &amp;Configuration
) -&gt; <span class="hljs-built_in">Result</span>&lt;Summary, EspError&gt;
</code></pre>
<p>There are two arguments that need to be passed, an IP address of type <code>Ipv4Addr</code> and and configuration. <code>Ipv4Addr</code> has a <code>from_str</code> associated method that allows the passing of a slice using the dot decimal notation. <code>Configuration</code> on the other hand is a struct containing the configuration parameters and is defined as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Configuration</span></span> {
    <span class="hljs-keyword">pub</span> count: <span class="hljs-built_in">u32</span>,
    <span class="hljs-keyword">pub</span> interval: Duration,
    <span class="hljs-keyword">pub</span> timeout: Duration,
    <span class="hljs-keyword">pub</span> data_size: <span class="hljs-built_in">u32</span>,
    <span class="hljs-keyword">pub</span> tos: <span class="hljs-built_in">u8</span>,
}
</code></pre>
<p>where <code>count</code> is the number of probes/messages to be sent, <code>interval</code> is the <code>Duration</code> between probes, <code>timeout</code> defines the wait <code>Duration</code> to wait for an echo, <code>data_size</code> is the size of each message, and <code>tos</code> defines the type of service where 0 defines normal service also referred to as best effort. In this post, the <code>defaultConfiguration</code> will be used. Consequently, a ping is initiated as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> ping_res = ping.ping(
    Ipv4Addr::from_str(<span class="hljs-string">"8.8.8.8"</span>).unwrap(),
    &amp;PingConfiguration::default(),
);
</code></pre>
<p>2️⃣ <strong>Process the Ping Response:</strong> from the signature shown earlier, <code>ping</code> returns a <code>Result</code> wrapped in a <code>Summary</code>. <code>Summary</code> has the following members:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Summary</span></span> {
    <span class="hljs-keyword">pub</span> transmitted: <span class="hljs-built_in">u32</span>,
    <span class="hljs-keyword">pub</span> received: <span class="hljs-built_in">u32</span>,
    <span class="hljs-keyword">pub</span> time: Duration,
}
</code></pre>
<p>Consequently, the <code>Result</code> is processed as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">match</span> ping_res {
    <span class="hljs-literal">Ok</span>(summary) =&gt; <span class="hljs-built_in">println!</span>(
        <span class="hljs-string">"Transmitted: {}, Recieved: {} Time: {:?}"</span>,
        summary.transmitted, summary.received, summary.time
    ),
    <span class="hljs-literal">Err</span>(e) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{:?}"</span>, e),
}
</code></pre>
<p>Thats it!</p>
<h2 id="heading-full-application-code"><strong>📱Full Application Code</strong></h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Also, the Wokwi project can be accessed <a target="_blank" href="https://wokwi.com/projects/389360760159060993"><strong>here</strong></a>.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::<span class="hljs-built_in">str</span>::FromStr;
<span class="hljs-keyword">use</span> esp_idf_hal::peripherals::Peripherals;
<span class="hljs-keyword">use</span> esp_idf_svc::eventloop::EspSystemEventLoop;
<span class="hljs-keyword">use</span> esp_idf_svc::ipv4::Ipv4Addr;
<span class="hljs-keyword">use</span> esp_idf_svc::nvs::EspDefaultNvsPartition;
<span class="hljs-keyword">use</span> esp_idf_svc::ping::{Configuration <span class="hljs-keyword">as</span> PingConfiguration, EspPing};
<span class="hljs-keyword">use</span> esp_idf_svc::wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi};

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() -&gt; anyhow::<span class="hljs-built_in">Result</span>&lt;()&gt; {
    <span class="hljs-comment">// Take Peripherals</span>
    <span class="hljs-keyword">let</span> peripherals = Peripherals::take().unwrap();
    <span class="hljs-keyword">let</span> sysloop = EspSystemEventLoop::take()?;
    <span class="hljs-keyword">let</span> nvs = EspDefaultNvsPartition::take()?;

    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> wifi = BlockingWifi::wrap(
        EspWifi::new(peripherals.modem, sysloop.clone(), <span class="hljs-literal">Some</span>(nvs))?,
        sysloop,
    )?;

    wifi.set_configuration(&amp;Configuration::Client(ClientConfiguration {
        ssid: <span class="hljs-string">"Wokwi-GUEST"</span>.try_into().unwrap(),
        bssid: <span class="hljs-literal">None</span>,
        auth_method: AuthMethod::<span class="hljs-literal">None</span>,
        password: <span class="hljs-string">""</span>.try_into().unwrap(),
        channel: <span class="hljs-literal">None</span>,
    }))?;

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Connecting to WiFi"</span>);

    <span class="hljs-comment">// Start Wifi</span>
    wifi.start()?;

    <span class="hljs-comment">// Connect Wifi</span>
    wifi.connect()?;

    <span class="hljs-comment">// Wait until the network interface is up</span>
    wifi.wait_netif_up()?;

    <span class="hljs-comment">// This line is for Wokwi only so that the console output is formatted correctly</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"\x1b[20h"</span>);

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Wifi Connected"</span>);

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Pinging Google DNS (8.8.8.8)"</span>);

    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> ping = EspPing::new(<span class="hljs-number">0_u32</span>);

    <span class="hljs-keyword">let</span> ping_res = ping.ping(
        Ipv4Addr::from_str(<span class="hljs-string">"8.8.8.8"</span>).unwrap(),
        &amp;PingConfiguration::default(),
    );

    <span class="hljs-keyword">match</span> ping_res {
        <span class="hljs-literal">Ok</span>(summary) =&gt; <span class="hljs-built_in">println!</span>(
            <span class="hljs-string">"Transmitted: {}, Recieved: {} Time: {:?}"</span>,
            summary.transmitted, summary.received, summary.time
        ),
        <span class="hljs-literal">Err</span>(e) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{:?}"</span>, e),
    }

    <span class="hljs-keyword">loop</span> {}
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Ping is a popular tool used by network devices to determine device reachability and latency information. This post introduced how to set up Ping on a ESP32C3 using Rust and the <code>esp_idf_svc</code>. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="substart"></div>]]></content:encoded></item><item><title><![CDATA[ESP Embedded Rust: Command Line Interface]]></title><description><![CDATA[Introduction
Command line interfaces (CLI) in embedded applications are probably not as common as non-embedded ones. One reason is that embedded applications do not often require direct user input from a terminal, but rather through some other physic...]]></description><link>https://blog.theembeddedrustacean.com/esp-embedded-rust-command-line-interface</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/esp-embedded-rust-command-line-interface</guid><category><![CDATA[Rust]]></category><category><![CDATA[embedded systems]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[iot]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Sat, 03 Feb 2024 20:06:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1706990412869/e6eff93b-25fd-4da0-8e87-f918d43b9d36.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Command line interfaces (CLI) in embedded applications are probably not as common as non-embedded ones. One reason is that embedded applications do not often require direct user input from a terminal, but rather through some other physical interface. As such, if a CLI is required in embedded there are different considerations.</p>
<p>In a typical non-embedded CLI, there is an underlying operating system (OS) with a CLI (in a shell) meant for accepting user input and passing commands to the OS itself. The OS in turn processes the input appropriately. As such, a command signifies a precompiled executable stored in some directory followed by a collection of arguments. The passed arguments are then processed by the named program when the OS executes it. The arguments are also typically allocated to dynamic memory.</p>
<p>In case a CLI is needed in embedded, things take a different turn. First, an underlying OS with a shell interface doesn't necessarily exist, but rather an already running application or RTOS. Second, the interface itself could be on another host device. Third, dynamic allocation is not desirable. This means that to create a CLI, logic is incorporated within an existing running application. This comes in the form of accepting commands over some logging interface like UART and processing them in the board.</p>
<p>In this post, I'm going to create a simple embedded CLI interface on an ESP32C3 device. However, the CLI logic not going to be created from scratch, but rather leveraging an existing crate. There are several <code>no-std</code> CLI crates that I encountered including; <a target="_blank" href="https://crates.io/crates/terminal_cli"><code>terminal-cli</code></a> , <a target="_blank" href="https://crates.io/crates/embedded-cli/0.1.0"><code>embedded-cli</code></a> , <a target="_blank" href="https://crates.io/crates/light-cli"><code>light-cli</code></a> , and <code>menu</code> . Looking at each of the crates, they all had friendly abstractions and great features. I ended up going for <code>menu</code> based on the number of downloads since it seemed to be the most popular.</p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Familiarity with UART and <a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-standard-library-embedded-rust-uart-communication">how to set it up for the ESP32C3</a>.</p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Note that if the code on the git repo is slightly different, it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<p>Additionally, the full project (code and simulation) is available on Wokwi <a target="_blank" href="https://wokwi.com/projects/388708808145859585"><strong>here</strong></a>.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨</strong> Software Design</h2>
<h3 id="heading-the-application"><strong>💻</strong> The Application</h3>
<p>For this post, I'm going to create a simple application called hello with a command <code>hw</code>. <code>hw</code> takes one argument and its a person's name. The application would then print hello followed by the name given. Here's a sample:</p>
<pre><code class="lang-bash">&gt; hw Omar
Hello, Omar!
</code></pre>
<p>Also, the application would support a <code>help</code> command to provide a summary of commands the terminal supports. Also there is a <code>help</code> option for each command providing the user with information about the command.</p>
<h3 id="heading-the-menu-crate">📦 The <code>menu</code> Crate</h3>
<p>The <code>menu</code> crate consists of four main components:</p>
<ol>
<li><p><strong>The</strong> <code>Menu</code> <strong>struct</strong>: Each <code>Menu</code> has a name or <code>label</code> , optional functions to call on entry and exit of the <code>Menu</code> and, most importantly, a collection of <code>Item</code>s.</p>
</li>
<li><p><strong>The</strong> <code>Item</code> <strong>struct</strong>: <code>Item</code>s can be viewed as command buckets within a <code>Menu</code>. <code>Item</code> members include an <code>ItemType</code> which specifies the type of the <code>Item</code> , a command string, and a help message associated with the command. <code>ItemType</code> is also a struct that specifies whether the <code>Item</code> opens up another sub <code>Menu</code>, or calls a function upon the invocation of the command.</p>
</li>
<li><p><strong>The</strong> <code>Runner</code> <strong>struct</strong>: This is the struct that handles all incoming input. When our application is executing incoming byte characters will be passed to an instantiation of this struct for processing. Obviously, in <code>Runner</code> instantiation, the the <code>Menu</code> would be required in addition to a source for the incoming bytes (Ex. UART channel).</p>
</li>
<li><p><strong>The Callback Functions</strong>: These are the functions that are called upon the invocation of a command. In these functions the logic associated with each command will be executed.</p>
</li>
</ol>
<h3 id="heading-implementation-steps">👣 Implementation Steps</h3>
<p>Following the earlier description, these are the steps we need to take:</p>
<ol>
<li><p>Define the Root Menu</p>
</li>
<li><p>Implement any Callback functions</p>
</li>
<li><p>Configure peripherals in <code>main</code></p>
</li>
<li><p>Instantiate the <code>Runner</code> in <code>main</code></p>
</li>
<li><p>Create the Application <code>loop</code></p>
</li>
</ol>
<h2 id="heading-code-implementation"><strong>👨‍💻</strong> Code Implementation</h2>
<h3 id="heading-crate-imports"><strong>📥 Crate Imports</strong></h3>
<p>In this implementation the crates required are as follows:</p>
<ul>
<li><p>The <code>esp_idf_hal</code> crate to import the needed device hardware abstractions.</p>
</li>
<li><p><code>menu</code> to provide the menu CLI structs and abstractions.</p>
</li>
<li><p><code>std::fmt</code> to import the <code>Write</code> trait.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp_idf_hal::delay::BLOCK;
<span class="hljs-keyword">use</span> esp_idf_hal::gpio;
<span class="hljs-keyword">use</span> esp_idf_hal::peripherals::Peripherals;
<span class="hljs-keyword">use</span> esp_idf_hal::prelude::*;
<span class="hljs-keyword">use</span> esp_idf_hal::uart::*;
<span class="hljs-keyword">use</span> menu::*;
<span class="hljs-keyword">use</span> std::fmt::Write;
</code></pre>
<h3 id="heading-define-the-root-menu">📝 Define the Root Menu</h3>
<p>Before implementing the logic, we need to define our root <code>Menu</code>. Since this this a simple application, the <code>Menu</code> will have one <code>Item</code> that contains the <code>hw</code> command. Menu has the following definition:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Menu</span></span>&lt;<span class="hljs-symbol">'a</span>, T&gt;
<span class="hljs-keyword">where</span>
    T: <span class="hljs-symbol">'a</span>,
{
    <span class="hljs-keyword">pub</span> label: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-built_in">str</span>,
    <span class="hljs-keyword">pub</span> items: &amp;<span class="hljs-symbol">'a</span> [&amp;<span class="hljs-symbol">'a</span> Item&lt;<span class="hljs-symbol">'a</span>, T&gt;],
    <span class="hljs-keyword">pub</span> entry: <span class="hljs-built_in">Option</span>&lt;MenuCallbackFn&lt;T&gt;&gt;,
    <span class="hljs-keyword">pub</span> exit: <span class="hljs-built_in">Option</span>&lt;MenuCallbackFn&lt;T&gt;&gt;,
}
</code></pre>
<p><code>label</code> is the label of the menu, <code>entry</code> and <code>exit</code> specify functions to call on entry and exit of the <code>Menu</code> , and <code>items</code> is an array of <code>Item</code> s. Also each <code>Item</code> has the following definition:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Item</span></span>&lt;<span class="hljs-symbol">'a</span>, T&gt;
<span class="hljs-keyword">where</span>
    T: <span class="hljs-symbol">'a</span>,
{
    <span class="hljs-keyword">pub</span> command: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-built_in">str</span>,
    <span class="hljs-keyword">pub</span> help: <span class="hljs-built_in">Option</span>&lt;&amp;<span class="hljs-symbol">'a</span> <span class="hljs-built_in">str</span>&gt;,
    <span class="hljs-keyword">pub</span> item_type: ItemType&lt;<span class="hljs-symbol">'a</span>, T&gt;,
}
</code></pre>
<p>where <code>command</code> is the command text for a particular <code>Item</code> , <code>help</code> is the help message associated with <code>command</code>, and <code>item_type</code> is an enum that specifies what the type of the item is. <code>ItemType</code> is defined as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">ItemType</span></span>&lt;<span class="hljs-symbol">'a</span>, T&gt;
<span class="hljs-keyword">where</span>
    T: <span class="hljs-symbol">'a</span>,
{
    Callback {
        function: ItemCallbackFn&lt;T&gt;,
        parameters: &amp;<span class="hljs-symbol">'a</span> [Parameter&lt;<span class="hljs-symbol">'a</span>&gt;],
    },
    Menu(&amp;<span class="hljs-symbol">'a</span> Menu&lt;<span class="hljs-symbol">'a</span>, T&gt;),
    _Dummy,
}
</code></pre>
<p>One can see that <code>Item</code> can be either a <code>Callback</code> that would call a function on the invocation of an <code>Item</code> <code>command</code>, and <code>parameters</code> that are passed to that command. Alternatively, an <code>Item</code> could be another <code>Menu</code> which specifies a sub-menu.</p>
<p>Given the above, for our application, we define a <code>const</code> <code>Menu</code> called <code>ROOT_MENU</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// CLI Root Menu Struct Initialization</span>
<span class="hljs-keyword">const</span> ROOT_MENU: Menu&lt;UartDriver&gt; = Menu {
    label: <span class="hljs-string">"root"</span>,
    items: &amp;[&amp;Item {
        item_type: ItemType::Callback {
            function: hello_name,
            parameters: &amp;[Parameter::Mandatory {
                parameter_name: <span class="hljs-string">"name"</span>,
                help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"Enter your name"</span>),
            }],
        },
        command: <span class="hljs-string">"hw"</span>,
        help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"This is an embedded CLI terminal. Check the summary for the list of supported commands"</span>),
    }],
    entry: <span class="hljs-literal">None</span>,
    exit: <span class="hljs-literal">None</span>,
};
</code></pre>
<p>Note here that <code>ROOT_MENU</code> is given a <code>label</code> "<code>root</code>". Additionally, our application would have only one <code>Item</code> with <code>command</code> text "<code>hw</code>" that, when entered by the user, would invoke a <code>function</code> called <code>hello_name</code> .The application <code>Item</code> also takes only one parameter defined as <code>name</code> . Also I've decided to not call any functions on <code>entry</code> and <code>exit</code> of the <code>root</code> <code>Menu</code>. The associated <code>help</code> messages are also defined at the <code>Menu</code> and <code>Item</code> level.</p>
<h3 id="heading-implement-the-callback-function">👨‍💻 Implement the Callback Function</h3>
<p>Given the earlier step, we now need to define the behaviour when the callback function <code>hello_name</code> associated with <code>hw</code> is called. However, we still need to know what are the arguments that will be passed to this function. As such, the <code>menu</code> crate defines the signature of the callback function <code>ItemCallbackFn</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">ItemCallbackFn</span></span>&lt;T&gt; = <span class="hljs-function"><span class="hljs-keyword">fn</span></span>(menu: &amp;Menu&lt;<span class="hljs-symbol">'_</span>, T&gt;, item: &amp;Item&lt;<span class="hljs-symbol">'_</span>, T&gt;, args: &amp;[&amp;<span class="hljs-built_in">str</span>], context: &amp;<span class="hljs-keyword">mut</span> T);
</code></pre>
<p>Following this signature, we implement the function <code>hello_name</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">hello_name</span></span>&lt;<span class="hljs-symbol">'a</span>&gt;(
    _menu: &amp;Menu&lt;UartDriver&gt;,
    item: &amp;Item&lt;UartDriver&gt;,
    args: &amp;[&amp;<span class="hljs-built_in">str</span>],
    context: &amp;<span class="hljs-keyword">mut</span> UartDriver,
) {
    <span class="hljs-comment">// Print to console passed "name" argument</span>
    <span class="hljs-built_in">writeln!</span>(
        context,
        <span class="hljs-string">"Hello, {}!"</span>,
        argument_finder(item, args, <span class="hljs-string">"name"</span>).unwrap().unwrap()
    )
    .unwrap();
}
</code></pre>
<p>Note that all the function is doing is using the <code>writeln</code> macro to print to the console. <code>writeln</code> accepts a ‘writer’, a format string, and a list of arguments. Arguments will be formatted according to the specified format string and the result will be passed to the writer. The writer is any type that implements the <code>Write</code> trait in which case its the <code>UartDriver</code> type for the <code>context</code> thats being passed to <code>hello_name</code> by the runner. Note how for the list of arguments and <code>argument_finder</code> function is being used. <code>argument_finder</code> is a <code>menu</code> crate function and it looks for the named parameter in the parameter list of the <code>Item</code>, then finds the correct argument. Note how <code>argument_finder</code> takes three arguments, the <code>Item</code> we want to look for an argument in, the list of arguments that have been passed to the CLI (<code>args</code>), and the argument we want to look for ("<code>name</code>").</p>
<h3 id="heading-configure-peripherals"><strong>🎛</strong> Configure Peripherals</h3>
<p>In the <code>main</code> the following code is used to instantiate and configure UART:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Take Peripherals</span>
<span class="hljs-keyword">let</span> peripherals = Peripherals::take().unwrap();

<span class="hljs-comment">// Configure UART</span>
<span class="hljs-comment">// Create handle for UART config struct</span>
<span class="hljs-keyword">let</span> config = config::Config::default().baudrate(Hertz(<span class="hljs-number">115_200</span>));

<span class="hljs-comment">// Instantiate UART</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> uart = UartDriver::new(
    peripherals.uart0,
    peripherals.pins.gpio21,
    peripherals.pins.gpio20,
    <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio0&gt;::<span class="hljs-literal">None</span>,
    <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio1&gt;::<span class="hljs-literal">None</span>,
    &amp;config,
)
.unwrap();
</code></pre>
<p>The detail behind how this code works has been covered in a prior post <a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-standard-library-embedded-rust-uart-communication">here</a>. If anything seems unclear I recommend referring back to the past post. Only thing to note here is that <code>uart0</code> is used with <code>gpio21</code> and <code>gpio20</code> for Tx and Rx pins. This is because this is the peripheral and the pins used to connect to a host on the <a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a><strong>.</strong></p>
<h3 id="heading-instantiate-the-runner">🏃‍♂️ Instantiate the <code>Runner</code></h3>
<p>In the <a target="_blank" href="https://docs.rs/menu/0.4.0/menu/struct.Runner.html"><code>menu</code> crate documentation</a>, <code>Runner</code> has a <code>new</code> method with the following signature:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>(menu: Menu&lt;<span class="hljs-symbol">'a</span>, T&gt;, buffer: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-keyword">mut</span> [<span class="hljs-built_in">u8</span>], context: T) -&gt; Runner&lt;<span class="hljs-symbol">'a</span>, T&gt;
</code></pre>
<p>The first parameter is a root <code>Menu</code> (defined in the first step!), the second is a buffer that the <code>Runner</code> can use (<code>clibuf</code>), finally, <code>context</code> could be any type that implements <code>Write</code> (this is our instantiated <code>uart</code> handle) As a result, the runner is instantiated with the following code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Create a buffer to store CLI input</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> clibuf = [<span class="hljs-number">0u8</span>; <span class="hljs-number">64</span>];
<span class="hljs-comment">// Instantiate CLI runner with root menu, buffer, and uart</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> r = Runner::new(ROOT_MENU, &amp;<span class="hljs-keyword">mut</span> clibuf, uart);
</code></pre>
<blockquote>
<p>📝 <strong>Note</strong>: The generic type <code>T</code> that appears in all prior structs seen earlier is defined by the context object that the <code>Runner</code> carries around. In our case its the <code>UartDriver</code> type.</p>
</blockquote>
<h3 id="heading-the-main-loop">🔄 The Main Loop</h3>
<p>In the <code>main</code> <code>loop</code>, all that needs to be done is read one byte at a time from the UART channel. Remember that the UART channel is now part of the runner <code>context</code>. This is done using the UART <code>read</code> method (again, for a refresher check out <a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-standard-library-embedded-rust-uart-communication">this</a> post). Every read byte is passed to the instantiated runner using the <code>input_byte</code> runner method.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">loop</span> {
    <span class="hljs-comment">// Create single element buffer for UART characters</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buf = [<span class="hljs-number">0_u8</span>; <span class="hljs-number">1</span>];
    <span class="hljs-comment">// Read single byte from UART</span>
    r.context.read(&amp;<span class="hljs-keyword">mut</span> buf, BLOCK).unwrap();
    <span class="hljs-comment">// Pass read byte to CLI runner for processing</span>
    r.input_byte(buf[<span class="hljs-number">0</span>]);
}
</code></pre>
<p>That's it for code!</p>
<h2 id="heading-full-application-code">📱Full Application Code</h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Also, the Wokwi project can be accessed <a target="_blank" href="https://wokwi.com/projects/388709326938299393"><strong>here</strong></a>.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp_idf_hal::delay::BLOCK;
<span class="hljs-keyword">use</span> esp_idf_hal::gpio;
<span class="hljs-keyword">use</span> esp_idf_hal::peripherals::Peripherals;
<span class="hljs-keyword">use</span> esp_idf_hal::prelude::*;
<span class="hljs-keyword">use</span> esp_idf_hal::uart::*;
<span class="hljs-keyword">use</span> menu::*;
<span class="hljs-keyword">use</span> std::fmt::Write;

<span class="hljs-comment">// CLI Root Menu Struct Initialization</span>
<span class="hljs-keyword">const</span> ROOT_MENU: Menu&lt;UartDriver&gt; = Menu {
    label: <span class="hljs-string">"root"</span>,
    items: &amp;[&amp;Item {
        item_type: ItemType::Callback {
            function: hello_name,
            parameters: &amp;[Parameter::Mandatory {
                parameter_name: <span class="hljs-string">"name"</span>,
                help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"Enter your name"</span>),
            }],
        },
        command: <span class="hljs-string">"hw"</span>,
        help: <span class="hljs-literal">Some</span>(<span class="hljs-string">"This is an embedded CLI terminal. Check the summary for the list of supported commands"</span>),
    }],
    entry: <span class="hljs-literal">None</span>,
    exit: <span class="hljs-literal">None</span>,
};

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-comment">// Take Peripherals</span>
    <span class="hljs-keyword">let</span> peripherals = Peripherals::take().unwrap();

    <span class="hljs-comment">// Configure UART</span>
    <span class="hljs-comment">// Create handle for UART config struct</span>
    <span class="hljs-keyword">let</span> config = config::Config::default().baudrate(Hertz(<span class="hljs-number">115_200</span>));

    <span class="hljs-comment">// Instantiate UART</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> uart = UartDriver::new(
        peripherals.uart0,
        peripherals.pins.gpio21,
        peripherals.pins.gpio20,
        <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio0&gt;::<span class="hljs-literal">None</span>,
        <span class="hljs-built_in">Option</span>::&lt;gpio::Gpio1&gt;::<span class="hljs-literal">None</span>,
        &amp;config,
    )
    .unwrap();

    <span class="hljs-comment">// This line is for Wokwi only so that the console output is formatted correctly</span>
    uart.write_str(<span class="hljs-string">"\x1b[20h"</span>).unwrap();

    <span class="hljs-comment">// Create a buffer to store CLI input</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> clibuf = [<span class="hljs-number">0u8</span>; <span class="hljs-number">64</span>];
    <span class="hljs-comment">// Instantiate CLI runner with root menu, buffer, and uart</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> r = Runner::new(ROOT_MENU, &amp;<span class="hljs-keyword">mut</span> clibuf, uart);

    <span class="hljs-keyword">loop</span> {
        <span class="hljs-comment">// Create single element buffer for UART characters</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buf = [<span class="hljs-number">0_u8</span>; <span class="hljs-number">1</span>];
        <span class="hljs-comment">// Read single byte from UART</span>
        r.context.read(&amp;<span class="hljs-keyword">mut</span> buf, BLOCK).unwrap();
        <span class="hljs-comment">// Pass read byte to CLI runner for processing</span>
        r.input_byte(buf[<span class="hljs-number">0</span>]);
    }
}

<span class="hljs-comment">// Callback function for hw commans</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">hello_name</span></span>&lt;<span class="hljs-symbol">'a</span>&gt;(
    _menu: &amp;Menu&lt;UartDriver&gt;,
    item: &amp;Item&lt;UartDriver&gt;,
    args: &amp;[&amp;<span class="hljs-built_in">str</span>],
    context: &amp;<span class="hljs-keyword">mut</span> UartDriver,
) {
    <span class="hljs-comment">// Print to console passed "name" argument</span>
    <span class="hljs-built_in">writeln!</span>(
        context,
        <span class="hljs-string">"Hello, {}!"</span>,
        argument_finder(item, args, <span class="hljs-string">"name"</span>).unwrap().unwrap()
    )
    .unwrap();
}
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>This post showed how to create a simple embedded command line interface over a UART channel. In the post, the application was created on the ESP32C3 using the <code>menu</code> crate. The application also leveraged the <code>esp-idf-hal</code> asbtractions. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[ESP32 Embedded Rust at the HAL: I2C Scanner]]></title><description><![CDATA[This blog post is the eleventh of a multi-part series of posts where I explore various peripherals in the ESP32C3 using embedded Rust at the HAL level. Please be aware that certain concepts in newer posts could depend on concepts in prior posts.

Pri...]]></description><link>https://blog.theembeddedrustacean.com/esp32-embedded-rust-at-the-hal-i2c-scanner</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/esp32-embedded-rust-at-the-hal-i2c-scanner</guid><category><![CDATA[Rust]]></category><category><![CDATA[rust lang]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[embedded systems]]></category><category><![CDATA[iot]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Fri, 26 Jan 2024 14:01:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1706277060056/ede81f34-90d0-4f58-a146-2a33160846d3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong><em>This blog post is the eleventh of a multi-part series of posts where I explore various peripherals in the ESP32C3 using embedded Rust at the HAL level. Please be aware that certain concepts in newer posts could depend on concepts in prior posts.</em></strong></p>
</blockquote>
<p>Prior posts include (in order of publishing):</p>
<ol>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-embedded-rust-at-the-hal-gpio-button-controlled-blinking"><strong>ESP32 Embedded Rust at the HAL: GPIO Button Controlled Blinking</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-embedded-rust-at-the-hal-button-controlled-blinking-by-timer-polling"><strong>ESP32 Embedded Rust at the HAL: Button-Controlled Blinking by Timer Polling</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-embedded-rust-at-the-hal-uart-serial-communication"><strong>ESP32 Embedded Rust at the HAL: UART Serial Communication</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-embedded-rust-at-the-hal-pwm-buzzer"><strong>ESP32 Embedded Rust at the HAL: PWM Buzzer</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-embedded-rust-at-the-hal-timer-ultrasonic-distance-measurement"><strong>ESP32 Embedded Rust at the HAL: Timer Ultrasonic Distance Measurement</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-embedded-rust-at-the-hal-analog-temperature-sensing-using-the-adc"><strong>ESP32 Embedded Rust at the HAL: Analog Temperature Sensing using the ADC</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-embedded-rust-at-the-hal-gpio-interrupts"><strong>ESP32 Embedded Rust at the HAL: GPIO Interrupts</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-embedded-rust-at-the-hal-spi-communication"><strong>ESP32 Embedded Rust at the HAL: SPI Communication</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-embedded-rust-at-the-hal-random-number-generator"><strong>ESP32 Embedded Rust at the HAL: Random Number Generator</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-embedded-rust-at-the-hal-remote-control-peripheral"><strong>ESP32 Embedded Rust at the HAL: Remote Control Peripheral</strong></a></p>
</li>
</ol>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h2 id="heading-introduction">Introduction</h2>
<p>I2C is a popular serial communication protocol in embedded systems. Another common name used is also Two-Wire Interface (TWI), since, well, it is. I2C is commonly found in sensors and interface boards as it offers a compact implementation with decent speeds reaching Mbps. Counter to UART, I2C allows multiple devices (up to 127) to tap into the same bus. As a result, it becomes often useful if there is a way to scan for devices that are connected.</p>
<p>In this post, I will be configuring and setting up I2C communication for the ESP32C3 to scan the bus for devices. The code would detect if there is a device connected to a particular address. The retrieved results will also be printed on the console.</p>
<h3 id="heading-knowledge-pre-requisites">📚 Knowledge Pre-requisites</h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Familiarity with the basic template for creating embedded applications in Rust.</p>
</li>
<li><p>Familiarity with <a target="_blank" href="https://learn.sparkfun.com/tutorials/i2c/all"><strong>I2C communication basics</strong></a>.</p>
</li>
</ul>
<h3 id="heading-software-setup">💾 Software Setup</h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<p>Additionally, the full project (code and simulation) is available on Wokwi <a target="_blank" href="https://wokwi.com/projects/387823866026280961"><strong>here</strong></a>.</p>
<h3 id="heading-hardware-setup">🛠 Hardware Setup</h3>
<h4 id="heading-materials"><strong>Materials</strong></h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp&amp;auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
</li>
<li><h4 id="heading-any-i2c-based-device-ex-adafruit-ds1307-real-time-clockhttpswwwadafruitcomproduct3296">Any I2C-based device (Ex. <a target="_blank" href="https://www.adafruit.com/product/3296"><strong>Adafruit DS1307 Real-Time Clock</strong></a>).</h4>
</li>
</ul>
<h4 id="heading-connections">🔌 Connections</h4>
<p>Connections include the following:</p>
<ul>
<li><p>Gpio2 wired to the SCL pin of all I2C devices.</p>
</li>
<li><p>Gpio3 wired to the SDA pin of all I2C devices.</p>
</li>
<li><p>Power and Gnd wired to all I2C devices on the bus.</p>
</li>
</ul>
<h2 id="heading-software-design">👨‍🎨 Software Design</h2>
<p>I2C communication involves a master device initiating communication with a slave device through a start condition, followed by sending the slave's address and a read/write bit. The slave responds with an acknowledgment, and data transfer occurs with subsequent acknowledgment or non-acknowledgment after each byte. Finally, the communication is concluded with a stop condition. Furthermore, the address field of an I2C frame is 7-bits wide which supports up to 127 devices on a connected single bus.</p>
<p>Note how after a slave address is sent, a slave device has to respond with an acknowledgment. If a slave does not respond, it means that it does not exist on the bus. As such, to create an I2C scanner the following steps need to be taken:</p>
<ol>
<li><p>Initialize <code>address</code> to 0.</p>
</li>
<li><p>Send a read frame to <code>address</code></p>
</li>
<li><p>If ack received, record that device detected at <code>address</code> , otherwise, record that no device is connected.</p>
</li>
<li><p>Increment <code>address</code> .</p>
</li>
<li><p>If <code>address</code>&lt; 127 Go back to step 2. Otherwise, terminate the application.</p>
</li>
</ol>
<h2 id="heading-code-implementation">👨‍💻 Code Implementation</h2>
<h3 id="heading-crate-imports">📥 Crate Imports</h3>
<p>In this implementation the crates required are as follows:</p>
<ul>
<li><p>The <code>esp32c3_hal</code> crate to import the ESP32C3 device hardware abstractions.</p>
</li>
<li><p>The <code>esp_backtrace</code> crate to define the panicking behavior.</p>
</li>
<li><p>The <code>esp_println</code> crate to provide <code>println!</code> implementation.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> esp32c3_hal::{clock::ClockControl, 
    i2c::I2C, 
    peripherals::Peripherals, 
    prelude::*, 
    IO,
};
<span class="hljs-keyword">use</span> esp_backtrace <span class="hljs-keyword">as</span> _;
<span class="hljs-keyword">use</span> esp_println::println;
</code></pre>
<h3 id="heading-peripheral-configuration-code">🎛 Peripheral Configuration Code</h3>
<p>1️⃣ <strong>Obtain a handle for the device peripherals</strong>: In embedded Rust, as part of the singleton design pattern, we first have to take the PAC-level device peripherals. This is done using the <code>take()</code> method. Here I create a device peripheral handler named <code>dp</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> peripherals = Peripherals::take();
</code></pre>
<p><strong>2️⃣ Instantiate and obtain handle to system clocks:</strong> The system clocks need to be configured as they are needed in setting up the <code>I2C</code> peripheral. To set up the system clocks we need to first make the <code>SYSTEM</code> struct compatible with the HAL using the <code>split</code> method (more insight on this <a target="_blank" href="https://apollolabsblog.hashnode.dev/demystifying-rust-embedded-hal-split-and-constrain-methods">here</a>). After that, the system clock control information is passed to <code>boot_defaults</code> in <code>ClockControl</code>. <code>ClockControl</code> controls the enablement of peripheral clocks providing necessary methods to enable and reset specific peripherals. Finally, the <code>freeze()</code> method is applied to freeze the clock configuration. Note that freezing the clocks is a protection mechanism by the HAL to avoid the clock configuration changing during runtime.</p>
<pre><code class="lang-rust">    <span class="hljs-keyword">let</span> system = peripherals.SYSTEM.split();
    <span class="hljs-keyword">let</span> clocks = ClockControl::boot_defaults(system.clock_control).freeze();
</code></pre>
<p>3️⃣ <strong>Instantiate and Create Handle for IO</strong>: When creating an instance for I2C, we'll be required to pass it instances of the SDA and SCL pins. As such, we'll need to create an <code>IO</code> struct instance. The <code>IO</code> struct instance provides a HAL-designed struct that gives us access to all gpio pins thus enabling us to create handles/instances for individual pins. We do this like prior posts, by calling the <code>new()</code> instance method on the <code>IO</code> struct as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
</code></pre>
<p>Note how the <code>new</code> method requires passing two parameters; the <code>GPIO</code> and <code>IO_MUX</code> peripherals.</p>
<p><strong>4️⃣ Configure and Obtain Handle for the I2C peripheral</strong>: To create an instance of an I2C peripheral we need to use the <code>new</code> instance method in <code>i2c::I2c</code> in the esp32c3-hal:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>&lt;SDA, SCL&gt;(
    i2c: <span class="hljs-keyword">impl</span> Peripheral&lt;P = T&gt; + <span class="hljs-symbol">'d</span>,
    sda: <span class="hljs-keyword">impl</span> Peripheral&lt;P = SDA&gt; + <span class="hljs-symbol">'d</span>,
    scl: <span class="hljs-keyword">impl</span> Peripheral&lt;P = SCL&gt; + <span class="hljs-symbol">'d</span>,
    frequency: Rate&lt;<span class="hljs-built_in">u32</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>&gt;,
    clocks: &amp;Clocks&lt;<span class="hljs-symbol">'_</span>&gt;
) -&gt; I2C&lt;<span class="hljs-symbol">'d</span>, T&gt;
<span class="hljs-keyword">where</span>
    SDA: OutputPin + InputPin,
    SCL: OutputPin + InputPin,
</code></pre>
<p>The <code>i2c</code> parameter expects and instance of an i2c peripheral, the <code>sda</code> and <code>scl</code> parameters expect instances of pins configured as input/output, <code>frequency</code> is the desired bus frequency, and <code>clocks</code> is an instance of the system clocks. As such, I create an instance for the <code>pulse</code> handle as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> i2c0 = I2C::new(
    peripherals.I2C0,
    io.pins.gpio3,
    io.pins.gpio2,
    <span class="hljs-number">100u32</span>.kHz(),
    &amp;clocks,
);
</code></pre>
<p>I've chosen <code>I2C0</code> wth <code>gpio3</code> for SDA, <code>gpio2</code> for SCL, and 100kHz for the bus frequency.</p>
<p>Off to the application!</p>
<h3 id="heading-application-code">📱 Application Code</h3>
<p>In the application, we are going to run a scan once for all possible addresses from 1 to 127. As such, a <code>for</code> loop can be utilized. In each iteration, we're first going to print the address being scanned followed by doing a <code>i2c</code> <code>read</code>. The <code>I2C</code> <code>read</code> method takes two parameters, a <code>u8</code> address and a <code>&amp;[u8]</code> buffer to store the read data. Also, in our case, we don't really care about any data received.</p>
<p><code>read</code> returns a <code>Result</code>, in which case, if the <code>Result</code> is <code>Ok</code> that means that a device was found. Otherwise, if the <code>Result</code> is <code>Err</code>, it means that an ACK was not received and a device was not found. The following is the application code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Start Scan at Address 1 going up to 127</span>
<span class="hljs-keyword">for</span> addr <span class="hljs-keyword">in</span> <span class="hljs-number">1</span>..=<span class="hljs-number">127</span> {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Scanning Address {}"</span>, addr <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>);

    <span class="hljs-comment">// Scan Address</span>
    <span class="hljs-keyword">let</span> res = i2c0.read(addr <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>, &amp;<span class="hljs-keyword">mut</span> [<span class="hljs-number">0</span>]);

    <span class="hljs-comment">// Check and Print Result</span>
    <span class="hljs-keyword">match</span> res {
        <span class="hljs-literal">Ok</span>(_) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Device Found at Address {}"</span>, addr <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>),
        <span class="hljs-literal">Err</span>(_) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">"No Device Found"</span>),
    }
}
</code></pre>
<p>Finally, since the <code>main</code> returns a <code>!</code>, then an empty <code>loop</code> is required.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">loop</span>{}
</code></pre>
<h2 id="heading-full-application-code">📱 Full Application Code</h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Also the Wokwi project can be accessed <a target="_blank" href="https://wokwi.com/projects/387823866026280961"><strong>here</strong></a>.</p>
<pre><code class="lang-rust"><span class="hljs-meta">#![no_std]</span>
<span class="hljs-meta">#![no_main]</span>
<span class="hljs-meta">#![feature(type_alias_impl_trait)]</span>

<span class="hljs-keyword">use</span> esp32c3_hal::{clock::ClockControl, i2c::I2C, peripherals::Peripherals, prelude::*, IO};
<span class="hljs-keyword">use</span> esp_backtrace <span class="hljs-keyword">as</span> _;
<span class="hljs-keyword">use</span> esp_println::println;

<span class="hljs-meta">#[entry]</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() -&gt; ! {
    <span class="hljs-keyword">let</span> peripherals = Peripherals::take();
    <span class="hljs-keyword">let</span> system = peripherals.SYSTEM.split();
    <span class="hljs-keyword">let</span> clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    <span class="hljs-comment">// Obtain handle for GPIO</span>
    <span class="hljs-keyword">let</span> io = IO::new(peripherals.GPIO, peripherals.IO_MUX);

    <span class="hljs-comment">// Initialize and configure I2C0</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> i2c0 = I2C::new(
        peripherals.I2C0,
        io.pins.gpio3,
        io.pins.gpio2,
        <span class="hljs-number">100u32</span>.kHz(),
        &amp;clocks,
    );

    <span class="hljs-comment">// This line is for Wokwi only so that the console output is formatted correctly</span>
    esp_println::<span class="hljs-built_in">print!</span>(<span class="hljs-string">"\x1b[20h"</span>);

    <span class="hljs-comment">// Start Scan at Address 1 going up to 127</span>
    <span class="hljs-keyword">for</span> addr <span class="hljs-keyword">in</span> <span class="hljs-number">1</span>..=<span class="hljs-number">127</span> {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Scanning Address {}"</span>, addr <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>);

        <span class="hljs-comment">// Scan Address</span>
        <span class="hljs-keyword">let</span> res = i2c0.read(addr <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>, &amp;<span class="hljs-keyword">mut</span> [<span class="hljs-number">0</span>]);

        <span class="hljs-comment">// Check and Print Result</span>
        <span class="hljs-keyword">match</span> res {
            <span class="hljs-literal">Ok</span>(_) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Device Found at Address {}"</span>, addr <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>),
            <span class="hljs-literal">Err</span>(_) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">"No Device Found"</span>),
        }
    }

    <span class="hljs-comment">// Loop Forever</span>
    <span class="hljs-keyword">loop</span> {}
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this post, a I2C scanner application was created leveraging the I2C peripheral for the ESP32C3. The I2C code was created at the HAL level using the Rust <code>no-std</code> <code>esp32c3-hal</code>. Have any questions/comments? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[Embassy on ESP: Timers]]></title><description><![CDATA[This blog post is the fifth of a multi-part series of posts where I will explore various peripherals of the ESP32 using the embedded Rust embassy framework.

Prior posts include (in order of publishing):

Embassy on ESP: Getting Started

Embassy on E...]]></description><link>https://blog.theembeddedrustacean.com/embassy-on-esp-timers</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embassy-on-esp-timers</guid><category><![CDATA[Rust]]></category><category><![CDATA[embedded systems]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Fri, 19 Jan 2024 19:38:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705692826738/659bf478-5792-48ba-b364-654fa36c6a14.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong><em>This blog post is the fifth of a multi-part series of posts where I will explore various peripherals of the ESP32 using the embedded Rust embassy framework.</em></strong></p>
</blockquote>
<p>Prior posts include (in order of publishing):</p>
<ol>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embassy-on-esp-getting-started"><strong>Embassy on ESP: Getting Started</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embassy-on-esp-gpio"><strong>Embassy on ESP: GPIO</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embassy-on-esp-uart-transmitter"><strong>Embassy on ESP: UART Transmitter</strong></a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embassy-on-esp-uart-echo"><strong>Embassy on ESP: UART Echo</strong></a></p>
</li>
</ol>
<h2 id="heading-introduction">Introduction</h2>
<p>Hardware timers while relatively simple circuits are really effective in several applications in embedded. Timer peripherals are effective in timing both software and hardware events. Timers also have features that allow the generation of hardware waveforms (Ex. PWM). In this post, I'll use embassy to measure the width of the pulse for two different signals. The resulting pulse width value will be printed on the console.</p>
<p>The square waves used in this post will be generated using the Wokwi custom external block. In general, this type of measurement would be useful as it emulates the behavior of applications that include tachometers or anemometers. Tachometers and anemometers generate square wave signals that are proportional to the speed of rotation. With some calculations, and depending on the application, the provided code can be expanded to provide frequency and/or rpm values.</p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚</strong> Knowledge Pre-requisites</h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li>Basic knowledge of coding in Rust.</li>
</ul>
<h3 id="heading-software-setup"><strong>💾</strong> Software Setup</h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3">apollolabs ESP32C3</a> git repo. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<p>Additionally, the full project (code and simulation) is available on Wokwi <a target="_blank" href="https://wokwi.com/projects/387275512547716097">here</a>.</p>
<h3 id="heading-hardware-setup"><strong>🛠</strong> Hardware Setup</h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg" alt class="image--center mx-auto" /></p>
</li>
<li><p><strong>Square Wave Generator/Pulse Generator</strong>: This can take many forms in real hardware though since I'm using Wokwi, there is the custom chip feature that allows me to generate square waves.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690962699761/0a69c59f-4495-41ed-9b5c-b849f0d9786b.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<h4 id="heading-connections"><strong>🔌</strong> Connections</h4>
<p><strong>📝 Note</strong></p>
<blockquote>
<p>All connection details are also shown in the <a target="_blank" href="https://wokwi.com/projects/387275512547716097">Wokwi example</a>.</p>
</blockquote>
<p>Connections include the following:</p>
<ul>
<li><p>Gpio0 wired to the top pin of the breakout custom chip.</p>
</li>
<li><p>Gpio1 wired to the bottom pin of the breakout custom chip.</p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨</strong> Software Design</h2>
<p>The square wave signal is going to be fed into the ESP32 as an input. For the purpose of this post, the code will measure the widths of the pulses in each square wave. The custom chip is designed such that one wave generates pulses that are 10ms wide and another that are 25ms wide. To measure the pulse width, the algorithm in this post needs to determine the time elapsed between every positive edge and the negative edge that follows.</p>
<p>Following the configuration of the pins and the device, for each pin the follow steps are taken:</p>
<ol>
<li><p><code>await</code> a positive edge transition.</p>
</li>
<li><p>Capture timer instant.</p>
</li>
<li><p><code>await</code> negative edge transition.</p>
</li>
<li><p>Calculate then print the duration of the pulse.</p>
</li>
<li><p>Go back to step 1</p>
</li>
</ol>
<p>Let's now jump into implementing this logic.</p>
<p><strong>🚨 <em>Important Note</em></strong></p>
<blockquote>
<p>The custom chip block in Wokwi has its own internal code that generates the square waves/pulses. There is a tab in the project where one can look at the source code. However, how to create and code a custom block is not part of this post. For the interested in creating custom chips in Wokwi I recommend checking out the <a target="_blank" href="https://docs.wokwi.com/chips-api/getting-started">chips api documentation</a>.</p>
</blockquote>
<h2 id="heading-code-implementation"><strong>👨‍💻</strong> Code Implementation</h2>
<h3 id="heading-crate-imports"><strong>📥</strong> Crate Imports</h3>
<p>In this implementation, the following crates are required:</p>
<ul>
<li><p>The <code>embassy_executor</code> crate to import the embassy executor.</p>
</li>
<li><p>The <code>esp32c3-hal</code> crate to import the necessary ESP32C3 abstractions.</p>
</li>
<li><p>The <code>esp_backtrace</code> crate needed to define panic behavior.</p>
</li>
<li><p>The <code>embassy_time</code> crate to obtain timer abstractions.</p>
</li>
<li><p>The <code>embedded_hal_async</code> crate to obtain digital pin <code>Wait</code> abstractions.</p>
</li>
<li><p>The <code>esp_backtrace</code> crate needed to define panic behavior.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> embassy_executor::Spawner;
<span class="hljs-keyword">use</span> embassy_time::Instant;
<span class="hljs-keyword">use</span> embedded_hal_async::digital::Wait;
<span class="hljs-keyword">use</span> esp32c3_hal::gpio::{AnyPin, Input, PullUp};
<span class="hljs-keyword">use</span> esp32c3_hal::{clock::ClockControl, embassy, peripherals::Peripherals, prelude::*, IO};
<span class="hljs-keyword">use</span> esp_backtrace <span class="hljs-keyword">as</span> _;
</code></pre>
<h3 id="heading-the-pulse-timer-task"><strong>💓 The Pulse Timer Task</strong></h3>
<p>There are two pulse timer tasks, one for each pin. Both will replicate the same behavior except for two different pins. The pulse timer task is expected to <code>await</code> for pin edges to time the pulse width. These are the required steps:</p>
<p>1️⃣ <strong>Create a Pulse Timer Task</strong>: Tasks are marked by the <code>#[embassy_executor::task]</code> macro followed by a <code>async</code> function implementation. The task created is referred to as <code>pulse1_timer</code> task defined as follows:</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[embassy_executor::task]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">pulse1_timer</span></span>(<span class="hljs-keyword">mut</span> pin: AnyPin&lt;Input&lt;PullUp&gt;&gt;) {
</code></pre>
<p>There is a similar task that will have the exact same code called <code>pulse2_timer</code> .</p>
<h4 id="heading-task-loop"><strong>🔁 Task Loop</strong></h4>
<p><strong>1️⃣ Wait for rising edge on pin:</strong> The first thing we need to do is <code>await</code> the incoming pulse to become high. For that, there exists a <code>wait_for_high</code> method implementation for the <code>Wait</code> trait in the <a target="_blank" href="https://docs.rs/embedded-hal-async/1.0.0-rc.2/embedded_hal_async/digital/trait.Wait.html"><code>embedded-hal-async</code></a>. <code>wait_for_high</code> is an <code>async</code> function that resolves into a <code>Future</code> if its waiting on a condition. Otherwise, we get a <code>Result</code> . We call <code>wait_for_high</code> on <code>pin</code> as follows:</p>
<pre><code class="lang-rust">pin.wait_for_high().<span class="hljs-keyword">await</span>.unwrap();
</code></pre>
<p><strong>2️⃣ Capture the time instant:</strong> now that the signal turned high, a timer needs to be started to time the duration of the pulse. Using the <code>new</code> method from the <code>Instant</code> abstraction in the <code>embassy-time</code> crate allows the capture of the current time instant. We can bind the captured instant to an <code>inst</code> variable as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> inst = Instant::now();
</code></pre>
<p><strong>3️⃣ Wait for falling edge on pin:</strong> Now all we need to do is <code>await</code> the incoming signal to become low marking the end of the pulse. Now instead we use a <code>wait_for_low</code> method implementation for the <code>Wait</code> trait in the <a target="_blank" href="https://docs.rs/embedded-hal-async/1.0.0-rc.2/embedded_hal_async/digital/trait.Wait.html"><code>embedded-hal-async</code></a>. We call <code>wait_for_low</code> on <code>pin</code> as follows:</p>
<pre><code class="lang-rust">pin.wait_for_low().<span class="hljs-keyword">await</span>.unwrap();
</code></pre>
<p><strong>4️⃣ Calculate and print duration:</strong> To calculate the <code>Duration</code>, <code>Instant</code> has a <code>checked_duration_since</code> method that takes two <code>Instant</code>s and calculates a <code>Duration</code>. As such, we have to calculate the difference between the current Instant (after falling edge) and <code>inst</code> that was captured after a rising edge. Also using the <code>as_millis</code> <code>Duration</code> method, we can extract the time in milliseconds and print as follows:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Calculate Duration</span>
<span class="hljs-keyword">let</span> pwidth = Instant::checked_duration_since(&amp;Instant::now(), inst).unwrap();
<span class="hljs-comment">// Print Duration</span>
esp_println::<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Sq Wave 1 Pulse Width is {}ms"</span>, pwidth.as_millis());
</code></pre>
<h3 id="heading-the-main-task"><strong>📱 The Main Task</strong></h3>
<p>The start of the main task is marked by the following code:</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[embassy_executor::main]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>(spawner: Spawner)
</code></pre>
<p>The following steps will mark the tasks performed in the main task.</p>
<p>1️⃣ <strong>Obtain a handle for the device peripherals &amp; system clocks</strong>: In embedded Rust, as part of the singleton design pattern, we first have to take the device peripherals. This is done using the <code>take()</code> method. Here I create a device peripheral handler named <code>peripherals</code> , a system peripheral handler <code>system</code>, and a system clock handler <code>clocks</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> peripherals = Peripherals::take();
<span class="hljs-keyword">let</span> system = peripherals.SYSTEM.split();
<span class="hljs-keyword">let</span> clocks = ClockControl::boot_defaults(system.clock_control).freeze();
</code></pre>
<p><strong>2️⃣ Initialize Embassy Timers for the ESP32C3:</strong> In embassy, there exists an <code>init</code> function that takes two parameters. The first is system clocks and the second is an instance of a timer. Under the hood, what this function does is initialize the embassy timers. As such, we can initialize the embassy timers as follows:</p>
<pre><code class="lang-rust">embassy::init(
    <span class="hljs-keyword">let</span> timer_group0 = esp32c3_hal::timer::TimerGroup::new(peripherals.TIMG0, &amp;clocks);
    embassy::init(&amp;clocks, timer_group0.timer0);
</code></pre>
<p>3️⃣ <strong>Instantiate and Create Handle for IO</strong>: We need to configure the signal input pins as inputs and obtain handlers for them. Before we can obtain any handles we need to create an <code>IO</code> struct instance. The <code>IO</code> struct instance provides a HAL-designed struct that gives us access to all gpio pins thus enabling us to create handles for individual pins. This is similar to the concept of a <code>split</code> method used in other HALs (more detail <a target="_blank" href="https://apollolabsblog.hashnode.dev/demystifying-rust-embedded-hal-split-and-constrain-methods"><strong>here</strong></a>). We do this by calling the <code>new()</code> instance method on the <code>IO</code> struct as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
</code></pre>
<p>Note how the <code>new</code> method requires passing the <code>GPIO</code> and <code>IO_MUX</code> peripherals.</p>
<p>4️⃣ <strong>Obtain a handle and configure the signal input pins</strong>: The signal inputs are connected to pins 0 and 1 (<code>gpio0</code> and <code>gpio1</code>) as stated earlier. Additionally, since the pin is driven by an external signal that has known states it can be configured as floating. A floating input can be configured for the pin using the <code>into_floating_input()</code> method as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> del_but = io.pins.gpio2.into_floating_input().degrade();
</code></pre>
<p>Note that we are using the <code>degrade</code> method which "degrades" the pin type into a generic <code>AnyPin</code> type that is required to pass to the <code>button_press</code> task.</p>
<p>5️⃣ <strong>Spawn Pulse Timer Tasks</strong>: now that configuration is done, we can kick off both the <code>pulse1_timer</code> and <code>pulse2_timer</code> tasks. This is done using the <code>spawn</code> method as follows:</p>
<pre><code class="lang-rust">spawner.spawn(pulse1_timer(pulse1)).ok();
spawner.spawn(pulse2_timer(pulse2)).ok();
</code></pre>
<h2 id="heading-full-application-code"><strong>📱</strong> Full Application Code</h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3">apollolabs ESP32C3</a> git repo. Also, the Wokwi project can be accessed <a target="_blank" href="https://wokwi.com/projects/387275512547716097">here</a>.</p>
<pre><code class="lang-rust"><span class="hljs-meta">#![no_std]</span>
<span class="hljs-meta">#![no_main]</span>
<span class="hljs-meta">#![feature(type_alias_impl_trait)]</span>

<span class="hljs-keyword">use</span> embassy_executor::Spawner;
<span class="hljs-keyword">use</span> embassy_time::{Instant, Duration};
<span class="hljs-keyword">use</span> embedded_hal_async::digital::Wait;
<span class="hljs-keyword">use</span> esp32c3_hal::gpio::{AnyPin, Input, Floating};
<span class="hljs-keyword">use</span> esp32c3_hal::{clock::ClockControl, embassy, peripherals::Peripherals, prelude::*, IO};
<span class="hljs-keyword">use</span> esp_backtrace <span class="hljs-keyword">as</span> _;

<span class="hljs-meta">#[embassy_executor::task]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">pulse1_timer</span></span>(<span class="hljs-keyword">mut</span> pin: AnyPin&lt;Input&lt;Floating&gt;&gt;) {
    <span class="hljs-keyword">loop</span> {
        <span class="hljs-comment">// Wait for rising edge</span>
        pin.wait_for_high().<span class="hljs-keyword">await</span>.unwrap();
        <span class="hljs-comment">// Capture time instant at rising edge</span>
        <span class="hljs-keyword">let</span> inst = Instant::now();
        <span class="hljs-comment">// Wait for falling edge</span>
        pin.wait_for_low().<span class="hljs-keyword">await</span>.unwrap();
        <span class="hljs-comment">// Calculate Duration</span>
        <span class="hljs-keyword">let</span> pwidth = Instant::checked_duration_since(&amp;Instant::now(), inst).unwrap();
        <span class="hljs-comment">// Print Duration</span>
        esp_println::<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Sq Wave 1 Pulse Width is {}ms"</span>, pwidth.as_millis());
        <span class="hljs-comment">// Uncomment below line to reduce console print frequency</span>
        <span class="hljs-comment">// Timer::after(Duration::from_millis(1000)).await;</span>
    }
}

<span class="hljs-meta">#[embassy_executor::task]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">pulse2_timer</span></span>(<span class="hljs-keyword">mut</span> pin: AnyPin&lt;Input&lt;Floating&gt;&gt;) {
    <span class="hljs-keyword">loop</span> {
        <span class="hljs-comment">// Wait for rising edge</span>
        pin.wait_for_high().<span class="hljs-keyword">await</span>.unwrap();
        <span class="hljs-comment">// Capture time instant at rising edge</span>
        <span class="hljs-keyword">let</span> inst = Instant::now();
        <span class="hljs-comment">// Wait for falling edge</span>
        pin.wait_for_low().<span class="hljs-keyword">await</span>.unwrap();
        <span class="hljs-comment">// Calculate Duration</span>
        <span class="hljs-keyword">let</span> pwidth = Instant::checked_duration_since(&amp;Instant::now(), inst).unwrap();
        <span class="hljs-comment">// Print Duration</span>
        esp_println::<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Sq Wave 1 Pulse Width is {}ms"</span>, pwidth.as_millis());
        <span class="hljs-comment">// Uncomment below line to reduce console print frequency</span>
        <span class="hljs-comment">// Timer::after(Duration::from_millis(1000)).await;</span>
    }
}

<span class="hljs-meta">#[main]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>(spawner: Spawner) {
    <span class="hljs-keyword">let</span> peripherals = Peripherals::take();
    <span class="hljs-keyword">let</span> system = peripherals.SYSTEM.split();
    <span class="hljs-keyword">let</span> clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    <span class="hljs-comment">// Initialize Embassy with needed timers</span>
    <span class="hljs-keyword">let</span> timer_group0 = esp32c3_hal::timer::TimerGroup::new(peripherals.TIMG0, &amp;clocks);
    embassy::init(&amp;clocks, timer_group0.timer0);

    <span class="hljs-comment">// This line is for Wokwi only so that the console output is formatted correctly</span>
    esp_println::<span class="hljs-built_in">print!</span>(<span class="hljs-string">"\x1b[20h"</span>);

    <span class="hljs-comment">// Inititalize and configure pins</span>
    <span class="hljs-comment">// Acquire Handle to IO</span>
    <span class="hljs-keyword">let</span> io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
    <span class="hljs-comment">// Configure pins as pull up input and degrade</span>
    <span class="hljs-keyword">let</span> pulse1 = io.pins.gpio0.into_floating_input().degrade();
    <span class="hljs-keyword">let</span> pulse2 = io.pins.gpio1.into_floating_input().degrade();

    <span class="hljs-comment">// Spawn pulse measurement tasks</span>
    spawner.spawn(pulse1_timer(pulse1)).ok();
    spawner.spawn(pulse2_timer(pulse2)).ok();
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this post, a timer application measuring square wave pulse durations was created. The application leverages the Timer peripherals for the ESP32C3 microcontroller. The code was created leveraging the embassy framework. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[Embassy on ESP: UART Echo]]></title><description><![CDATA[This blog post is the fourth of a multi-part series of posts where I will explore various peripherals of the ESP32 using the embedded Rust embassy framework.

Prior posts include (in order of publishing):

Embassy on ESP: Getting Started

Embassy on ...]]></description><link>https://blog.theembeddedrustacean.com/embassy-on-esp-uart-echo</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embassy-on-esp-uart-echo</guid><category><![CDATA[Rust]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[embedded]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[iot]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Fri, 12 Jan 2024 17:38:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705080332954/920df4d5-54d3-4d80-9c73-563fb9c055e0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong><em>This blog post is the fourth of a multi-part series of posts where I will explore various peripherals of the ESP32 using the embedded Rust embassy framework.</em></strong></p>
</blockquote>
<p>Prior posts include (in order of publishing):</p>
<ol>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embassy-on-esp-getting-started">Embassy on ESP: Getting Started</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embassy-on-esp-gpio">Embassy on ESP: GPIO</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embassy-on-esp-uart-transmitter">Embassy on ESP: UART Transmitter</a></p>
</li>
</ol>
<h2 id="heading-introduction">Introduction</h2>
<p>It is probably more common that UART is used in simplex one-direction communication Ex. to log device messages. As such, there might be less mention of UART receiver functionality. With receive functionality UART can be set up to receive and process incoming messages. For embedded applications, it can be useful if one wants to enable user input from a serial terminal. In this post, using embassy and the ESP32C3, I will be expanding on the last <a target="_blank" href="https://apollolabsblog.hashnode.dev/embassy-on-esp-uart-transmitter">UART Transmitter</a> post to include receive functionality as well. In the example in this post, I will create a UART echo application. The application will receive user input from the terminal and "echo" it back to the terminal.</p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Knowledge of how the embassy executor works.</p>
</li>
<li><p>Basic knowledge of UART Communication.</p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<p>Additionally, the full project (code and simulation) is available on Wokwi <a target="_blank" href="https://wokwi.com/projects/386561280544436225"><strong>here</strong></a>.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
</li>
</ul>
<h4 id="heading-connections"><strong>🔌 Connections</strong></h4>
<p>No special connections are required for this implementation. The UART lines on the ESP32-C3 in DevKitM are connected on-board to the UART bridge.</p>
<blockquote>
<p>🗒️ <strong>Note:</strong> The ESP uses the UART0 peripheral for <code>esp_println</code> . Be mindful of that if you want to modify the code in this example.</p>
</blockquote>
<h2 id="heading-software-design"><strong>👨‍🎨 Software Design</strong></h2>
<p>In the application developed in this post, the ESP will wait for user input from the serial monitor and echo it back to the monitor. As such, the ESP will wait for incoming messages on the UART receiver end. Once a new message is entered, the ESP will keep buffering input characters until a valid end of transmission (EOT) character is received. After that the ESP will send the same buffered characters back to the monitor using its transmitter.</p>
<p>For this functionality there will be two tasks, a <code>uart_reader</code> (receive) task and a <code>uart_writer</code> (transmit) task. In between the tasks there will be a shared character (<code>u8</code>) buffer that I'll call <code>DATAPIPE</code>.</p>
<p>On a read event (incoming characters from monitor/terminal), the <code>uart_reader</code> task executes in taking the following steps:</p>
<ol>
<li><p><code>read</code> characters from UART receive buffer until a valid EOT character.</p>
</li>
<li><p>On the successful completion of a <code>read</code>, share the received characters with the <code>uart_writer</code> task by writing them to <code>DATAPIPE</code>.</p>
</li>
</ol>
<p>The <code>uart_writer</code> task will kick in once new data appears in <code>DATAPIPE</code> taking the following steps:</p>
<ol>
<li><p><code>write</code> the <code>DATAPIPE</code> contents to the UART <code>write</code> buffer.</p>
</li>
<li><p>Transmit a new line (carriage return <code>0x0D</code> followed by line feed <code>0x0A</code> characters)</p>
</li>
<li><p>Flush the transmit buffer.</p>
</li>
</ol>
<p>Let's now jump into the implementation.</p>
<h2 id="heading-code-implementation"><strong>👨‍💻 Code Implementation</strong></h2>
<h3 id="heading-crate-imports">📥 Crate Imports</h3>
<p>In this implementation the crates required are as follows:</p>
<ul>
<li><p>The <code>embassy_sync</code> crate to import <code>Pipe</code>, a special abstraction needed for synchronization.</p>
</li>
<li><p>The <code>embassy_executor</code> crate to import the embassy executor.</p>
</li>
<li><p>The <code>esp32c3-hal</code> crate to import the necessary ESP32C3 abstractions.</p>
</li>
<li><p>The <code>esp_backtrace</code> crate needed to define panic behavior.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> embassy_executor::Spawner;
<span class="hljs-keyword">use</span> embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
<span class="hljs-keyword">use</span> esp32c3_hal::{
    clock::ClockControl,
    embassy,
    peripherals::{Peripherals, UART0},
    prelude::*,
    uart::{config::AtCmdConfig, UartRx, UartTx},
    Uart,
};
<span class="hljs-keyword">use</span> esp_backtrace <span class="hljs-keyword">as</span> _;
</code></pre>
<h4 id="heading-global-variables-amp-constants">🌍 Global Variables &amp; Constants</h4>
<p>In the application at hand, there will be two tasks that share a character buffer. The <code>uart_reader</code> task will write to the shared buffer and the <code>uart_writer</code> task will read from this buffer. As such, we can create a global buffer <code>DATAPIPE</code> to "pipe" the characters are going to be passed around. We declare <code>DATAPIPE</code> as <code>static</code> and define it as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">static</span> DATAPIPE: Pipe&lt;CriticalSectionRawMutex, READ_BUF_SIZE&gt; = Pipe::new();
</code></pre>
<blockquote>
<p>📝 <strong>Note:</strong> Global variables shared among tasks in Rust could be a sticky topic. This is because sharing global data is <code>unsafe</code> since it can cause race conditions. You can read more about it <a target="_blank" href="https://doc.rust-lang.org/beta/embedded-book/concurrency/index.html">here</a>. Embassy offers several synchronization primitives that provide safe abstractions depending on what needs to be accomplished. There is a prior post where I go into detail about these primitives <a target="_blank" href="https://apollolabsblog.hashnode.dev/sharing-data-among-tasks-in-rust-embassy-synchronization-primitives">here</a>.</p>
</blockquote>
<p>In addition to the above, it would be convenient to define the following constants:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Read Buffer Size</span>
<span class="hljs-keyword">const</span> READ_BUF_SIZE: <span class="hljs-built_in">usize</span> = <span class="hljs-number">64</span>;
<span class="hljs-comment">// End of Transmission Character (Carrige Return -&gt; 13 or 0x0D in ASCII)</span>
<span class="hljs-keyword">const</span> AT_CMD: <span class="hljs-built_in">u8</span> = <span class="hljs-number">0x0D</span>;
</code></pre>
<p><code>READ_BUF_SIZE</code> will be used to define the sizes of the buffers we declare. Additionally <code>AT_CMD</code> defines the character that we will use for detecting an EOT sequence.</p>
<h3 id="heading-the-uart-reader-task">🤓 The UART Reader Task</h3>
<p>The UART reader task is expected to accept a UART instance as input and <code>loop</code> constantly check/<code>await</code> if <code>MYSIGNAL</code> gets updated. Though ahead of the <code>loop</code> it might be beneficial to indicate that the task has started. These are the required steps:</p>
<p>1️⃣ <strong>Create a UART Reader Task</strong>: Tasks are marked by the <code>#[embassy_executor::task]</code> macro followed by a <code>async</code> function implementation. The task created is referred to as <code>uart_reader</code> task defined as follows:</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[embassy_executor::task]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">uart_reader</span></span>(<span class="hljs-keyword">mut</span> tx: UartRx&lt;<span class="hljs-symbol">'static</span>, UART0&gt;)
</code></pre>
<p><code>UartRx</code> marks a UART receiver type that is configured with an instance of <code>UART0</code>. This means when spawning the task, we need pass a handle for a UART reciever configured with an instance of <code>UART0</code>. This will be done in the <code>main</code> task before spawning the <code>uart_reader</code> task.</p>
<p><strong>2️⃣ Declare a Read Buffer:</strong> Before entering the task loop, we need to declare read buffer <code>rbuf</code> to store characters that will be received. <code>rbuf</code> will later store characters written to <code>DATAPIPE</code> .</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> rbuf: [<span class="hljs-built_in">u8</span>; READ_BUF_SIZE] = [<span class="hljs-number">0u8</span>; READ_BUF_SIZE];
</code></pre>
<h4 id="heading-task-loop">🔁 Task Loop</h4>
<p><strong>1️⃣ Read Characters from Monitor/Terminal into Buffer:</strong></p>
<p>As part of the <code>embedded_io_async</code> crate we can use the <code>read</code> implementation of the <code>Read</code> trait to asynchronously read a message. <code>read</code> has the following signature:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">read</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, buf: &amp;<span class="hljs-keyword">mut</span> [<span class="hljs-built_in">u8</span>]) -&gt; <span class="hljs-built_in">Result</span>&lt;<span class="hljs-built_in">usize</span>, Self::Error&gt;
</code></pre>
<p>As such, we need to pass a mutable reference of a type (<code>UartRx</code> in our case) that implements <code>Read</code> and our message as a slice of <code>u8</code>. Since <code>read</code> is an <code>async</code> function, the program needs to <code>await</code> for the read operation to complete. Using <code>read</code> we can receive characters into our buffer <code>rbuf</code> as follows:</p>
<pre><code class="lang-rust"> <span class="hljs-keyword">let</span> r = embedded_io_async::Read::read(&amp;<span class="hljs-keyword">mut</span> rx, &amp;<span class="hljs-keyword">mut</span> rbuf[<span class="hljs-number">0</span>..]).<span class="hljs-keyword">await</span>;
</code></pre>
<p><code>read</code> will keep on reading characters into <code>rbuf</code> until reaching a valid EOT character. The EOT character will be defined later in out <code>main</code> when we configure the <code>UART0</code> peripheral. <code>read</code> also returns a <code>Result</code> that contains the amount of characters that were read.</p>
<p><strong>2️⃣ Write Buffered Characters to Pipe:</strong> The <code>Pipe</code> we need to write to is <code>DATAPIPE</code>. For that, we need to write all of the characters in <code>rbuf</code> to <code>DATAPIPE</code> There exists a <code>write_all</code> method for the <code>Pipe</code> type that has the following signature:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">write_all</span></span>(&amp;<span class="hljs-keyword">self</span>, buf: &amp;[<span class="hljs-built_in">u8</span>])
</code></pre>
<p><code>write_all</code> will write all bytes from <code>buf</code> into the <code>Pipe</code>. This results in the following code for us:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">match</span> r {
    <span class="hljs-literal">Ok</span>(len) =&gt; {
        <span class="hljs-comment">// If read succeeds then write recieved characters to pipe</span>
        DATAPIPE.write_all(&amp;rbuf[..len]).<span class="hljs-keyword">await</span>;
    }
    <span class="hljs-literal">Err</span>(e) =&gt; esp_println::<span class="hljs-built_in">println!</span>(<span class="hljs-string">"RX Error: {:?}"</span>, e),
}
</code></pre>
<p>The <code>match</code> is used to process the <code>Result</code> returned from the <code>read</code> method. If a receive error occurs, then <code>esp_println</code> is used to log the error.</p>
<h3 id="heading-the-uart-writer-task">🕹️ The UART Writer Task</h3>
<p>The UART writer task is expected to accept a UART instance as input and <code>loop</code> constantly check/<code>await</code> if <code>DATAPIPE</code> has new data. These are the required steps:</p>
<p>1️⃣ <strong>Create a UART Writer Task</strong>: Tasks are marked by the <code>#[embassy_executor::task]</code> macro followed by a <code>async</code> function implementation. The task created is referred to as <code>uart_writer</code> task defined as follows:</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[embassy_executor::task]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">uart_writer</span></span>(<span class="hljs-keyword">mut</span> tx: UartTx&lt;<span class="hljs-symbol">'static</span>, UART0&gt;)
</code></pre>
<p><code>UartTx</code> marks a UART transmitter type that is configured with an instance of <code>UART0</code>. This means when spawning the task, we need pass a handle for a UART transmitter configured with an instance of <code>UART0</code>. This will be done in the <code>main</code> task before spawning the <code>uart_writer</code> task.</p>
<p><strong>2️⃣ Declare a Write Buffer:</strong> Before entering the task loop, we need to declare write buffer <code>wbuf</code> to store characters that will be transmitted. <code>wbuf</code> will later store characters read from <code>DATAPIPE</code> .</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> wbuf: [<span class="hljs-built_in">u8</span>; READ_BUF_SIZE] = [<span class="hljs-number">0u8</span>; READ_BUF_SIZE];
</code></pre>
<h4 id="heading-task-loop-1">🔁 Task Loop</h4>
<p><strong>1️⃣ Read Characters from Pipe into Write Buffer:</strong> In the task <code>loop</code>, the first thing we need to do is <code>await</code> a change on <code>DATAPIPE</code>. For that, there exists a <code>read</code> method for the <code>Pipe</code> type. <code>read</code> has the following signature:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">read</span></span>&lt;<span class="hljs-symbol">'a</span>&gt;(&amp;<span class="hljs-symbol">'a</span> <span class="hljs-keyword">self</span>, buf: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-keyword">mut</span> [<span class="hljs-built_in">u8</span>]) -&gt; ReadFuture&lt;<span class="hljs-symbol">'a</span>, M, N&gt;
</code></pre>
<p><code>read</code> will read a nonzero amount of bytes from the pipe into <code>buf</code>. If it is not possible to read a nonzero amount of bytes because the pipe’s buffer is empty, this method will wait until it isn’t. Consequently, we'll read <code>DATAPIPE</code> contents into <code>wbuf</code> as follows:</p>
<pre><code class="lang-rust">DATAPIPE.read(&amp;<span class="hljs-keyword">mut</span> wbuf).<span class="hljs-keyword">await</span>;
</code></pre>
<p>2️⃣ <strong>Complete Write Operations:</strong></p>
<p>As part of the <code>embedded_io_async</code> crate we can use the <code>write</code> implementation of the <code>Write</code> trait to asynchronously write a message. <code>write</code> has the following signature:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">write</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, buf: &amp;[<span class="hljs-built_in">u8</span>]) -&gt; <span class="hljs-built_in">Result</span>&lt;<span class="hljs-built_in">usize</span>, Self::Error&gt;
</code></pre>
<p>As such, we need to pass a mutable reference of a type (<code>UartTx</code> in our case) that implements <code>Write</code> and our message as a slice of <code>u8</code>. Since <code>write</code> is an <code>async</code> function, the program needs to <code>await</code> for the write operation to complete. Using <code>write</code> we'll need to transmit the buffer contents, transmit a new line, and flush the transmit buffer. This results in the following code:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Transmit Buffer Contents</span>
embedded_io_async::Write::write(&amp;<span class="hljs-keyword">mut</span> tx, &amp;wbuf)
    .<span class="hljs-keyword">await</span>
    .unwrap();
<span class="hljs-comment">// Transmit a new line</span>
embedded_io_async::Write::write(&amp;<span class="hljs-keyword">mut</span> tx, &amp;[<span class="hljs-number">0x0D</span>, <span class="hljs-number">0x0A</span>])
    .<span class="hljs-keyword">await</span>
    .unwrap();
<span class="hljs-comment">// Flush transmit buffer</span>
embedded_io_async::Write::flush(&amp;<span class="hljs-keyword">mut</span> tx).<span class="hljs-keyword">await</span>.unwrap();
</code></pre>
<p><code>flush</code> will flush the output stream and ensure that all the buffer contents reach their destination.</p>
<blockquote>
<p>📝 Note: I could have easily also used <code>esp_println</code> which provides more convenient abstractions to print to the console. While in this context it does not make much difference, I did this for two reasons. First, since this is a UART example, we need to demonstrate how to use UART abstractions. Second, the previous code is a good demonstration of the use of the <code>embedded-io-async</code> traits. Using the <code>embedded-io-async</code> traits allows for more portable code in some contexts.</p>
</blockquote>
<h3 id="heading-the-main-task"><strong>📱 The Main Task</strong></h3>
<p>The start of the main task is marked by the following code:</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[main]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>(spawner: Spawner)
</code></pre>
<p>As the documentation states: "The main entry point of an Embassy application is defined using the <code>#[main]</code> macro. The entry point is also required to take a <code>Spawner</code> argument." As we'll see, <code>Spawner</code> is what will allow us to spawn or kick-off the <code>uart_writer</code> task.</p>
<p>The following steps will mark the tasks performed in the main task.</p>
<p>1️⃣ <strong>Obtain a handle for the device peripherals &amp; system clocks</strong>: In embedded Rust, as part of the singleton design pattern, we first have to take the PAC-level device peripherals. This is done using the <code>take()</code> method. Here I create a device peripheral handler named <code>peripherals</code> , a system peripheral handler <code>system</code>, and a system clock handler <code>clocks</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> peripherals = Peripherals::take();
<span class="hljs-keyword">let</span> system = peripherals.SYSTEM.split();
<span class="hljs-keyword">let</span> clocks = ClockControl::boot_defaults(system.clock_control).freeze();
</code></pre>
<p><strong>2️⃣ Initialize Embassy Timers for the ESP32C3:</strong></p>
<p>In embassy, there exists an <code>init</code> function that takes two parameters. The first is system clocks and the second is an instance of a timer. Under the hood, what this function does is initialize the embassy timers. As such, we can initialize the embassy timers as follows:</p>
<pre><code class="lang-rust">embassy::init(
    <span class="hljs-keyword">let</span> timer_group0 = esp32c3_hal::timer::TimerGroup::new(peripherals.TIMG0, &amp;clocks);
    embassy::init(&amp;clocks, timer_group0.timer0);
</code></pre>
<blockquote>
<p><strong><em>📝 Note:</em></strong> <em>At the time of writing this post, I couldn't really locate the</em> <code>init</code> function <a target="_blank" href="http://docs.rs"><strong>docs.rs</strong></a> documentation. It didn't seem easily accessible through any of the current HAL implementation documentation. Nevertheless, I reached the signature of the function through the source <a target="_blank" href="https://github.com/esp-rs/esp-hal/blob/ece40abaed0e642b751a8752ce6406740efa4af6/esp-hal-common/src/embassy/mod.rs#L100"><strong><em>here</em></strong></a><em>.</em></p>
</blockquote>
<p>3️⃣ <strong>Obtain Handle and Configure UART</strong>: to create an instance of <code>UART0</code>, there exists a <code>new</code> instance method under <code>esp32c3_hal::Uart</code> that requires two parameters; a <code>UART</code> peripheral type and a <code>Clocks</code> type. There also exists a <code>split</code> method that allows us to "split" the <code>uart0</code> instance into separate transmitter and receiver instances with handles <code>tx</code> and <code>rx</code>. <code>split</code> returns a tuple with two instances. We also will be using <code>UART</code> methods <code>set_at_cmd</code> and <code>set_rx_fifo_full_threshold</code> to configure the <code>AT_CMD</code> character and the read buffer size.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> uart0 = Uart::new(peripherals.UART0, &amp;clocks);
uart0.set_at_cmd(AtCmdConfig::new(<span class="hljs-literal">None</span>, <span class="hljs-literal">None</span>, <span class="hljs-literal">None</span>, AT_CMD, <span class="hljs-literal">None</span>));
uart0
    .set_rx_fifo_full_threshold(READ_BUF_SIZE <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span>)
    .unwrap();
<span class="hljs-keyword">let</span> (tx, rx) = uart0.split();
</code></pre>
<p>Note the following:</p>
<ul>
<li><p><code>UART0</code> is chosen since it is the one that ties to the UART logging pins on board. Additionally, <code>new</code> configures UART with the default 8N1 setup.</p>
</li>
<li><p><code>set_at_cmd</code> takes a <code>AtCmdConfig</code> parameter that is a struct with configuration options for the AT-CMD detection functionality. We only need to configure the EOT character. The rest of the members can be viewed in the <a target="_blank" href="https://docs.rs/esp32c3-hal/0.13.0/esp32c3_hal/uart/config/struct.AtCmdConfig.html">documentation</a>.</p>
</li>
</ul>
<p>4️⃣ <strong>Spawn UART Reader and Writer Tasks</strong>: before entering the button press loop, we're going to need to kick off our <code>uart_writer</code> and <code>uart_reader</code> tasks. This is done using the <code>spawn</code> method as follows:</p>
<pre><code class="lang-rust">spawner.spawn(uart_writer(tx)).unwrap();\
spawner.spawn(uart_reader(tx)).unwrap();
</code></pre>
<p>Next, we can move on to the task <code>loop</code>.</p>
<p>This concludes the code for the full application.</p>
<h2 id="heading-full-application-code">📱 Full Application Code</h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Also, the Wokwi project can be accessed <a target="_blank" href="https://wokwi.com/projects/386561280544436225"><strong>here</strong></a>.</p>
<pre><code class="lang-rust"><span class="hljs-meta">#![no_std]</span>
<span class="hljs-meta">#![no_main]</span>
<span class="hljs-meta">#![feature(type_alias_impl_trait)]</span>

<span class="hljs-keyword">use</span> embassy_executor::Spawner;
<span class="hljs-keyword">use</span> embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
<span class="hljs-keyword">use</span> esp32c3_hal::{
    clock::ClockControl,
    embassy,
    peripherals::{Peripherals, UART0},
    prelude::*,
    uart::{config::AtCmdConfig, UartRx, UartTx},
    Uart,
};
<span class="hljs-keyword">use</span> esp_backtrace <span class="hljs-keyword">as</span> _;

<span class="hljs-comment">// Read Buffer Size</span>
<span class="hljs-keyword">const</span> READ_BUF_SIZE: <span class="hljs-built_in">usize</span> = <span class="hljs-number">64</span>;

<span class="hljs-comment">// End of Transmission Character (Carrige Return -&gt; 13 or 0x0D in ASCII)</span>
<span class="hljs-keyword">const</span> AT_CMD: <span class="hljs-built_in">u8</span> = <span class="hljs-number">0x0D</span>;

<span class="hljs-comment">// Declare Pipe sync primitive to share data among Tx and Rx tasks</span>
<span class="hljs-keyword">static</span> DATAPIPE: Pipe&lt;CriticalSectionRawMutex, READ_BUF_SIZE&gt; = Pipe::new();

<span class="hljs-meta">#[embassy_executor::task]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">uart_writer</span></span>(<span class="hljs-keyword">mut</span> tx: UartTx&lt;<span class="hljs-symbol">'static</span>, UART0&gt;) {
    <span class="hljs-comment">// Declare write buffer to store Tx characters</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> wbuf: [<span class="hljs-built_in">u8</span>; READ_BUF_SIZE] = [<span class="hljs-number">0u8</span>; READ_BUF_SIZE];
    <span class="hljs-keyword">loop</span> {
        <span class="hljs-comment">// Read characters from pipe into write buffer</span>
        DATAPIPE.read(&amp;<span class="hljs-keyword">mut</span> wbuf).<span class="hljs-keyword">await</span>;
        <span class="hljs-comment">// Transmit/echo buffer contents over UART</span>
        embedded_io_async::Write::write(&amp;<span class="hljs-keyword">mut</span> tx, &amp;wbuf)
            .<span class="hljs-keyword">await</span>
            .unwrap();
        <span class="hljs-comment">// Transmit a new line</span>
        embedded_io_async::Write::write(&amp;<span class="hljs-keyword">mut</span> tx, &amp;[<span class="hljs-number">0x0D</span>, <span class="hljs-number">0x0A</span>])
            .<span class="hljs-keyword">await</span>
            .unwrap();
        <span class="hljs-comment">// Flush transmit buffer</span>
        embedded_io_async::Write::flush(&amp;<span class="hljs-keyword">mut</span> tx).<span class="hljs-keyword">await</span>.unwrap();
    }
}

<span class="hljs-meta">#[embassy_executor::task]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">uart_reader</span></span>(<span class="hljs-keyword">mut</span> rx: UartRx&lt;<span class="hljs-symbol">'static</span>, UART0&gt;) {
    <span class="hljs-comment">// Declare read buffer to store Rx characters</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> rbuf: [<span class="hljs-built_in">u8</span>; READ_BUF_SIZE] = [<span class="hljs-number">0u8</span>; READ_BUF_SIZE];
    <span class="hljs-keyword">loop</span> {
        <span class="hljs-comment">// Read characters from UART into read buffer until EOT</span>
        <span class="hljs-keyword">let</span> r = embedded_io_async::Read::read(&amp;<span class="hljs-keyword">mut</span> rx, &amp;<span class="hljs-keyword">mut</span> rbuf[<span class="hljs-number">0</span>..]).<span class="hljs-keyword">await</span>;
        <span class="hljs-keyword">match</span> r {
            <span class="hljs-literal">Ok</span>(len) =&gt; {
                <span class="hljs-comment">// If read succeeds then write recieved characters to pipe</span>
                DATAPIPE.write_all(&amp;rbuf[..len]).<span class="hljs-keyword">await</span>;
            }
            <span class="hljs-literal">Err</span>(e) =&gt; esp_println::<span class="hljs-built_in">println!</span>(<span class="hljs-string">"RX Error: {:?}"</span>, e),
        }
    }
}

<span class="hljs-meta">#[main]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>(spawner: Spawner) {
    <span class="hljs-keyword">let</span> peripherals = Peripherals::take();
    <span class="hljs-keyword">let</span> system = peripherals.SYSTEM.split();
    <span class="hljs-keyword">let</span> clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    <span class="hljs-comment">// Initialize Embassy with needed timers</span>
    <span class="hljs-keyword">let</span> timer_group0 = esp32c3_hal::timer::TimerGroup::new(peripherals.TIMG0, &amp;clocks);
    embassy::init(&amp;clocks, timer_group0.timer0);

    <span class="hljs-comment">// Initialize and configure UART0</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> uart0 = Uart::new(peripherals.UART0, &amp;clocks);
    uart0.set_at_cmd(AtCmdConfig::new(<span class="hljs-literal">None</span>, <span class="hljs-literal">None</span>, <span class="hljs-literal">None</span>, AT_CMD, <span class="hljs-literal">None</span>));
    uart0
        .set_rx_fifo_full_threshold(READ_BUF_SIZE <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span>)
        .unwrap();
    <span class="hljs-comment">// Split UART0 to create seperate Tx and Rx handles</span>
    <span class="hljs-keyword">let</span> (tx, rx) = uart0.split();

    <span class="hljs-comment">// Spawn Tx and Rx tasks</span>
    spawner.spawn(uart_reader(rx)).ok();
    spawner.spawn(uart_writer(tx)).ok();
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this post, a UART application receiving and transmitting from/to a host was created for the ESP32C3 microcontroller. The code leveraged the <code>Pipe</code> synchronization primitive with the embassy async framework to enable a UART echo application. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[Embedded Rust Education: 2023 Reflections & 2024 Visions]]></title><description><![CDATA[Introduction
Inspired by James Munns's call, and as 2023 is coming to an end, I figure it's a good opportunity to reflect and look forward to 2024. It's been a bit over 1.5 years since I embarked on my embedded Rust journey and it's been nothing less...]]></description><link>https://blog.theembeddedrustacean.com/embedded-rust-education-2023-reflections-2024-visions</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embedded-rust-education-2023-reflections-2024-visions</guid><category><![CDATA[embedded systems]]></category><category><![CDATA[Rust]]></category><category><![CDATA[2024]]></category><category><![CDATA[2023]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Fri, 15 Dec 2023 09:32:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1702551452452/b722a452-ed87-4437-9143-15a899633fce.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Inspired by <a target="_blank" href="https://github.com/rust-embedded/wg/issues/720">James Munns's call</a>, and as 2023 is coming to an end, I figure it's a good opportunity to reflect and look forward to 2024. It's been a bit over 1.5 years since I embarked on my embedded Rust journey and it's been nothing less than exciting since. So here it goes.</p>
<h2 id="heading-the-beginnings">The Beginnings 👶</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/Xw6yFn7frR3Y4/giphy.gif">https://media.giphy.com/media/Xw6yFn7frR3Y4/giphy.gif</a></div>
<p> </p>
<p>It all started through a habit I have of reading job descriptions, particularly in the embedded space, to understand market trends. Back in 2021, alongside the usual suspects of C and C++, I recall seeing Rust as a preferred skill and having no clue what it was. After that, the more I got into Rust, the more I figured that it makes the most sense for embedded going forward.</p>
<p>Rust is stereotyped as a language that is hard to learn. My perspective is that what makes it feel hard is that it requires an adapted mindset. Because the thing is, once you get used to Rust, you start feeling that it's easier to deal with compared to other languages. For me, at this point, I find it hard sometimes to adapt my mindset back to languages like C. Though regardless, huge strides have been made in teaching the language. At this point, there is no shortage of free and paid educational material, and resources are only increasing. This is also impacted by the sheer number of people adopting Rust.</p>
<p>In the embedded Rust space, things were (and still are somewhat) a bit reversed. What I mean by this is that it is harder to get started with embedded Rust than Rust itself, yet, there probably isn't nearly as much guidance/resources. There is a growing gap between the quickly evolving ecosystem and the limited educational resources. There is a good number of options/combinations to get started with from choices of HALs, PACs, Embassy, RTIC, std, no-std....etc. This is apart from existing challenges for embedded beginners like identifying a controller/dev board and setting up a toolchain. Many ways to get started, without necessarily a clear path add intimidation and confusion factors. This can be an obstacle to increased adoption which is needed to facilitate more (and thus faster) growth.</p>
<p>I might be naive in thinking this, but the formula in my mind is simple; easy access leads to faster adoption. For a newbie, comparing Rust to embedded Rust, it's two contrasting experiences. It literally takes a few seconds to get started with Rust while with embedded, well, it varies. It varies with the level of experience and platform, but it probably ranges from seconds to hours.</p>
<h2 id="heading-the-gaps">The Gaps 🕳️</h2>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d9/MindTheGapVictoria.jpg/1024px-MindTheGapVictoria.jpg" alt="undefined" /></p>
<p>I subscribe to the belief that increasing educational resources is a crucial part of increased adoption. As a result, after gaining some footing I set out myself with a focus on embedded Rust education. Some of the gaps I realized included:</p>
<ol>
<li><p><strong>Material Focus:</strong> Many educational resources take the approach of focusing on what Rust brings to embedded, rather than how embedded works with Rust. For example, if you grab any traditional embedded book, the material assumes knowledge of C/C++ and explains embedded concepts with C as a tool. This is not something that I found to be the case with Rust-embedded material (but was an expectation of mine). This might not be ideal for one starting with embedded and Rust. Why? because embedded context is assumed to exist. I felt at the time there was somewhat of a community mindset focused on convincing familiar users that Rust is good for embedded. Now though I think the evolving mindset needs to be that Rust is here to stay and this is how embedded is done using Rust. Thinking of it, one who knows how good Rust is would only want to try it in a different context. This made it one of my goals to get more non-embedded Rustaceans into the field.</p>
</li>
<li><p><strong>Incomplete Documentation:</strong> Going through existing resources at the time, I always had thoughts of "what about if I wanted to do x or y?". For that, existing learning material only would give a flavor. So I dived into the world of embedded crate documentation and a long dive it was. I recall how navigating and interpreting documentation was quite a challenging experience at the start. Also while there were many abstractions there were several methods without descriptions. Some methods were self-explanatory, others were not. I often had to dig into the source to figure out certain things. A reverse engineering task. I even published several posts since trying to address this gap. Also while the documentation part remains an issue, I feel it is recognized within the community.</p>
</li>
<li><p><strong>Missing Context:</strong> Code examples are great, which many HAL repositories include, though I don't think raw examples are beginner material. The biggest issue is that many of the examples miss context or explanation. As a result, figuring out what the example does becomes another reverse engineering task. I recall instances that I had to figure out from the code example what particular device/sensor is being interacted with. The examples might have not targeted beginners, but they must do so with time. Maybe supplemented with more comments or associated with explanatory material.</p>
</li>
<li><p><strong>Unnoticed Work:</strong> There are a lot of great contributions done by the Rust community that I felt might be going unnoticed. Many members have invested incredible time in contributing toward different projects and creating material but I felt they might not have gotten the deserved exposure.</p>
</li>
</ol>
<h2 id="heading-2023-reflections">2023 Reflections 🪞</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://tenor.com/view/funny-dog-shocked-mirror-gif-12765874">https://tenor.com/view/funny-dog-shocked-mirror-gif-12765874</a></div>
<p> </p>
<p>In the content I've been producing and the contributions I make, my goal was always and still is to address the gaps I mentioned earlier. A big part of it is to attract first-time embedded curious users who love Rust. At least in the educational space, there's a lot of ground to cover, and I had hoped to do much more, but I am doing this mostly in my spare time. However, at least in 2023, here are some of the milestones I reached and accomplishments I achieved:</p>
<ul>
<li><p><strong>✍️ 88 Blog Posts</strong>: These blog posts have been mostly Rust-embedded tutorials that I publish weekly. The idea or focus was to help beginners get started with tutorial posts. Each post is also associated with a template project to provide a starting point. Out of the 88 Posts, there were focused series including the following:</p>
<ul>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/series/stm32f4-embedded-rust-hal">STM32 HAL Series</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/series/stm32f4-embedded-rust-pac">STM32 PAC Series</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/series/rust-embassy">STM32 Embassy Series</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/series/embedded-rust-drivers">Building Drivers with Rust</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/series/esp32c3-embedded-rust-hal"><code>no-std</code> ESP32</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/series/esp32-std-embedded-rust"><code>std</code> ESP32</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/series/iot-with-rust-on-esp">ESP32 IoT Series</a></p>
</li>
<li><p>(On Going) <a target="_blank" href="https://apollolabsblog.hashnode.dev/series/embassy-on-esp">ESP32 Embassy Series</a></p>
</li>
</ul>
</li>
<li><p><strong>📰</strong> <a target="_blank" href="https://www.theembeddedrustacean.com/subscribe"><strong>The</strong> <strong>Embedded Rustacean Newsletter</strong></a>: This was an effort I established to help bring more focus on different parts of embedded Rust. The space is moving fast and having a resource summarizing activities periodically helps community members stay up to date. This includes; highlighting projects, educational material, industry trends, Rust adoptions, and jobs among others. Since launching the newsletter, several individuals have expressed how there is much more going on in embedded Rust than they perceived.</p>
</li>
<li><p>🛰️ <a target="_blank" href="https://www.youtube.com/watch?v=-DhPgWIPHCY"><strong>AeroRust Space Conference</strong></a>: Part of this conference was a 1-day workshop on embedded Rust that I contributed to. The workshop material targets embedded space enthusiasts who want to build with Rust. The workshop included the building of a pseudo-nano satellite platform using Embassy and ESP. For the interested, the material is all open-sourced <a target="_blank" href="https://github.com/AeroRust/nanosat-workshop">here</a>. The learners got to tinker with real hardware building a small nanosatellite and also programming it.</p>
</li>
<li><p>📖 <a target="_blank" href="https://www.theembeddedrustacean.com/c/wired-world-a-beginners-guide-to-embedded-electronic-interfaces"><strong>Embedded Electronics Book</strong></a>: While this is not embedded Rust-focused, it was inspired by community beginner struggles. Many new learners get lost in the early electronic configurations of microcontrollers. I figured a resource like this would help fill in some gaps.</p>
</li>
</ul>
<h2 id="heading-a-look-forward-to-2024">A Look Forward to 2024 🔮</h2>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://tenor.com/view/crystal-ball-fortune-teller-betty-white-kristallkugel-scry-gif-22610039">https://tenor.com/view/crystal-ball-fortune-teller-betty-white-kristallkugel-scry-gif-22610039</a></div>
<p> </p>
<h3 id="heading-what-i-have-in-plan">What I Have in Plan</h3>
<ul>
<li><p><strong>A Book on Embedded Rust</strong>: The electronics book was actually a distraction from another book project I was working on. This new book aligns with filling one of the earlier gaps mentioned; A book with material that covers embedded foundations with Rust.</p>
</li>
<li><p><strong>More &amp; More Blog Posts</strong>: With upcoming posts, I hope to fill in more educational gaps. I notice there is a lot of interest within the community in Embassy. I have also gotten notes for creating tutorials on additional devices like the Raspberry Pi Pico and the nRF series of devices. I'd personally also like to highlight exciting projects like Shuttle and Ockam that I find have a lot of potential. Even work with several driver crates.</p>
</li>
<li><p><strong>Expand Embedded Rust Reach:</strong> I hope to do this by expanding the reach of the newsletter. Highlight embedded trends and opportunities as they come along. Also, keep the community informed of activities that are going on and give exposure to the work that is being done. This would be hopefully a vehicle to attract more embedded enthusiasts into the Rust space as well.</p>
</li>
</ul>
<h3 id="heading-what-id-hope-to-see">What I'd Hope to See</h3>
<p>These are things that are probably better suited as community efforts:</p>
<ul>
<li><p><strong>Plug and Play:</strong> This goes to the goal of being able to get started with embedded Rust in seconds. A self-contained solution including hardware with a preconfigured toolchain and a project template to get started developing embedded Rust in a jiffy. Maybe the best way to explain this is to have an Arduino-like model. This includes embedded Rust-branded development boards integrating controllers that the community identifies/adopts. Along with a VSCode extension like PlatformIO or even a lightweight IDE like Arduino to spawn preconfigured projects quickly. All this can bring more community focus on developing examples and crates around the Rust boards. Having boards also answer a beginner's big question of "What hardware should I buy?". I would say from my viewpoint that the current effort that looks closest to this is the ecosystem Espressif is building around ESPs with Rust.</p>
</li>
<li><p><strong>More Wokwi Integration:</strong> <a target="_blank" href="https://wokwi.com/">Wokwi</a> is an amazing embedded simulator and is great for getting started quickly. For a learner, there's no need for toolchain setup or even the purchase of hardware. There are many features as well that make it quite a flexible tool supporting a lot of features right from the browser. Users can also vote for more features. Still maybe at some point, one might want to tinker with physical hardware. However, at that point, they would have gained some confidence first. Currently, only ESP boards are supported with Rust on Wokwi. I hope for the variety to expand soon.</p>
</li>
</ul>
<h2 id="heading-acknowledgments">Acknowledgments 🙏</h2>
<p><img src="https://media.giphy.com/media/I6bh4ENiiZKfpBcNwq/giphy.gif" alt class="image--center mx-auto" /></p>
<p>None of this work would have been possible without community support. I always thought that even had Rust sucked, I would probably stick around because of the community. I would like to give special thanks to the following people:</p>
<ul>
<li><p><strong>Juraj Michalek</strong> and <strong>Sergio Gasquez</strong> of Espressif Systems for embracing my work, helping integrate me into community meetings, proofreading my book, and offering different means of support.</p>
</li>
<li><p><strong>Aissata Maiga</strong> of Ferrous Systems in her support proofreading my book and providing invaluable input. Also as one of the community members to recognize my work early on.</p>
</li>
<li><p><strong>Lachezar Lechev</strong> of AeroRust for inviting me to become part of the AeroRust space conference. Also for being a great host during my time at the conference.</p>
</li>
<li><p><strong>Jeremy Lempereur</strong> of Apollo GraphQL for sponsoring my trip to the AeroRust space conference.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I think that a core driver to accelerated Embedded Rust adoption is the production of more educational material. Those interested in embedded Rust do not all necessarily come from an embedded background. Creating educational material comes with the challenge of simplifying concepts and providing a quick way to start. It's easy for a regular practitioner to get caught up with what they think is easy though it's different for a beginner. Strides have been made in this context for embedded Rust but more work remains. 2023 was an exciting year for embedded Rust in that regard, I only look forward to what's to come in 2024.</p>
]]></content:encoded></item><item><title><![CDATA[Embassy on ESP: UART Transmitter]]></title><description><![CDATA[This blog post is the third of a multi-part series of posts where I will explore various peripherals of the ESP32 using the embedded Rust embassy framework.

Prior posts include (in order of publishing):

Embassy on ESP: Getting Started

Embassy on E...]]></description><link>https://blog.theembeddedrustacean.com/embassy-on-esp-uart-transmitter</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embassy-on-esp-uart-transmitter</guid><category><![CDATA[Rust]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[embedded]]></category><category><![CDATA[iot]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Sat, 09 Dec 2023 06:44:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1702103963148/976f57a7-8af1-4617-82cc-d7544b3a808e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong><em>This blog post is the third of a multi-part series of posts where I will explore various peripherals of the ESP32 using the embedded Rust embassy framework.</em></strong></p>
</blockquote>
<p>Prior posts include (in order of publishing):</p>
<ol>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embassy-on-esp-getting-started">Embassy on ESP: Getting Started</a></p>
</li>
<li><p><a target="_blank" href="https://apollolabsblog.hashnode.dev/embassy-on-esp-gpio">Embassy on ESP: GPIO</a></p>
</li>
</ol>
<h2 id="heading-introduction">Introduction</h2>
<p>Setting up UART serial communication remains useful for several types of device-to-device (point-to-point) communication. One of the common examples is interacting with a host PC. UART implementations are also still found in some industrial and wireless applications. In this post, using embassy and the ESP32C3, I will be configuring and setting up UART communication with a host PC. UART will be configured as a transmitter where the application will detect and send button press counts over the communication channel.</p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Knowledge of how the embassy executor works.</p>
</li>
<li><p>Basic knowledge of UART Communication.</p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<p>Additionally, the full project (code and simulation) is available on Wokwi <a target="_blank" href="https://wokwi.com/projects/383561090802699265"><strong>here</strong></a>.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
</li>
</ul>
<h4 id="heading-connections"><strong>🔌 Connections</strong></h4>
<p>No special connections are required for this implementation. The UART lines on the ESP32-C3 in DevKitM are connected on-board to the UART bridge.</p>
<h2 id="heading-software-design"><strong>👨‍🎨 Software Design</strong></h2>
<p>In the application developed in this post, button presses will be detected and counted. The button press count will be sent over UART to the host PC. A button press input will be configured for interrupts and increment a <code>count</code> each time the button is pressed. Consequently, every time a button press is detected the UART interface will transmit the <code>count</code>. The button press logic and the UART logic will each be implemented in their own tasks.</p>
<p>Figure 1 below expresses the states/logic of the button press task. Additionally, Figure 2 expresses the states/logic the UART task will alternate between. The button task will be implemented in the main task. Note that both tasks are being observed as independent each running their own logic. In reality, each will treated by the embassy executor as such. However, the UART task is dependent on a <code>count</code> variable being updated by the button press state machine. For that, we will need to use a special abstraction that allows safe exchange of data among tasks.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1702053857193/00cd524f-dc05-499b-bbdf-24e85bf8853b.png" alt class="image--center mx-auto" /></p>
<p>Let's now jump into implementing this algorithm.</p>
<h2 id="heading-code-implementation"><strong>👨‍💻 Code Implementation</strong></h2>
<h3 id="heading-crate-imports">📥 Crate Imports</h3>
<p>In this implementation the crates required are as follows:</p>
<ul>
<li><p>The <code>embassy_sync</code> crate to import <code>Signal</code>, a special abstraction needed for synchronization.</p>
</li>
<li><p>The <code>embassy_executor</code> crate to import the embassy executor.</p>
</li>
<li><p>The <code>embedded-hal-async</code> crate to import the GPIO abstractions to detect button presses.</p>
</li>
<li><p>The <code>esp32c3-hal</code> crate to import the needed ESP32C3 abstractions.</p>
</li>
<li><p>The <code>esp_backtrace</code> crate needed to define panic behavior.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> embassy_executor::Spawner;
<span class="hljs-keyword">use</span> embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
<span class="hljs-keyword">use</span> embassy_sync::signal::Signal;
<span class="hljs-keyword">use</span> embedded_hal_async::digital::Wait;
<span class="hljs-keyword">use</span> esp32c3_hal::{
    clock::ClockControl,
    embassy, interrupt,
    peripherals::{Interrupt, Peripherals, UART0},
    prelude::*,
    Uart, UartTx, IO,
};
<span class="hljs-keyword">use</span> esp_backtrace <span class="hljs-keyword">as</span> _;
</code></pre>
<h4 id="heading-global-variables">🌍 Global Variables</h4>
<p>In the application at hand, there will be two tasks that share a count value. The button press detection task will increment a count the UART writer task will use it. As such, we can create a global variable <code>MYSIGNAL</code> to "signal" the count value that is going to be passed around. We declare <code>MYSIGNAL</code> as <code>static</code> and define it as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">static</span> MYSIGNAL: Signal&lt;CriticalSectionRawMutex, <span class="hljs-built_in">u32</span>&gt; = Signal::new();
</code></pre>
<blockquote>
<p>📝 Note: Global variables shared among tasks in Rust is a very sticky topic. This is because sharing global data is <code>unsafe</code> since it can cause race conditions. You can read more about it <a target="_blank" href="https://doc.rust-lang.org/beta/embedded-book/concurrency/index.html">here</a>. Embassy offers several synchronization primitives that provide safe abstractions depending on what needs to be accomplished. There is a prior post where I go into detail about these primitives <a target="_blank" href="https://apollolabsblog.hashnode.dev/sharing-data-among-tasks-in-rust-embassy-synchronization-primitives">here</a>.</p>
</blockquote>
<h3 id="heading-the-uart-writer-task">🕹️ The UART Writer Task</h3>
<p>The UART writer task is expected to accept a UART instance as input and <code>loop</code> constantly check/<code>await</code> if <code>MYSIGNAL</code> gets updated. Though ahead of the <code>loop</code> it might be beneficial to indicate that the task has started. These are the required steps:</p>
<p>1️⃣ <strong>Create a UART Writer Task</strong>: Tasks are marked by the <code>#[embassy_executor::task]</code> macro followed by a <code>async</code> function implementation. The task created is referred to as <code>uart_writer</code> task defined as follows:</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[embassy_executor::task]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">uart_writer</span></span>(<span class="hljs-keyword">mut</span> tx: UartTx&lt;<span class="hljs-symbol">'static</span>, UART0&gt;)
</code></pre>
<p><code>UartTx</code> marks a UART transmitted type that is configured with an instance of <code>UART0</code>. This means when spawning the task, we need pass a handle for a UART transmitter configured with an instance of <code>UART0</code>. This will be done in the <code>main</code> task before spawning the <code>uart_writer</code> task.</p>
<p>2️⃣ <strong>Print a Message</strong>: As part of the <code>embedded_io_async</code> crate we can use the <code>write</code> implementation in the <code>Write</code> trait to asynchronously write a message. <code>write</code> has the following signature:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">write</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, buf: &amp;[<span class="hljs-built_in">u8</span>]) -&gt; <span class="hljs-built_in">Result</span>&lt;<span class="hljs-built_in">usize</span>, Self::Error&gt;
</code></pre>
<p>As such, we need to pass a mutable reference of a type (<code>UartTx</code> in our case) that implements <code>Write</code> and our message as a slice of <code>u8</code>. Since <code>write</code> is an <code>async</code> function, the program needs to <code>await</code> for the write operation to complete. This results in the following code:</p>
<pre><code class="lang-rust">embedded_io_async::Write::write(
    &amp;<span class="hljs-keyword">mut</span> tx,
    <span class="hljs-string">b"UART Task Spawned. Waiting for Button Press...\r\n"</span>,
)
.<span class="hljs-keyword">await</span>
.unwrap();
</code></pre>
<blockquote>
<p>📝 Note: I could have easily also used <code>esp_println</code> which provides more convenient abstractions to print to the console. While in this context it does not make much difference, I did this for two reasons. First, since this is a UART example, we need to demonstrate how to use UART abstractions. Second, the previous code is a good demonstration of the use of the <code>embedded-io-async</code> traits. Using the <code>embedded-io-async</code> traits allows for more portable code in some contexts.</p>
</blockquote>
<p>3️⃣ <strong>Define the task loop</strong>: Next enter the task <code>loop</code>. The first thing we need to do is <code>await</code> a change on <code>MYSIGNAL</code>. For that, there exists a <code>wait</code> method for the <code>Signal</code> type. <code>wait_for_rising_edge</code> is an <code>async</code> function that resolves into a <code>Future</code> if its waiting on a signal. Once the <code>Future</code> resolves, we get back a value corresponding to the <code>press_count</code>. We then print the <code>press_count</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">loop</span> {
    <span class="hljs-keyword">let</span> press_count = MYSIGNAL.wait().<span class="hljs-keyword">await</span>;
    esp_println::<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Button Pressed {} time(s)"</span>, press_count);
}
</code></pre>
<h3 id="heading-the-main-task"><strong>📱 The Main Task</strong></h3>
<p>The start of the main task is marked by the following code:</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[main]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>(spawner: Spawner)
</code></pre>
<p>As the documentation states: "The main entry point of an Embassy application is defined using the <code>#[main]</code> macro. The entry point is also required to take a <code>Spawner</code> argument." As we'll see, <code>Spawner</code> is what will allow us to spawn or kick-off the <code>button_press</code> task.</p>
<p>The following steps will mark the tasks performed in the main task.</p>
<p>1️⃣ <strong>Obtain a handle for the device peripherals &amp; system clocks</strong>: In embedded Rust, as part of the singleton design pattern, we first have to take the PAC-level device peripherals. This is done using the <code>take()</code> method. Here I create a device peripheral handler named <code>peripherals</code> , a system peripheral handler <code>system</code>, and a system clock handler <code>clocks</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> peripherals = Peripherals::take();
<span class="hljs-keyword">let</span> system = peripherals.SYSTEM.split();
<span class="hljs-keyword">let</span> clocks = ClockControl::boot_defaults(system.clock_control).freeze();
</code></pre>
<p><strong>2️⃣ Initialize Embassy Timers for the ESP32C3:</strong></p>
<p>In embassy, there exists an <code>init</code> function that takes two parameters. The first is system clocks and the second is an instance of a timer. Under the hood, what this function does is initialize the embassy timers. As such, we can initialize the embassy timers as follows:</p>
<pre><code class="lang-rust">embassy::init(
    &amp;clocks,
    esp32c3_hal::timer::TimerGroup::new(peripherals.TIMG0, &amp;clocks).timer0,
);
</code></pre>
<blockquote>
<p><strong><em>📝 Note:</em></strong> <em>At the time of writing this post, I couldn't really locate the</em> <code>init</code> function <a target="_blank" href="http://docs.rs"><strong>docs.rs</strong></a> documentation. It didn't seem easily accessible through any of the current HAL implementation documentation. Nevertheless, I reached the signature of the function through the source <a target="_blank" href="https://github.com/esp-rs/esp-hal/blob/ece40abaed0e642b751a8752ce6406740efa4af6/esp-hal-common/src/embassy/mod.rs#L100"><strong><em>here</em></strong></a><em>.</em></p>
</blockquote>
<p>3️⃣ <strong>Instantiate and Create Handle for IO</strong>: We need to configure the LED pins as a push-pull output and obtain a handler for the pin so that we can control it. Similarly, we need to obtain a handle for the button input pin. Before we can obtain any handles for the LEDs and the button we need to create an <code>IO</code> struct instance. The <code>IO</code> struct instance provides a HAL-designed struct that gives us access to all gpio pins thus enabling us to create handles for individual pins. This is similar to the concept of a <code>split</code> method used in other HALs (more detail <a target="_blank" href="https://apollolabsblog.hashnode.dev/demystifying-rust-embedded-hal-split-and-constrain-methods"><strong>here</strong></a>). We do this by calling the <code>new()</code> instance method on the <code>IO</code> struct as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
</code></pre>
<p>Note how the <code>new</code> method requires passing the <code>GPIO</code> and <code>IO_MUX</code> peripherals.</p>
<p>4️⃣ <strong>Obtain a handle and configure the input button</strong>: The push button is connected to pin 2 (<code>gpio2</code>) as stated earlier. Additionally, in the pressed state, the button pulls to ground. Consequently, for the button unpressed state, a pull-up resistor needs to be included so the pin goes high. An internal pull-up can be configured for the pin using the <code>into_pull_up_input()</code> method as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> button = io.pins.gpio2.into_pull_up_input();
</code></pre>
<p>5️⃣ <strong>Obtain Handle and Configure UART</strong>: to create an instance of <code>UART0</code>, there exists a <code>new</code> instance method under <code>esp32c3_hal::Uart</code> that requires two parameters; a <code>UART</code> peripheral type and a <code>Clocks</code> type. Since we need only an instance of a UART transmitted, there also exists a <code>split</code> method that allows us to "split" the <code>uart0</code> instance into separate transmitter and receiver instances. <code>split</code> returns a tuple with two instances. Since we don't need the receiver instance an underscore <code>_</code> is used.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> uart0 = Uart::new(peripherals.UART0, &amp;clocks);
<span class="hljs-keyword">let</span> (tx, _) = uart0.split();
</code></pre>
<p><code>UART0</code> is chosen since it is the one that ties to the UART logging pins on board. Additionally, <code>new</code> configures UART with the default 8N1 setup.</p>
<p>6️⃣ <strong>Enable GPIO Interrupts</strong>: At this point, the <code>button</code> instance is just an input pin. In order to make the device respond to push button events, interrupts need to be enabled for <code>button</code>. This results in the following lines of code:</p>
<pre><code class="lang-rust">esp32c3_hal::interrupt::enable(
    esp32c3_hal::peripherals::Interrupt::GPIO,
    esp32c3_hal::interrupt::Priority::Priority1,
)
</code></pre>
<p>4️⃣ <strong>Spawn UART Writer Task</strong>: before entering the button press loop, we're going to need to kick off our <code>uart_writer</code> task. This is done using the <code>spawn</code> method as follows:</p>
<pre><code class="lang-rust">spawner.spawn(uart_writer(tx)).unwrap();
</code></pre>
<p>Next, we can move on to the task <code>loop</code>.</p>
<h4 id="heading-main-task-loop">🔁 Main Task Loop</h4>
<p>Following the design described earlier, in the main task (button press task), we will <code>await</code> a button press to occur. Once a press has occurred, <code>press_count</code> is incremented and signaled. Here's the code:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> press_count = <span class="hljs-number">0</span>;
<span class="hljs-keyword">loop</span> {
    <span class="hljs-comment">// Await Button Press</span>
    button.wait_for_rising_edge().<span class="hljs-keyword">await</span>.unwrap();
    <span class="hljs-comment">// Increment Count</span>
    press_count += <span class="hljs-number">1</span>;
    <span class="hljs-comment">// Signal Press Count</span>
    MYSIGNAL.signal(press_count);
}
</code></pre>
<p>Note here the use of the <code>signal</code> method on <code>MYSIGNAL</code> to update its value and "signal" it to the <code>uart_writer</code> task.</p>
<p>This concludes the code for the full application.</p>
<h2 id="heading-full-application-code">📱 Full Application Code</h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Also, the Wokwi project can be accessed <a target="_blank" href="https://wokwi.com/projects/383561090802699265"><strong>here</strong></a>.</p>
<pre><code class="lang-rust"><span class="hljs-meta">#![no_std]</span>
<span class="hljs-meta">#![no_main]</span>
<span class="hljs-meta">#![feature(type_alias_impl_trait)]</span>

<span class="hljs-keyword">use</span> embassy_executor::Spawner;
<span class="hljs-keyword">use</span> embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
<span class="hljs-keyword">use</span> embassy_sync::signal::Signal;
<span class="hljs-keyword">use</span> embedded_hal_async::digital::Wait;
<span class="hljs-keyword">use</span> esp32c3_hal::{
    clock::ClockControl,
    embassy, interrupt,
    peripherals::{Interrupt, Peripherals, UART0},
    prelude::*,
    Uart, UartTx, IO,
};
<span class="hljs-keyword">use</span> esp_backtrace <span class="hljs-keyword">as</span> _;

<span class="hljs-keyword">static</span> MYSIGNAL: Signal&lt;CriticalSectionRawMutex, <span class="hljs-built_in">u32</span>&gt; = Signal::new();

<span class="hljs-meta">#[embassy_executor::task]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">uart_writer</span></span>(<span class="hljs-keyword">mut</span> tx: UartTx&lt;<span class="hljs-symbol">'static</span>, UART0&gt;) {
    embedded_io_async::Write::write(
        &amp;<span class="hljs-keyword">mut</span> tx,
        <span class="hljs-string">b"UART Task Spawned. Waiting for Button Press...\r\n"</span>,
    )
    .<span class="hljs-keyword">await</span>
    .unwrap();
    <span class="hljs-keyword">loop</span> {
        <span class="hljs-keyword">let</span> press_count = MYSIGNAL.wait().<span class="hljs-keyword">await</span>;
        esp_println::<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Button Pressed {} time(s)"</span>, press_count);
    }
}

<span class="hljs-meta">#[main]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>(spawner: Spawner) {
    <span class="hljs-keyword">let</span> peripherals = Peripherals::take();
    <span class="hljs-keyword">let</span> system = peripherals.SYSTEM.split();
    <span class="hljs-keyword">let</span> clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    <span class="hljs-comment">// Initilize Embassy Timers</span>
    embassy::init(
        &amp;clocks,
        esp32c3_hal::timer::TimerGroup::new(peripherals.TIMG0, &amp;clocks).timer0,
    );

    <span class="hljs-comment">// Configure UART</span>
    <span class="hljs-keyword">let</span> uart0 = Uart::new(peripherals.UART0, &amp;clocks);
    <span class="hljs-keyword">let</span> (tx, _) = uart0.split();

    <span class="hljs-comment">// Configure GPIO</span>
    <span class="hljs-keyword">let</span> io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> button = io.pins.gpio2.into_pull_up_input();

    <span class="hljs-comment">// Enable Interrupts for GPIO</span>
    interrupt::enable(Interrupt::GPIO, interrupt::Priority::Priority1).unwrap();

    spawner.spawn(uart_writer(tx)).ok();

    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> press_count = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">loop</span> {
        <span class="hljs-comment">// Detect and Count Button Presses</span>
        button.wait_for_rising_edge().<span class="hljs-keyword">await</span>.unwrap();
        press_count += <span class="hljs-number">1</span>;
        <span class="hljs-comment">// Signal Press Count</span>
        MYSIGNAL.signal(press_count);
    }
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this post, a UART application transmitting to a host was created for the ESP32C3 microcontroller. The code combined the use of Signals, GPIO, and interrupts with the embassy async framework. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item><item><title><![CDATA[Embassy on ESP: GPIO]]></title><description><![CDATA[This blog post is the second of a multi-part series of posts where I will explore various peripherals of the ESP32 using the embedded Rust embassy framework.

Prior posts include (in order of publishing):

Embassy on ESP: Getting Started

Introductio...]]></description><link>https://blog.theembeddedrustacean.com/embassy-on-esp-gpio</link><guid isPermaLink="true">https://blog.theembeddedrustacean.com/embassy-on-esp-gpio</guid><category><![CDATA[Rust]]></category><category><![CDATA[embedded]]></category><category><![CDATA[ESP32]]></category><category><![CDATA[Internet of Things]]></category><category><![CDATA[iot]]></category><dc:creator><![CDATA[Omar Hiari]]></dc:creator><pubDate>Sun, 03 Dec 2023 06:36:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1701585332175/163136e6-29dc-4591-830f-26d73a39cc09.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong><em>This blog post is the second of a multi-part series of posts where I will explore various peripherals of the ESP32 using the embedded Rust embassy framework.</em></strong></p>
</blockquote>
<p>Prior posts include (in order of publishing):</p>
<ol>
<li><a target="_blank" href="https://apollolabsblog.hashnode.dev/embassy-on-esp-getting-started">Embassy on ESP: Getting Started</a></li>
</ol>
<h2 id="heading-introduction">Introduction</h2>
<p>In the first post from last week, a basic application was built in embassy on the ESP32C3. This was mainly to introduce basic operations and also a template to build on. One of the things that is amazing about embassy and async is how much easier it is to implement interrupt-driven code. There are two prior blog posts that you can compare to for ESP. Both for <a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-standard-library-embedded-rust-gpio-interrupts">std</a> and <a target="_blank" href="https://apollolabsblog.hashnode.dev/esp32-embedded-rust-at-the-hal-gpio-interrupts">no-std</a> implementation of interrupts. I recommend revisiting those prior posts to draw comparisons.</p>
<p>In this post, we'll get to start experimenting with GPIO interrupts in embassy. We'll see how we can configure GPIO, read inputs, and manipulate output. We'll be developing an application that uses a 10 LED bar graph to circulate a light at different speeds. The speed is altered by a button press.</p>
<div class="hn-embed-widget" id="substart"></div><p> </p>
<h3 id="heading-knowledge-pre-requisites"><strong>📚 Knowledge Pre-requisites</strong></h3>
<p>To understand the content of this post, you need the following:</p>
<ul>
<li><p>Basic knowledge of coding in Rust.</p>
</li>
<li><p>Knowledge of how the embassy executor works</p>
</li>
</ul>
<h3 id="heading-software-setup"><strong>💾 Software Setup</strong></h3>
<p>All the code presented in this post is available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Note that if the code on the git repo is slightly different then it means that it was modified to enhance the code quality or accommodate any HAL/Rust updates.</p>
<p>Additionally, the full project (code and simulation) is available on Wokwi <a target="_blank" href="https://wokwi.com/projects/382810046853433345"><strong>here</strong></a>.</p>
<h3 id="heading-hardware-setup"><strong>🛠 Hardware Setup</strong></h3>
<h4 id="heading-materials">Materials</h4>
<ul>
<li><p><a target="_blank" href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html"><strong>ESP32-C3-DevKitM</strong></a></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681796942083/da81fb7b-1f90-4593-a848-11c53a87821d.jpeg?auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
</li>
<li><p>10 Segment LED Bar Graph</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689175567250/bbe8cf6f-a46c-4913-a61e-2e334af5e8eb.jpeg?auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
<ul>
<li>Pushbutton</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689175647786/f97380f4-e55d-4517-a49e-16d0137f008f.jpeg?auto=compress,format&amp;format=webp" alt class="image--center mx-auto" /></p>
<h4 id="heading-connections"><strong>🔌 Connections</strong></h4>
<p><strong>📝 Note</strong></p>
<blockquote>
<p><strong><em>All connection details are also shown in the</em></strong> <a target="_blank" href="https://wokwi.com/projects/382810046853433345"><strong><em>Wokwi example</em></strong></a><strong><em>.</em></strong></p>
</blockquote>
<p>Connections include the following:</p>
<ul>
<li><p>LED Bar Graph Anode A10 to gpio1 on the devkit.</p>
</li>
<li><p>LED Bar Graph Anode A9 to gpio10 on the devkit.</p>
</li>
<li><p>LED Bar Graph Anode A8 to gpio19 on the devkit.</p>
</li>
<li><p>LED Bar Graph Anode A7 to gpio18 on the devkit.</p>
</li>
<li><p>LED Bar Graph Anode A6 to gpio4 on the devkit.</p>
</li>
<li><p>LED Bar Graph Anode A5 to gpio5 on the devkit.</p>
</li>
<li><p>LED Bar Graph Anode A4 to gpio6 on the devkit.</p>
</li>
<li><p>LED Bar Graph Anode A3 to gpio7 on the devkit.</p>
</li>
<li><p>LED Bar Graph Anode A2 to gpio8 on the devkit.</p>
</li>
<li><p>LED Bar Graph Anode A1 to gpio9 on the devkit.</p>
</li>
<li><p>All LED Bar Graph Cathodes C1-C10 are connected to each other and the devkit GND.</p>
</li>
<li><p>On one end, the button pin should be connected to gpio3 of the devkit. The gpio3 pin will be configured as input. On the same button end, the other pin of the switch will be connected to the devkit GND.</p>
</li>
</ul>
<h2 id="heading-software-design"><strong>👨‍🎨 Software Design</strong></h2>
<p>In the application developed in this post, I want to cycle through turning on LEDs on an LED bar. A button will also be used to change how fast the light is cycling. Meaning, that every time I press the button, I want to see the LED cycling at a different speed. Obviously, the device pins would need to be configured first, which I will cover in the next section. In this section, I will focus on the design of the application algorithm.</p>
<p>The design will use interrupts to detect button press events. The button press will modify a delay variable shared with the main task. We can consider that the application has two tasks, an LED cycling task and a button-press task. Both applications share a <code>del</code> variable that is adjusted according to button presses.</p>
<p>In the LED cycling (<code>main</code>) task, starting with the first LED in the sequence on the LED bar, here are the steps the algorithm would go through:</p>
<ol>
<li><p>Turn on LED.</p>
</li>
<li><p><code>await</code> for a delay of <code>del</code> to expire.</p>
</li>
<li><p>Turn off the LED.</p>
</li>
<li><p><code>await</code> for a delay of 100ms to expire.</p>
</li>
<li><p>Repeat steps 1-5 for the next LED in sequence.</p>
</li>
<li><p>Once all LEDs are done, loop back to the first LED in sequence.</p>
</li>
</ol>
<p>In the button-pressed task, here are the steps the algorithm would go through:</p>
<ol>
<li><p><code>await</code> for a button press to occur (ex. rising edge to occur).</p>
</li>
<li><p>Adjust <code>del</code></p>
</li>
<li><p>Go back to step 1.</p>
</li>
</ol>
<p>Note that every time we have an <code>await</code> the task is yielding to the executor to determine what to do next.</p>
<p>For the delay adjusting procedure in the button-pressed task, the delay value is changed so that the rate of LED cycling decreases. However, we need to make sure that the new delay value does not go negative. As such, if the <code>del</code> drops below a certain threshold we'd want to reset it to the original value we started with.</p>
<p>For step 4 in the <code>main</code> task, note the 100 ms delay. This is to make sure that the current LED is off (visually) before turning on the next one in the sequence. You can experiment with this and see that if removed, you would notice an effect that the previous LED is still on when the current one turns on. You could probably live with a smaller delay as long as your eye does not notice it, I just used 100ms to stay on the safe side.</p>
<p>Let's now jump into implementing this algorithm.</p>
<h2 id="heading-code-implementation"><strong>👨‍💻 Code Implementation</strong></h2>
<blockquote>
<p><strong><em>📝 Although I followed the documentation to set up my first project, it wasn't all smooth sailing. It was mainly had to do with configurations/settings of configuration (toml) files. I figure this has to do with embassy still being considered to be experimental. Set up I managed to get working is available up in my</em></strong> <a target="_blank" href="https://github.com/apollolabsdev/stm32-nucleo-f401re"><strong><em>git repo</em></strong></a><strong><em>.</em></strong></p>
</blockquote>
<h3 id="heading-crate-imports">📥 Crate Imports</h3>
<p>In this implementation the crates required are as follows:</p>
<ul>
<li><p>The <code>core::sync::atomic</code> and <code>portable_atomic</code> crates to import <code>Atomic</code> and <code>Ordering</code> that will be needed for syncronization.</p>
</li>
<li><p>The <code>embassy_executor</code> crate to import the embassy executor.</p>
</li>
<li><p>The <code>embassy_time</code> crate to import <code>Timer</code> abstractions for delays.</p>
</li>
<li><p>The <code>embedded-hal-async</code> crate to import the GPIO abstractions to detect button presses.</p>
</li>
<li><p>The <code>esp32c3-hal</code> crate to import the needed ESP32C3 abstractions.</p>
</li>
<li><p>The <code>esp_backtrace</code> crate needed to define panic behavior.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> core::sync::atomic::Ordering;
<span class="hljs-keyword">use</span> portable_atomic::AtomicU32;
<span class="hljs-keyword">use</span> embassy_executor::Spawner;
<span class="hljs-keyword">use</span> embassy_time::{Duration, Timer};
<span class="hljs-keyword">use</span> embedded_hal_async::digital::Wait;
<span class="hljs-keyword">use</span> esp32c3_hal::{clock::ClockControl, embassy, peripherals::Peripherals, prelude::*, IO};
<span class="hljs-keyword">use</span> esp32c3_hal::gpio::{AnyPin, Input, PullUp};
<span class="hljs-keyword">use</span> esp_backtrace <span class="hljs-keyword">as</span> _;
</code></pre>
<h4 id="heading-global-variables">🌍 Global Variables</h4>
<p>In the application at hand, there will be two tasks that share a delay value. The button press detection task will adjust the delay and the LED control task will use it. As such, we can create a global variable <code>BLINK_DELAY</code> to carry the delay value that is going to be passed around. Here the <code>AtomicU32</code> type is used which is an integer type that can be safely shared between threads. The <code>AtomicU32</code> type has the same in-memory representation as the underlying integer type, <code>u32</code> but is considered safe to share between threads.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">static</span> BLINK_DELAY: AtomicU32 = AtomicU32::new(<span class="hljs-number">200_u32</span>);
</code></pre>
<blockquote>
<p>📝 Note: Global variables shared among tasks in Rust is a very sticky topic. This is because sharing global data is <code>unsafe</code> since it can cause race conditions. You can read more about it <a target="_blank" href="https://doc.rust-lang.org/beta/embedded-book/concurrency/index.html">here</a>. Embassy offers several synchronization primitives that provide safe abstractions depending on what needs to be accomplished. There is a prior post about these primitives <a target="_blank" href="https://apollolabsblog.hashnode.dev/sharing-data-among-tasks-in-rust-embassy-synchronization-primitives">here</a>.</p>
</blockquote>
<h3 id="heading-the-button-press-task">🕹️ The Button Press Task</h3>
<p>The button press task is expected to accept a GPIO pin as input and loop forever checking if the button is pressed. These are the required steps:</p>
<p>1️⃣ <strong>Create a blinking task and handle for the button</strong>: Tasks are marked by the <code>#[embassy_executor::task]</code> macro followed by a <code>async</code> function implementation. The task created is referred to as <code>press_button</code> task defined as follows:</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[embassy_executor::task]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">press_button</span></span>(<span class="hljs-keyword">mut</span> button: AnyPin&lt;Input&lt;PullUp&gt;&gt;)
</code></pre>
<p><code>AnyPin</code> marks a generic pin type that is configured as a <code>PullUp</code> <code>Input</code>. This means we need to obtain a handle for the button pin and configure it to an input with a pull-up. This will be done in the <code>main</code> task before spawning the <code>button_press</code> task</p>
<p>2️⃣ <strong>Define the task loop</strong>: Next enter the task <code>loop</code>. The first thing we need to do is <code>await</code> a button press. For that, there exists a <code>wait_for_rising_edge</code> method implementation for the <code>Wait</code> trait in the <a target="_blank" href="https://docs.rs/embedded-hal-async/1.0.0-rc.2/embedded_hal_async/digital/trait.Wait.html"><code>embedded-hal-async</code></a>. <code>wait_for_rising_edge</code> is an <code>async</code> function that resolves into a <code>Future</code> if its waiting on a condition. Otherwise, we get a <code>Result</code> . We call <code>wait_for_rising_edge</code> on <code>button</code> as follows:</p>
<pre><code class="lang-rust">button.wait_for_rising_edge().<span class="hljs-keyword">await</span>.unwrap();
</code></pre>
<p>Using embassy, <code>async</code> events from interrupts or otherwise are managed through <code>Futures</code>. This means that execution can be yielded using <code>await</code> to allow other code to progress until the event attached to a <code>Future</code> occurs. The executor manages all of this in the background and more detail about it is provided <a target="_blank" href="https://embassy.dev/dev/runtime.html"><strong>in the embassy documentation</strong></a>.</p>
<p>3️⃣ <strong>Retrieve the delay</strong>: Next we <code>load</code> the delay value from the global context as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> del = BLINK_DELAY.load(Ordering::Relaxed);
</code></pre>
<p><code>load</code> is a method part of the <code>AtomicU32</code> synchronization abstraction.</p>
<p><strong>4️⃣ Adjust the Delay:</strong> This is the final step in the task and involves adjusting the delay according to our desired logic. Here we are doing decrements of 50 ms and if we reach a value less than 50, we reset back to the starting value of 200.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">if</span> del &lt;= <span class="hljs-number">50_u32</span> {
  BLINK_DELAY.store(<span class="hljs-number">200_u32</span>,Ordering::Relaxed);
  esp_println:: <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Delay is now 200ms"</span>);
} <span class="hljs-keyword">else</span> {
  BLINK_DELAY.store(del - <span class="hljs-number">50_u32</span>,Ordering::Relaxed);
  esp_println:: <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Delay is now {}ms"</span>, del - <span class="hljs-number">50_u32</span>);
}
</code></pre>
<h3 id="heading-the-main-task-led-cycling-task">📱 The Main Task (LED Cycling Task)</h3>
<p>The start of the main task is marked by the following code:</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[embassy_executor::main]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>(spawner: Spawner)
</code></pre>
<p>As the documentation states: "The main entry point of an Embassy application is defined using the <code>#[embassy_executor::main]</code> macro. The entry point is also required to take a <code>Spawner</code> argument." As we've seen in last week's post, <code>Spawner</code> is what will allow us to spawn or kick-off <code>button_task</code>.</p>
<p>As indicated before, the main task will also be where we manage the LED cycling logic. The following steps will mark the tasks performed in the main task.</p>
<p>1️⃣ <strong>Obtain a handle for the device peripherals &amp; system clocks</strong>: In embedded Rust, as part of the singleton design pattern, we first have to take the PAC-level device peripherals. This is done using the <code>take()</code> method. Here I create a device peripheral handler named <code>peripherals</code> , a system peripheral handler <code>system</code>, and a system clock handler <code>clocks</code> as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> peripherals = Peripherals::take();
<span class="hljs-keyword">let</span> system = peripherals.SYSTEM.split();
<span class="hljs-keyword">let</span> clocks = ClockControl::boot_defaults(system.clock_control).freeze();
</code></pre>
<p><strong>2️⃣ Initialize Embassy Timers for the ESP32C3:</strong></p>
<p>In embassy, there exists an <code>init</code> function that takes two parameters. The first is system clocks and the second is an instance of a timer. Under the hood, what this function does is initialize the embassy timers. As such, we can initialize the embassy timers as follows:</p>
<pre><code class="lang-rust">embassy::init(
    &amp;clocks,
    esp32c3_hal::timer::TimerGroup::new(peripherals.TIMG0, &amp;clocks).timer0,
);
</code></pre>
<blockquote>
<p><strong><em>📝 Note:</em></strong> <em>At the time of writing this post, I couldn't really locate the</em> <code>init</code> function <a target="_blank" href="http://docs.rs/"><strong>docs.rs</strong></a> documentation. It didn't seem easily accessible through any of the current HAL implementation documentation. Nevertheless, I reached the signature of the function through the source <a target="_blank" href="https://github.com/esp-rs/esp-hal/blob/ece40abaed0e642b751a8752ce6406740efa4af6/esp-hal-common/src/embassy/mod.rs#L100"><strong><em>here</em></strong></a><em>.</em></p>
</blockquote>
<p>3️⃣ <strong>Instantiate and Create Handle for IO</strong>: We need to configure the LED pins as a push-pull output and obtain a handler for the pin so that we can control it. Similarly, we need to obtain a handle for the button input pin. Before we can obtain any handles for the LEDs and the button we need to create an <code>IO</code> struct instance. The <code>IO</code> struct instance provides a HAL-designed struct that gives us access to all gpio pins thus enabling us to create handles for individual pins. This is similar to the concept of a <code>split</code> method used in other HALs (more detail <a target="_blank" href="https://apollolabsblog.hashnode.dev/demystifying-rust-embedded-hal-split-and-constrain-methods"><strong>here</strong></a>). We do this by calling the <code>new()</code> instance method on the <code>IO</code> struct as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
</code></pre>
<p>Note how the <code>new</code> method requires passing the <code>GPIO</code> and <code>IO_MUX</code> peripherals.</p>
<p>4️⃣ <strong>Obtain a handle and configure the input button</strong>: The push button is connected to pin 2 (<code>gpio2</code>) as stated earlier. Additionally, in the pressed state, the button pulls to ground. Consequently, for the button unpressed state, a pull-up resistor needs to be included so the pin goes high. An internal pull-up can be configured for the pin using the <code>into_pull_up_input()</code> method as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> del_but = io.pins.gpio2.into_pull_up_input().degrade();
</code></pre>
<p>Note that as opposed to the LED outputs, the <code>button</code> handle here does not need to be mutable since we will only be reading it. Additionally, here we are using the <code>degrade</code> method which "degrades" the pin type into a generic <code>AnyPin</code> type that is required to pass to the <code>button_press</code> task.</p>
<p>5️⃣ <strong>Obtain handles for the LEDs and configure them to output</strong>: We have 10 LEDs that we need to activate individually. One approach is to configure each separately and activate it separately. However, in order to make things efficient, we can combine the LED pin handles all in one array. This will allow us to iterate over the individual pins using a <code>for</code> loop. However, there is a challenge here. Each pin will have a different type and arrays require that all elements are of a similar type. This is another good example for usage of <code>degrade</code>. Using <code>degrade</code> we can make all the pins of the same <code>AnyPin</code> type. This is how it looks like:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> leds = [
    io.pins.gpio1.into_push_pull_output().degrade(),
    io.pins.gpio10.into_push_pull_output().degrade(),
    io.pins.gpio19.into_push_pull_output().degrade(),
    io.pins.gpio18.into_push_pull_output().degrade(),
    io.pins.gpio4.into_push_pull_output().degrade(),
    io.pins.gpio5.into_push_pull_output().degrade(),
    io.pins.gpio6.into_push_pull_output().degrade(),
    io.pins.gpio7.into_push_pull_output().degrade(),
    io.pins.gpio8.into_push_pull_output().degrade(),
    io.pins.gpio9.into_push_pull_output().degrade(),
];
</code></pre>
<p>3️⃣ <strong>Enable GPIO Interrupts</strong>: At this point, the <code>button</code> instance is just an input pin. In order to make the device respond to push button events, interrupts need to be enabled for <code>button</code>. In the <code>interrupt</code> module in the <a target="_blank" href="https://docs.rs/esp32c3-hal/latest/esp32c3_hal/interrupt/fn.enable.html">esp32c3-hal</a>, there exists an <code>enable</code> method that allows the enabling of different interrupts. The <code>enable</code> method has the following signature:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">enable</span></span>(interrupt: Interrupt, level: Priority) -&gt; <span class="hljs-built_in">Result</span>&lt;(), Error&gt;
</code></pre>
<p><code>interrupt</code> expects an <code>Interrupt</code> type which is an enumeration of all interrupts for the esp32c3. Also level expects a <code>priority</code> which is also an enumeration of all priorites. This results in the following line of code:</p>
<pre><code class="lang-rust">esp32c3_hal::interrupt::enable(
    esp32c3_hal::peripherals::Interrupt::GPIO,
    esp32c3_hal::interrupt::Priority::Priority1,
)
</code></pre>
<p>I chose a priority of one since there are no other interrupts. this would only make a difference when you have several interrupts that might compete for processor time.</p>
<p>4️⃣ <strong>Spawn Button Press Task</strong>: before entering the LED cycling loop, we're going to need to kick off our <code>button_press</code> task. Then <code>button_press</code> task can be kicked off using the <code>spawn</code> method as follows:</p>
<pre><code class="lang-rust">spawner.spawn(press_button(del_but)).unwrap();
</code></pre>
<p>Next, we can move on to the application Loop.</p>
<h4 id="heading-main-task-loop">🔁 Main Task Loop</h4>
<p>Following the design described earlier, in the main task, we will cycle through turning on and off LEDs. Along the way we should <code>await</code> a <code>BLINK_DELAY</code> amount of time before going to the next LED. Since we packaged all LEDs in an array this is done in a <code>for</code> loop as follows:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">loop</span> {
    <span class="hljs-keyword">for</span> led <span class="hljs-keyword">in</span> &amp;<span class="hljs-keyword">mut</span> leds {
        led.set_high().unwrap();
        Timer::after(Duration::from_millis(BLINK_DELAY.load(Ordering::Relaxed) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u64</span>)).<span class="hljs-keyword">await</span>;
        led.set_low().unwrap();
        Timer::after(Duration::from_millis(<span class="hljs-number">100</span>)).<span class="hljs-keyword">await</span>;
    }
}
</code></pre>
<p>Note the usage of <code>Timer</code> that comes from the <a target="_blank" href="https://docs.rs/embassy-time/0.1.0/embassy_time/struct.Timer.html"><code>embassy_time</code> crate</a>. <code>after</code> is a <code>Timer</code> instance method that accepts a <code>Duration</code> and returns a <code>Future</code>. As such, <code>await</code> allows us to yield execution to the executor such that the task can be polled later to check if the delay expired. Note that we are delaying <code>BLINK_DELAY</code> amount of delay after setting the LED to high. This is the shared value that is adjusted by the <code>button_press</code> task.</p>
<p>This concludes the code for the full application.</p>
<h2 id="heading-full-application-code">📱 Full Application Code</h2>
<p>Here is the full code for the implementation described in this post. You can additionally find the full project and others available on the <a target="_blank" href="https://github.com/apollolabsdev/ESP32C3"><strong>apollolabs ESP32C3</strong></a> git repo. Also, the Wokwi project can be accessed <a target="_blank" href="https://wokwi.com/projects/382346257842181121"><strong>here</strong></a>.</p>
<pre><code class="lang-rust"><span class="hljs-meta">#![no_std]</span>
<span class="hljs-meta">#![no_main]</span>
<span class="hljs-meta">#![feature(type_alias_impl_trait)]</span>

<span class="hljs-keyword">use</span> core::sync::atomic::Ordering;
<span class="hljs-keyword">use</span> portable_atomic::AtomicU32;
<span class="hljs-keyword">use</span> embassy_executor::Spawner;
<span class="hljs-keyword">use</span> embassy_time::{Duration, Timer};
<span class="hljs-keyword">use</span> embedded_hal_async::digital::Wait;
<span class="hljs-keyword">use</span> esp32c3_hal::{clock::ClockControl, embassy, peripherals::Peripherals, prelude::*, IO};
<span class="hljs-keyword">use</span> esp32c3_hal::gpio::{AnyPin, Input, PullUp};
<span class="hljs-keyword">use</span> esp_backtrace <span class="hljs-keyword">as</span> _;

<span class="hljs-comment">// Global Variable to Control LED Rotation Speed</span>
<span class="hljs-keyword">static</span> BLINK_DELAY: AtomicU32 = AtomicU32::new(<span class="hljs-number">200_u32</span>);

<span class="hljs-meta">#[main]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>(spawner: Spawner) {
    <span class="hljs-comment">// Take Peripherals</span>
    <span class="hljs-keyword">let</span> peripherals = Peripherals::take();
    <span class="hljs-keyword">let</span> system = peripherals.SYSTEM.split();
    <span class="hljs-keyword">let</span> clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    <span class="hljs-comment">// Initilize Embassy Timers</span>
    embassy::init(
        &amp;clocks,
        esp32c3_hal::timer::TimerGroup::new(peripherals.TIMG0, &amp;clocks).timer0,
    );

    <span class="hljs-comment">// Acquire Handle to IO</span>
    <span class="hljs-keyword">let</span> io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
    <span class="hljs-comment">// Configure Delay Button to Pull Up input</span>
    <span class="hljs-keyword">let</span> del_but = io.pins.gpio2.into_pull_up_input().degrade();
    <span class="hljs-comment">// Configure LED Array Pins to Output &amp; Store in Array</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> leds = [
        io.pins.gpio1.into_push_pull_output().degrade(),
        io.pins.gpio10.into_push_pull_output().degrade(),
        io.pins.gpio19.into_push_pull_output().degrade(),
        io.pins.gpio18.into_push_pull_output().degrade(),
        io.pins.gpio4.into_push_pull_output().degrade(),
        io.pins.gpio5.into_push_pull_output().degrade(),
        io.pins.gpio6.into_push_pull_output().degrade(),
        io.pins.gpio7.into_push_pull_output().degrade(),
        io.pins.gpio8.into_push_pull_output().degrade(),
        io.pins.gpio9.into_push_pull_output().degrade(),
    ];
    <span class="hljs-comment">// Enable GPIO Interrupts</span>
    esp32c3_hal::interrupt::enable(
        esp32c3_hal::peripherals::Interrupt::GPIO,
        esp32c3_hal::interrupt::Priority::Priority1,
    )
    .unwrap();
    <span class="hljs-comment">// Spawn Button Press Task</span>
    spawner.spawn(press_button(del_but)).unwrap();

    <span class="hljs-comment">// This line is for Wokwi only so that the console output is formatted correctly</span>
    esp_println::<span class="hljs-built_in">print!</span>(<span class="hljs-string">"\x1b[20h"</span>);

    <span class="hljs-comment">// Enter Application Loop Blinking on LED at a Time</span>
    <span class="hljs-keyword">loop</span> {
        <span class="hljs-keyword">for</span> led <span class="hljs-keyword">in</span> &amp;<span class="hljs-keyword">mut</span> leds {
            led.set_high().unwrap();
            Timer::after(Duration::from_millis(BLINK_DELAY.load(Ordering::Relaxed) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u64</span>)).<span class="hljs-keyword">await</span>;
            led.set_low().unwrap();
            Timer::after(Duration::from_millis(<span class="hljs-number">100</span>)).<span class="hljs-keyword">await</span>;
        }
    }
}


<span class="hljs-meta">#[embassy_executor::task]</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">press_button</span></span>(<span class="hljs-keyword">mut</span> button: AnyPin&lt;Input&lt;PullUp&gt;&gt;) {
    <span class="hljs-keyword">loop</span> {
      <span class="hljs-comment">// Wait for Button Press</span>
      button.wait_for_rising_edge().<span class="hljs-keyword">await</span>.unwrap();
      esp_println:: <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Button Pressed!"</span>);
      <span class="hljs-comment">// Retrieve Delay Global Variable</span>
      <span class="hljs-keyword">let</span> del = BLINK_DELAY.load(Ordering::Relaxed);
      <span class="hljs-comment">// Adjust Delay Accordingly</span>
      <span class="hljs-keyword">if</span> del &lt;= <span class="hljs-number">50_u32</span> {
        BLINK_DELAY.store(<span class="hljs-number">200_u32</span>,Ordering::Relaxed);
        esp_println:: <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Delay is now 200ms"</span>);
      } <span class="hljs-keyword">else</span> {
        BLINK_DELAY.store(del - <span class="hljs-number">50_u32</span>,Ordering::Relaxed);
        esp_println:: <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Delay is now {}ms"</span>, del - <span class="hljs-number">50_u32</span>);
      } 
    }
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this post, an LED control application was created leveraging the GPIO peripheral for the ESP32C3 microcontroller. The code was created using interrupts and the embassy async framework. It shows how embassy really simplifies the development of interrupt-based code. Have any questions? Share your thoughts in the comments below 👇.</p>
<div class="hn-embed-widget" id="subend"></div>]]></content:encoded></item></channel></rss>