<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ChristopherDebray</title>
    <description>The latest articles on DEV Community by ChristopherDebray (@christopherdebray).</description>
    <link>https://dev.to/christopherdebray</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3660224%2F7ae36460-ccf6-4944-aa52-135e088e7f7b.png</url>
      <title>DEV Community: ChristopherDebray</title>
      <link>https://dev.to/christopherdebray</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/christopherdebray"/>
    <language>en</language>
    <item>
      <title>Building a Modular Starter Kit for M5StickC-Plus2: From Messy Code to Clean Architecture</title>
      <dc:creator>ChristopherDebray</dc:creator>
      <pubDate>Sun, 14 Dec 2025 21:35:23 +0000</pubDate>
      <link>https://dev.to/christopherdebray/building-a-modular-starter-kit-for-m5stickc-plus2-from-messy-code-to-clean-architecture-1mb0</link>
      <guid>https://dev.to/christopherdebray/building-a-modular-starter-kit-for-m5stickc-plus2-from-messy-code-to-clean-architecture-1mb0</guid>
      <description>&lt;h2&gt;
  
  
  Why Another M5Stack Project?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;When I first got my M5StickC-Plus2, I was excited to build something cool. But like many developers, I quickly hit a wall of... boring setup work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You know the drill: configuring buttons, managing display coordinates, handling menus, dealing with power management, setting up timers. Before I could even start on the fun part of my project, I had to write hundreds of lines of infrastructure code.&lt;/p&gt;

&lt;p&gt;Libraries help, but they come with their own problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Black boxes: You can't see or modify how they work internally&lt;/li&gt;
&lt;li&gt;Over-abstraction: Sometimes you need fine-grained control&lt;/li&gt;
&lt;li&gt;Learning curve: Each library has its own API to learn&lt;/li&gt;
&lt;li&gt;Dependencies: One library pulls in five others&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted something different: &lt;strong&gt;a starter kit where you own all the code&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Philosophy: A Foundation, Not a Framework
&lt;/h2&gt;

&lt;p&gt;This project isn't a library you import. It's a &lt;strong&gt;starting point you customize&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of it like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Library: "Here's a menu system, use these methods"&lt;/li&gt;
&lt;li&gt;This starter: "Here's how I built a menu system, change whatever you want"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Full source code you can read and understand&lt;/li&gt;
&lt;li&gt;✅ Working examples you can modify&lt;/li&gt;
&lt;li&gt;✅ Architectural patterns you can extend&lt;/li&gt;
&lt;li&gt;✅ No hidden dependencies or magic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don't like how the menu scrolling works? Change it. Want different colors? Modify the display handler. Need a different button layout? Update the controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Journey: From Arduino IDE to PlatformIO
&lt;/h2&gt;

&lt;p&gt;I started this project in Arduino IDE (as many do), but quickly switched to &lt;strong&gt;VSCode + PlatformIO&lt;/strong&gt;. Here's why:&lt;/p&gt;

&lt;h3&gt;
  
  
  Arduino IDE Pain Points
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Where is this function defined?&lt;/span&gt;
&lt;span class="c1"&gt;// Which library does this come from?&lt;/span&gt;
&lt;span class="c1"&gt;// Good luck finding it...&lt;/span&gt;
&lt;span class="n"&gt;M5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setCursor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
   PlatformIO Wins
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IntelliSense&lt;/strong&gt;: Auto-completion that actually works&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go to Definition&lt;/strong&gt;: Jump to any function's source&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project Structure&lt;/strong&gt;: Proper file organization (the biggest issue for me)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Library Management&lt;/strong&gt;: Clear dependency handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern C++&lt;/strong&gt;: Full C++11/14/17 support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The switch took an hour. It saved me dozens of hours afterward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Architecture Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
   1. The Page System: Lifecycle Management
&lt;/h3&gt;

&lt;p&gt;Early on, I realized I needed multiple "screens" or "pages". But switching between them was messy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ The messy way&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;drawClock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;drawMenu&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;drawSettings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I needed a proper lifecycle. Enter the &lt;strong&gt;PageManager&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PageBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// Called when entering page&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// Called every frame&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Called when leaving page&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now each page manages itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;ClockPage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;clearScreen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;clockHandler&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;drawClock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;ClockPage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hasActiveMenu&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Pause if menu is open&lt;/span&gt;
    &lt;span class="c1"&gt;// Update clock every second&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson learned&lt;/strong&gt;: Give each component its own lifecycle. Don't manage everything from main().&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Menu Stack: Nested Menus Done Right
&lt;/h3&gt;

