<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:webfeeds="http://webfeeds.org/rss/1.0">
  <title>Nozomi&#39;s Blog</title>
  <icon>https://cdn.jsdelivr.net/gh/hayschan/oss@main/2021/10/apple-touch-icon-swYdRb.png</icon>
  <subtitle>这是 Nozomi 的个人博客，aka Hays Chan、陈希，一名计算机学生，在这里分享生活、科技、编程。</subtitle>
  <link href="https://blog.haysc.tech/feed.xml" rel="self"/>
  <link href="https://pubsubhubbub.appspot.com/" rel="hub"/>
  <link href="https://blog.haysc.tech/"/>
  <updated>2024-12-06T11:11:18.190Z</updated>
  <id>https://blog.haysc.tech/</id>
  
  <author>
    <name>Nozomi</name>
    <email>me@haysc.tech</email>
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>

 <webfeeds:cover image="https://cdn.jsdelivr.net/gh/hayschan/static@master/2020/12/gFn7zm.JPG" />
 <webfeeds:icon>https://cdn.jsdelivr.net/gh/hayschan/static@master/2020/12/VAlBPs.png</webfeeds:icon>
 <webfeeds:logo>https://cdn.jsdelivr.net/gh/hayschan/static@master/2020/12/XDzm9G.svg</webfeeds:logo>
 <webfeeds:accentColor>0abab5</webfeeds:accentColor>
 <webfeeds:related layout="card" target="browser"/>

  
  <entry>
    <title>Solving Incorrect Time Display on Meshtastic Devices</title>
    <link href="https://blog.haysc.tech/meshtastic-time-zone-problem/"/>
    <id>https://blog.haysc.tech/meshtastic-time-zone-problem/</id>
    <published>2024-12-06T11:09:28.000Z</published>
    <updated>2024-12-06T11:11:18.190Z</updated>
    
    <content type="html"><![CDATA[<p>If you’re using a Meshtastic device and notice that the time is always incorrect, particularly where the minutes and seconds are correct, but the hour value is wrong, this is likely a timezone issue. This problem can be frustrating, especially if accurate time synchronization is critical for your application.</p><p>After an investigation, it was determined that this issue stems from how Meshtastic handles timezone settings, and it specifically requires a <strong>POSIX timezone format</strong>.</p><p>The good news is that you can fix this by modifying the timezone settings appropriately. Below is a detailed guide on how to troubleshoot and solve this issue.</p><h2 id="Investigation-Summary">Investigation Summary</h2><p>Upon noticing the incorrect time on your Meshtastic device, you might initially assume that it’s just a simple time zone misconfiguration. The typical steps you might try include setting or resetting the timezone. However, if you do this and the problem persists—i.e., the minutes and seconds remain accurate, but the hour is still wrong—then it’s likely that the timezone string is not being properly interpreted by the Meshtastic firmware.</p><p>After further research, you will discover that <strong>Meshtastic requires the timezone to be in a POSIX format</strong>. This format differs from the typical timezone names like “Asia/Hong_Kong” or “Asia/Shanghai” that you might be used to seeing.</p><h2 id="Steps-to-Resolve-the-Problem">Steps to Resolve the Problem</h2><p>Here is a step-by-step approach you can use to correct the time on your Meshtastic device:</p><ol><li><p><strong>Identify Your Timezone</strong></p><ul><li>Meshtastic uses the POSIX timezone format to set the correct local time. To resolve the time issue, the first thing you need to do is identify the correct POSIX timezone format for your location.</li><li>Fortunately, there is a helpful resource that contains the required POSIX timezone strings: <a href="https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv">POSIX Timezone Database CSV File</a>.</li><li>Open the link above and search for your local timezone. For example, if you are in <strong>Hong Kong</strong>, you will find the corresponding timezone is <code>HKT-8</code>. If you are in <strong>Shanghai</strong>, you will use <code>CST-8</code>.</li></ul></li><li><p><strong>Access Your Meshtastic Device Configuration</strong></p><ul><li>To modify the timezone setting, you need to connect to your Meshtastic device. You can use the <strong>Meshtastic app</strong> (on mobile or desktop) or <strong>Meshtastic CLI</strong> if you prefer command-line access.</li><li>If you are using the <strong>Meshtastic CLI</strong>, you can connect to the device via USB and execute commands. Make sure you have the CLI installed, and connect to your device by using the appropriate serial port.</li></ul></li><li><p><strong>Set the Correct Timezone</strong></p><ul><li>Most users will use the <strong>Meshtastic app</strong> to set the timezone. In the app, you can find the timezone settings under <strong>Settings -&gt; Device -&gt; Time Zone</strong>.</li><li>Alternatively, if you are using the <strong>Meshtastic CLI</strong>, use the following command to set the timezone: Use the following command to set the timezone on your device. The command is slightly different based on the interface you use:<ul><li><strong>Using Meshtastic CLI</strong>: Run the command below to update the timezone:<pre><code class="hljs plain">meshtastic --set time.zone &quot;HKT-8&quot;</code></pre>This command sets the device’s timezone to <strong>Hong Kong Time</strong> (HKT) with an offset of <strong>-8 hours</strong>.</li><li>Alternatively, if you are in Shanghai, you can set the timezone to CST by running:<pre><code class="hljs plain">meshtastic --set time.zone &quot;CST-8&quot;</code></pre>Note that the POSIX format may look different from typical time zone names, and it’s important to use the correct format.</li></ul></li></ul></li><li><p><strong>Reboot the Device</strong></p><ul><li>After changing the timezone setting in the app, the module itself does <strong>not reboot automatically</strong>. You will need to manually reboot the device to apply the new timezone setting.</li></ul></li><li><p><strong>Verify the Change</strong></p><ul><li>After setting the new timezone and rebooting the device, verify the change by checking the display on your Meshtastic device. You can either look at the OLED screen directly or use the CLI to query the device configuration.</li><li>For verification via CLI, run:<pre><code class="hljs plain">meshtastic --get time</code></pre>This command will return the current device time, and you should now see the correct local hour displayed along with correct minutes and seconds.</li><li>Alternatively, you can use the <strong>USER button</strong> (also known as the <strong>BOOT button</strong>) on the device to navigate to the last page of the screens. This screen displays the <strong>current time</strong>, <strong>battery voltage level</strong>, <strong>Channel Utilization</strong>, and <strong>GPS info</strong>.</li></ul></li></ol><h2 id="Example">Example</h2><p>For a concrete example, consider the time discrepancy you observed in the images you shared:</p><ul><li>The <strong>minutes and seconds were correct</strong>, but the <strong>hour was consistently off</strong>. Initially, you tried to set the timezone using general formats like <code>Asia/Hong_Kong</code>, but the incorrect hour persisted.</li><li>After consulting the <strong>POSIX timezone database</strong>, you updated the timezone to <code>HKT-8</code> for Hong Kong or <code>CST-8</code> for Shanghai. These timezone strings worked correctly, and the displayed time was then synchronized properly.</li></ul><h2 id="Additional-Notes">Additional Notes</h2><ul><li><strong>Why POSIX Format?</strong>: The POSIX format is widely used in embedded systems because it allows for precise control over timezone offsets, daylight savings, and other factors. Meshtastic’s firmware expects the timezone to be given in this specific format, which is why other typical formats may not work correctly.</li><li><strong>Common Pitfalls</strong>: Ensure you are entering the timezone string correctly, as even minor deviations can lead to persistent errors. For instance, using <code>HKT-8</code> instead of <code>Asia/Hong_Kong</code> is crucial.</li><li><strong>Testing</strong>: After setting the timezone, consider testing your configuration over a period of time, especially if you need consistent synchronization with other devices.</li></ul><h2 id="Conclusion">Conclusion</h2><p>The incorrect time display issue on Meshtastic devices is a common hurdle and is primarily due to incorrect timezone configuration. By setting the timezone in the proper <strong>POSIX format</strong>, as detailed above, you can ensure that the hour, minutes, and seconds display accurately.</p><p>Referencing the <a href="https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv">POSIX Timezone CSV File</a> is an important step in finding the correct timezone format for your specific region.</p><p>By following these steps, including <strong>manually rebooting the device</strong> after setting the timezone, you should be able to correct the time display issue and have your Meshtastic device displaying the correct local time.</p>]]></content>
    
    
    <summary type="html">A detailed guide to solving the incorrect time display issue on Meshtastic devices by properly configuring the POSIX timezone</summary>
    
    
    
    <category term="技术" scheme="https://blog.haysc.tech/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="Meshtastic" scheme="https://blog.haysc.tech/tags/Meshtastic/"/>
    
    <category term="time" scheme="https://blog.haysc.tech/tags/time/"/>
    
  </entry>
  
  <entry>
    <title>从道光年的徐继畬，到今天的我</title>
    <link href="https://blog.haysc.tech/%E4%BB%8E%E5%BE%90%E7%BB%A7%E7%95%AC%E5%88%B0%E6%88%91%E7%9A%84%E5%8F%8D%E6%80%9D/"/>
    <id>https://blog.haysc.tech/%E4%BB%8E%E5%BE%90%E7%BB%A7%E7%95%AC%E5%88%B0%E6%88%91%E7%9A%84%E5%8F%8D%E6%80%9D/</id>
    <published>2024-11-16T17:00:07.000Z</published>
    <updated>2024-12-06T11:11:19.490Z</updated>
    
    <content type="html"><![CDATA[<p>翻开《瀛寰志略》，那段关于英国议会制度的描述映入眼帘：「都城有公会所，内分两所，一曰爵房，一曰乡绅房……」这些简练的文字，如同一幅素描，用极少的笔触勾勒出一个陌生的世界。让我惊讶的，不只是文字本身的清晰与精确，而是它所呈现出的视野——一个生活在道光年间的清朝官员，如何在那个信息闭塞的时代，洞悉这些遥远制度的精髓？</p><p>徐继畬的书写没有附和同时代传统士大夫「天朝上国」的嘴硬。他在书中提到，英国议会两院制度中，「乡绅房」由民选代表组成，而增减税赋这样的重大决策，必须经过两院逐层审议。这种将权力交付于制度的设计，对道光年间的他而言无疑是前所未见的。而在谈到美国，他用朴素而精准的语言概括出联邦制的核心：「仍各部之旧，分建为国，每国政统领一，副统领佐之……四年为任满，集部众议，众皆曰贤，则再留四年。」在天朝上国朝贡体系的年代，他已看到了权力被规范、轮替与制约的深意。</p><p>这种视野，让他看穿了表面的「船坚炮利」。他隐隐暗示，大清不应只关注科技或武器，而在于社会制度的设计和执行。这种见解，如今读来依然振聋发聩。</p><p>可是，他的生平，却与这样的大视野形成了戏剧性的反差。鸦片战争之后，他在福州处理外国人的纠纷，因与林则徐等意见相左，屡遭弹劾，最后被革职返乡。彼时，他甚至连巡抚的任职都未到，却因神光寺的冲突，被定性为「不善抚民」，几乎断送了仕途。他不是未曾试图改变现状的空想家，而是一个在现实挫败中依然选择表达的人。书写《瀛寰志略》的徐继畬，是被困在山西乡野中的一个被罢官的老御史，他在纸上遥望的世界，显得与他的处境格格不入。</p><p>可正是这份「格格不入」，让他的声音在同时代里显得如此稀有。梁启超在《三十自述》中提到，正是因为在书肆中购得一本《瀛寰志略》，他才第一次意识到「地球上竟有五大洲之别，各国互异」。此书对明治维新有所启发，而对洋务运动和戊戌变法则产生了直接而深远的影响。甚至在美国华盛顿纪念碑上，那块唯一含有中文的石碑上，也刻着他对华盛顿的评价：「不僭位号，不传子孙，而创为推举之法，几于天下为公。」他不仅看到了当时西方的强大，更看到这强大的根源——在于制度中蕴藏的「天下为公」。</p><p>这让我深感震撼。徐继畬用一部书跨越了他的时代，而我呢？日复一日地在工作中奔忙，似乎努力着，却说不清这些努力的意义何在。与他相比，我的日常显得如此微小，甚至卑微。我很难想象，自己的这些琐碎忙碌，有没有可能像他一样，穿越时间，留下一点点痕迹，哪怕只是微小的启发。</p><p>在被罢官的清朝乡野，他将目光投向全球，将一些遥远国家的地理和制度介绍给未曾听闻的中国士人。他没有依附现实的荣辱得失，而是选择为未来书写。如果没有这些记录，梁启超或许仍然困于传统士大夫的偏见，日本的知识分子也可能少了一份启发，而我，也不会在百余年后读到他的书，开始重新思考自己。</p><p>我深知，自己无法像他一样留下影响世界的文字，但他至少提醒了我：我的时间是否正用在那些真正重要的事情上？我是否在对未来的某个「他们」说话，而不是只活在当下的局促里？</p><p>徐继畬在荒野中以文字开疆，而我呢？今天的反思，不仅是对他的敬意，也是一次与自己的对话。或许我需要的，不是更大的目标，而是更有意义的选择。</p><p>印度尼西亚雅加达<br>2024 年 11 月 17 日</p>]]></content>
    
    
    <summary type="html">起事勇于胜广，割据雄于曹刘，既已提三尺剑，开疆万里，乃不僭位号，不传子孙，而创为推举之法，几于天下为公，骎骎乎三代之遗意</summary>
    
    
    
    <category term="随笔" scheme="https://blog.haysc.tech/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="历史" scheme="https://blog.haysc.tech/tags/%E5%8E%86%E5%8F%B2/"/>
    
  </entry>
  
  <entry>
    <title>Validating MAC Addresses with Vue and Vuetify</title>
    <link href="https://blog.haysc.tech/vuetify-mac-address-validation/"/>
    <id>https://blog.haysc.tech/vuetify-mac-address-validation/</id>
    <published>2024-06-27T03:59:57.000Z</published>
    <updated>2024-12-06T11:11:19.490Z</updated>
    
    <content type="html"><![CDATA[<p>I have been searching for a guide on how to validate MAC addresses with Vue and Vuetify and came up empty. So, I decided to put together something myself to fill that gap.</p><p>In this post, I’ll walk through how to set up a validation rule in Vue that makes sure your MAC addresses are not just any random string, but actually follow the correct format, such as:</p><ul><li><code>XX:XX:XX:XX:XX:XX</code> e.g. <code>00:1A:2B:3C:4D:5E</code></li><li><code>XX-XX-XX-XX-XX-XX</code> e.g. <code>00-1A-2B-3C-4D-5E</code></li></ul><p>The validation rule uses a regular expression (regex) to check if the input value is a valid MAC address.</p><pre><code class="hljs js">rules: &#123;    macAddress: <span class="hljs-function"><span class="hljs-params">value</span> =&gt;</span> &#123;      <span class="hljs-keyword">const</span> pattern = <span class="hljs-regexp">/^([0-9A-Fa-f]&#123;2&#125;[:-])&#123;5&#125;([0-9A-Fa-f]&#123;2&#125;)$/</span>      <span class="hljs-keyword">return</span> pattern.test(value) || <span class="hljs-string">&#x27;Invalid MAC address.&#x27;</span>    &#125;,&#125;,</code></pre><p>This rule checks if the input value is a valid MAC address. The pattern used is <code>/^([0-9A-Fa-f]&#123;2&#125;[:-])&#123;5&#125;([0-9A-Fa-f]&#123;2&#125;)$/</code>, which matches the following format: either <code>XX:XX:XX:XX:XX:XX</code> or <code>XX-XX-XX-XX-XX-XX</code>, where <code>X</code> is a hexadecimal digit. If the input does not match this format, the validation will fail, displaying the error message <code>'Invalid MAC address.'</code>.</p><p>To enforce the format to use <code>:</code> as the separator, you can modify the pattern to the following. Notice the minor change from <code>[:-]</code> to <code>:</code>:</p><pre><code class="hljs js">rules: &#123;    macAddress: <span class="hljs-function"><span class="hljs-params">value</span> =&gt;</span> &#123;      <span class="hljs-keyword">const</span> pattern = <span class="hljs-regexp">/^([0-9A-Fa-f]&#123;2&#125;:)&#123;5&#125;([0-9A-Fa-f]&#123;2&#125;)$/</span>      <span class="hljs-keyword">return</span> pattern.test(value) || <span class="hljs-string">&#x27;Invalid MAC address. Please use the format XX:XX:XX:XX:XX:XX&#x27;</span>    &#125;,&#125;,</code></pre><p>This updated pattern ensures that the MAC address is in the format <code>XX:XX:XX:XX:XX:XX</code>.</p><p>I have created a <a href="https://play.vuetifyjs.com/#eNrNU21r2zAQ/isXMUgCke1lLDDhBLKOQT/syz4V4rRR4wsYZNmV5LQl9X+vLLu1XCilFErBNvfcy3N3D+fNiWi1D9dlGRwrJIzEBvNScIOrRALER3ooVO5sh/aFNDyTqDqXc6ri9hlax4hS+Lc+g3WaKtQazmVZGaDUz2mYBNhXLxPyfZ4Q0Lm1FgnxslyewTtDDxmK1A8AHGlepChsUc73XauEDHOYqgQ2HTbOCBTeVJnCdAYt7iuvuBDbl+WCX7sG/jKZhN3FBRs+OyhU46bDZzckXMWhv44vhw1YLaznTRkTwqxYXIPGkitubONCivuvLq8dpZBXzaQfUtnWv1/THveH2kb9W2483a3HofcTWKj3KiuNy8K7slAGUjzwShg4tWwpN3wyfUIACk2lZI8BeikYjKOI/o7on4guftCzOZ0vxrM+00nH/NqGrxWWwZGLCmG5gtGoNR8eYPy/Cwc+j9+zuW6vdsAN9kykNlByY9AOvYTwcrKJ6K81/cvpYXua1xtGt9PTz/qFe/otHBJ1a3dMgUFtJq7p1I15Li3IUnfUvB0sGPsM9Wvj99fzgS3YZ6/Q23VrtA4L4vD5pEg9k5UQ7rN9BKycnos=">Vuetify example</a> that demonstrates this validation rule in action. The code snippet is hosted on Vuetify Playground. The example is interactive and can be tested by entering different MAC addresses in the input field.</p><p>You can find the code snippet below:</p><pre><code class="hljs js">&lt;template&gt;  &lt;v-form&gt;    &lt;v-container&gt;      &lt;v-row&gt;        &lt;!-- MAC Address Input --&gt;        &lt;v-col cols=<span class="hljs-string">&quot;12&quot;</span> sm=<span class="hljs-string">&quot;6&quot;</span>&gt;          &lt;v-text-field            v-model=<span class="hljs-string">&quot;macAddress&quot;</span>            :rules=<span class="hljs-string">&quot;[rules.required, rules.macAddress_all]&quot;</span>            label=<span class="hljs-string">&quot;MAC Address in `XX:XX:XX:XX:XX:XX` or `XX-XX-XX-XX-XX-XX`&quot;</span>          &gt;&lt;/v-text-field&gt;        &lt;/v-col&gt;        &lt;!-- MAC Address Input <span class="hljs-string">&quot;:&quot;</span> <span class="hljs-keyword">as</span> separator only --&gt;        &lt;v-col cols=<span class="hljs-string">&quot;12&quot;</span> sm=<span class="hljs-string">&quot;6&quot;</span>&gt;          &lt;v-text-field            v-model=<span class="hljs-string">&quot;macAddress&quot;</span>            :rules=<span class="hljs-string">&quot;[rules.required, rules.macAddress_colon_only]&quot;</span>            label=<span class="hljs-string">&quot;MAC Address in `XX:XX:XX:XX:XX:XX` only&quot;</span>          &gt;&lt;/v-text-field&gt;        &lt;/v-col&gt;      &lt;/v-row&gt;    &lt;/v-container&gt;  &lt;/v-form&gt;&lt;/template&gt;&lt;script&gt;  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;    data() &#123;      <span class="hljs-keyword">return</span> &#123;        macAddress: <span class="hljs-string">&#x27;00-B0-D0-63-C2-26&#x27;</span>,        rules: &#123;          required: <span class="hljs-function"><span class="hljs-params">value</span> =&gt;</span> !!value || <span class="hljs-string">&#x27;Required.&#x27;</span>,          macAddress_all: <span class="hljs-function"><span class="hljs-params">value</span> =&gt;</span> &#123;            <span class="hljs-keyword">const</span> pattern = <span class="hljs-regexp">/^([0-9A-Fa-f]&#123;2&#125;[:-])&#123;5&#125;([0-9A-Fa-f]&#123;2&#125;)$/</span>            <span class="hljs-keyword">return</span> pattern.test(value) || <span class="hljs-string">&#x27;Invalid MAC address.&#x27;</span>          &#125;,          macAddress_colon_only: <span class="hljs-function"><span class="hljs-params">value</span> =&gt;</span> &#123;            <span class="hljs-keyword">const</span> pattern = <span class="hljs-regexp">/^([0-9A-Fa-f]&#123;2&#125;:)&#123;5&#125;([0-9A-Fa-f]&#123;2&#125;)$/</span>            <span class="hljs-keyword">return</span> pattern.test(value) || <span class="hljs-string">&#x27;Invalid MAC address.&#x27;</span>          &#125;,        &#125;,      &#125;    &#125;,  &#125;&lt;/script&gt;</code></pre>]]></content>
    
    
    <summary type="html">Learn how to validate MAC addresses with Vue and Vuetify using a regular expression (regex) to ensure the input follows the correct format.</summary>
    
    
    
    <category term="Tutorial" scheme="https://blog.haysc.tech/categories/Tutorial/"/>
    
    
    <category term="Vue" scheme="https://blog.haysc.tech/tags/Vue/"/>
    
    <category term="Vuetify" scheme="https://blog.haysc.tech/tags/Vuetify/"/>
    
    <category term="Validation" scheme="https://blog.haysc.tech/tags/Validation/"/>
    
    <category term="Regular Expression" scheme="https://blog.haysc.tech/tags/Regular-Expression/"/>
    
  </entry>
  
  <entry>
    <title>Searching for a Master&#39;s in Business</title>
    <link href="https://blog.haysc.tech/master-business-options/"/>
    <id>https://blog.haysc.tech/master-business-options/</id>
    <published>2024-06-14T16:54:57.000Z</published>
    <updated>2024-12-06T11:11:18.190Z</updated>
    
    <content type="html"><![CDATA[<p>Hey everyone! Diving into the world of online master’s programs in business while keeping a full-time job felt like walking a tightrope.</p><p>But hey, it’s possible, and I’m here to share how I managed to find programs that were both easy on the wallet and super flexible—without having to take a break from work.</p><p>Hopefully, my experiences can make your search a bit easier.</p><h2 id="Requirement">Requirement</h2><p>I have a busy job and I need it to pay my bills. I am looking for a program that does not require me to quit my job or take a break from work. The program being online and flexible is crucial for me.</p><p>My requirements for a master’s degree in business are as follows:</p><ol><li>Online: I prefer an online program to maintain my current job.</li><li>Affordable: I am looking for a program that is affordable and does not require a significant financial investment. I do not want to be burdened with student loans.</li><li>Flexible: I need a program that is flexible and allows me to balance my work and study. It would be great if the program offers part-time options. I will also prefer competency-based programs, where I can progress at my own pace.</li></ol><p>Optional requirements:</p><ol><li>Ranking: I would prefer a program with a good reputation and ranking. At least it should be on the QS World University Rankings or similar ranking. Being on a ranking may be useful for foreign job applications.</li><li>Workload and difficulty: I am looking for a program that is not too demanding. I hope to find a program that requires less than 10 hours of study per week.</li></ol><p>Based on these requirements, I have shortlisted the following programs. It took me a while to gather all the information, through various websites, forums, and personal emails. I am sharing my research here so that others in a similar situation can benefit from it. I hope this will save you some time.</p><h2 id="UMPI">UMPI</h2><p>University of Maine at Presque Isle (UMPI) is a significant player in the CBE (Competency-Based Education) field. They offer a lot of different undergraduate programs. They are open to credit transfer from Sophia and Study.com, which is a great plus to lower the cost.</p><p>UMPI is highly rated in the CBE community, beating out Western Governors University (WGU) and TESU (Thomas Edison State University) in recent years.</p><p>They even expanded their offerings to include graduate program as well - Master of Arts in Organizational Leadership (MAOL). The program is fully online and offers a flexible schedule. The program is competency-based, which means I can progress at my own pace.</p><p>The tuition cost is $2,150 flat-rate tuition per session, with no course limit per session. If I can complete the program in three or four session, the total cost will be between $6450 to $8,600. The program is affordable and offers a good balance between work and study.</p><p>The program is affordable and offers a good balance between work and study.</p><p>The downside is that UMPI is not a high-ranking university. It is not listed in the QS World University Rankings 2025. If you need to use that ranking to justify your degree, UMPI may not be the best choice. I know some HR departments use the QS ranking to evaluate the degree.</p><p>The another downside is that MAOL from UMPI is very time-consuming. It is reported on Degreeforum.net that writing more than 100 pages of papers for each course milestone is common. This is a lot of work for a working professional.</p><p>To summarize, UMPI provides a CBE Master of Arts in Organizational Leadership (MAOL) for $2150 per 8-week term. Depending on the transfer of up to three undergraduate courses, students can complete this program in 4 to 12 months. This program is especially beneficial for those looking to enhance their leadership skills in organizational settings. Learn more about this program <a href="https://www.umpi.edu/yourpace/maol/">here</a>.</p><h2 id="UK-route-Level-7-Diploma-MBA-Top-up">UK route: Level 7 Diploma + MBA Top-up</h2><p>The second method is to earn a Level 7 Diploma in Business or Management. Then, you can use the diploma to apply for an MBA top-up program at a UK university. This is an affordable option that allows you to earn a UK MBA degree without spending a fortune. The Diploma costs around £1,000 to £2,000, and the MBA top-up program costs around £4,000 to £6,000. This method is very flexible, since you can choose both the diploma and the top-up program from different institutions.</p><p>In this approach, I will finish 120 credits of a Level 7 Diploma in business or management. Then, I will apply for an MBA top-up program at a UK university. By doing this, only 60 credits are needed to complete the MBA - usually a project or a dissertation.</p><p>In the UK education system, a master degree is usually 180 credits. A Level 7 Diploma is 120 credits, and an MBA top-up program is 60 credits. If one chooses an 80 credits Level 7 Diploma, then the MBA top-up program will be 100 credits.</p><p>To fulfill my online requirement, I will look for a Level 7 Diploma in Business or Management from an online provider. Then, I will search for an online MBA top-up program from a UK university.</p><p>Here are the Level 7 Diploma providers I am considering:</p><ol><li>NCC Education</li><li>OTHM</li><li>ATHE</li><li>BTC</li><li>LRN</li></ol><h3 id="NCC-Education">NCC Education</h3><p><em>2024 Update</em>: NCC does not offer direct online programs anymore. All programs have to be taken through their partner centers.</p><p>One combination of the diploma and top-up program is the Level 7 Diploma in Business Management (L7DBM) from NCC Education. The MBA top-up program will be one of the following universities:</p><ol><li>The University of Plymouth. QS World University Rankings 2025 is 661-670. The MBA top-up program fee is £5,000.</li><li>Cardiff Metropolitan University. QS World University Rankings 2025 is not listed. The MBA top-up program fee is £3180.</li><li>Leeds Beckett University. QS World University Rankings 2025 is 1001-1200. The MBA top-up program is a whopping £15,000. Totally not worth it.</li></ol><p>The cost for the NCC Education L7DBM is around £1,000. Add that to the MBA top-up program fee.</p><p>Here are other UK universities that offer MBA top-up programs:</p><ol><li>University of Northampton. QS World University Rankings 2025 is 1001-1200. The <a href="https://www.northampton.ac.uk/courses/executive-mba-top-up/">MBA top-up program</a>.</li><li>University of Portsmouth. QS World University Rankings 2025 is 631-640. The <a href="https://www.port.ac.uk/study/courses/postgraduate-taught/msc-leadership-and-management-top-up">MBA top-up program</a>.</li><li>University of Sunderland. QS World University Rankings 2025 is not listed. The <a href="https://www.sunderland.ac.uk/study/business-and-management/mba-top-up/">MBA top-up program</a>.</li></ol><h4 id="University-of-Plymouth">University of Plymouth</h4><p>Taking account of the ranking and the cost, the University of Plymouth would be the best bet. The cost is reasonable and the ranking is good enough.</p><p>The top-up program also accept entry other than the NCC Education L7DBM.</p><blockquote><p>Minimum 120 Level 7 credits or equivalent in a business or management subject. Examples of such qualifications include:</p><ul><li>a Diploma in Management Studies from a British University</li><li>Edexcel Extended Diploma in Strategy Management</li><li>Pearson BTEC Extended Diploma in Management and Leadership</li><li>Postgraduate Diploma in Business and/or Management related studies</li><li>NCC Education PG Diploma in Business Management</li></ul></blockquote><p>If the NCC Education L7DBM is too expensive, I can also consider other qualifications to enter the top-up program.</p><p>Time cost is also important. I will have to make sure that the Level 7 Diploma have to be recognized by the top-up program. I should check that even before entering the Level 7 Diploma program.</p><h3 id="OTHM">OTHM</h3><p>Compared to NCC Education, OTHM has more programs options and more recognized universities for top-up. The flexibility will be higher.</p><p>The one that grabs my attention is the Level 7 Diploma in Strategic Management and Leadership. The cost is around £1,000. This is at a similar price point to NCC Education.</p><p>OTHM have a lot of programs that are recognized by UK universities to top-up. I have curated a table of the programs. The data source is from their <a href="https://othm.org.uk/university-progression">University Progressions website</a>.</p><!-- | University                    | Program Type                                              | Category        | Link                                                                                                                       ||-------------------------------|-----------------------------------------------------------|-----------------|----------------------------------------------------------------------------------------------------------------------------|| Anglia Ruskin University      | MA Human Resource Management (top-up)                     | Business        | [Link](https://www.aru.ac.uk/study/postgraduate/human-resource-management-ma)                                              || Arden University              | MBA (top-up)                                              | Business        | [Link](https://arden.ac.uk/our-courses/postgraduate/mba/mba-top-up)                                                       || Aston University              | MSc Professional Accounting & Finance (part-time) (top-up)| Business        | [Link](https://www.aston.ac.uk/study/courses/professional-accounting-and-finance-msc)                                      || Cardiff Metropolitan University| MBA Advanced Entry (top-up)                               | Business        | [Link](https://www.cardiffmet.ac.uk/management/courses/Pages/MBA-Advanced-Entry-Programme-(MBA-Top-Up)--.aspx)             || Northumbria University        | Accounting and Finance MSc                                | Business        | [Link](https://www.northumbria.ac.uk/study-at-northumbria/courses/msc-accounting-and-finance-dtfaaf6/)                     ||                               | Global Human Resource Management MSc                      | Business        | [Link](https://www.northumbria.ac.uk/study-at-northumbria/courses/msc-global-human-resource-management-dtfghr6/)           ||                               | Global Logistics Operations and Supply Chain Management MSc| Business        | [Link](https://www.northumbria.ac.uk/study-at-northumbria/courses/msc-global-logistics-and-supply-chain-management-with-advanced-practice-dtsgsc6/) ||                               | Strategic Leadership MSc                                  | Business        | [Link](https://london.northumbria.ac.uk/course/msc-strategic-leadership-part-time/)                                       ||                               | Business with Hospitality & Tourism Management MSc        | Business        | [Link](https://london.northumbria.ac.uk/course/msc-business-with-hospitality-and-tourism-management/)                     ||                               | International Hospitality and Tourism Management MSc      | Hospitality     | [Link](https://www.northumbria.ac.uk/study-at-northumbria/courses/msc-international-hospitality-and-tourism-management-dtfihy6/) || University of Bolton          | MBA (top-up)                                              | Business        | [Link](https://www.bolton.ac.uk/course/BAM054-F-UOB-UX)                                                                   || University of Northampton     | MSc in Accounting and Finance (top-up)                    | Business        | [Link](https://www.northampton.ac.uk/study/courses/accounting-and-finance-top-up-msc/)                                    ||                               | MA Human Resource Management (top-up)                     | Business        | [Link](https://www.northampton.ac.uk/courses/human-resource-management-topup-ma/)                                         ||                               | Master of Business Administration (Executive) (top-up) (MBA)| Business       | [Link](https://www.northampton.ac.uk/courses/executive-mba-top-up/)                                                      || University of Sunderland      | Master of Business Administration (top-up) MBA            | Business        | [Link](https://london.sunderland.ac.uk/study/master-business-administration/mba-top-up/)                                  ||                               | MSc Tourism and Hospitality (Dissertation Only)           | Hospitality     | [Link](https://london.sunderland.ac.uk/study/tourism/msc-tourism-hospitality-dissertation/)                               || University of the Highlands and Islands | MSc Tourism                            | Tourism         | [Link](https://www.uhi.ac.uk/en/courses/msc-tourism/)                                                                      ||                               | MSc Applied Data Analytics                                | IT              | [Link](https://www.uhi.ac.uk/en/courses/msc-applied-data-analytics/#tabanchor)                                             ||                               | MSc Web Technologies                                      | IT              | [Link](https://www.uhi.ac.uk/en/courses/msc-web-technologies/)                                                             || University of Chester         | MA Educational Leadership                                 | Education       | [Link](https://www.chester.ac.uk/study/course-search/educational-leadership-ma/)                                           |QS Ranking --><!-- Anglia Ruskin University: not listedArden University: not listedAston University: 423Cardiff Metropolitan University: not listedNorthumbria University: 535University of Bolton: not listedUniversity of Northampton: 1001-1200University of Sunderland: not listedUniversity of the Highlands and Islands: not listedUniversity of Chester: not listed --><table><thead><tr><th>University</th><th>Program Type</th><th>Category</th><th>Link</th><th>QS 2025 Ranking</th></tr></thead><tbody><tr><td>Anglia Ruskin University</td><td>MA Human Resource Management (top-up)</td><td>Business</td><td><a href="https://www.aru.ac.uk/study/postgraduate/human-resource-management-ma">Link</a></td><td>Not Listed</td></tr><tr><td>Arden University</td><td>MBA (top-up)</td><td>Business</td><td><a href="https://arden.ac.uk/our-courses/postgraduate/mba/mba-top-up">Link</a></td><td>Not Listed</td></tr><tr><td>Aston University</td><td>MSc Professional Accounting &amp; Finance (part-time) (top-up)</td><td>Business</td><td><a href="https://www.aston.ac.uk/study/courses/professional-accounting-and-finance-msc">Link</a></td><td>423</td></tr><tr><td>Cardiff Metropolitan University</td><td>MBA Advanced Entry (top-up)</td><td>Business</td><td><a href="https://www.cardiffmet.ac.uk/management/courses/Pages/MBA-Advanced-Entry-Programme-(MBA-Top-Up)--.aspx">Link</a></td><td>Not Listed</td></tr><tr><td>Northumbria University</td><td>Accounting and Finance MSc</td><td>Business</td><td><a href="https://www.northumbria.ac.uk/study-at-northumbria/courses/msc-accounting-and-finance-dtfaaf6/">Link</a></td><td>535</td></tr><tr><td></td><td>Global Human Resource Management MSc</td><td>Business</td><td><a href="https://www.northumbria.ac.uk/study-at-northumbria/courses/msc-global-human-resource-management-dtfghr6/">Link</a></td><td></td></tr><tr><td></td><td>Global Logistics Operations and Supply Chain Management MSc</td><td>Business</td><td><a href="https://www.northumbria.ac.uk/study-at-northumbria/courses/msc-global-logistics-and-supply-chain-management-with-advanced-practice-dtsgsc6/">Link</a></td><td></td></tr><tr><td></td><td>Strategic Leadership MSc</td><td>Business</td><td><a href="https://london.northumbria.ac.uk/course/msc-strategic-leadership-part-time/">Link</a></td><td></td></tr><tr><td></td><td>Business with Hospitality &amp; Tourism Management MSc</td><td>Business</td><td><a href="https://london.northumbria.ac.uk/course/msc-business-with-hospitality-and-tourism-management/">Link</a></td><td></td></tr><tr><td></td><td>International Hospitality and Tourism Management MSc</td><td>Hospitality</td><td><a href="https://www.northumbria.ac.uk/study-at-northumbria/courses/msc-international-hospitality-and-tourism-management-dtfihy6/">Link</a></td><td></td></tr><tr><td>University of Bolton</td><td>MBA (top-up)</td><td>Business</td><td><a href="https://www.bolton.ac.uk/course/BAM054-F-UOB-UX">Link</a></td><td>Not Listed</td></tr><tr><td>University of Northampton</td><td>MSc in Accounting and Finance (top-up)</td><td>Business</td><td><a href="https://www.northampton.ac.uk/study/courses/accounting-and-finance-top-up-msc/">Link</a></td><td>1001-1200</td></tr><tr><td></td><td>MA Human Resource Management (top-up)</td><td>Business</td><td><a href="https://www.northampton.ac.uk/courses/human-resource-management-topup-ma/">Link</a></td><td></td></tr><tr><td></td><td>Master of Business Administration (Executive) (top-up) (MBA)</td><td>Business</td><td><a href="https://www.northampton.ac.uk/courses/executive-mba-top-up/">Link</a></td><td></td></tr><tr><td>University of Sunderland</td><td>Master of Business Administration (top-up) MBA</td><td>Business</td><td><a href="https://london.sunderland.ac.uk/study/master-business-administration/mba-top-up/">Link</a></td><td>Not Listed</td></tr><tr><td></td><td>MSc Tourism and Hospitality (Dissertation Only)</td><td>Hospitality</td><td><a href="https://london.sunderland.ac.uk/study/tourism/msc-tourism-hospitality-dissertation/">Link</a></td><td>Not Listed</td></tr><tr><td>University of the Highlands and Islands</td><td>MSc Tourism</td><td>Tourism</td><td><a href="https://www.uhi.ac.uk/en/courses/msc-tourism/">Link</a></td><td>Not Listed</td></tr><tr><td></td><td>MSc Applied Data Analytics</td><td>IT</td><td><a href="https://www.uhi.ac.uk/en/courses/msc-applied-data-analytics/#tabanchor">Link</a></td><td>Not Listed</td></tr><tr><td></td><td>MSc Web Technologies</td><td>IT</td><td><a href="https://www.uhi.ac.uk/en/courses/msc-web-technologies/">Link</a></td><td>Not Listed</td></tr><tr><td>University of Chester</td><td>MA Educational Leadership</td><td>Education</td><td><a href="https://www.chester.ac.uk/study/course-search/educational-leadership-ma/">Link</a></td><td>Not Listed</td></tr></tbody></table><h3 id="ATHE">ATHE</h3><p>In the jargons of ATHE, the Level 7 Diploma (120 credits) is called Extended Diploma. The normal Level 7 Diploma only has 60 credits.</p><p>You should always go for the 120 credits, as it is the minimum requirement for an MBA top-up program. It also saves you time and money since the tuition fee per credit is way more expensive in the top-up program.</p><p>ATHE has a lot of programs that are recognized by UK universities to top-up. I have filtered out institutions that are not listed in the QS World University Rankings 2025. The data source is from their <a href="https://athe.co.uk/progression-routes/">University Progressions website</a>.</p><h4 id="Ulster-University-United-Kingdom">Ulster University (United Kingdom)</h4><table><thead><tr><th>Type</th><th>Programme</th><th>Total Credits Required</th><th>Total Qualifications Required</th><th>Progression From</th></tr></thead><tbody><tr><td>Master’s</td><td><a href="https://www.ulster.ac.uk/courses/202223/mba-master-of-business-administration-28254">MBA</a></td><td>60</td><td>1</td><td>ATHE Level 7 Diploma in Strategic Management</td></tr><tr><td>Master’s</td><td><a href="https://www.ulster.ac.uk/courses/202223/mba-master-of-business-administration-28254">MBA</a></td><td>120</td><td>1</td><td>ATHE Level 7 Extended Diploma in Strategic Management</td></tr></tbody></table><h4 id="University-of-Portsmouth-United-Kingdom">University of Portsmouth (United Kingdom)</h4><table><thead><tr><th>Type</th><th>Programme</th><th>Total Credits Required</th><th>Total Qualifications Required</th><th>Progression From</th></tr></thead><tbody><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/ma-business-management-learning-at-work">MA Business Management</a></td><td>120</td><td>1</td><td>ATHE Level 6 Extended Diploma in Management</td></tr><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/ma-business-management-learning-at-work">MA Business Management</a></td><td>60</td><td>1</td><td>ATHE Level 7 Diploma in Strategic Management</td></tr><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/ma-business-management-learning-at-work">MA Business Management</a></td><td>120</td><td>1</td><td>ATHE Level 7 Extended Diploma in Strategic Management</td></tr><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/ma-business-and-computer-studies-learning-at-work">MA Business and Computer Studies</a></td><td>30</td><td>1</td><td>ATHE Level 7 Certificate in Healthcare Management</td></tr><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/ma-business-and-computer-studies-learning-at-work">MA Business and Computer Studies</a></td><td>120</td><td>1</td><td>ATHE Level 7 Diploma in Healthcare Management</td></tr><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/msc-applied-computing-learning-at-work">MSc Applied Computing</a></td><td>120</td><td>1</td><td>ATHE Level 7 Extended Diploma in Computing Technologies (Software Engineering)</td></tr><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/ma-business-management-learning-at-work">MA Business Management</a></td><td>10</td><td>1</td><td>ATHE Level 7 Award in Programme Leadership</td></tr><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/msc-professional-studies-learning-at-work">MSc/MA Professional Studies</a></td><td>120</td><td>1</td><td>ATHE Level 7 Extended Diploma in Strategic Management</td></tr><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/msc-professional-studies-learning-at-work">MSc Applied Science</a></td><td>120</td><td>1</td><td>ATHE Level 7 Extended Diploma in Computing Technologies (Application Development)</td></tr><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/msc-engineering-learning-at-work">MSc Engineering</a></td><td>120</td><td>1</td><td>ATHE Level 7 Extended Diploma in Computing Technologies (Data Analytics)</td></tr><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/msc-engineering-and-management-learning-at-work">MSc Engineering and Management</a></td><td>120</td><td>1</td><td>ATHE Level 7 Extended Diploma in Computing Technologies (Networking)</td></tr><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/msc-engineering-project-management-learning-at-work">MSc Engineering Project Management</a></td><td>120</td><td>1</td><td>ATHE Level 7 Extended Diploma in Computing Technologies (Artificial Intelligence)</td></tr><tr><td>Master’s</td><td><a href="https://www.port.ac.uk/study/courses/postgraduate-taught/msc-maritime-studies-learning-at-work">MSc Maritime Studies</a></td><td>120</td><td>1</td><td>ATHE Level 7 Extended Diploma in Strategic Management</td></tr></tbody></table><p>This table provides a detailed overview of various Master’s programs at the University of Portsmouth, outlining their credit requirements and progression from specific qualifications.</p><h4 id="University-of-Westminster-United-Kingdom">University of Westminster (United Kingdom)</h4><table><thead><tr><th>Type</th><th>Programme</th><th>Total Credits Required</th><th>Total Qualifications Required</th><th>Progression From</th></tr></thead><tbody><tr><td>Master’s</td><td><a href="https://www.westminster.ac.uk/business-and-management-courses/2023-24/january/full-time/master-of-business-administration-mba">MBA</a></td><td>120</td><td>1</td><td>ATHE Level 6 Extended Diploma in Management (with Merit profile)</td></tr><tr><td>Master’s</td><td><a href="https://www.westminster.ac.uk/business-and-management-courses/2023-24/january/full-time/international-business-and-management-msc">MSc/MA International Business and Management</a></td><td>120</td><td>1</td><td>ATHE Level 6 Extended Diploma in Management (with Merit profile)</td></tr><tr><td>Master’s</td><td><a href="https://www.westminster.ac.uk/computer-science-and-engineering-courses/2023-24/january/full-time/cyber-security-and-forensics-msc">MSc Cyber Security and Forensics</a></td><td>120</td><td>1</td><td>ATHE Level 7 Extended Diploma in Computing Technologies (with Merit profile)</td></tr><tr><td>Master’s</td><td><a href="https://www.westminster.ac.uk/computer-science-and-engineering-data-science-and-informatics-courses/2023-24/january/full-time/data-science-and-analytics-msc">MSc Data Science and Analytics</a></td><td>120</td><td>1</td><td>ATHE Level 7 Extended Diploma in Computing Technologies (with Merit profile)</td></tr></tbody></table><h2 id="US-Programs">US Programs</h2><!-- Western Governors University CBE ACBSP MBA $4755 per term (can complete in 1 term)[https://www.wgu.edu/online-business-degr...ogram.html](https://www.wgu.edu/online-business-degrees/mba-masters-business-administration-program.html)Purdue Unversity Global CBE MBA $2833 per 10-week term (someone reported [completing 27 modules in 5 weeks](https://www.degreeforum.net/mybb/Thread-A-very-big-thank-you-to-this-group-I-have-completed-my-Masters-in-IT-from-PUG?page=3), so expect 2-4 terms to complete an MBA)[https://www.purdueglobal.edu/degree-prog...ba-degree/](https://www.purdueglobal.edu/degree-programs/business/exceltrack-competency-based-mba-degree/)UMPI CBE MAOL $2150 per 8wk term (4-12 months) (quicker if you transfer in 3 courses from undergrad)https://www.umpi.edu/yourpace/maol/Walden Unversity CBE ACBSP MBA $13,000 (12 months)[https://info.waldenu.edu/walden-programs...nistration](https://info.waldenu.edu/walden-programs/business-management/masters/master-of-business-administration)LSU Shreveport AACSB MBA $12,474 (10 months)[https://online.lsu.edu/master-business-a...ration-gp/](https://online.lsu.edu/master-business-administration-gp/)Eastern University ACBSP MBA $9900 (10 months)[https://www.eastern.edu/academics/gradua...management](https://www.eastern.edu/academics/graduate-programs/mba-management)Missouri State University AACSB MBA $15,100 (as little as 12 months full time)https://mba.missouristate.edu/Online_Program.htmAmberton University ACBSP $9900 (10 months)https://amberton.edu/masters-degrees/HAU $3000 (4 semesters or 15 months)https://mba.hauniv.edu/academics/mba-program/ --><p>The following are all US programs. They are highly regarded on Degreeforum.net and offer a variety of benefits, including affordability, flexibility, and accelerated completion times.</p><p><strong>Western Governors University (WGU)</strong> offers a Competency-Based Education (CBE) MBA program accredited by ACBSP. Priced at $4755 per term, this program is notable for its efficiency; many students have reported completing it in just one term. This program is ideal for students who have a clear grasp of business concepts and can move through the material quickly without the constraints of a traditional semester timeline. More details can be found <a href="https://www.wgu.edu/online-business-degrees/mba-masters-business-administration-program.html">here</a>.</p><p><strong>Purdue University Global</strong> also offers a CBE MBA, which costs $2833 per 10-week term. Anecdotal evidence from Degreeforum.net suggests that motivated students can complete 27 modules in as little as 5 weeks, potentially finishing the entire program in 2 to 4 terms. This makes Purdue Global an attractive option for students looking to quickly advance their careers without a prolonged educational commitment. Details about the program are available <a href="https://www.purdueglobal.edu/degree-programs/business/exceltrack-competency-based-mba-degree/">here</a>.</p><p><strong>Walden University</strong> offers a CBE MBA accredited by ACBSP, with a total program cost of $13,000 and a duration of 12 months. This program is designed for professionals who are eager to develop advanced skills in business management and leadership. More information can be found <a href="https://info.waldenu.edu/walden-programs/business-management/masters/master-of-business-administration">here</a>.</p><p><strong>Louisiana State University Shreveport (LSU Shreveport)</strong> provides an AACSB-accredited MBA that can be completed in as little as 10 months for $12,474. This program is structured for quick completion, offering high-quality education in a condensed timeframe. Further details are available <a href="https://online.lsu.edu/master-business-administration-gp/">here</a>.</p><p><strong>Eastern University</strong> offers an ACBSP-accredited MBA for $9900, which can also be completed in 10 months. This program is suitable for students who are looking for an affordable, accredited, and fast-track option. Learn more <a href="https://www.eastern.edu/academics/graduate-programs/mba-management">here</a>.</p><p><strong>Missouri State University</strong> provides an AACSB-accredited MBA for $15,100, which can be completed full-time in as little as 12 months. This program is ideal for those seeking a reputable accreditation coupled with an accelerated path. More details can be found on their official <a href="https://mba.missouristate.edu/Online_Program.htm">website</a>.</p><p><strong>Amberton University</strong> offers a $9900 ACBSP-accredited MBA that can be completed in 10 months. This program is targeted at working professionals looking for an affordable and quick MBA option. Additional information is available <a href="https://amberton.edu/masters-degrees/">here</a>.</p><p><strong>Harrisburg American University (HAU)</strong> offers an MBA for $3000, which can be completed over four semesters or 15 months. This program is one of the most economical options available, making it an excellent choice for students on a tight budget. Further details can be found <a href="https://mba.hauniv.edu/academics/mba-program/">here</a>. <strong>⚠ This $3K offer is only available for US residents and not open to international students.</strong></p><h2 id="My-Decision">My Decision</h2><p>After evaluating the various programs based on my requirements, I have decided to pursue the Level 7 Diploma + MBA top-up route. Both NCC and OTHM is having similar price point.</p><p>Since the NCC does not offer direct online programs anymore, I will go with OTHM. The Level 7 Diploma in Strategic Management and Leadership is a good fit for my career goals. The cost is around £1,000, which is affordable.</p>]]></content>
    
    
    <summary type="html">In this blog post, I&#39;ll explore various online master&#39;s programs in business that are affordable, flexible, and highly regarded, providing a comprehensive overview of the options available.</summary>
    
    
    
    <category term="Study" scheme="https://blog.haysc.tech/categories/Study/"/>
    
    
    <category term="Master" scheme="https://blog.haysc.tech/tags/Master/"/>
    
    <category term="Business" scheme="https://blog.haysc.tech/tags/Business/"/>
    
    <category term="Online" scheme="https://blog.haysc.tech/tags/Online/"/>
    
    <category term="CBE" scheme="https://blog.haysc.tech/tags/CBE/"/>
    
    <category term="MBA" scheme="https://blog.haysc.tech/tags/MBA/"/>
    
  </entry>
  
  <entry>
    <title>Triathlon Journey: The Initial Steps [Part 1]</title>
    <link href="https://blog.haysc.tech/triathlon-journey-the-initial-steps-part-1/"/>
    <id>https://blog.haysc.tech/triathlon-journey-the-initial-steps-part-1/</id>
    <published>2024-06-08T16:54:57.000Z</published>
    <updated>2024-12-06T11:11:19.490Z</updated>
    
    <content type="html"><![CDATA[<p>Earlier this year, I went on a cycling trip with my family. I feel exhausted even after a short ride. I was very disappointed with my fitness level. Then, I decided to take up a small challenge after that trip - a 5KM race hosted at Macau.</p><p>I followed a Coach to 5K program and trained for 8 weeks. I completed the 5KM run and broke my personal best time. I felt peace and satisfaction when I stood at the finish line, hearing the cheers of the crowd, and feeling the adrenaline rush. That’s when I decided to take up a bigger challenge - a triathlon.</p><p>I am careful that the goal should be achievable but still challenging enough. The objective of the triathlon is to complete it, not to win it. A sprint distance triathlon is a great starting point for me. It is the shortest triathlon distance, consisting of a 750m swim, 20KM bike ride, and 5KM run. This is still challenging to me though. I know I have to plan my training, set realistic goals, and stay disciplined.</p><p>I am participating in the Shanghai Jinshan Triathlon in October 2024, four months from now.</p><pre class="mermaid">timelinetitle Triathlon JourneyJan 31, 2024 : Japan cyclingMarch 17, 2024 : Macau 5KM raceApril 6, 2024 : Decision to Register Shanghai Jinshan TriathlonOctober 19, 2024 : Shanghai Jinshan Triathlon</pre><p>I will write a series of blog posts to document my journey from a total beginner to the race day, whether I succeed or fail. This is my biggest challenge yet, both to my body and my mind.</p><p>This blog post is the first in the series. It is about the initial steps I took.</p><h2 id="Learning-the-Basics">Learning the Basics</h2><p>I don’t have a coach or a mentor. I am learning everything from the internet, which is my favorite way of learning. The resource is abundant and free. Here are the best resources I found.</p><p>GTN (Global Triathlon Network) have a lot of videos on triathlon training, tips, and gears. Coaches Corner is a great series, where they answer questions from viewers and provide training tips as if they are your personal coach. It help me a lot to understand the science behind the training, such as nutrition and recovery.</p><p>Patrick Delorenzi starts out as a total beginner in triathlon and documents his journey on YouTube. He is very honest and open about his struggles and achievements. I can relate to him a lot. He gives me a lot of motivation - anything can happen through careful planning and <em>real hard work</em>. His video are detailed and informative. I love to watch his videos during my drive to work.</p><p>Matt LeGrand is a reviewer of triathlon electronic gadgets. I watched his videos about the Apple Watch on swimming, cycling and running. Thanks to his review on the multisport mode of the Apple Watch, I am certain that my Series 4 is more than enough for me on my triathlon journey. I don’t need to buy a dedicated triathlon watch, which is expensive.</p><h2 id="Training">Training</h2><p>Triathlon Taren has a lot of videos on triathlon training. He created an app called MOTTIV. After entering my information and goals and the date of the race, the app generated a training plan for me. I followed the plan for the first month.</p><table><thead><tr><th>Day</th><th>Activity</th><th>Duration</th></tr></thead><tbody><tr><td>Monday</td><td>Swim MAIN</td><td>45 - 60 min</td></tr><tr><td>Tuesday</td><td>STRENGTH #1</td><td>25 - 35 min</td></tr><tr><td>Wednesday</td><td>Cycling INTENSE #1</td><td>32 min</td></tr><tr><td>Thursday</td><td>Running INTENSE</td><td>33 min</td></tr><tr><td>Friday</td><td>STRENGTH #2</td><td>25 - 35 min</td></tr><tr><td>Saturday</td><td>Cycling MAIN</td><td>45 min - 3 hrs</td></tr><tr><td>Sunday</td><td>Running MAIN</td><td>30 - 75 min</td></tr></tbody></table><p>Triathlon is sport that requires not the best in one discipline, but good in all three.</p><h3 id="Swimming">Swimming</h3><p>Swimming is my biggest challenge. I have no swimming background at all. I can swim, but I have not gone through any formal training at all. Even worse, I only know how to swim breaststroke, which is not the most efficient stroke for long distance swimming. I must self-learn freestyle and swim for 750 meters.</p><h3 id="Cycling">Cycling</h3><p>For the cycling training, I have an old road bike. It is bought a few years ago when I was in college. I have not used it for a long time.</p><p>According to my original plan, I will ride that bike outdoor. However, the weather is not good in my area. It is either raining or too hot. I don’t want to ride in the rain or under the sun. I have to find an alternative.</p><p>I bought a smart trainer. It is a device that can turn my road bike into a stationary bike. I can ride indoor. The resistance can be adjusted to match the structured workout plan, changing the power in each interval.</p><p>I used Zwift as the training platform software. Zwift is quite expensive and I have tried to find other alternative. However, I can’t find any that is as good as Zwift. I will stick to Zwift for now.</p><h3 id="Running">Running</h3><p>This is the easiest discipline for me. I have been running for months. I did not run 5KM very often though. I will have to increase the distance to match with the race distance.</p>]]></content>
    
    
    <summary type="html">In this blog post, I&#39;ll share why I chose to compete in a triathlon, the initial steps I took, and my experiences from the first month of training.</summary>
    
    
    
    <category term="Triathlon" scheme="https://blog.haysc.tech/categories/Triathlon/"/>
    
    
    <category term="Training" scheme="https://blog.haysc.tech/tags/Training/"/>
    
    <category term="Cycling" scheme="https://blog.haysc.tech/tags/Cycling/"/>
    
    <category term="Running" scheme="https://blog.haysc.tech/tags/Running/"/>
    
    <category term="Swimming" scheme="https://blog.haysc.tech/tags/Swimming/"/>
    
  </entry>
  
  <entry>
    <title>Setting Up Pytest-Embedded with Wokwi Simulator for ESP32 Testing</title>
    <link href="https://blog.haysc.tech/pytest-embedded-wokwi-simulator-setup/"/>
    <id>https://blog.haysc.tech/pytest-embedded-wokwi-simulator-setup/</id>
    <published>2024-06-03T16:54:57.000Z</published>
    <updated>2024-12-06T11:11:19.242Z</updated>
    
    <content type="html"><![CDATA[<p>Testing embedded systems can often be a complex task involving physical hardware setups and intricate configurations. However, with tools like Pytest-embedded and the Wokwi Simulator, this process can be significantly streamlined. This post will guide you through setting up Pytest-embedded to work with the Wokwi Simulator, using a practical example repository that demonstrates how to retrieve geolocation data from an IP address on an ESP32 device.</p><h2 id="Prerequisite-Installing-Pytest-Embedded"><strong>Prerequisite: Installing Pytest-Embedded</strong></h2><p>Before diving into testing with the Wokwi Simulator, you must first install Pytest-embedded. This tool extends Pytest, a popular Python testing framework, to support testing embedded systems software. To install Pytest-embedded and its dependencies, you can use pip:</p><pre><code class="hljs bash">pip install pytest-embedded</code></pre><p>Ensure your development environment is set up with Python and that you have access to the command line or terminal for executing the installation commands.</p><h2 id="Setting-Up-Your-Testing-Environment"><strong>Setting Up Your Testing Environment</strong></h2><p>You can take reference from my example project that does automated CI/CD testing of an ESP32 device using Pytest-embedded and the Wokwi Simulator. The project <a href="https://github.com/hayschan/esp32-ip-to-geolocation">hayschan/esp32-ip-to-geolocation</a> is available on GitHub. Feel free to clone or fork the repository to follow along with the setup.</p><p>This repository includes the necessary configuration files and sample code to get you started. Here’s what you need to make your project compatible:</p><ol><li><strong>pytest.ini</strong>: This file contains configuration details for Pytest, such as command line options and plugins.</li><li><strong>pytest_*.py</strong>: The test file where you’ll write your tests.</li><li><strong>Wokwi related files</strong>: These include <code>diagram.json</code> and <code>Wokwi.toml</code>, which are crucial for simulating the hardware on Wokwi.</li></ol><h3 id="Setting-the-WOKWI-CLI-TOKEN-Environment-Variable">Setting the <code>WOKWI_CLI_TOKEN</code> Environment Variable</h3><p>You must obtain and set the <code>WOKWI_CLI_TOKEN</code> environment variable. This token allows the Wokwi CLI to authenticate with the Wokwi servers, enabling simulation capabilities.</p><p>If you have not set your token, there will be an error message when you run the tests.</p><pre><code class="hljs bash">&gt; wokwi-cli .Wokwi CLI v0.9.0 (abc2bb2a3530)Error: Missing WOKWI_CLI_TOKEN environment variable. Please <span class="hljs-built_in">set</span> it to your Wokwi token.Get your token at https://wokwi.com/dashboard/ci.</code></pre><p>To obtain the token, visit the <a href="https://wokwi.com/dashboard/ci">Wokwi Dashboard</a> and copy the token provided. Next, you need to set the <code>WOKWI_CLI_TOKEN</code> environment variable on your system.</p><p>There is also a guide on the WokWi dashboard when you obtain the token.</p><h4 id="Windows"><strong>Windows</strong></h4><p>Open PowerShell and enter the following command:</p><pre><code class="hljs powershell"><span class="hljs-variable">$env:WOKWI_CLI_TOKEN</span>=<span class="hljs-string">&quot;your_token_here&quot;</span></code></pre><p>Again, replace <code>&quot;your_token_here&quot;</code> with your token.</p><h4 id="macOS-and-Linux"><strong>macOS and Linux</strong></h4><p>On macOS and Linux, the process is similar, and you typically use the terminal to set the environment variable:</p><pre><code class="hljs bash"><span class="hljs-built_in">export</span> WOKWI_CLI_TOKEN=<span class="hljs-string">&quot;your_token_here&quot;</span></code></pre><h4 id="Verifying-the-Environment-Variable"><strong>Verifying the Environment Variable</strong></h4><p>To verify that your environment variable is set correctly, you can re-run the <code>wokwi-cli</code> command:</p><pre><code class="hljs bash">&gt; wokwi-cli .PS D:\GitHub\esp32-ip-to-geolocation&gt; wokwi-cli .Wokwi CLI v0.9.0 (abc2bb2a3530)Connected to Wokwi Simulation API 1.0.0-20240527-g6fb749d7</code></pre><p>If the output shows the version and connection details, your environment variable is set correctly.</p><h2 id="Building-the-Project"><strong>Building the Project</strong></h2><p>Once your files and setup are ready, the next step is to build your project. This is done using Espressif’s IDF build system:</p><pre><code class="hljs bash">idf.py build</code></pre><p>This command compiles your project and prepares it for deployment and testing.</p><h4 id="Running-Tests-with-Pytest"><strong>Running Tests with Pytest</strong></h4><p>With your project built, you can now run tests using the following command:</p><pre><code class="hljs bash">pytest --embedded-services idf,wokwi</code></pre><p>During the test session, Pytest-embedded interacts with the Wokwi Simulator, allowing for a virtualized test environment that mimics the actual hardware. Below is a snippet from a typical test session:</p><h4 id="Conclusion"><strong>Conclusion</strong></h4><p>Integrating Pytest-embedded with the Wokwi Simulator provides a robust framework for testing embedded software without needing physical hardware for each test iteration. This setup not only saves time but also enhances the debugging capabilities by providing a controlled and reproducible environment. For detailed instructions and more examples, check out the example repository and start enhancing your testing workflow today!</p>]]></content>
    
    
    <summary type="html">Learn how to set up Pytest-embedded with the Wokwi Simulator for testing ESP32 applications, streamlining your embedded systems testing without needing physical hardware.</summary>
    
    
    
    <category term="Embedded Systems" scheme="https://blog.haysc.tech/categories/Embedded-Systems/"/>
    
    
    <category term="ESP32" scheme="https://blog.haysc.tech/tags/ESP32/"/>
    
    <category term="Wokwi Simulator" scheme="https://blog.haysc.tech/tags/Wokwi-Simulator/"/>
    
    <category term="Testing" scheme="https://blog.haysc.tech/tags/Testing/"/>
    
    <category term="Pytest-Embedded" scheme="https://blog.haysc.tech/tags/Pytest-Embedded/"/>
    
  </entry>
  
  <entry>
    <title>Implementing Model Predictive Control (MPC) Algorithm for Oven Control System</title>
    <link href="https://blog.haysc.tech/mpc-control-algorithm-implementation/"/>
    <id>https://blog.haysc.tech/mpc-control-algorithm-implementation/</id>
    <published>2024-05-23T16:54:57.000Z</published>
    <updated>2024-12-06T11:11:18.214Z</updated>
    
    <content type="html"><![CDATA[<p>In some scenario, PID control is not enough. Model Predictive Control (MPC) is another control algorithm that can handle complex systems.</p><blockquote><p><strong>What is Model Predictive Control (MPC)?</strong></p><p>Model Predictive Control (MPC) is an advanced control algorithm that uses a dynamic model of the system to predict future behavior and optimize control inputs. It is widely used in process control, robotics, automotive, and other fields where complex systems need to be controlled.</p><pre class="mermaid">> graph LR    B[Measure current state]    B --> C[Define prediction horizon]    C --> D[Use model to predict future states]    D --> E[Define control objectives and constraints]    E --> F[Optimize control inputs]    F --> G[Apply optimized control inputs]    G --> H[Move to next time step]    H --> B    G</pre></blockquote><p>The maths behind MPC is more complicated than PID. We will use an oven as an example to explain the MPC algorithm. Oven is a common household appliance and it is easy to understand as an example. Using a nuclear reactor as an example may be too daunting.</p><p>In this post, I will explain the MPC algorithm and how to implement it for an oven control system.</p><p>We will start by modeling the oven system, then discretize the model for implementation on a microcontroller. We will also consider disturbances like door opening and incorporate them into the model. Finally, we will simulate the MPC control strategy to regulate the oven temperature.</p><h3 id="Identify-the-Key-Variables">Identify the Key Variables</h3><p>First, we need to identify the key variables that describe the oven’s behavior. These include:</p><ul><li><strong>State Variable</strong>: Oven temperature $T(t)$</li><li><strong>Control Input</strong>: Heating element power $P(t)$</li><li><strong>Disturbances</strong>: Ambient temperature, door opening, etc.</li></ul><p>The state variable represents the internal state of the system that we want to control.</p><p>The control input is the variable we can manipulate to achieve our control objectives.</p><p>Disturbances are external factors that affect the system but are not directly controlled. In the context of PID, disturbances are often either ignored, or handled by the integral term. In MPC, disturbances are explicitly modeled and accounted for in the control strategy. In the case of an oven, the ambient temperature and door opening can be considered as disturbances that affect the oven temperature.</p><h3 id="Basic-Heat-Transfer-Principles">Basic Heat Transfer Principles</h3><p>The temperature dynamics of an oven can be modeled using principles of heat transfer, typically focusing on conduction and convection. The rate of change of the oven temperature can be described by an energy balance equation.</p><p>Conduction refers to the transfer of heat through a solid material, such as the walls of the oven. Convection refers to the transfer of heat between a solid surface and a fluid (e.g., air) moving over the surface. In the case of an oven, convection is the dominant mode of heat transfer.</p><p>We would only consider convection in this simple model.</p><p>The heat transfer rate can be described by Newton’s Law of Cooling, which states that the rate of heat transfer is proportional to the temperature difference between the object and the surrounding environment. The simple equation of heat transfer can be given by:</p><p>$$<br>\text{Heat Transfer Rate} = U \cdot (T(t) - T_{\text{ambient}})<br>$$</p><p>Where:</p><ul><li>$U$ is the overall heat transfer coefficient.</li><li>$T(t)$ is the oven temperature at time $t$.</li><li>$T_{\text{ambient}}$ is the ambient temperature.</li></ul><h3 id="Energy-Balance-Equation">Energy Balance Equation</h3><p>$$<br>\frac{dT(t)}{dt} = \frac{1}{C} \left( P(t) \cdot \eta - U \cdot (T(t) - T_{\text{ambient}}) \right)<br>$$</p><p>Where:</p><ul><li>$T(t)$ is the oven temperature at time $t$.</li><li>$P(t)$ is the power of the heating element at time $t$.</li><li>$\eta$ is the efficiency of the heating element (fraction of electrical energy converted to heat).</li><li>$C$ is the thermal capacity of the oven (amount of energy required to change the temperature).</li><li>$U$ is the overall heat transfer coefficient (accounts for heat losses to the environment).</li><li>$T_{\text{ambient}}$ is the ambient temperature.</li></ul><p>$\frac{dT(t)}{dt}$ is the formula represents how fast the temperature $T$ changes over time $t$. It’s like looking at how quickly the temperature is going up or down.</p><p>Thermal Capacity $C$ is a property of the material or substance you’re dealing with. It tells you how much heat energy is needed to change the temperature of a certain amount of that material. For example, water has a high thermal capacity, which means it takes a lot of heat to make it get hotter or cooler compared to some other materials. Here, we’re using $\frac{1}{C}$ to represent how the material’s thermal capacity affects the rate of temperature change. If $C$ is large, it means it takes a lot of energy to change the temperature, so the rate of change $\frac{dT(t)}{dt}$ will be slower.</p><p>Heat Added $P(t) \cdot \eta$ shows how much heat is being added to the system. $P(t)$ represents the power being supplied, like the electricity going into a heater, and $\eta$ is a number between 0 and 1 that tells you how efficiently that power is being turned into heat. For example, if $\eta = 0.8$, it means 80% of the power is turning into heat, and the rest might be lost as other forms of energy like light or sound.</p><p>Heat Lost $U \cdot (T(t) - T_{\text{ambient}})$  shows how much heat is being lost from the system. $U$ represents a value called the heat transfer coefficient, which tells you how fast heat can move from one place to another. For example, if $U$ is high, it means heat can move quickly, so the system might cool down faster. $T(t) - T_{\text{ambient}}$ tells you the difference between the temperature of the system $T(t)$ and the temperature of the surrounding environment $T_{\text{ambient}}$. If the system is hotter than the surroundings, heat will be lost. If it’s colder, heat might be gained from the surroundings.</p><p>By putting them all together, the formula is like a balance between how fast heat is being added and how fast it’s being lost. If more heat is being added than lost, the temperature will go up $\frac{dT(t)}{dt} &gt; 0$. If more heat is being lost than added, the temperature will go down $\frac{dT(t)}{dt} &lt; 0$. If they’re equal, the temperature will stay the same $\frac{dT(t)}{dt} = 0$.</p><h3 id="Discretize-the-Model">Discretize the Model</h3><p>Microcontrollers operate in a digital environment, meaning they process data in discrete time steps, e.g. every millisecond.</p><p>Continuous-time models, like differential equations, cannot be directly implemented on digital systems without discretization. For implementation in a digital controller like MPC, the continuous-time model needs to be discretized.</p><p>Assuming a sampling time $\Delta t$, the discrete-time model can be approximated as:</p><p>$$<br>T[k+1] = T[k] + \Delta t \cdot \frac{1}{C} \left( P[k] \cdot \eta - U \cdot (T[k] - T_{\text{ambient}}) \right)<br>$$</p><p>Where:</p><ul><li>$T[k]$ is the oven temperature at the $k$-th sampling time.</li><li>$P[k]$ is the heating power at the $k$-th sampling time.</li></ul><p>To use this model, the parameters $C$, $\eta$, $U$, and $\Delta t$ need to be determined. These parameters can be obtained through system identification techniques involving experimental data collection and parameter estimation.</p><h3 id="Example-Calculation">Example Calculation</h3><p>Assume we have the following parameters:</p><ul><li>Thermal capacity $C = 500 , \text{J/°C}$</li><li>Efficiency $\eta = 0.8$</li><li>Heat transfer coefficient $U = 10 , \text{W/°C}$</li><li>Sampling time $\Delta t = 1 , \text{second}$</li><li>Ambient temperature $T_{\text{ambient}} = 25 , \text{°C}$</li></ul><p>For an initial oven temperature $T[0] = 180 , \text{°C}$ and heating power $P[0] = 1000 , \text{W}$:</p><p>$$<br>T[1] = T[0] + \Delta t \cdot \frac{1}{C} \left( P[0] \cdot \eta - U \cdot (T[0] - T_{\text{ambient}}) \right)<br>$$</p><p>$$<br>T[1] = 180 + 1 \cdot \frac{1}{500} \left( 1000 \cdot 0.8 - 10 \cdot (180 - 25) \right)<br>$$</p><p>$$<br>T[1] = 180 + 0.002 \left( 800 - 1550 \right)<br>$$</p><p>$$<br>T[1] = 180 + 0.002 \cdot (-750)<br>$$</p><p>$$<br>T[1] = 180 - 1.5<br>$$</p><p>$$<br>T[1] = 178.5 , \text{°C}<br>$$</p><h3 id="Refining-the-Model">Refining the Model</h3><p>In practice, the model can be refined by considering additional factors like heat losses due to door openings, non-linear effects, and more complex heat transfer mechanisms.</p><p>In the following section, I will use adding door opening as a disturbance to refine the model.</p><h4 id="Adding-Door-Opening-as-a-Disturbance">Adding Door Opening as a Disturbance</h4><p>Let’s denote the door opening event as a binary variable $D(t)$ . $D(t) = 1$ when the door is open and $D(t) = 0$ when the door is closed.</p><p>To know that the door is opened, an additional sensor is also needed, such as a switch or a proximity sensor.</p><p>Next, we should model the heat loss due to the door opening. When the door is opened, a significant amount of heat escapes. This can be modeled as a term proportional to the temperature difference between the oven interior and the ambient temperature. This model only considers the convective heat loss through the open door.</p><p>Let $Q_{\text{loss}}$ represent the rate of heat loss when the door is opened. A simple way to model this is:</p><p>$$<br>Q_{\text{loss}} = K_{\text{door}} \cdot D(t) \cdot (T(t) - T_{\text{ambient}})<br>$$</p><p>Where:</p><ul><li>$K_{\text{door}}$ is a coefficient representing the rate of heat loss when the door is open.</li></ul><p>Next, we incorporate this heat loss term into the energy balance equation.</p><p>Originally, the energy balance equation was:</p><p>$$<br>\frac{dT(t)}{dt} = \frac{1}{C} \left( P(t) \cdot \eta - U \cdot (T(t) - T_{\text{ambient}}) \right)<br>$$</p><p>The modified energy balance equation including the door opening effect becomes, adding the heat loss term $Q_{\text{loss}}$:</p><p>$$<br>\frac{dT(t)}{dt} = \frac{1}{C} \left( P(t) \cdot \eta - U \cdot (T(t) - T_{\text{ambient}}) - Q_{\text{loss}} \right)<br>$$</p><p>Expand the heat loss term $Q_{\text{loss}}$, then we get:</p><p>$$<br>\frac{dT(t)}{dt} = \frac{1}{C} \left( P(t) \cdot \eta - U \cdot (T(t) - T_{\text{ambient}}) - K_{\text{door}} \cdot D(t) \cdot (T(t) - T_{\text{ambient}}) \right)<br>$$</p><p>Then, discretize the modified continuous-time model to get the discrete-time model:</p><p>$$<br>T[k+1] = T[k] + \Delta t \cdot \frac{1}{C} \left( P[k] \cdot \eta - U \cdot (T[k] - T_{\text{ambient}}) - K_{\text{door}} \cdot D[k] \cdot (T[k] - T_{\text{ambient}}) \right)<br>$$</p><p>The maths is done, now let’s see an example calculation.</p><p>Let’s assume the following parameters for the door opening effect:</p><ul><li>$K_{\text{door}} = 50 , \text{W/°C}$</li></ul><p>Given:</p><ul><li>Initial oven temperature $T[0] = 180 , \text{°C}$</li><li>Heating power $P[0] = 1000 , \text{W}$</li><li>Ambient temperature $T_{\text{ambient}} = 25 , \text{°C}$</li><li>Door opening status $D[0] = 1$ (door is open)</li></ul><p>Using the modified discrete-time model:</p><p>$$<br>T[1] = T[0] + \Delta t \cdot \frac{1}{C} \left( P[0] \cdot \eta - U \cdot (T[0] - T_{\text{ambient}}) - K_{\text{door}} \cdot D[0] \cdot (T[0] - T_{\text{ambient}}) \right)<br>$$</p><p>Substitute the values:</p><p>$$<br>T[1] = 180 + 1 \cdot \frac{1}{500} \left( 1000 \cdot 0.8 - 10 \cdot (180 - 25) - 50 \cdot 1 \cdot (180 - 25) \right)<br>$$</p><p>$$<br>T[1] = 180 + 0.002 \left( 800 - 1550 - 7750 \right)<br>$$</p><p>$$<br>T[1] = 180 + 0.002 \cdot (-8500)<br>$$</p><p>$$<br>T[1] = 180 - 17<br>$$</p><p>$$<br>T[1] = 163 , \text{°C}<br>$$</p><p>Here, the oven temperature drops significantly due to the heat loss when the door is opened.</p>]]></content>
    
    
    <summary type="html">Model Predictive Control (MPC) is an advanced control algorithm that uses a dynamic model of the system to predict future behavior and optimize control inputs. In this post, I will explain the MPC algorithm and how to implement it for an oven control system.</summary>
    
    
    
    <category term="Algorithm" scheme="https://blog.haysc.tech/categories/Algorithm/"/>
    
    
    <category term="Control" scheme="https://blog.haysc.tech/tags/Control/"/>
    
    <category term="MPC" scheme="https://blog.haysc.tech/tags/MPC/"/>
    
    <category term="PID" scheme="https://blog.haysc.tech/tags/PID/"/>
    
    <category term="Oven" scheme="https://blog.haysc.tech/tags/Oven/"/>
    
    <category term="Heat Transfer" scheme="https://blog.haysc.tech/tags/Heat-Transfer/"/>
    
  </entry>
  
  <entry>
    <title>When Tech Fails: A Disappointing Experience with the Apple Watch During an Earth Day Run</title>
    <link href="https://blog.haysc.tech/apple-watch-earth-day-run-experience/"/>
    <id>https://blog.haysc.tech/apple-watch-earth-day-run-experience/</id>
    <published>2024-04-21T14:13:00.000Z</published>
    <updated>2024-12-06T11:11:17.754Z</updated>
    
    <content type="html"><![CDATA[<p>During my recent attempt to earn the Earth Day Achievement Medal, I planned a straightforward 30-minute run—what should have been an easy feat for my trusty Apple Watch Series 5. Yet, despite my preparations, I was left utterly frustrated by its performance, compelling me to question the reliability of this beloved gadget.</p><p>I equipped my Apple Watch with an external Bluetooth heart rate monitor, connected via a chest strap, to ensure precise tracking of my fitness metrics. Everything seemed set for a successful outdoor run. However, the experience was far from satisfactory.</p><p>With only four minutes left to complete my run, the Apple Watch abruptly shut down. The battery, unbeknownst to me, had depleted. This was more than just a minor hiccup—it meant that my efforts to capture this workout data for the Earth Day medal were in jeopardy. After rushing home to charge the device, I was hopeful that not all was lost.</p><p>Upon rebooting, the Apple Watch did manage to sync some of the workout data back to Apple Health on my iPhone. Yet, to my dismay, it was immediately apparent that all the heart rate data collected during the run was missing. This is unacceptable. The primary purpose of using a heart rate monitor is to gain detailed insights into one’s cardiovascular performance and overall fitness level. Without this critical piece of data, the entire exercise felt futile.</p><p>This incident highlights a significant reliability issue with the Apple Watch Series 5, especially concerning its battery life and data integrity. One would expect that a device of this caliber, particularly when paired with Apple’s own ecosystem of products, would manage its resources better and ensure that data isn’t lost so easily.</p><p>Moreover, this isn’t just about losing a few minutes of exercise data but about the trust we place in technology to assist us in our health and fitness goals. It raises questions about what improvements are necessary to prevent such disappointments in the future. Is it better battery management, more transparent low-battery notifications, or perhaps a fail-safe for data preservation?</p><p>This experience has certainly dampened my enthusiasm for relying on the Apple Watch during critical workouts and leaves me pondering the viability of alternative fitness trackers that might handle such situations more adeptly.</p>]]></content>
    
    
    <summary type="html">Discover the unexpected challenges I faced with the Apple Watch Series 5 during a critical 30-minute Earth Day run. From unexpected shutdowns due to low battery to the complete loss of heart rate data, learn how this technology let me down when I needed it most.</summary>
    
    
    
    <category term="随笔" scheme="https://blog.haysc.tech/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="Apple" scheme="https://blog.haysc.tech/tags/Apple/"/>
    
    <category term="Watch" scheme="https://blog.haysc.tech/tags/Watch/"/>
    
  </entry>
  
  <entry>
    <title>如何在中国的 Windows 11 机器上使用 DNS Over HTTPS (DoH) 提升网络安全</title>
    <link href="https://blog.haysc.tech/dns-over-https-dnspod-windows-11/"/>
    <id>https://blog.haysc.tech/dns-over-https-dnspod-windows-11/</id>
    <published>2024-02-23T02:41:00.000Z</published>
    <updated>2024-12-06T11:11:17.834Z</updated>
    
    <content type="html"><![CDATA[<p>在互联网日益成为生活中不可或缺的部分时，网络安全和隐私保护变得尤为重要。DNS Over HTTPS (DoH) 是一种提升网络安全性的技术，它可以帮助用户避免 ISP（网络服务提供商）的 DNS 污染，保护你的网络请求不被窥视。</p><p>本文将指导你如何在 Windows 11 系统中配置和使用 DoH。</p><h2 id="为什么选择-DNS-Over-HTTPS？">为什么选择 DNS Over HTTPS？</h2><p>想象你（用户）是一位开车前往陌生地方的司机。为了找到目的地，你需要向路人（DNS 服务器）询问路线。在这个过程中，存在两个主要风险，类似于我们网络世界中的 DNS 污染和 DNS 劫持：</p><ol><li><p><strong>DNS污染</strong> 相当于你向路人询问去图书馆的路，但故意给你指错方向，让你根本到不了图书馆。这是因为有人不希望你到达那里，通过故意“污染”你获得的指引，使你无法到达正确的目的地。</p></li><li><p><strong>DNS劫持</strong> 则更加狡猾，这就像你问路时，有人故意引导你到一个看起来很像你要去的地方的假冒地点。这个地方可能是一个假的商店，他们想要你在那里消费，或者更糟糕的是，抢劫你。这样，他们不仅误导了你的方向，还可能让你遭受损失或危险。</p></li></ol><pre class="mermaid">sequenceDiagram    participant U as 用户设备    participant D as DNS客户端    participant R as DNS解析器    participant I as 互联网资源    U->>D: 发起DNS查询请求    Note over U,D: 查询请求明文传输    D->>R: 发送DNS查询    Note over D,R: 传输过程可能被监听    R->>I: 向互联网资源发起DNS查询    I-->>R: 返回DNS查询结果    R-->>D: 发送DNS查询结果    Note over R,D: 响应同样明文传输    D-->>U: 返回DNS查询结果    Note over U,D: 查询结果可能被篡改</pre><p>在这两种情况下，你作为司机因为信任了错误的信息，结果可能完全偏离了预期的目的地。这就是为什么在互联网世界中，使用加密的DNS查询，比如 DNS Over HTTPS（DoH），非常重要。它就像是一个可靠的、更新的 GPS 导航系统，即使在你完全不熟悉的地方，也能确保你安全、准确地到达目的地，而不会被误导或陷入危险。</p><pre class="mermaid">sequenceDiagram    participant U as 用户设备    participant D as DoH客户端    participant S as DoH服务端    participant I as 互联网资源    U->>D: 发起DNS查询请求    D->>S: 通过HTTPS发送加密的DNS查询    Note over D,S: 查询内容被SSL/TLS加密    S->>S: 解析DNS查询    S->>I: 向互联网资源发起DNS查询    I-->>S: 返回DNS查询结果    S->>D: 通过HTTPS发送加密的DNS查询结果    Note over S,D: 响应内容同样被SSL/TLS加密    D-->>U: 返回解密后的DNS查询结果    Note over U,D: 用户设备获得加密且安全的查询结果</pre><h2 id="如何配置-DNS-Over-HTTPS？">如何配置 DNS Over HTTPS？</h2><p>腾讯云（DNSpod）提供的 <a href="https://docs.dnspod.cn/public-dns/dot-doh/">免费 DoH 服务</a>，为用户在中国提供了一种简便的解决方案。</p><blockquote><p>基础免费服务</p><ul><li>DoH 地址：https://doh.pub/dns-query</li><li>DoH（IP）：https://1.12.12.12/dns-query 、 https://120.53.53.53/dns-query</li><li>DoT 地址：dot.pub</li><li>DoT（IP）：1.12.12.12、120.53.53.53</li></ul></blockquote><p>接下来，我将指导你如何在 Windows 11 上配置腾讯云（DNSpod）的 DoH 服务。</p><h3 id="1-打开网络设置">1. 打开网络设置</h3><p>首先，点击任务栏右下角的网络图标，然后点击“网络和Internet”设置。根据你的网络连接类型选择“以太网”或者“WiFi”。本指南以WiFi为例。</p><p><img src="/dns-over-https-dnspod-windows-11/DoH%20-%20Network%20and%20Internet%20Select%20WiFi.png" alt=""></p><h3 id="2-修改DNS设置">2. 修改DNS设置</h3><p>在 WiFi 设置页面，找到你当前连接的网络，点击“属性”，滚动找到“DNS 服务器设置”部分，点击“编辑”按钮。在这里，我们将修改DNS的配置。</p><p><img src="/dns-over-https-dnspod-windows-11/DoH%20-%20Network%20and%20Internet%20Edit%20DNS%20server%20assignment.png" alt=""></p><h3 id="3-设置DNS-Over-HTTPS">3. 设置DNS Over HTTPS</h3><p>在打开的窗口中，将“设置IP地址”选项从“自动”更改为“手动”。接着，启用 IPv4，并填写下面的DNS地址：</p><ul><li>首选 DNS<ul><li>1.12.12.12</li><li>DNS over HTTPS：打开（手动模板）</li><li>DNS over HTTPS 模板：<code>https://doh.pub/dns-query</code></li></ul></li><li>备用 DNS<ul><li>120.53.53.53</li><li>DNS over HTTPS：打开（手动模板）</li><li>DNS over HTTPS 模板：<code>https://doh.pub/dns-query</code></li></ul></li></ul><p><img src="/dns-over-https-dnspod-windows-11/DoH%20-%20Network%20and%20Internet%20Type%20IPv4%20DNSpod%20DoH%20details.png" alt=""></p><p>为确保你的DNS Over HTTPS (DoH)设置已经成功并生效，你可以通过几个简单的命令行步骤来进行验证。这里是如何使用Windows的命令提示符（cmd）或PowerShell来检查DoH配置的完整指南：</p><h2 id="检查-DoH-配置是否生效">检查 DoH 配置是否生效</h2><ol><li><p>打开 Command Prompt（<code>cmd.exe</code>）或 PowerShell。</p></li><li><p><strong>清除 DNS 缓存</strong>：</p><ul><li>首先，你需要清空你电脑上的 DNS 解析缓存。这可以确保你的检查是基于最新的 DNS 配置。在命令行中输入以下命令：</li></ul><pre><code class="hljs powershell">ipconfig /flushdns</code></pre><ul><li>你会看到一个确认信息，说明 DNS 解析缓存已经成功清除。</li></ul></li><li><p><strong>使用 <code>nslookup</code> 命令进行 DNS 查询测试</strong>：</p><ul><li>接下来，使用 <code>nslookup</code> 命令来查询一个网站的DNS信息。这个命令会显示处理你的 DNS 查询的服务器信息。输入例如：</li></ul><pre><code class="hljs powershell">nslookup blog.haysc.tech</code></pre><ul><li>替换 <code>blog.haysc.tech</code> 为你想查询的任何域名。</li></ul></li><li><p><strong>检查命令回文</strong>：</p><ul><li>在命令执行后，检查输出信息。如果一切设置正确，你应该会看到 <code>Server: pdns.dnspod.cn</code>，以及一个腾讯云（DNSpod）的IP地址。</li></ul><p>例如，输出看起来可能像这样：</p><pre><code class="hljs powershell">Server:  pdns.dnspod.cnAddress:  <span class="hljs-number">119.29</span>.<span class="hljs-number">29.29</span>Non<span class="hljs-literal">-authoritative</span> answer:Name:    hayschan.netlify.appAddresses:  <span class="hljs-number">2406</span>:da18:b3d:e200::<span class="hljs-number">64</span>          <span class="hljs-number">2406</span>:da18:<span class="hljs-number">880</span>:<span class="hljs-number">3801</span>::c8          <span class="hljs-number">13.215</span>.<span class="hljs-number">144.61</span>          <span class="hljs-number">13.228</span>.<span class="hljs-number">199.255</span>Aliases:  blog.haysc.tech</code></pre><p><code>pdns.dnspod.cn</code> 和获得的 IP 地址表明你的 DNS 查询是通过腾讯云的DoH服务进行的。</p></li></ol><h2 id="结论">结论</h2><p>通过使用 DNS Over HTTPS，你可以有效地防止 ISP 的 DNS 污染和提高上网的隐私性。腾讯云的 DoH 服务为中国用户提供了一个可靠且易于配置的选项。通过上述步骤，你可以轻松在 Windows 11 系统中配置 DoH，享受更加安全、私密的互联网访问体验。</p>]]></content>
    
    
    <summary type="html">探索如何在 Windows 上使用腾讯云的 DNS Over HTTPS 服务提升网络安全和隐私。本篇技术博客提供了一步步的配置指南，帮助你避免 ISP 的 DNS 污染，确保网络请求的加密和隐私保护。快速、简易的设置过程，让每位用户都能享受更安全的上网体验。</summary>
    
    
    
    <category term="Security" scheme="https://blog.haysc.tech/categories/Security/"/>
    
    
    <category term="DoH" scheme="https://blog.haysc.tech/tags/DoH/"/>
    
    <category term="DNS" scheme="https://blog.haysc.tech/tags/DNS/"/>
    
    <category term="Windows 11" scheme="https://blog.haysc.tech/tags/Windows-11/"/>
    
    <category term="Tencent Cloud" scheme="https://blog.haysc.tech/tags/Tencent-Cloud/"/>
    
  </entry>
  
  <entry>
    <title>Solving Node.js Terser Plugin Error: `Digital Envelope Routines::Unsupported`</title>
    <link href="https://blog.haysc.tech/Solving-Nodejs-Terser-Plugin-Error-for-ESP32-Web-Applications/"/>
    <id>https://blog.haysc.tech/Solving-Nodejs-Terser-Plugin-Error-for-ESP32-Web-Applications/</id>
    <published>2024-02-21T03:18:00.000Z</published>
    <updated>2024-12-06T11:11:17.742Z</updated>
    
    <content type="html"><![CDATA[<h2 id="TL-DR">TL;DR</h2><p>If you’re encountering a Terser plugin error related to “digital envelope routines::unsupported” during your build process with Node.js, especially versions 17 and above, a quick workaround is to set the <code>NODE_OPTIONS</code> environment variable to <code>--openssl-legacy-provider</code>. This can be done by running <code>$env:NODE_OPTIONS=&quot;--openssl-legacy-provider&quot;</code> in your terminal (PowerShell for Windows users) before initiating your build command. This solution helps bypass the compatibility issues with OpenSSL 3.0 in newer Node.js versions, allowing your build to complete successfully.</p><h2 id="Context">Context</h2><p>In the midst of reviving the <a href="https://github.com/ArthurFDLR/esp32-webapp">ESP-IDF example</a> to incorporate a Vue 2 and Vuetify front end for an ESP32 web application, I encountered a peculiar issue.</p><p>The project aimed to build a webpage that would later be flashed to the ESP32, serving as a modern interface for interacting with the device. Following the outlined steps, I navigated to the <code>./front/web-blinker</code> directory, installed the necessary dependencies with <code>npm install</code>, and initiated the build process using <code>npm run build</code>.e</p><p>This project serves as an enhanced version of the ESP-IDF’s RESTful server example. It’s an ideal setup for developing applications requiring a RESTful API server alongside a sophisticated UI.</p><p>However, during the build, an unexpected error surfaced, related to the Terser plugin and division deprecation warnings from Dart Sass. The Terser plugin error, in particular, was perplexing, hindering the build process and posing a significant obstacle to progress. This issue was especially surprising given the straightforward nature of the task at hand—building a static web page for deployment to an ESP32 module.</p><h2 id="Symptom-Error-Encountered">Symptom/Error Encountered</h2><p>During the build process of a web application, you might encounter a failure with a series of errors stemming from the Terser plugin, a tool used for minifying JavaScript. The error message prominently includes:</p><pre><code class="hljs plain">Error: error:0308010C:digital envelope routines::unsupported</code></pre><p>This issue particularly affects projects after upgrading to Node.js version 17 or newer due to changes in the cryptographic library (OpenSSL 3.0) that Node.js uses. The Terser plugin, along with other dependencies that rely on OpenSSL for encryption or hashing, may not be fully compatible with these changes, leading to the aforementioned build errors.</p><h2 id="The-Solution">The Solution</h2><p>To resolve this issue, you can use the <code>--openssl-legacy-provider</code> flag with Node.js to switch back to the legacy provider for OpenSSL. This flag is a temporary measure that allows your projects to build successfully by bypassing the incompatibilities introduced with OpenSSL 3.0.</p><p>For <strong>Shell</strong> users, the command is as follows:</p><pre><code class="hljs shell">export NODE_OPTIONS=--openssl-legacy-provider</code></pre><p>For <strong>Windows PowerShell</strong> (<code>PowerShell.exe</code>) users, the command is as follows:</p><pre><code class="hljs powershell"><span class="hljs-variable">$env:NODE_OPTIONS</span>=<span class="hljs-string">&quot;--openssl-legacy-provider&quot;</span></code></pre><p>For <strong>Windows Command Prompt</strong> (<code>cmd.exe</code>) users, the command is as follows:</p><pre><code class="hljs powershell"><span class="hljs-built_in">set</span> NODE_OPTIONS=-<span class="hljs-literal">-openssl</span><span class="hljs-literal">-legacy</span><span class="hljs-literal">-provider</span></code></pre><p>After setting this environment variable, proceed with your build command, typically <code>npm run build</code> or a similar script defined in your <code>package.json</code>. This should allow the build process to complete without encountering the Terser plugin error.</p><h3 id="Additional-Notes">Additional Notes</h3><ul><li>It’s important to note that this workaround is intended as a temporary fix. Keep an eye on updates from Node.js, the Terser plugin, and other related dependencies for more permanent solutions.</li><li>Ensure to test your application thoroughly after the build to verify that the use of the legacy provider does not introduce any new issues.</li><li>If you’re working in a team or on a continuous integration (CI) system, make sure to document this workaround and communicate it clearly to avoid build inconsistencies.</li></ul><p>This workaround provides a quick and effective way to deal with specific build errors related to OpenSSL 3.0 incompatibilities in Node.js projects. By applying this fix, you can ensure your project’s build process remains smooth and uninterrupted while waiting for long-term solutions from dependency maintainers.</p>]]></content>
    
    
    <summary type="html">Learn how to quickly resolve the `digital envelope routines::unsupported` error in Node.js projects by setting the `NODE_OPTIONS` environment variable to use the legacy OpenSSL provider.</summary>
    
    
    
    <category term="NodeJS" scheme="https://blog.haysc.tech/categories/NodeJS/"/>
    
    
    <category term="error" scheme="https://blog.haysc.tech/tags/error/"/>
    
    <category term="restful" scheme="https://blog.haysc.tech/tags/restful/"/>
    
    <category term="vue" scheme="https://blog.haysc.tech/tags/vue/"/>
    
  </entry>
  
  <entry>
    <title>苹果「日记」：简单不一定美</title>
    <link href="https://blog.haysc.tech/apple-journal-lag-behind/"/>
    <id>https://blog.haysc.tech/apple-journal-lag-behind/</id>
    <published>2024-02-16T08:09:10.000Z</published>
    <updated>2024-12-06T11:11:17.754Z</updated>
    
    <content type="html"><![CDATA[<p>最近我尝试了苹果 iOS 17 内置的「日记」应用。遗憾的是，这个应用似乎还未完全成熟，其唯一的亮点可能就是它的免费性。</p><p>这个应用缺少了你可能期待在苹果官方应用中看到的许多特性：</p><ol><li><p>不支持iPad。这一点让人难以理解，因为使用iPad配合键盘记录日记本应是一个顺理成章的使用场景。然而，苹果的官方“日记”应用竟然没有iPad支持。</p></li><li><p>不支持横屏模式。由于iPad不受支持，我尝试将蓝牙键盘连接至手机，并希望能够横屏使用，以便输入和预览内容。然而，多次尝试后发现，这一基础功能竟然缺失。</p></li><li><p>在最近的 iOS 17 更新中，苹果引入了情绪记录功能。自然地，我认为日记记录与情绪跟踪可以产生深远的影响。按照心理学的理论，如果你持续认为自己表现不佳，大脑会迅速搜寻证据来支持这一想法；将思绪记录下来可以瞬间缓解大脑的警觉状态，因为写作过程需要动用理性思考，从而有助于缓解负面情绪。例如，当情绪记录显示不佳时，应用可以建议你撰写一篇日记来探究背后的原因。或者，能够在一篇日记中记录当天的情绪，并自动将其同步到健康应用中。从技术角度来看，这些都是可行的改进。</p></li></ol><p>这个版本的“日记”应用虽然有待提升，但也许正如特斯拉简约至上的内饰，简单也有其独到之处。</p><p>我爸就是个例子，他特别害怕记忆力变差，几年前让我帮他用 Wordpress 建了个网站存日记。但这过程太折腾了：</p><ul><li>进 Wordpress 后台就跟迷宫似的；</li><li>把手机里的照片传到电脑上去；</li><li>网站又是公开的，得小心翼翼挑选哪些私事能发。</li></ul><p>结果，几年下来，发的日记少得可怜。《掌控习惯》一书里花了颇多墨水，述说培养习惯得遵循几个原则：</p><ol><li>显而易见</li><li>有吸引力</li><li>容易执行</li><li>令人满意</li></ol><p>显然，Wordpress 那一套太复杂，违背了“容易执行”的原则。反过来，我爸把苹果的“日记”应用一放桌面，既显眼又简单，完美契合前两条原则。</p><p>至于“令人满意”，写日记的成就感其实不在于写的那一刻，而是几个月甚至几年后回头看，那种“我当时怎么想的”、“我怎么过来的”感慨，才是真正的满足。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近我尝试了苹果 iOS 17 内置的「日记」应用。遗憾的是，这个应用似乎还未完全成熟，其唯一的亮点可能就是它的免费性。&lt;/p&gt;
&lt;p&gt;这个应用缺少了你可能期待在苹果官方应用中看到的许多特性：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;不支持iPad。这一点让人难以理解，因为使用i</summary>
      
    
    
    
    <category term="随笔" scheme="https://blog.haysc.tech/categories/%E9%9A%8F%E7%AC%94/"/>
    
    
    <category term="Apple" scheme="https://blog.haysc.tech/tags/Apple/"/>
    
    <category term="Journal" scheme="https://blog.haysc.tech/tags/Journal/"/>
    
  </entry>
  
  <entry>
    <title>AIYATO Tracker Review: The MFi-Certified AirTag Alternative That Won’t Break the Bank</title>
    <link href="https://blog.haysc.tech/airtag-chinese-alternative/"/>
    <id>https://blog.haysc.tech/airtag-chinese-alternative/</id>
    <published>2024-02-10T08:09:10.000Z</published>
    <updated>2024-12-06T11:11:17.746Z</updated>
    
    <content type="html"><![CDATA[<p>In the vibrant ecosystem of personal tech, where the quest to keep track of our digital and physical belongings merges, Apple’s AirTag has emerged as the sleek harbinger of the ‘never lose anything ever again’ age.</p><blockquote><p>Ultra-wideband communication is a radio technology focused on precise ranging (measuring the location to an accuracy of 10 cm) between devices.</p></blockquote><p>With its chic design and UWB (Ultra-wideband) technology, it’s like enlisting a homing pigeon with a GPS strapped to its back. However, for those not keen on spending $29 for the privilege of unfailing item recovery, there’s a new player in town: the AIYATO tracker.</p><h2 id="Apple’s-Certification-Speaks-Volumes">Apple’s Certification Speaks Volumes</h2><p>Hailing from the tech metropolis of Shenzhen, the AIYATO tracker enters the arena with a tempting proposition—affordability without skimping on functionality. With a price tag of about 20 yuan (~$2.5 USD), it’s an attractive alternative for the price-conscious. But what makes AIYATO stand out isn’t just its price. It’s the Apple MFi (Made for iPhone/iPad) certification it boasts, as confirmed by a recent dive into Apple’s MFi Licensed Accessories database.</p><p><img src="/airtag-chinese-alternative/screenshot-mfi-licensed-accesories-aiyato.png" alt="Screenshot of AIYATO listed on Apple's MFi Licensed Accessories database"></p><p>This certification means that AIYATO is a product that has met the standards set by Apple itself for accessory compatibility. Users can rest assured that the AIYATO tracker has been engineered to work harmoniously with their Apple devices, an assurance that’s often missing in the more affordable corners of tech accessories.</p><h2 id="Review">Review</h2><p>In hand, the AIYATO tracker feels robust, with a textured surface aimed at providing a secure grip and a sense of durability. It’s practical too, featuring a built-in loophole for threading — <em>something the AirTag lacks without an additional accessory purchase</em>.</p><p>In terms of size, its 30mm (1.18 inch) by 55mm (2.16 inch) footprint is unobtrusive, and at a mere 4mm (0.15 inch) thick, it’s as pocket-friendly as it is wallet-friendly.</p><p>Setting up the AIYATO is refreshingly straightforward. Much like pairing AirPods, a simple button press, a few taps in the iPhone’s Find My app, and the tracker is ready to go, sitting alongside AirTags and other Find My-enabled devices in the app’s interface. While it may not offer the precise, down-to-the-inch locating capabilities of the AirTag thanks to the absence of UWB technology, the AIYATO does a commendable job of leading you to the general vicinity of your misplaced items.</p><p>While the AIYATO tracker doesn’t boast the ultra-precision of UWB, for everyday use—where a few meters’ radius is more than sufficient — it excels. Within those meters, the buzzer is loud enough to hear and find the tracker out.</p><p>The device claims to be dustproof and waterproof, though these features haven’t been put to the test. It also promises a year of battery life off a single CR2032 battery, which is user-replaceable, ensuring long-term usability and ease of maintenance.</p><h2 id="Conclusion">Conclusion</h2><p>The AIYATO tracker is a robust, MFi-certified addition to the Apple Find My network. It demonstrates that cost-effective solutions can indeed stand shoulder to shoulder with premium offerings. While it may not match the AirTag’s pinpoint accuracy, it navigates the balance between affordability and functionality with finesse.</p><p>For those integrated into the Apple ecosystem and looking for a tracker that won’t leave a dent in their finances, the AIYATO is a compelling proposition. It’s not just about tracking; it’s about being smart with your tech choices.</p><p>The AIYATO tracker, with its MFi certification, proves that you can have your tech cake and eat it too, without the premium price tag.</p>]]></content>
    
    
    <summary type="html">Explore the AIYATO Tracker in our latest review, an MFi-certified gadget vying to replace the Apple AirTag at a fraction of the cost. I assess its performance, ease of use, and how it fares within the Apple ecosystem. Perfect for those seeking a budget-friendly alternative for tracking their belongings.</summary>
    
    
    
    <category term="Review" scheme="https://blog.haysc.tech/categories/Review/"/>
    
    
    <category term="Apple" scheme="https://blog.haysc.tech/tags/Apple/"/>
    
    <category term="Find My" scheme="https://blog.haysc.tech/tags/Find-My/"/>
    
  </entry>
  
  <entry>
    <title>Resolving `EXTRA_COMPONENT_DIRS` Path Issues in ESP-IDF CI/CD with GitHub Actions</title>
    <link href="https://blog.haysc.tech/resolving-esp-idf-ci-cd-path-issues-gitHub-actions/"/>
    <id>https://blog.haysc.tech/resolving-esp-idf-ci-cd-path-issues-gitHub-actions/</id>
    <published>2024-01-12T03:18:10.000Z</published>
    <updated>2024-12-06T11:11:19.242Z</updated>
    
    <content type="html"><![CDATA[<p>Developing with ESP-IDF and integrating CI/CD workflows can sometimes lead to unexpected challenges, particularly when paths in the build environment differ between local and CI setups.</p><p>This post outlines my journey in troubleshooting and solving a path issue with <code>EXTRA_COMPONENT_DIRS</code> in a GitHub Actions environment, saving you time and effort.</p><h2 id="The-Problem">The Problem</h2><p>While working on an ESP-IDF component, I encountered a peculiar issue. Locally, everything worked flawlessly: I could navigate to <code>./test_apps/default_example</code> and run <code>idf.py build</code> without any errors. However, the GitHub Action I set up for building the <code>test_app</code> in a Docker container failed.</p><p>The error was specific:</p><pre><code class="hljs shell">Directory specified in EXTRA_COMPONENT_DIRS doesn&#x27;t exist:  /__w/max6675/MAX6675</code></pre><p>This error suggested a path-related issue in my GitHub Action configuration, pointing to a non-existent directory.</p><p>Here’s how my component’s file structure looked:</p><pre><code class="hljs shell">|   CMakeLists.txt|   idf_component.yml|   max6675.c|   README.md|+---include|       max6675.h|\---test_apps    \---default_example        |   CMakeLists.txt        |   pytest_max6675.py        |   sdkconfig.defaults        |        \---main                CMakeLists.txt                max6675_test.c</code></pre><h2 id="Troubleshooting-Steps">Troubleshooting Steps</h2><p>To unravel this, I followed a systematic approach:</p><ol><li><p><strong>Checking <code>EXTRA_COMPONENT_DIRS</code> Setting</strong>: My initial suspicion was on the <code>EXTRA_COMPONENT_DIRS</code> in the CMake configuration. This setting points to additional directories for the build, and it needed to be correctly relative to the project’s root. However, the GitHub Actions environment has a different working directory structure.</p></li><li><p><strong>Inspecting GitHub Actions Workflow</strong>: I then reviewed the <code>.github/workflows/build_and_test.yml</code> file. It was crucial to ensure that each step, particularly the checkout and build steps, was accurately configured to match the expected structure.</p></li><li><p><strong>Verifying Repository Structure</strong>: Ensuring that my GitHub repository’s structure matched my local setup was next. The error message hinted at an incorrect directory reference.</p></li><li><p><strong>Reviewing <code>CMakeLists.txt</code> Files</strong>: A deep dive into the <code>CMakeLists.txt</code> files, especially in <code>test_apps/default_example</code>, was necessary to confirm the correct referencing of the component directory and paths.</p></li></ol><h2 id="The-Solution">The Solution</h2><p>The breakthrough came when I found a similar ESP-IDF CI/CD testing framework in <a href="https://github.com/hfudev/esp-idf-build-and-test-ci-template/">this repository</a>. It’s a CI/CD template for ESP-IDF components with various helpful features.</p><p>A crucial difference I noticed in their <a href="https://github.com/hfudev/esp-idf-build-and-test-ci-template/blob/8447ebf968320c1af269e77fc6c438ce29044ca0/examples/hello_world/CMakeLists.txt#L7C1-L7C69"><code>CMakeLists.txt</code></a> was the following line:</p><pre><code class="hljs YAML"><span class="hljs-string">set(EXTRA_COMPONENT_DIRS</span> <span class="hljs-string">&quot;PROJECT_SOURCE_DIR/../../../src/test_cmp&quot;</span><span class="hljs-string">)</span></code></pre><p>Inspired by this, I modified my <code>test_apps\default_example\CMakeLists.txt</code>:</p><pre><code class="hljs diff"># file path: test_apps\default_example\CMakeLists.txtcmake_minimum_required(VERSION 3.5)<span class="hljs-deletion">- set(EXTRA_COMPONENT_DIRS ../../max6675)</span><span class="hljs-addition">+ set(EXTRA_COMPONENT_DIRS &quot;PROJECT_SOURCE_DIR/../../..&quot;)</span>include($ENV&#123;IDF_PATH&#125;/tools/cmake/project.cmake)add_compile_options(&quot;-Wno-format&quot;)get_filename_component(ProjectId $&#123;CMAKE_CURRENT_LIST_DIR&#125; NAME)string(REPLACE &quot; &quot; &quot;_&quot; ProjectId $&#123;ProjectId&#125;)project($&#123;ProjectId&#125;)</code></pre><p>And it works!</p><h2 id="Conclusion">Conclusion</h2><p>The modification to the <code>EXTRA_COMPONENT_DIRS</code> in the <code>CMakeLists.txt</code> was a critical step in resolving the GitHub Actions build issue. Importantly, this change did not disrupt the existing local build process that was already functioning without errors.</p><p>By using the <code>PROJECT_SOURCE_DIR</code> variable, the path dynamically adapts to the environment in which the build is occurring, whether it’s on a local machine or in a GitHub Actions runner.</p><p>This flexibility ensures that the <code>test_apps</code> can be successfully built both in the local development environment and in the GitHub Actions CI/CD pipeline, maintaining consistency across different build environments.</p><p>This approach highlights the importance of adaptable configurations in CI/CD workflows, especially when dealing with relative paths in project structures.</p>]]></content>
    
    
    <summary type="html">A deep dive into troubleshooting and resolving path issues in ESP-IDF projects when integrating with GitHub Actions.</summary>
    
    
    
    <category term="ESP-IDF" scheme="https://blog.haysc.tech/categories/ESP-IDF/"/>
    
    
    <category term="ESP-IDF" scheme="https://blog.haysc.tech/tags/ESP-IDF/"/>
    
    <category term="GitHub Actions" scheme="https://blog.haysc.tech/tags/GitHub-Actions/"/>
    
    <category term="CI/CD" scheme="https://blog.haysc.tech/tags/CI-CD/"/>
    
  </entry>
  
  <entry>
    <title>Building a Button Matrix using ESP Button Component</title>
    <link href="https://blog.haysc.tech/matrix-keypad-esp-iot-solution/"/>
    <id>https://blog.haysc.tech/matrix-keypad-esp-iot-solution/</id>
    <published>2024-01-10T07:33:10.000Z</published>
    <updated>2024-12-06T11:11:18.190Z</updated>
    
    <content type="html"><![CDATA[<p>In this tutorial, we’ll explore how to create a button matrix using the ESP IoT Solution <a href="https://docs.espressif.com/projects/esp-iot-solution/en/latest/input_device/button.html">button component</a>. This is particularly useful in applications requiring multiple button inputs, like keypads or control panels. We’ll use the ESP-IDF framework for programming and a <a href="https://docs.wokwi.com/parts/wokwi-membrane-keypad">Wokwi Membrane Keypad</a> for hardware interaction.</p><h2 id="Line-by-line-Explanation">Line by line Explanation</h2><p>The provided <a href="#Full-source-code">source code</a> for the ESP-IDF button matrix is a great example of how to handle multiple button inputs in an embedded system.</p><p>Let’s break it down into two key parts: initializing the button matrix and configuring button callbacks.</p><h3 id="Initializing-the-Button-Matrix">Initializing the Button Matrix</h3><p>The initialization of the button matrix is done in the <code>initialize_keypad</code> function. Here’s a step-by-step explanation:</p><ol><li><p><strong>Defining GPIO Pins:</strong></p><pre><code class="hljs c"><span class="hljs-keyword">int32_t</span> row_gpio[<span class="hljs-number">4</span>] = &#123;<span class="hljs-number">14</span>, <span class="hljs-number">13</span>, <span class="hljs-number">12</span>, <span class="hljs-number">11</span>&#125;;<span class="hljs-keyword">int32_t</span> col_gpio[<span class="hljs-number">4</span>] = &#123;<span class="hljs-number">10</span>, <span class="hljs-number">9</span>, <span class="hljs-number">46</span>, <span class="hljs-number">3</span>&#125;;</code></pre><ul><li>This code specifies the GPIO pins used for the keypad’s rows and columns. The ESP32 connects to the keypad through these GPIOs.</li></ul></li><li><p><strong>Button Configuration:</strong></p><pre><code class="hljs c"><span class="hljs-keyword">button_config_t</span> cfg = &#123;    .type = BUTTON_TYPE_MATRIX,    .long_press_time = <span class="hljs-number">3000</span>, <span class="hljs-comment">// in ms</span>    .short_press_time = <span class="hljs-number">500</span>, <span class="hljs-comment">// in ms</span>    ...&#125;;</code></pre><ul><li>Here, a <code>button_config_t</code> struct is initialized, setting the button type to a matrix. It also defines the time durations for long and short presses.</li></ul></li><li><p><strong>Creating Buttons:</strong></p><pre><code class="hljs c"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">4</span>; i++) &#123;    cfg.matrix_button_config.row_gpio_num = row_gpio[i];    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>; j &lt; <span class="hljs-number">4</span>; j++) &#123;        cfg.matrix_button_config.col_gpio_num = col_gpio[j];        g_btns[i * <span class="hljs-number">4</span> + j] = iot_button_create(&amp;cfg);        ...    &#125;&#125;</code></pre><ul><li>This nested loop iterates over rows and columns, creating buttons using <code>iot_button_create</code> and storing them in <code>g_btns</code>.</li></ul></li></ol><h3 id="Configuring-Button-Callbacks">Configuring Button Callbacks</h3><p>Callbacks are functions called in response to various button events. Each button can have multiple callbacks for different events like press down, release, etc.</p><ol><li><p><strong>Registering Callbacks:</strong></p><pre><code class="hljs c">iot_button_register_cb(g_btns[i * <span class="hljs-number">4</span> + j], BUTTON_PRESS_DOWN, button_press_down_cb, <span class="hljs-literal">NULL</span>);</code></pre><ul><li>This line registers a callback for the “press down” event. Similar lines are used for other events like “press up”, “single click”, etc.</li></ul></li><li><p><strong>Callback Functions:</strong></p><ul><li>Each callback function is defined to perform a specific action. For example, <code>button_press_down_cb</code> logs the button press down event.</li></ul><pre><code class="hljs c"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">button_press_down_cb</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg, <span class="hljs-keyword">void</span> *data)</span> </span>&#123;    ESP_LOGI(TAG, <span class="hljs-string">&quot;BTN%s: BUTTON_PRESS_DOWN&quot;</span>, get_btn_index((<span class="hljs-keyword">button_handle_t</span>)arg));&#125;</code></pre><ul><li>This function uses <code>get_btn_index</code> to determine which button was pressed and logs the event.</li></ul></li><li><p><strong>Handling Different Press Types:</strong></p><ul><li>The code includes various callback functions for handling single clicks, double clicks, long presses, etc. Each callback function is tailored to its specific event.</li></ul></li></ol><h2 id="Testing-in-Wokwi-Simulator">Testing in Wokwi Simulator</h2><p>To test the button matrix with an ESP32 in the Wokwi Simulator, follow these steps.</p><h3 id="Connection-of-the-Keypad-and-ESP32">Connection of the Keypad and ESP32</h3><p>Connect the rows and columns of the keypad to the ESP32 using the following ASCII diagram:</p><pre><code class="hljs plain">Keypad    ESP32R1  ----&gt; GPIO 14R2  ----&gt; GPIO 13R3  ----&gt; GPIO 12R4  ----&gt; GPIO 11C1  ----&gt; GPIO 10C2  ----&gt; GPIO 9C3  ----&gt; GPIO 46C4  ----&gt; GPIO 3</code></pre><h3 id="Serial-Output-Interpretation">Serial Output Interpretation</h3><p>When a button is pressed, the ESP32 will output different statuses to the serial monitor, indicating the type of press:</p><ul><li><code>BUTTON_LONG_PRESS_HOLD</code> is printed repeatedly as long as a button is held down.</li><li><code>BUTTON_PRESS_UP</code> appears when the button is released.</li><li><code>BUTTON_PRESS_DOWN</code> is shown when a button is initially pressed.</li><li>For single and double clicks, <code>BUTTON_SINGLE_CLICK</code> and <code>BUTTON_DOUBLE_CLICK</code> messages are displayed.</li><li><code>BUTTON_PRESS_REPEAT_DONE</code> is printed after a single or double click is recognized.</li></ul><p>Example output in the serial console:</p><pre><code class="hljs shell">(88295) BUTTON TEST: BTN6: BUTTON_LONG_PRESS_HOLD(88315) BUTTON TEST: BTN6: BUTTON_LONG_PRESS_HOLD(88335) BUTTON TEST: BTN6: BUTTON_LONG_PRESS_HOLD(88355) BUTTON TEST: BTN6: BUTTON_PRESS_UP(89685) BUTTON TEST: BTN9: BUTTON_PRESS_DOWN(90765) BUTTON TEST: BTN9: BUTTON_PRESS_UP(91275) BUTTON TEST: BTN9: BUTTON_SINGLE_CLICK(91275) BUTTON TEST: BTN9: BUTTON_PRESS_REPEAT_DONE(95185) BUTTON TEST: BTN8: BUTTON_PRESS_DOWN(97665) BUTTON TEST: BTN8: BUTTON_PRESS_UP(98175) BUTTON TEST: BTN8: BUTTON_SINGLE_CLICK(98175) BUTTON TEST: BTN8: BUTTON_PRESS_REPEAT_DONE(100815) BUTTON TEST: BTN1: BUTTON_PRESS_DOWN(101935) BUTTON TEST: BTN1: BUTTON_PRESS_UP(102435) BUTTON TEST: BTN1: BUTTON_SINGLE_CLICK(102435) BUTTON TEST: BTN1: BUTTON_PRESS_REPEAT_DONE</code></pre><p>This setup allows you to test various button interactions and observe their corresponding outputs in the Wokwi Simulator.</p><h2 id="Full-source-code">Full source code</h2><p>The following is the complete source code of driving a matrix keypad using the button component.</p><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&quot;esp_log.h&quot;</span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&quot;iot_button.h&quot;</span></span><span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *TAG = <span class="hljs-string">&quot;BUTTON TEST&quot;</span>;<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> BUTTON_NUM 16</span><span class="hljs-keyword">static</span> <span class="hljs-keyword">button_handle_t</span> g_btns[BUTTON_NUM] = &#123;<span class="hljs-number">0</span>&#125;;<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> NUMBER_OF_ROWS 4</span><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> NUMBER_OF_COLUMNS 4</span><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> LONG_PRESS_DURATION_MS 1500</span><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> SHORT_PRESS_DURATION_MS 500</span><span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>* keys[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS] = &#123;    &#123;<span class="hljs-string">&quot;1&quot;</span>, <span class="hljs-string">&quot;2&quot;</span>, <span class="hljs-string">&quot;3&quot;</span>, <span class="hljs-string">&quot;A&quot;</span>&#125;,    &#123;<span class="hljs-string">&quot;4&quot;</span>, <span class="hljs-string">&quot;5&quot;</span>, <span class="hljs-string">&quot;6&quot;</span>, <span class="hljs-string">&quot;B&quot;</span>&#125;,    &#123;<span class="hljs-string">&quot;7&quot;</span>, <span class="hljs-string">&quot;8&quot;</span>, <span class="hljs-string">&quot;9&quot;</span>, <span class="hljs-string">&quot;C&quot;</span>&#125;,    &#123;<span class="hljs-string">&quot;*&quot;</span>, <span class="hljs-string">&quot;0&quot;</span>, <span class="hljs-string">&quot;#&quot;</span>, <span class="hljs-string">&quot;D&quot;</span>&#125;&#125;;<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>* <span class="hljs-title">get_btn_index</span><span class="hljs-params">(<span class="hljs-keyword">button_handle_t</span> btn)</span></span><span class="hljs-function"></span>&#123;    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">size_t</span> i = <span class="hljs-number">0</span>; i &lt; BUTTON_NUM; i++) &#123;        <span class="hljs-keyword">if</span> (btn == g_btns[i]) &#123;            <span class="hljs-comment">// Calculate row and column from the linear index</span>            <span class="hljs-keyword">size_t</span> row = i / NUMBER_OF_ROWS; <span class="hljs-comment">// Integer division to get the row</span>            <span class="hljs-keyword">size_t</span> col = i % NUMBER_OF_COLUMNS; <span class="hljs-comment">// Modulo operation to get the column</span>            <span class="hljs-keyword">return</span> keys[row][col];        &#125;    &#125;    <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>; <span class="hljs-comment">// Return NULL if the button is not found</span>&#125;<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">button_press_down_cb</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg, <span class="hljs-keyword">void</span> *data)</span></span><span class="hljs-function"></span>&#123;    ESP_LOGI(TAG, <span class="hljs-string">&quot;BTN%s: BUTTON_PRESS_DOWN&quot;</span>, get_btn_index((<span class="hljs-keyword">button_handle_t</span>)arg));&#125;<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">button_press_up_cb</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg, <span class="hljs-keyword">void</span> *data)</span></span><span class="hljs-function"></span>&#123;    ESP_LOGI(TAG, <span class="hljs-string">&quot;BTN%s: BUTTON_PRESS_UP[%d]&quot;</span>, get_btn_index((<span class="hljs-keyword">button_handle_t</span>)arg), iot_button_get_ticks_time((<span class="hljs-keyword">button_handle_t</span>)arg));&#125;<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">button_press_repeat_cb</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg, <span class="hljs-keyword">void</span> *data)</span></span><span class="hljs-function"></span>&#123;    ESP_LOGI(TAG, <span class="hljs-string">&quot;BTN%s: BUTTON_PRESS_REPEAT[%d]&quot;</span>, get_btn_index((<span class="hljs-keyword">button_handle_t</span>)arg), iot_button_get_repeat((<span class="hljs-keyword">button_handle_t</span>)arg));&#125;<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">button_single_click_cb</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg, <span class="hljs-keyword">void</span> *data)</span></span><span class="hljs-function"></span>&#123;    ESP_LOGI(TAG, <span class="hljs-string">&quot;BTN%s: BUTTON_SINGLE_CLICK&quot;</span>, get_btn_index((<span class="hljs-keyword">button_handle_t</span>)arg));&#125;<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">button_double_click_cb</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg, <span class="hljs-keyword">void</span> *data)</span></span><span class="hljs-function"></span>&#123;    ESP_LOGI(TAG, <span class="hljs-string">&quot;BTN%s: BUTTON_DOUBLE_CLICK&quot;</span>, get_btn_index((<span class="hljs-keyword">button_handle_t</span>)arg));&#125;<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">button_long_press_start_cb</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg, <span class="hljs-keyword">void</span> *data)</span></span><span class="hljs-function"></span>&#123;    ESP_LOGI(TAG, <span class="hljs-string">&quot;BTN%s: BUTTON_LONG_PRESS_START&quot;</span>, get_btn_index((<span class="hljs-keyword">button_handle_t</span>)arg));&#125;<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">button_long_press_hold_cb</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg, <span class="hljs-keyword">void</span> *data)</span></span><span class="hljs-function"></span>&#123;    ESP_LOGI(TAG, <span class="hljs-string">&quot;BTN%s: BUTTON_LONG_PRESS_HOLD[%d],count is [%d]&quot;</span>, get_btn_index((<span class="hljs-keyword">button_handle_t</span>)arg), iot_button_get_ticks_time((<span class="hljs-keyword">button_handle_t</span>)arg), iot_button_get_long_press_hold_cnt((<span class="hljs-keyword">button_handle_t</span>)arg));&#125;<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">button_press_repeat_done_cb</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg, <span class="hljs-keyword">void</span> *data)</span></span><span class="hljs-function"></span>&#123;    ESP_LOGI(TAG, <span class="hljs-string">&quot;BTN%s: BUTTON_PRESS_REPEAT_DONE[%d]&quot;</span>, get_btn_index((<span class="hljs-keyword">button_handle_t</span>)arg), iot_button_get_repeat((<span class="hljs-keyword">button_handle_t</span>)arg));&#125;<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">initialize_keypad</span><span class="hljs-params">()</span> </span>&#123;    <span class="hljs-comment">// Define the GPIO pins for the keypad rows and columns</span>    <span class="hljs-keyword">int32_t</span> row_gpio[NUMBER_OF_ROWS] = &#123;<span class="hljs-number">14</span>, <span class="hljs-number">13</span>, <span class="hljs-number">12</span>, <span class="hljs-number">11</span>&#125;; <span class="hljs-comment">// Example GPIOs for rows</span>    <span class="hljs-keyword">int32_t</span> col_gpio[NUMBER_OF_COLUMNS] = &#123;<span class="hljs-number">10</span>, <span class="hljs-number">9</span>, <span class="hljs-number">46</span>, <span class="hljs-number">3</span>&#125;; <span class="hljs-comment">// Example GPIOs for columns</span>    <span class="hljs-keyword">button_config_t</span> cfg = &#123;        .type = BUTTON_TYPE_MATRIX,        .long_press_time = LONG_PRESS_DURATION_MS,        .short_press_time = SHORT_PRESS_DURATION_MS,        .matrix_button_config = &#123;            .row_gpio_num = <span class="hljs-number">0</span>,            .col_gpio_num = <span class="hljs-number">0</span>,        &#125;    &#125;;    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; NUMBER_OF_ROWS; i++) &#123;        cfg.matrix_button_config.row_gpio_num = row_gpio[i];        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>; j &lt; NUMBER_OF_COLUMNS; j++) &#123;            cfg.matrix_button_config.col_gpio_num = col_gpio[j];            g_btns[i * NUMBER_OF_ROWS + j] = iot_button_create(&amp;cfg);            iot_button_register_cb(g_btns[i * NUMBER_OF_ROWS + j], BUTTON_PRESS_DOWN, button_press_down_cb, <span class="hljs-literal">NULL</span>);            iot_button_register_cb(g_btns[i * NUMBER_OF_ROWS + j], BUTTON_PRESS_UP, button_press_up_cb, <span class="hljs-literal">NULL</span>);            iot_button_register_cb(g_btns[i * NUMBER_OF_ROWS + j], BUTTON_PRESS_REPEAT, button_press_repeat_cb, <span class="hljs-literal">NULL</span>);            iot_button_register_cb(g_btns[i * NUMBER_OF_ROWS + j], BUTTON_SINGLE_CLICK, button_single_click_cb, <span class="hljs-literal">NULL</span>);            iot_button_register_cb(g_btns[i * NUMBER_OF_ROWS + j], BUTTON_DOUBLE_CLICK, button_double_click_cb, <span class="hljs-literal">NULL</span>);            iot_button_register_cb(g_btns[i * NUMBER_OF_ROWS + j], BUTTON_LONG_PRESS_START, button_long_press_start_cb, <span class="hljs-literal">NULL</span>);            iot_button_register_cb(g_btns[i * NUMBER_OF_ROWS + j], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, <span class="hljs-literal">NULL</span>);            iot_button_register_cb(g_btns[i * NUMBER_OF_ROWS + j], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, <span class="hljs-literal">NULL</span>);        &#125;    &#125;&#125;<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">app_main</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>&#123;    initialize_keypad();&#125;</code></pre>]]></content>
    
    
    <summary type="html">Dive into the world of ESP32 and learn how to create a versatile button matrix using ESP IoT Solution button component. This detailed guide covers code explanation, Wokwi simulation, and practical insights for embedded system enthusiasts.</summary>
    
    
    
    <category term="Embedded Systems" scheme="https://blog.haysc.tech/categories/Embedded-Systems/"/>
    
    
    <category term="ESP32" scheme="https://blog.haysc.tech/tags/ESP32/"/>
    
    <category term="IoT" scheme="https://blog.haysc.tech/tags/IoT/"/>
    
    <category term="Button Matrix" scheme="https://blog.haysc.tech/tags/Button-Matrix/"/>
    
    <category term="Wokwi Simulator" scheme="https://blog.haysc.tech/tags/Wokwi-Simulator/"/>
    
    <category term="Embedded Programming" scheme="https://blog.haysc.tech/tags/Embedded-Programming/"/>
    
  </entry>
  
  <entry>
    <title>Mastering Embedded Testing in ESP-IDF: Unity and pytest-embedded with the Button Component Example</title>
    <link href="https://blog.haysc.tech/unity-pytest-embedded-button-component-example/"/>
    <id>https://blog.haysc.tech/unity-pytest-embedded-button-component-example/</id>
    <published>2024-01-10T01:33:10.000Z</published>
    <updated>2024-12-06T11:11:19.490Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Introduction">Introduction</h2><p>In the intricate world of embedded systems, ensuring the reliability and functionality of software is paramount. Embedded systems are often deployed in critical applications where a failure can have significant consequences. This is where the role of rigorous testing becomes undeniable. Testing not only helps in identifying and fixing bugs before deployment but also ensures that the system meets all the specified requirements and functions as expected in the real world.</p><p>Enter the Espressif IoT Development Framework (ESP-IDF), a popular choice for developing applications on ESP32 and other ESP-series microcontrollers. ESP-IDF not only provides a rich set of libraries and APIs for building complex applications but also offers robust tools for testing these applications. Among these tools, the Unity Testing Framework and pytest-embedded stand out for their effectiveness in testing embedded systems.</p><p><a href="https://github.com/ThrowTheSwitch/Unity">Unity</a> is a lightweight, powerful unit testing framework designed for C programming, which is the bedrock of embedded system development. It provides a set of assertions for testing code and generates clear and concise results about test passes and failures. This simplicity and power make Unity an ideal choice for testing the logic of individual components in embedded systems.</p><p>Complementing Unity, <a href="https://github.com/espressif/pytest-embedded">pytest-embedded</a> is a pytest plugin that extends the capabilities of unit testing to include the hardware layer. It allows developers to write test scripts in Python, which interact with the hardware and the Unity tests running on the device. This integration enables a comprehensive testing process, covering both the software logic and the interaction with the hardware.</p><h2 id="Understanding-the-Button-Component-in-Espressif-IoT-Solution">Understanding the Button Component in Espressif IoT Solution</h2><p>The <a href="https://github.com/espressif/esp-iot-solution/tree/master/components/button">button</a> component in the Espressif IoT Solution serves as an excellent example to demonstrate the integration and functionality of testing frameworks in embedded systems. This component is a part of the Espressif IoT Solution’s extensive library, designed to handle button inputs in embedded applications. It simplifies the process of detecting various button events like single press, long press, and double press, which are common in user interfaces for embedded devices.</p><p>The button component abstracts the complexity involved in handling physical button inputs on ESP32-based devices. It provides a convenient way to manage debouncing and interpret different types of button presses, which are essential for creating responsive and reliable user interfaces. The component supports various button types, including GPIO (General Purpose Input/Output) based buttons and touch sensor buttons, offering versatility for different hardware setups.</p><h3 id="Structure-of-the-Button-Component">Structure of the Button Component</h3><p>In the Espressif IoT Solution repository, the button component is organized in a structured manner, typical of ESP-IDF components. The structure is as follows:</p><ul><li><p><strong>Source Files</strong>: The core functionality of the button component is encapsulated in C source files. These files contain the logic for handling button inputs, debouncing algorithms, and event detection mechanisms.</p></li><li><p><strong>Header Files</strong>: Accompanying the source files are header files, which declare the functions, macros, and data structures used by the button component. These files provide the necessary interface for other parts of an application to interact with the button component.</p></li><li><p><strong>Example Applications</strong>: The component includes example applications demonstrating its usage. These examples serve as a practical guide for developers to understand how to integrate the button component into their projects.</p></li><li><p><strong>Test Directory</strong>: Crucially, the component contains a <code>test</code> directory, which is the focal point for our exploration of testing frameworks. This directory houses the test code written specifically for the button component.</p><ul><li><p><strong>Unity Test Files</strong>: Inside the test directory, you’ll find C files containing Unity tests. These files (<code>button_test.c</code>, for example) include a series of unit tests written in C, using Unity’s assertions to validate the functionality of the button component.</p></li><li><p><strong>pytest-embedded Test Scripts</strong>: Alongside the Unity tests, there are Python test scripts designed for pytest-embedded. These scripts (<code>pytest_button.py</code>, for instance) are used to automate the testing process, including flashing the test firmware to the device, running the Unity tests, and handling the interaction between the test code and the hardware.</p></li></ul></li></ul><p>The button component’s structure in the Espressif IoT Solution repository not only reflects the standard organization of components within the framework but also illustrates how embedded testing is seamlessly integrated into the development process. This integration is key to ensuring that each component of the Espressif IoT Solution is reliable and performs as expected in various scenarios.</p><h2 id="Unity-Testing-Framework-Basics-and-Integration">Unity Testing Framework: Basics and Integration</h2><p>Unity is a versatile and straightforward unit testing framework designed for C, the primary language used in embedded system programming. Its simplicity and ease of use make it an ideal choice for developers looking to implement unit tests in their embedded projects. Unity provides a set of assert macros for various data types and conditions, enabling developers to write comprehensive test cases that check the functionality of their code.</p><h3 id="How-Unity-is-Integrated-into-the-Espressif-IoT-Solution-Button-Component">How Unity is Integrated into the Espressif IoT Solution Button Component</h3><p>In the ESP-IDF framework, Unity is seamlessly integrated to facilitate unit testing of individual components like the button component. This integration is evident in the structure of the component’s directory, particularly within the <code>test</code> folder. Here, Unity tests are written as C files, which are then compiled and executed as part of the testing process. This approach allows for testing the internal logic of the component in isolation, ensuring that each function behaves as expected.</p><h3 id="Exploring-the-button-test-c-File-Understanding-Test-Cases-and-Assertions">Exploring the <code>button_test.c</code> File: Understanding Test Cases and Assertions</h3><p>The <code>button_test.c</code> file within the button component’s test directory is a concrete example of Unity in action. This file contains multiple test functions, each designed to test a specific aspect of the button component.</p><pre><code class="hljs c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&quot;unity.h&quot;</span></span><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&quot;iot_button.h&quot;</span></span><span class="hljs-comment">// Example setup function</span><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setUp</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>&#123;    <span class="hljs-comment">// Code to set up test environment</span>&#125;<span class="hljs-comment">// Example teardown function</span><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">tearDown</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>&#123;    <span class="hljs-comment">// Code to clean up after tests</span>&#125;<span class="hljs-comment">// Example test function for button press</span><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">test_button_press</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>&#123;    <span class="hljs-comment">// Initialize the button</span>    <span class="hljs-keyword">button_handle_t</span> btn_handle = iot_button_create(...);    <span class="hljs-comment">// Simulate a button press</span>    simulate_button_press(btn_handle);    <span class="hljs-comment">// Assert that the button state is as expected</span>    TEST_ASSERT_EQUAL(expected_state, actual_state);    <span class="hljs-comment">// Clean up</span>    iot_button_delete(btn_handle);&#125;<span class="hljs-comment">// Main function to run all tests</span><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>&#123;    UNITY_BEGIN();    RUN_TEST(test_button_press);    <span class="hljs-comment">// Add more RUN_TEST calls for other test functions</span>    UNITY_END();&#125;</code></pre><p>Let’s break down the key elements of this file:</p><ul><li><p><strong>Test Functions</strong>: Each test function in <code>button_test.c</code> is structured to test a particular functionality of the button component. For instance, a test function might check how the component handles a button press event.</p><pre><code class="hljs c"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">test_button_press</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>&#123;    <span class="hljs-comment">// Test code here</span>&#125;</code></pre></li><li><p><strong>Setup and Teardown</strong>: Unity allows for setup and teardown routines, which are executed before and after each test function, respectively. These routines are used to initialize the necessary environment for the test and clean up afterwards.</p><pre><code class="hljs c"><span class="hljs-comment">// Example setup function</span><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setUp</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>&#123;    <span class="hljs-comment">// Code to set up test environment</span>&#125;<span class="hljs-comment">// Example teardown function</span><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">tearDown</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>&#123;    <span class="hljs-comment">// Code to clean up after tests</span>&#125;</code></pre></li><li><p><strong>Assertions</strong>: The core of Unity’s testing capability lies in its assertions. In <code>button_test.c</code>, you’ll find various assert macros being used, such as <code>TEST_ASSERT</code>, <code>TEST_ASSERT_EQUAL</code>, and <code>TEST_ASSERT_NOT_NULL</code>. These assertions validate the outcomes of different operations performed by the button component. For example, an assertion might check if the button state is correctly updated after a simulated press event.</p><pre><code class="hljs c">TEST_ASSERT_EQUAL(expected_state, actual_state);</code></pre></li><li><p><strong>Running Tests</strong>: The Unity framework provides a mechanism to execute all the test functions in the file. This is typically done through a main function that calls <code>UNITY_BEGIN()</code>, followed by calls to each test function, and concludes with <code>UNITY_END()</code>. This process executes each test and outputs the results, indicating success or failure.</p><pre><code class="hljs c"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">app_main</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>&#123;  unity_run_menu();&#125;</code></pre></li></ul><p>The <code>button_test.c</code> file is a testament to how Unity can be effectively used to ensure the reliability of individual components in embedded systems. By writing targeted test cases and utilizing a variety of assertions, developers can confidently verify the functionality of their code, leading to more robust and error-free applications.</p><h2 id="Automating-Tests-with-pytest-embedded">Automating Tests with pytest-embedded</h2><p>pytest-embedded is a significant extension to the traditional testing approach in embedded systems. It’s a pytest plugin specifically designed to bridge the gap between software and hardware testing in the embedded domain. In the context of ESP-IDF, pytest-embedded plays a crucial role by enabling automated testing that involves both the software components and the actual hardware they run on. This tool brings the convenience and power of Python’s pytest framework to the embedded world, allowing for sophisticated test automation, hardware interaction, and comprehensive test coverage.</p><h3 id="Setting-up-pytest-embedded-for-the-Button-Component">Setting up pytest-embedded for the Button Component</h3><p>Integrating pytest-embedded with a component like the button in Espressif IoT Solution involves a few key steps. Firstly, the necessary Python dependencies, including pytest and pytest-embedded, must be installed. This setup enables the execution of pytest scripts that are written to test the button components.</p><p>In the button component’s test directory, alongside the Unity test files, there’s a specific setup for pytest-embedded. This setup typically includes a Python script (like <code>pytest_button.py</code>) that is responsible for automating the testing process. This script is configured to communicate with the ESP-IDF build system, ensuring that the correct firmware is compiled and flashed onto the target hardware for testing.</p><h3 id="Writing-Test-Scripts-A-Look-at-pytest-button-py">Writing Test Scripts: A Look at <code>pytest_button.py</code></h3><p>The <code>pytest_button.py</code> script is a prime example of how pytest-embedded is utilized in practice. This script contains Python code that defines the test environment, prepares the device under test (DUT), and specifies the test cases to be run. It uses various pytest-embedded functionalities to control the hardware, such as flashing the device, resetting it, and monitoring its output.</p><pre><code class="hljs python"><span class="hljs-keyword">import</span> pytest<span class="hljs-keyword">from</span> pytest_embedded <span class="hljs-keyword">import</span> Dut<span class="hljs-meta">@pytest.mark.target(&#x27;esp32s3&#x27;)</span><span class="hljs-meta">@pytest.mark.env(&#x27;button&#x27;)</span><span class="hljs-meta">@pytest.mark.parametrize(</span>    <span class="hljs-string">&#x27;config&#x27;</span>,    [        <span class="hljs-string">&#x27;defaults&#x27;</span>,    ],)<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_usb_stream</span>(<span class="hljs-params">dut: Dut</span>)-&gt; <span class="hljs-keyword">None</span>:</span>    dut.expect_exact(<span class="hljs-string">&#x27;Press ENTER to see the list of tests.&#x27;</span>)    dut.write(<span class="hljs-string">&#x27;[auto]&#x27;</span>)    dut.expect_unity_test_output(timeout = <span class="hljs-number">300</span>)</code></pre><p>In this script, test cases are written as functions in Python. These functions can send commands to the DUT, receive and analyze its responses, and assert conditions based on the output. The script can interact with the Unity tests running on the device, parse their results, and report back in a structured format that’s easy to analyze.</p><h3 id="How-pytest-embedded-Interacts-with-the-Hardware-and-the-Unity-Framework">How pytest-embedded Interacts with the Hardware and the Unity Framework</h3><p>pytest-embedded shines in its ability to interact directly with the hardware. When a test script is executed, pytest-embedded takes care of establishing a communication link with the DUT, typically via a serial connection. It then handles the process of flashing the device with the firmware that includes the Unity tests.</p><pre><code class="hljs python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_usb_stream</span>(<span class="hljs-params">dut: Dut</span>)-&gt; <span class="hljs-keyword">None</span>:</span>    dut.expect_exact(<span class="hljs-string">&#x27;Press ENTER to see the list of tests.&#x27;</span>)    dut.write(<span class="hljs-string">&#x27;[auto]&#x27;</span>)    dut.expect_unity_test_output(timeout = <span class="hljs-number">300</span>)</code></pre><p>During test execution, pytest-embedded sends commands to the DUT and reads its serial output (using <code>expect_exact()</code> and <code>expect_unity_test_output()</code>). This interaction is crucial for tests that require hardware manipulation or rely on hardware states. For instance, in the case of the button component, pytest-embedded can simulate button presses and releases, and then use the output from the Unity tests to verify the correct behavior of the button handling code.</p><p>Furthermore, pytest-embedded integrates smoothly with the Unity framework running on the ESP32. It captures the test results output by Unity over the serial connection, interprets them, and presents them in the pytest format. This integration allows developers to leverage the strengths of both Unity and pytest-embedded, combining detailed unit testing with high-level test automation and hardware interaction.</p><p>In summary, pytest-embedded provides a powerful and flexible way to automate testing in ESP-IDF projects. By enabling direct interaction with the hardware and seamless integration with the Unity framework, it opens up new possibilities for thorough and efficient testing of embedded systems.</p><h2 id="Running-and-Interpreting-Test-Results">Running and Interpreting Test Results</h2><p>Running tests in an ESP-IDF project using Unity and pytest-embedded involves a series of steps that ensure the software and hardware components work together seamlessly. Here’s how you can execute these tests, particularly for the button component:</p><ol><li><p><strong>Build the Test Firmware</strong>: First, use the ESP-IDF build system to compile the test firmware. This firmware includes the Unity tests written for the button component.</p></li><li><p><strong>Flash the Firmware</strong>: Once the firmware is built, it needs to be flashed onto the ESP32 device. This step is crucial as it loads the test code onto the hardware.</p></li><li><p><strong>Execute pytest Script</strong>: Run the <code>pytest_button.py</code> script using <code>pytest</code>. This script automates the process of initializing the test, interacting with the device, and executing the Unity tests.</p> <pre><code class="hljs shell">collected 1 itemtest_basic.py .                                                        [100%]============================= 1 passed in 0.01s =============================</code></pre></li><li><p><strong>Monitor the Test Execution</strong>: As the tests run, pytest-embedded communicates with the ESP32 through a serial connection, sending commands, and receiving outputs.</p></li><li><p><strong>View Results</strong>: Upon completion, the test results are displayed. pytest-embedded formats these results, making them easy to read and understand.</p></li></ol><p>The output from running the tests is a combination of Unity’s detailed test results and pytest’s high-level summary. Unity’s output typically includes information about each test case, such as the name of the test, whether it passed or failed, and any relevant messages or values. pytest-embedded captures this output and integrates it into its own reporting system, providing a comprehensive view of the test results.</p><p>The pytest summary includes the number of tests run, the number of assertions made, and a tally of passed and failed tests. It may also include details about any errors or exceptions that occurred during testing.</p><h3 id="Debugging-Common-Issues-in-Test-Cases">Debugging Common Issues in Test Cases</h3><p>When tests fail or produce unexpected results, debugging is essential to identify and resolve the underlying issues. Here are some common strategies for debugging test cases in Unity and pytest-embedded:</p><ul><li><p><strong>Review Test Assertions</strong>: Check if the assertions in your Unity tests accurately reflect the expected behavior of the component. Incorrect assertions can lead to false positives or negatives.</p></li><li><p><strong>Analyze Serial Outputs</strong>: Look at the serial output from the ESP32 during the test. This output can provide insights into what the device was doing at the time of the test and can help pinpoint where things went wrong.</p></li><li><p><strong>Check Hardware Setup</strong>: Ensure that the ESP32 device and any connected hardware are set up correctly. Hardware issues can often lead to failed tests.</p></li><li><p><strong>Utilize Logging</strong>: Both Unity and pytest-embedded offer logging capabilities. Use these logs to track the flow of the test execution and to capture detailed information about the test environment and operations.</p></li><li><p><strong>Isolate Test Cases</strong>: If you’re facing issues with a specific part of your test suite, isolate and run individual test cases. This can help in identifying the exact source of the problem.</p></li></ul><p>By following these steps and utilizing these debugging strategies, developers can effectively run and interpret test results, ensuring that their ESP-IDF projects are robust and reliable.</p><h2 id="Best-Practices-for-Writing-Effective-Tests">Best Practices for Writing Effective Tests</h2><ol><li><p><strong>Focus on One Aspect per Test</strong>: Each test case should focus on a single functionality or aspect of the code. This approach makes it easier to identify the cause of failures and enhances the readability of your tests.</p></li><li><p><strong>Use Descriptive Test Names</strong>: Name your test functions descriptively. The name should convey what the test is checking, making it easier to understand the purpose of the test at a glance.</p></li><li><p><strong>Keep Tests Independent</strong>: Ensure that each test is independent and can run on its own. Tests should not rely on the state left by previous tests or external factors.</p></li><li><p><strong>Utilize Setup and Teardown Functions</strong>: Use setup functions to prepare the environment for each test and teardown functions to clean up afterward. This practice helps maintain a consistent state across tests.</p></li><li><p><strong>Assert with Precision</strong>: Use the most specific assertion possible. For example, instead of using <code>TEST_ASSERT</code>, use <code>TEST_ASSERT_EQUAL</code> if you are checking for equality. Precise assertions give clearer feedback when a test fails.</p></li><li><p><strong>Check for Both Positive and Negative Cases</strong>: Test not only for the expected behavior (positive testing) but also for how the code handles invalid or unexpected inputs (negative testing).</p></li><li><p><strong>Regularly Run Your Tests</strong>: Integrate testing into your regular development workflow. Regular testing helps catch issues early in the development process.</p></li></ol><h3 id="Strategies-for-Automating-Tests-with-pytest-embedded">Strategies for Automating Tests with pytest-embedded</h3><ol><li><p><strong>Automate Repetitive Tasks</strong>: Use pytest-embedded to automate tasks that are repetitive and common across tests, such as flashing the device, resetting it, and standard output checks.</p></li><li><p><strong>Parameterize Tests for Different Conditions</strong>: Utilize pytest’s parameterization feature to run the same test under different conditions. This approach is efficient for testing various scenarios with minimal code duplication.</p> <pre><code class="hljs python"><span class="hljs-meta">@pytest.mark.target(&#x27;esp32s3&#x27;)</span><span class="hljs-meta">@pytest.mark.env(&#x27;led_indicator&#x27;)</span><span class="hljs-meta">@pytest.mark.parametrize(</span>  <span class="hljs-string">&#x27;config&#x27;</span>,  [      <span class="hljs-string">&#x27;defaults&#x27;</span>,  ],)</code></pre></li><li><p><strong>Simulate Real-world Scenarios</strong>: Design your tests to simulate real-world usage as closely as possible. This includes testing various hardware states and interactions.</p></li><li><p><strong>Handle Hardware Interactions Gracefully</strong>: When writing tests that interact with hardware, ensure that your test scripts can handle hardware variability and potential communication issues gracefully.</p></li><li><p><strong>Integrate Continuous Testing</strong>: Incorporate pytest-embedded tests into your continuous integration (CI) pipeline. This integration ensures that tests are run automatically on every code change, helping to maintain code quality.</p></li><li><p><strong>Use Logging for Debugging</strong>: Implement logging within your pytest scripts. Logs can be invaluable for debugging issues, especially when tests are run in an automated CI environment.</p></li><li><p><strong>Keep Test Scripts Maintainable</strong>: Write clean, readable, and maintainable test scripts. This practice is crucial as the complexity of tests grows and more people collaborate on the project.</p></li></ol><p>By following these best practices, developers can create robust and efficient test suites using Unity and pytest-embedded, leading to higher quality and more reliable ESP-IDF projects.</p><h2 id="Conclusion">Conclusion</h2><p>As we reach the end of our exploration into the world of embedded testing with Unity and pytest-embedded in ESP-IDF, let’s recap the key takeaways from our journey. We delved into the intricacies of the button component, using it as a lens to understand how testing frameworks are integrated and utilized in ESP-IDF projects. We saw how Unity provides a solid foundation for writing unit tests in C, allowing us to verify the functionality of individual components. Complementing this, pytest-embedded brings the power of Python’s pytest framework to the embedded domain, enabling automated testing that encompasses both software and hardware aspects.</p><p>The importance of testing in the development cycle of embedded systems cannot be overstated. In a field where reliability and precision are paramount, rigorous testing ensures that the software not only meets its functional requirements but also behaves predictably in real-world scenarios. The combination of Unity and pytest-embedded in ESP-IDF projects represents a robust approach to achieving this reliability. Unity allows for detailed unit testing at the code level, while pytest-embedded extends these tests to the realm of hardware, ensuring that the software interacts correctly with the physical world.</p><p>For developers working with ESP-IDF, embracing these testing frameworks is not just a best practice but a pathway to building more reliable, efficient, and high-quality embedded applications. Whether you are developing a simple IoT device or a complex embedded system, the principles and practices of testing covered here are invaluable.</p><p>As you continue your journey in embedded development with ESP-IDF, I encourage you to integrate these testing methodologies into your workflow. Experiment with writing your own test cases, automate your testing processes, and observe how this elevates the quality and robustness of your projects. Remember, every test you write is a step towards a more reliable and robust application. Happy testing!</p><h2 id="References">References</h2><p>For those who wish to delve deeper into the topics discussed in this blog post, the following resources will prove invaluable. They provide detailed information and insights into the implementation and usage of Unity and pytest-embedded in ESP-IDF, particularly with respect to the button component.</p><ol><li><p><strong>Button Component Source Code</strong>:</p><ul><li><a href="https://github.com/espressif/esp-iot-solution/tree/master/components/button">Button Component</a><ul><li>This link directs you to the GitHub repository of the Espressif IoT Solution, where you can explore the source code of the button component, including its implementation and test cases.</li></ul></li><li><a href="https://github.com/espressif/esp-iot-solution/tree/master/components/led/led_indicator">LED Indicator Component</a><ul><li>This link directs you to the GitHub repository of the Espressif IoT Solution, where you can explore the source code of the LED Indicator component, including its implementation and test cases.</li></ul></li></ul></li><li><p><strong>ESP-IDF Documentation</strong>:</p><ul><li><a href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/unit-tests.html">Unit Testing in ESP32</a><ul><li>This documentation provides a comprehensive guide on unit testing in ESP32, covering the basics of the Unity framework and how it is integrated into the ESP-IDF.</li></ul></li><li><a href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32/contribute/esp-idf-tests-with-pytest.html">pytest in ESP-IDF</a><ul><li>Here, you can find detailed information about using pytest-embedded in ESP-IDF. It offers insights into automating tests, handling hardware interactions, and integrating tests into the development cycle.</li></ul></li></ul></li></ol><p>These resources are essential for anyone looking to understand and implement effective testing strategies in their ESP-IDF projects. They offer a wealth of information that ranges from basic concepts to advanced techniques, catering to both beginners and experienced developers in the field of embedded systems.</p>]]></content>
    
    
    <summary type="html">Explore the integration and application of Unity and pytest-embedded in ESP-IDF, using the button component as a practical example. Dive into the world of embedded testing and discover how these frameworks enhance the reliability and functionality of ESP32-based applications.</summary>
    
    
    
    <category term="Embedded Systems" scheme="https://blog.haysc.tech/categories/Embedded-Systems/"/>
    
    
    <category term="ESP-IDF" scheme="https://blog.haysc.tech/tags/ESP-IDF/"/>
    
    <category term="Unit Testing" scheme="https://blog.haysc.tech/tags/Unit-Testing/"/>
    
    <category term="Unity Testing Framework" scheme="https://blog.haysc.tech/tags/Unity-Testing-Framework/"/>
    
    <category term="pytest-embedded" scheme="https://blog.haysc.tech/tags/pytest-embedded/"/>
    
    <category term="Embedded Software Development" scheme="https://blog.haysc.tech/tags/Embedded-Software-Development/"/>
    
    <category term="Python" scheme="https://blog.haysc.tech/tags/Python/"/>
    
    <category term="C Programming" scheme="https://blog.haysc.tech/tags/C-Programming/"/>
    
  </entry>
  
  <entry>
    <title>Streamlining Keypad Testing: C++ Macros and Template Metaprogramming</title>
    <link href="https://blog.haysc.tech/unit-testing-metaprogramming-cpp/"/>
    <id>https://blog.haysc.tech/unit-testing-metaprogramming-cpp/</id>
    <published>2024-01-09T07:33:10.000Z</published>
    <updated>2024-12-06T11:11:19.490Z</updated>
    
    <content type="html"><![CDATA[<p>Testing a keypad with multiple buttons can be a daunting task, especially when dealing with both short and long press functionalities for each button. In this blog post, we explore how to streamline this process using C++ macros and template meta-programming, particularly in the context of the ESP-IDF unity unit testing framework. This approach is not only efficient but also makes your code more maintainable and readable.</p><h2 id="Traditional-Approach">Traditional Approach</h2><p>Initially, testing each button on a keypad might involve writing separate functions for each test case. For instance, if you have a keypad with eight buttons, and you need to test both short and long presses for each button, you end up with a total of 32 functions - eight buttons times two press types times two roles (master and slave).</p><h3 id="Example-of-Traditional-Test-Functions">Example of Traditional Test Functions</h3><pre><code class="hljs c"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">test_button_1_short_press_master</span><span class="hljs-params">()</span> </span>&#123;    <span class="hljs-comment">// Master logic for button 1 short press</span>&#125;<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">test_button_1_short_press_slave</span><span class="hljs-params">()</span> </span>&#123;    <span class="hljs-comment">// Slave logic for button 1 short press</span>&#125;<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">test_button_1_long_press_master</span><span class="hljs-params">()</span> </span>&#123;    <span class="hljs-comment">// Master logic for button 1 long press</span>&#125;<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">test_button_1_long_press_slave</span><span class="hljs-params">()</span> </span>&#123;    <span class="hljs-comment">// Slave logic for button 1 long press</span>&#125;<span class="hljs-comment">// Repeat for each button and press type</span></code></pre><p>This approach quickly becomes cumbersome and hard to manage. Imagine the amount of redundant code and the effort needed to maintain or update these test cases.</p><h2 id="Leveraging-C-Templates-and-Macros">Leveraging C++ Templates and Macros</h2><p>To tackle this challenge, we turn to C++ templates and macros. The idea is to define a generic test function template and then instantiate this template for each specific button and press type using macros. This method significantly reduces the amount of code and makes it more manageable.</p><h3 id="Template-Function-Definitions">Template Function Definitions</h3><pre><code class="hljs cpp"><span class="hljs-comment">// Enum to differentiate between press types</span><span class="hljs-keyword">enum</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PressType</span> &#123;</span>    ShortPress,    LongPress&#125;;<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">int</span> Button, PressType Press&gt;<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">test_button_master</span><span class="hljs-params">()</span> </span>&#123;    <span class="hljs-comment">// Master logic for button press, customized based on Button and Press</span>&#125;<span class="hljs-keyword">template</span> &lt;<span class="hljs-keyword">int</span> Button, PressType Press&gt;<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">test_button_slave</span><span class="hljs-params">()</span> </span>&#123;    <span class="hljs-comment">// Slave logic for button press, customized based on Button and Press</span>&#125;</code></pre><p>These templates act as blueprints for generating the specific test functions we need. They take two template parameters: the button number and the press type (short or long).</p><h3 id="Macro-for-Instantiating-Test-Cases">Macro for Instantiating Test Cases</h3><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> CREATE_TEST_CASES(Button) \</span>    TEST_CASE_MULTIPLE_DEVICES(<span class="hljs-string">&quot;Button &quot;</span> #Button <span class="hljs-string">&quot; Short Press Test&quot;</span>, <span class="hljs-string">&quot;[keypad]&quot;</span>, \                               test_button_master&lt;Button, PressType::ShortPress&gt;, \                               test_button_slave&lt;Button, PressType::ShortPress&gt;); \    TEST_CASE_MULTIPLE_DEVICES(<span class="hljs-string">&quot;Button &quot;</span> #Button <span class="hljs-string">&quot; Long Press Test&quot;</span>, <span class="hljs-string">&quot;[keypad]&quot;</span>, \                               test_button_master&lt;Button, PressType::LongPress&gt;, \                               test_button_slave&lt;Button, PressType::LongPress&gt;)</code></pre><p>This macro <code>CREATE_TEST_CASES</code> takes a button number and expands to create two test cases: one for a short press and one for a long press. It uses the C++ preprocessor stringizing operator <code>#</code> to convert the button number into a string.</p><h3 id="Final-Code-Structure">Final Code Structure</h3><p>With the templates and macro in place, our final code structure for creating all the necessary test cases becomes remarkably succinct:</p><pre><code class="hljs cpp">CREATE_TEST_CASES(<span class="hljs-number">1</span>);CREATE_TEST_CASES(<span class="hljs-number">2</span>);<span class="hljs-comment">// ... up to button 8</span></code></pre><h2 id="Conclusion">Conclusion</h2><p>By embracing the power of C++ templates and macros, we’ve transformed a potentially unwieldy testing task into a manageable and scalable solution. This approach not only saves time and effort in the initial coding phase but also simplifies future maintenance and updates, allowing for more robust and reliable testing of complex hardware like keypads.</p>]]></content>
    
    
    <summary type="html">Discover how to efficiently test keypads using C++ macros and template metaprogramming, simplifying your code and boosting test coverage.</summary>
    
    
    
    <category term="Programming" scheme="https://blog.haysc.tech/categories/Programming/"/>
    
    
    <category term="ESP-IDF" scheme="https://blog.haysc.tech/tags/ESP-IDF/"/>
    
    <category term="C++" scheme="https://blog.haysc.tech/tags/C/"/>
    
    <category term="Template Metaprogramming" scheme="https://blog.haysc.tech/tags/Template-Metaprogramming/"/>
    
    <category term="Macros" scheme="https://blog.haysc.tech/tags/Macros/"/>
    
    <category term="Unit Testing" scheme="https://blog.haysc.tech/tags/Unit-Testing/"/>
    
  </entry>
  
  <entry>
    <title>解决 Flutter 编译问题：适应中国网络环境</title>
    <link href="https://blog.haysc.tech/flutter-app-china-stuck-change-source/"/>
    <id>https://blog.haysc.tech/flutter-app-china-stuck-change-source/</id>
    <published>2024-01-05T15:19:00.000Z</published>
    <updated>2024-12-06T11:11:17.994Z</updated>
    
    <content type="html"><![CDATA[<p>在 Flutter 开发过程中，尤其是在中国这样的特殊网络环境下，经常会遇到各种编译和构建问题。本文将分享我在编译 Flutter 安卓手机 App 应用时遇到的问题、原因分析以及解决方案。</p><h2 id="症状描述">症状描述</h2><p>在执行 <code>flutter run</code> 命令时，编译过程卡在了 <code>Running Gradle task 'assembleDebug'...</code> 这一步。通过使用 <code>flutter run -v</code> 查看详细日志，发现问题出现在下载 Kotlin 插件这一环节。</p><pre><code class="hljs shell"><span class="hljs-meta">&gt;</span><span class="bash"> flutter run -v</span>[ +105 ms] Downloadinghttps://jcenter.bintray.com/org/jetbrains/kotlin/kotlin-gradle-plugin/1.7.10/kotlin-gradle-plugin-1.7.10-gradle70.jar touse_path\.gradle\.tmp\gradle_download56489745132789bin</code></pre><h2 id="成因分析">成因分析</h2><p>由于中国大陆的网络环境受到 GFW 的影响，访问国外服务器时常会遇到连接缓慢或者无法连接的问题。这直接导致了从 Gradle 官方网站和 JCenter 等源下载依赖时的困难。</p><h2 id="解决方法">解决方法</h2><h3 id="1-修改-build-gradle">1. 修改 <code>build.gradle</code></h3><p>在项目的 <code>build.gradle</code> 文件中，我替换了原有的 <code>google()</code> 和 <code>jcenter()</code> 源为阿里云提供的Maven仓库。这样可以通过国内的镜像加速下载过程。</p><pre><code class="hljs diff">buildscript &#123;    ext.kotlin_version = &#x27;1.7.10&#x27;    repositories &#123;<span class="hljs-deletion">-       google()</span><span class="hljs-deletion">-       jcenter()</span><span class="hljs-addition">+       maven &#123; url &#x27;https://maven.aliyun.com/repository/google&#x27; &#125;</span><span class="hljs-addition">+       maven &#123; url &#x27;https://maven.aliyun.com/repository/jcenter&#x27; &#125;</span><span class="hljs-addition">+       maven &#123; url &#x27;https://maven.aliyun.com/nexus/content/groups/public&#x27; &#125;</span>    &#125;    ...&#125;allprojects &#123;    repositories &#123;<span class="hljs-deletion">-       google()</span><span class="hljs-deletion">-       jcenter()</span><span class="hljs-addition">+       maven &#123; url &#x27;https://maven.aliyun.com/repository/google&#x27; &#125;</span><span class="hljs-addition">+       maven &#123; url &#x27;https://maven.aliyun.com/repository/jcenter&#x27; &#125;</span><span class="hljs-addition">+       maven &#123; url &#x27;https://maven.aliyun.com/nexus/content/groups/public&#x27; &#125;</span>    &#125;&#125;</code></pre><h3 id="2-修改-gradle-wrapper-properties">2. 修改 <code>gradle-wrapper.properties</code></h3><p>将 Gradle 的分发 URL 从官方地址更改为腾讯云提供的镜像地址，这样可以更快地下载 Gradle 包。</p><pre><code class="hljs diff">distributionBase=GRADLE_USER_HOMEdistributionPath=wrapper/distszipStoreBase=GRADLE_USER_HOMEzipStorePath=wrapper/dists<span class="hljs-deletion">- distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip</span><span class="hljs-addition">+ distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-7.5.1-all.zip</span></code></pre><h2 id="结果">结果</h2><p>通过上述修改后，我成功解决了编译卡顿的问题，现在可以顺利在模拟器上运行应用。</p><h2 id="小结">小结</h2><p>对于在中国进行 Flutter 开发的开发者来说，适应当地的网络环境是非常重要的。通过选择合适的镜像源，可以显著提高依赖下载速度，从而提升开发效率。</p><p>希望我的经验能帮助到遇到类似问题的开发者。</p><p>参考：</p><ul><li>《<a href="https://rocket049.cn/flutter-android.md">如何解决flutter编译安卓程序速度慢的问题</a>》</li></ul>]]></content>
    
    
    <summary type="html">本文分享了在中国特殊网络环境下，解决 Flutter 应用编译卡顿问题的经验。详细介绍了问题的症状、成因分析以及具体的解决方法，以使用国内镜像源加速依赖下载。适用于在中国进行 Flutter 开发的开发者，旨在帮助他们提高开发效率，顺利编译和运行Flutter应用。</summary>
    
    
    
    <category term="编程" scheme="https://blog.haysc.tech/categories/%E7%BC%96%E7%A8%8B/"/>
    
    
    <category term="Flutter" scheme="https://blog.haysc.tech/tags/Flutter/"/>
    
    <category term="Kotlin" scheme="https://blog.haysc.tech/tags/Kotlin/"/>
    
    <category term="Dart" scheme="https://blog.haysc.tech/tags/Dart/"/>
    
    <category term="网络优化" scheme="https://blog.haysc.tech/tags/%E7%BD%91%E7%BB%9C%E4%BC%98%E5%8C%96/"/>
    
  </entry>
  
  <entry>
    <title>Connecting ESP-IDF MCU to MQTT Broker with ESP-MQTT-C++</title>
    <link href="https://blog.haysc.tech/esp-mqtt-cxx-thingsboard/"/>
    <id>https://blog.haysc.tech/esp-mqtt-cxx-thingsboard/</id>
    <published>2023-10-14T12:44:10.000Z</published>
    <updated>2024-12-06T11:11:17.990Z</updated>
    
    <content type="html"><![CDATA[<p>In this tutorial, we’ll explore how to connect an ESP-IDF MCU to an MQTT broker using the ESP-MQTT-C++ component. This component is a wrapper over the <code>esp_mqtt</code> client, designed to provide a higher-level API for working with MQTT in C++ applications.</p><blockquote><p>If you’re using C instead of C++, you can find the corresponding article on my blog.</p></blockquote><h2 id="Prerequisites">Prerequisites</h2><p>Before you begin, make sure you have the necessary prerequisites in place:</p><ol><li><p>Clone the ESP Protocols repository from Espressif by running the following command:</p><pre><code class="hljs shell">git clone https://github.com/espressif/esp-protocols</code></pre></li><li><p>In the <code>esp-protocols</code> repository, navigate to the <code>./components/esp_mqtt_cxx/examples</code> directory. You can either modify the TCP or SSL examples based on your requirements. The rest of this article is based on the TCP example, but the SSL example should behave similarly.</p></li></ol><h2 id="Implementation">Implementation</h2><h3 id="Configure-MQTT-Broker">Configure MQTT Broker</h3><p>To connect to your MQTT broker with an URL, port number, and a username, you should configure it as follows:</p><pre><code class="hljs cpp">mqtt::BrokerConfiguration broker&#123;    .address = &#123;        mqtt::BrokerAddress&#123;            .address = &#123;mqtt::URI&#123;<span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>&#123;CONFIG_BROKER_URL&#125;&#125;&#125;, <span class="hljs-comment">// Add your Broker URL</span>            .port = <span class="hljs-number">1883</span>  <span class="hljs-comment">// Add the Broker port here</span>        &#125;    &#125;,    .security =  mqtt::Insecure&#123;&#125;&#125;;mqtt::ClientCredentials credentials&#123;    .username = <span class="hljs-string">&quot;JohnDoe&quot;</span>, <span class="hljs-comment">// Fill in your username</span>&#125;;mqtt::Configuration config&#123;&#125;;MyClient client&#123;broker, credentials, config&#125;;</code></pre><p>For Thingsboard users, your username could be your <code>$ACCESS_TOKEN</code>. This is a straightforward way (though not as secure) to connect to Thingsboard with the MQTT protocol. The alternative authentication options include X.509 Certificates, Basic MQTT Credentials (Client ID, Username, Password).</p><h3 id="Sending-MQTT-JSON">Sending MQTT JSON</h3><p>In ESP-MQTT, the <code>MQTT_EVENT_CONNECTED</code> event is posted by the MQTT client when it successfully establishes a connection to the broker. This event indicates that the client is ready to send and receive data.</p><blockquote><p>For information on other MQTT events and their meanings, please refer to <a href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mqtt.html#events">Espressif’s MQTT documentation</a>.</p></blockquote><p>To send MQTT JSON data when the <code>MQTT_EVENT_CONNECTED</code> event occurs, you can override the <code>mqtt::Client::on_connected()</code> method. Here’s how you can create a loop that runs every five seconds, prepares a JSON string containing telemetry data, and uses the <code>publish()</code> method to send the message to the topic <code>&quot;v1/devices/me/telemetry&quot;</code>:</p><pre><code class="hljs cpp"><span class="hljs-keyword">private</span>:    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">on_connected</span><span class="hljs-params">(<span class="hljs-keyword">esp_mqtt_event_handle_t</span> <span class="hljs-keyword">const</span> event)</span> <span class="hljs-keyword">override</span></span><span class="hljs-function">    </span>&#123;        <span class="hljs-keyword">using</span> mqtt::QoS;        subscribe(messages.get());        subscribe(sent_load.get(), QoS::AtMostOnce);        <span class="hljs-keyword">while</span>(<span class="hljs-literal">true</span>)        &#123;            <span class="hljs-comment">// dummy telemetry JSON</span>            <span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span> telemetryData = <span class="hljs-string">&quot;&#123;\&quot;stringKey\&quot;:\&quot;value1\&quot;,\&quot;booleanKey\&quot;:true,\&quot;doubleKey\&quot;:42.0,\&quot;longKey\&quot;:73,\&quot;jsonKey\&quot;:&#123;\&quot;someNumber\&quot;:42,\&quot;someArray\&quot;:[1,2,3],\&quot;someNestedObject\&quot;:&#123;\&quot;key\&quot;:\&quot;value\&quot;&#125;&#125;&#125;&quot;</span>;            <span class="hljs-comment">// Define a message struct with the telemetry data, QoS, and retain configuration</span>            mqtt::Message&lt;<span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>&gt; message&#123;                .data = telemetryData,                .qos = QoS::AtLeastOnce,                .retain = mqtt::Retain::NotRetained            &#125;;            publish(<span class="hljs-string">&quot;v1/devices/me/telemetry&quot;</span>, message);            vTaskDelay(<span class="hljs-number">5000</span> / portTICK_PERIOD_MS);        &#125;    &#125;</code></pre><p>With this setup, your ESP-IDF MCU will successfully connect to the MQTT broker and periodically send telemetry data as JSON payloads to the specified topic.</p><h2 id="Further-Resources-for-Customization">Further Resources for Customization</h2><p>If you’re eager to dive deeper into customizing your ESP-MQTT-C++ implementation and want to explore more details or cross-reference the C version of the documentation, here are some valuable resources:</p><ul><li><p><strong><a href="https://docs.espressif.com/projects/esp-protocols/esp_mqtt_cxx/docs/latest/index.html">ESP MQTT C++ Documentation</a>:</strong> This official documentation for the ESP MQTT C++ component provides insights into the specifics of working with the C++ wrapper. You’ll find detailed information on the C++ API and various configuration options. It’s an essential resource for understanding the ESP-MQTT-C++ component.</p></li><li><p><strong><a href="https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/mqtt.html">ESP MQTT C Documentation</a>:</strong> The C version of the ESP MQTT documentation offers comprehensive information about the MQTT client in ESP-IDF. While the C++ documentation may not be as detailed, it’s a good practice to cross-reference the C documentation when working with the C++ component. This will help you gain a more comprehensive understanding of MQTT in the ESP-IDF ecosystem and troubleshoot any issues that might arise during customization.</p></li></ul><p>By leveraging these resources, you can explore advanced features, troubleshoot potential problems, and fine-tune your ESP-MQTT-C++ implementation to meet the specific requirements of your IoT project. Don’t hesitate to experiment, innovate, and make the most of the flexibility that the ESP-IDF framework and ESP-MQTT-C++ offer.</p>]]></content>
    
    
    <summary type="html">Learn how to connect an ESP-IDF MCU to an MQTT broker using the ESP-MQTT-C++ component. This tutorial covers prerequisites, implementation steps, and provides valuable resources for customization. Explore the power of ESP-IDF and MQTT for your IoT projects.</summary>
    
    
    
    <category term="Embedded System" scheme="https://blog.haysc.tech/categories/Embedded-System/"/>
    
    
    <category term="ESP-IDF" scheme="https://blog.haysc.tech/tags/ESP-IDF/"/>
    
    <category term="MQTT Broker" scheme="https://blog.haysc.tech/tags/MQTT-Broker/"/>
    
    <category term="C++" scheme="https://blog.haysc.tech/tags/C/"/>
    
    <category term="Thingsboard" scheme="https://blog.haysc.tech/tags/Thingsboard/"/>
    
    <category term="IoT Development" scheme="https://blog.haysc.tech/tags/IoT-Development/"/>
    
  </entry>
  
  <entry>
    <title>Using Git Repositories as Dependencies in ESP-IDF Projects</title>
    <link href="https://blog.haysc.tech/esp-idf-dependency-git-repo/"/>
    <id>https://blog.haysc.tech/esp-idf-dependency-git-repo/</id>
    <published>2023-08-23T05:02:10.000Z</published>
    <updated>2024-12-06T11:11:17.990Z</updated>
    
    <content type="html"><![CDATA[<p>Managing project dependencies is a crucial aspect of modern software development. When working on ESP-IDF (Espressif IoT Development Framework) projects, efficiently handling dependencies is equally important. The IDF Component Manager is a powerful tool that automates the process of downloading and integrating dependencies for ESP-IDF CMake projects. In this blog post, we’ll delve into the process of incorporating Git repositories as dependencies in your ESP-IDF projects using the IDF Component Manager.</p><h2 id="What-is-IDF-Component-Manager">What is IDF Component Manager?</h2><p>The IDF Component Manager simplifies the management of dependencies in ESP-IDF projects. It automatically downloads required components during the CMake configuration process, ensuring a seamless development experience. These components can come from the official component registry or from external Git repositories.</p><h2 id="Adding-Git-Repository-Dependencies">Adding Git Repository Dependencies</h2><p>To include a dependency from a Git repository, you need to provide the URL of the repository itself. The IDF Component Manager uses this information to fetch the necessary code for your project.</p><blockquote><p>The <code>idf_component.yml</code> file is a YAML file that describes the component. The file is located in the root directory of the component.</p></blockquote><p>Here’s a basic example of how to define a Git repository dependency in your <code>idf_component.yml</code> file:</p><pre><code class="hljs yml"><span class="hljs-attr">dependencies:</span>  <span class="hljs-attr">LovyanGFX:</span>    <span class="hljs-attr">git:</span> <span class="hljs-string">https://github.com/lovyan03/LovyanGFX.git</span></code></pre><p>In this example, <code>LovyanGFX</code> is the name of the dependency, and we’ve provided the URL of the Git repository. The IDF Component Manager will automatically fetch the required code from the <a href="https://github.com/lovyan03/LovyanGFX">repository</a> when you run CMake (aka <code>idf.py build</code>) for your project.</p><h3 id="Specifying-Dependency-Versions">Specifying Dependency Versions</h3><p>You can also specify a particular version of the Git repository dependency using the <code>version</code> field. This ensures that your project consistently uses a specific version of the dependency, promoting stability. There are several ways to specify the version:</p><ol><li><p>Using Git Tags:</p><p>Git tags are labels that mark specific points in a repository’s history. They are commonly used to represent releases or stable versions of software. To specify a version using a Git tag, modify your dependency configuration as follows:</p><pre><code class="hljs yml"><span class="hljs-attr">dependencies:</span>  <span class="hljs-attr">LovyanGFX:</span>    <span class="hljs-attr">git:</span> <span class="hljs-string">https://github.com/lovyan03/LovyanGFX.git</span>    <span class="hljs-attr">version:</span> <span class="hljs-number">1.1</span><span class="hljs-number">.7</span></code></pre></li><li><p>Using Commit IDs:</p><p>Each commit in a Git repository has a unique identifier known as a commit hash. You can use a commit hash to specify a specific point in the repository’s history. This can be helpful when you need to ensure that your project uses a precise commit. To use a commit ID as a version, modify your dependency configuration as follows:</p><pre><code class="hljs yml"><span class="hljs-attr">dependencies:</span>  <span class="hljs-attr">LovyanGFX:</span>    <span class="hljs-attr">git:</span> <span class="hljs-string">https://github.com/lovyan03/LovyanGFX.git</span>    <span class="hljs-attr">version:</span> <span class="hljs-string">19a2e711e70a5a072d5463be6c09c06204a35835</span></code></pre></li></ol><h3 id="Conclusion">Conclusion</h3><p>Effectively managing project dependencies is essential for maintaining a robust and efficient development process. With the IDF Component Manager, incorporating Git repositories as dependencies in ESP-IDF projects has become more accessible than ever. By specifying the repository URL and optionally providing a version, you can seamlessly integrate external code into your project. This not only simplifies development but also encourages modular and reusable code practices.</p><p>As you continue your journey with ESP-IDF and the IDF Component Manager, remember to regularly update your dependencies to take advantage of the latest features and bug fixes. Additionally, familiarize yourself with the Git repository’s documentation to ensure you’re using the correct version references and leveraging the full potential of your chosen dependencies. Happy coding!</p>]]></content>
    
    
    <summary type="html">Managing project dependencies is a crucial aspect of modern software development. When working on ESP-IDF (Espressif IoT Development Framework) projects, efficiently handling dependencies is equally important. The IDF Component Manager is a powerful tool that automates the process of downloading and integrating dependencies for ESP-IDF CMake projects. In this blog post, we&#39;ll delve into the process of incorporating Git repositories as dependencies in your ESP-IDF projects using the IDF Component Manager.</summary>
    
    
    
    <category term="Embedded System" scheme="https://blog.haysc.tech/categories/Embedded-System/"/>
    
    
    <category term="ESP-IDF" scheme="https://blog.haysc.tech/tags/ESP-IDF/"/>
    
    <category term="components" scheme="https://blog.haysc.tech/tags/components/"/>
    
    <category term="dependency" scheme="https://blog.haysc.tech/tags/dependency/"/>
    
  </entry>
  
  <entry>
    <title>Intune Win32 Magic: Deploying Modbus Poll with Silent Install</title>
    <link href="https://blog.haysc.tech/modbus-poll-intune-mem-endpoint-deploy/"/>
    <id>https://blog.haysc.tech/modbus-poll-intune-mem-endpoint-deploy/</id>
    <published>2023-07-05T07:15:15.000Z</published>
    <updated>2024-12-06T11:11:18.190Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Introduction">Introduction</h2><p>Modbus Poll is a popular software tool used in industrial automation for testing and troubleshooting Modbus communication. When deploying applications within an organization, it’s essential to have a streamlined process that allows for silent installation and configuration. In this blog post, we will explore how to convert the Modbus Poll executable file to an Intune app using the Intune MEM Installation Behavior User. Additionally, we will cover the necessary steps to achieve a silent install using the command line.</p><h2 id="Converting-Modbus-Poll-Exe-to-Intune-App">Converting Modbus Poll Exe to Intune App</h2><p>To convert the Modbus Poll executable file to an Intune app, we will utilize the Microsoft Win32 Content Prep Tool, available on GitHub. This tool allows us to wrap Win32 applications, making them compatible with Intune. Let’s walk through the process using an example of wrapping Fiddler as an Intune app:</p><ol><li>Download the Microsoft Win32 Content Prep Tool from the following repository: https://github.com/microsoft/Microsoft-Win32-Content-Prep-Tool.</li><li>Open a command prompt and navigate to the directory where the Win32 Content Prep Tool is saved.</li><li>Execute the following command to begin the conversion process:</li></ol><pre><code class="hljs shell">IntuneWinAppUtil.exe</code></pre><ol start="5"><li>When prompted, specify the source folder where the Modbus Poll setup file is located, for example:</li></ol><pre><code class="hljs shell">Please specify the source folder: C:\Sources\Win32 App\Modbus Poll\Source</code></pre><ol start="6"><li>Provide the name of the Modbus Poll setup file:</li></ol><pre><code class="hljs shell">Please specify the setup file: ModbusPollSetup.exe</code></pre><ol start="7"><li>Choose the output folder where the Intune-wrapped package will be saved:</li></ol><pre><code class="hljs shell">Please specify the output folder: C:\Sources\Win32 App\Modbus Poll\IntuneWinPackage</code></pre><ol start="8"><li>Optionally, if you have a catalog folder to include, you can specify its location; otherwise, choose “N” to proceed without it.</li></ol><h2 id="Intune-settings">Intune settings</h2><ul><li><strong>Step 1:</strong> Login to endpoint.microsoft.com Portal: Log in to the endpoint.microsoft.com portal using your administrator credentials. This portal provides a comprehensive management interface for Intune.</li><li><strong>Step 2:</strong> Navigate to App Management: Once logged in, go to the “All Apps” section and select “Windows”. This is where you can add a new app. Click on the “+Add” button to start the app creation process.</li><li><strong>Step 3:</strong> Select the App Type: During the app creation process, you need to specify the app type you want to deploy. Choose the Intune app type called “Windows app (Win32)” and click the “Select” button to proceed.</li><li><strong>Step 4:</strong> Upload the IntuneWin Package: You can now upload the IntuneWin package that you created earlier. Click on the “Select app package file” hyperlink and locate the “FiddlerSetup.intunewin” file you generated previously. Upload this file to continue.</li><li><strong>Step 5:</strong> Configure Install and Uninstall Commands: Navigate to the “Program” page within the app configuration settings. Here, you will need to specify the installation and uninstallation commands for the Modbus Poll application.</li></ul><p>For the installation command, enter the following:</p><pre><code class="hljs shell">ModbusPollSetup64Bit.exe /S</code></pre><p>For the uninstallation command, enter:</p><pre><code class="hljs shell">C:\Program Files\Modbus Tools\Modbus Poll\uninstall.exe /S</code></pre><p>After completing these configuration steps, proceed through the remaining sections to customize any additional settings as per your organization’s requirements. Once done, save the settings and deploy the Modbus Poll application to your desired target devices.</p><h3 id="Detection-rules">Detection rules</h3><p>…To be completed</p><h2 id="Conclusion">Conclusion</h2><p>By following this tutorial, you have successfully configured the necessary Intune settings to deploy Modbus Poll within your organization. The silent installation and uninstallation commands provided ensure a seamless deployment experience while meeting the specific requirements of your environment.</p><p>Silent installations are particularly useful in enterprise environments where large-scale deployments are necessary. Implementing Modbus Poll in this manner allows for efficient testing and troubleshooting of Modbus communication while minimizing disruption to end-users.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;Introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Modbus Poll is a popular software tool used in industrial automation for testing and troubleshoot</summary>
      
    
    
    
    <category term="IT management" scheme="https://blog.haysc.tech/categories/IT-management/"/>
    
    
  </entry>
  
</feed>