&lt;p&gt;Menus were surprisingly hard. I wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A main menu&lt;/li&gt;
&lt;li&gt;Submenus (Settings → Display Settings → Brightness)&lt;/li&gt;
&lt;li&gt;A "back" button that works correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The solution? A &lt;strong&gt;stack&lt;/strong&gt; (Last In, First Out):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MenuManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;private:&lt;/span&gt;
    &lt;span class="n"&gt;MenuHandler&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;menuStack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MAX_MENU_STACK&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;stackSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;pushMenu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MenuHandler&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;menuStack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stackSize&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;menu&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;popMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stackSize&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stackSize&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;menuStack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stackSize&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// Redraw previous menu&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now submenus just work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Clock Page
  → Open Menu
    → Settings
      → Display
        → [Back]
      → [Back]
    → [Back]
  Clock Page (restored)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson learned&lt;/strong&gt;: Choose the right data structure. A stack naturally handles nested navigation.&lt;/p&gt;

&lt;h3&gt;
  
  
   3. The Pointer Function vs std::function Saga
&lt;/h3&gt;

&lt;p&gt;This was a &lt;strong&gt;4-hour debugging session&lt;/strong&gt; that taught me a crucial C++ lesson.&lt;/p&gt;

&lt;p&gt;I wanted menu callbacks that could access class members:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// I wanted to do this:&lt;/span&gt;
&lt;span class="n"&gt;mainMenu&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Start Timer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;clockHandler&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;startTimer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// Access class member&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But I got errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error: no suitable conversion from lambda to void (*)()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem? My MenuItem struct used old C-style function pointers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Old way (C-style)&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;MenuItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;  &lt;span class="c1"&gt;// Can't capture 'this'!&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix? Modern C++ std::function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ New way (C++11)&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;MenuItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Can capture anything!&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;mainMenu&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Settings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;openSettingsSubmenu&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// 'this' captured, works perfectly&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson learned&lt;/strong&gt;: Use std::function for callbacks in modern C++. It's more flexible and handles lambdas with captures.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Display Positioning: No More Magic Numbers
&lt;/h3&gt;

&lt;p&gt;Early code looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ What does this even mean?&lt;/span&gt;
&lt;span class="n"&gt;M5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setCursor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;M5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setTextSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;M5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created the &lt;strong&gt;DisplayHandler&lt;/strong&gt; to abstract positions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Semantic and clear&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;displayMainTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;displaySubtitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Subtitle"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;displayStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ready"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MSG_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behind the scenes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;displayMainTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MessageType&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;M5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setTextSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SIZE_TITLE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Consistent size&lt;/span&gt;
    &lt;span class="n"&gt;M5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setTextColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getColorForType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SCREEN_WIDTH&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;textWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Auto-center&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ZONE_CENTER_Y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;M5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setCursor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;M5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson learned&lt;/strong&gt;: Abstract low-level details. Your future self will thank you.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Deep Sleep: The 3-Hour Power Management Bug
&lt;/h3&gt;

&lt;p&gt;The M5StickC-Plus2 has great battery life... if you use deep sleep correctly.&lt;/p&gt;

&lt;p&gt;My first attempt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ This crashes the device on wake-up&lt;/span&gt;
&lt;span class="n"&gt;esp_deep_sleep_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After diving into documentation and forums, I found the issue: &lt;strong&gt;GPIO4 must stay HIGH during sleep&lt;/strong&gt; or the device loses power.&lt;/p&gt;

&lt;p&gt;The working solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;M5deepSleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt; &lt;span class="n"&gt;microseconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// CRITICAL: Keep power pin high&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;gpio_hold_en&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GPIO_NUM_4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;gpio_deep_sleep_hold_en&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;esp_sleep_enable_timer_wakeup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;microseconds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;esp_deep_sleep_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This powers my Pomodoro timer: 25 minutes of sleep, wake up, beep alarm, show clock.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson learned&lt;/strong&gt;: Hardware-specific quirks require hardware-specific solutions. Don't always assume standard APIs work out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Button Controls: Finding the Ergonomic Sweet Spot
&lt;/h2&gt;

&lt;p&gt;The M5StickC-Plus2 has three buttons:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      ______PWR          (side)
                    A    (front)
      ___B_____          (side, opposite)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After testing different layouts, I settled on:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No menu active&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PWR: Change page&lt;/li&gt;
&lt;li&gt;A: Open menu&lt;/li&gt;
&lt;li&gt;B: Page-specific action (e.g., start timer on double click)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Menu active&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PWR: Navigate down&lt;/li&gt;
&lt;li&gt;A: Select item&lt;/li&gt;
&lt;li&gt;B (short): Navigate up&lt;/li&gt;
&lt;li&gt;B (long hold): Close menu&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why this layout?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Side buttons for navigation&lt;/strong&gt;: Easier to press while holding device&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Center button for actions&lt;/strong&gt;: Most important button in prime position&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long press for "back"&lt;/strong&gt;: Prevents accidental exits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lesson learned&lt;/strong&gt;: Button ergonomics matter. Test on actual hardware, not just in your head.&lt;/p&gt;

&lt;h2&gt;
  
  
   The Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform&lt;/strong&gt;: M5StickC-Plus2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IDE&lt;/strong&gt;: VSCode + PlatformIO&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language&lt;/strong&gt;: C++ (C++11 features)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Libraries&lt;/strong&gt;: M5Unified&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture&lt;/strong&gt;: OOP with composition pattern
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lib/
├── display_handler.h      # Display abstraction
├── menu_handler.h         # Individual menu logic
├── menu_manager.h         # Menu stack
├── page_manager.h         # Page lifecycle
├── clock_handler.h        # Time &amp;amp; timers
├── battery_handler.h      # Power management
└── pages/
    ├── page_base.h        # Abstract base class
    └── clock_page.h       # Default clock page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Start with std::function
&lt;/h3&gt;

&lt;p&gt;Don't use C-style function pointers for callbacks. Go straight to std::function.&lt;br&gt;
Althought it is more consuming than the pointers, but i didn’t have time to figure a better option (for now)&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Test Deep Sleep Early
&lt;/h3&gt;

&lt;p&gt;Don't wait until the end to test power management. It's hardware-dependent and can break everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Design Button Layout on Paper
&lt;/h3&gt;

&lt;p&gt;Sketch the button layout before writing code. Changing it later affects everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Use PlatformIO from Day 1
&lt;/h3&gt;

&lt;p&gt;Don't start in Arduino IDE. The migration takes time and breaks things.&lt;/p&gt;

&lt;h2&gt;
  
  
   What Worked Really Well
&lt;/h2&gt;

&lt;h3&gt;
  
  
   1. Composition Over Inheritance
&lt;/h3&gt;

&lt;p&gt;Every page gets a &lt;code&gt;DisplayHandler*&lt;/code&gt; and &lt;code&gt;MenuManager*&lt;/code&gt;. They don't inherit display logic—they compose it.&lt;/p&gt;

&lt;h3&gt;
  
  
   2. Clear Separation of Concerns
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;*_handler.h&lt;/code&gt;: Focused, reusable components&lt;/p&gt;

&lt;p&gt;&lt;code&gt;*_manager.h&lt;/code&gt;: Complex orchestration&lt;/p&gt;

&lt;p&gt;&lt;code&gt;*_utils.h&lt;/code&gt;: Utility functions&lt;/p&gt;

&lt;h3&gt;
  
  
   3. Lambda Callbacks
&lt;/h3&gt;

&lt;p&gt;Being able to write &lt;code&gt;[this]() { myMethod(); }&lt;/code&gt; inline makes code so much cleaner than separate callback functions.&lt;/p&gt;

&lt;h3&gt;
  
  
   4. The Base Page Pattern
&lt;/h3&gt;

&lt;p&gt;Every page inherits from &lt;code&gt;PageBase&lt;/code&gt;, which provides menu management for free. No duplicate code.&lt;/p&gt;

&lt;h2&gt;
  
  
   Try It Yourself
&lt;/h2&gt;

&lt;p&gt;The complete starter kit is &lt;a href="https://github.com/ChristopherDebray/M5StickC-Plus2-starter" rel="noopener noreferrer"&gt;&lt;strong&gt;available on GitHub&lt;/strong&gt;&lt;/a&gt;. Clone it, upload to your M5StickC-Plus2, and you'll have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ A working clock page&lt;/li&gt;
&lt;li&gt;✅ Battery indicator&lt;/li&gt;
&lt;li&gt;✅ Menu system with submenus&lt;/li&gt;
&lt;li&gt;✅ Page navigation&lt;/li&gt;
&lt;li&gt;✅ Pomodoro timer&lt;/li&gt;
&lt;li&gt;✅ All the code to modify&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Want to build a fitness tracker? Keep the page system, replace the clock logic.&lt;/p&gt;

&lt;p&gt;Building a game? Use the menu system for your settings, swap in your game loop.&lt;/p&gt;

&lt;p&gt;Creating an IoT dashboard? The display handler abstracts all the positioning for you.&lt;/p&gt;

&lt;h2&gt;
  
  
   Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Building this starter kit taught me that &lt;strong&gt;good architecture is invisible&lt;/strong&gt;. When it works, you don't think about pages or menus—you just build features.&lt;/p&gt;

&lt;p&gt;That's the goal: give you the boring stuff so you can focus on the interesting stuff.&lt;/p&gt;

&lt;p&gt;The M5StickC-Plus2 is a fantastic device. With the right foundation, you can build something amazing in a weekend instead of spending that weekend setting up infrastructure.&lt;/p&gt;

&lt;p&gt;Now go build something cool. 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/m5stack/M5Unified" rel="noopener noreferrer"&gt;M5Unified&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>arduino</category>
      <category>iot</category>
      <category>developer</category>
      <category>cpp</category>
    </item>
  </channel>
</rss>
