Jekyll2024-02-06T08:02:16+00:00https://tewarid.github.io/feed.xmlA Mutable LogA blog by Devendra TewariPicamera2 performance2023-01-26T00:00:00+00:002023-01-26T00:00:00+00:00https://tewarid.github.io/2023/01/26/picamera2-performance<h1 id="picamera2-performance">Picamera2 performance</h1>
<p><a href="https://github.com/raspberrypi/picamera2">Picamera2</a> is a Python library based on libcamera that replaces <a href="https://github.com/waveform80/picamera">Picamera</a>.</p>
<p>This post discusses sample code that captures full-resolution (5MP) still images from a OV5604 (camera module V1) sensor and its performance, on a Raspberry Pi 4 with both 32-bit and 64-bit Raspberry Pi OS. Raspberry Pi OS already has all the dependencies required to run the sample code. <code class="language-plaintext highlighter-rouge">/etc/os-release</code> shows the Raspberry Pi OS version to be <code class="language-plaintext highlighter-rouge">11 (bullseye)</code>. <code class="language-plaintext highlighter-rouge">apt info libcamera0</code> shows its version to be <code class="language-plaintext highlighter-rouge">0~git20230124+9b860a66-1</code>.</p>
<p>Power supplies can significantly influence performance—always use the original 3A power adapter, or better. SD cards can also influence performance, use <code class="language-plaintext highlighter-rouge">fio</code> to benchmark and compare different cards</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>fio
fio <span class="nt">--randrepeat</span><span class="o">=</span>1 <span class="nt">--ioengine</span><span class="o">=</span>libaio <span class="nt">--direct</span><span class="o">=</span>1 <span class="nt">--gtod_reduce</span><span class="o">=</span>1 <span class="nt">--name</span><span class="o">=</span><span class="nb">test</span> <span class="nt">--filename</span><span class="o">=</span><span class="nb">test</span> <span class="nt">--bs</span><span class="o">=</span>4k <span class="nt">--iodepth</span><span class="o">=</span>64 <span class="nt">--size</span><span class="o">=</span>32M <span class="nt">--readwrite</span><span class="o">=</span>randrw <span class="nt">--rwmixread</span><span class="o">=</span>75
</code></pre></div></div>
<h2 id="capture-to-file-sequentially">Capture to file sequentially</h2>
<p>The code below sequentially captures twenty full-resolution JPEG images using <code class="language-plaintext highlighter-rouge">capture_file</code> function</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">picamera2</span> <span class="kn">import</span> <span class="n">Picamera2</span><span class="p">,</span> <span class="n">Preview</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="n">picam2</span> <span class="o">=</span> <span class="n">Picamera2</span><span class="p">()</span>
<span class="n">camera_config</span> <span class="o">=</span> <span class="n">picam2</span><span class="p">.</span><span class="n">create_still_configuration</span><span class="p">()</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">configure</span><span class="p">(</span><span class="n">camera_config</span><span class="p">)</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
<span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"capture </span><span class="si">{</span><span class="n">counter</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">capture_file</span><span class="p">(</span><span class="sa">f</span><span class="s">"test</span><span class="si">{</span><span class="n">counter</span><span class="si">}</span><span class="s">.jpg"</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">counter</span> <span class="o">==</span> <span class="mi">20</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">end</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="n">end</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span>
</code></pre></div></div>
<p>It takes <code class="language-plaintext highlighter-rouge">5.64</code> seconds to run on 32-bit Raspberry Pi OS and <code class="language-plaintext highlighter-rouge">3.85</code> seconds on 64-bit.</p>
<h2 id="capture-and-convert-sequentially">Capture and convert sequentially</h2>
<p>The code below is quite similar to the one above, but it makes a <a href="https://github.com/python-pillow/Pillow">Python Image Library</a> (PIL) image from the capture buffer using <code class="language-plaintext highlighter-rouge">make_image</code> helper function, then calls <code class="language-plaintext highlighter-rouge">save</code></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">picamera2</span> <span class="kn">import</span> <span class="n">Picamera2</span><span class="p">,</span> <span class="n">Preview</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="n">picam2</span> <span class="o">=</span> <span class="n">Picamera2</span><span class="p">()</span>
<span class="n">camera_config</span> <span class="o">=</span> <span class="n">picam2</span><span class="p">.</span><span class="n">create_still_configuration</span><span class="p">()</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">configure</span><span class="p">(</span><span class="n">camera_config</span><span class="p">)</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
<span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"capture </span><span class="si">{</span><span class="n">counter</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="p">(</span><span class="nb">buffer</span><span class="p">,</span> <span class="p">),</span> <span class="n">metadata</span> <span class="o">=</span> <span class="n">picam2</span><span class="p">.</span><span class="n">capture_buffers</span><span class="p">([</span><span class="s">"main"</span><span class="p">])</span>
<span class="n">img</span> <span class="o">=</span> <span class="n">picam2</span><span class="p">.</span><span class="n">helpers</span><span class="p">.</span><span class="n">make_image</span><span class="p">(</span><span class="nb">buffer</span><span class="p">,</span> <span class="n">camera_config</span><span class="p">[</span><span class="s">"main"</span><span class="p">])</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">helpers</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">metadata</span><span class="p">,</span> <span class="sa">f</span><span class="s">"test</span><span class="si">{</span><span class="n">counter</span><span class="si">}</span><span class="s">.jpg"</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">counter</span> <span class="o">==</span> <span class="mi">20</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">end</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="n">end</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span>
</code></pre></div></div>
<p>It takes <code class="language-plaintext highlighter-rouge">6.36</code> seconds to run on 32-bit Raspberry Pi OS and <code class="language-plaintext highlighter-rouge">3.82</code> seconds on 64-bit.</p>
<h2 id="convert-asynchronously-and-save-to-file">Convert asynchronously and save to file</h2>
<p>This code changes the previous code to use a separate thread to call <code class="language-plaintext highlighter-rouge">make_image</code> helper function, then call <code class="language-plaintext highlighter-rouge">save</code></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">picamera2</span> <span class="kn">import</span> <span class="n">Picamera2</span><span class="p">,</span> <span class="n">Preview</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">threading</span>
<span class="n">picam2</span> <span class="o">=</span> <span class="n">Picamera2</span><span class="p">()</span>
<span class="n">camera_config</span> <span class="o">=</span> <span class="n">picam2</span><span class="p">.</span><span class="n">create_still_configuration</span><span class="p">(</span><span class="n">buffer_count</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">configure</span><span class="p">(</span><span class="n">camera_config</span><span class="p">)</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">def</span> <span class="nf">write_jpg</span><span class="p">(</span><span class="nb">buffer</span><span class="p">):</span>
<span class="n">img</span> <span class="o">=</span> <span class="n">picam2</span><span class="p">.</span><span class="n">helpers</span><span class="p">.</span><span class="n">make_image</span><span class="p">(</span><span class="nb">buffer</span><span class="p">,</span> <span class="n">camera_config</span><span class="p">[</span><span class="s">"main"</span><span class="p">])</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">helpers</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">metadata</span><span class="p">,</span> <span class="sa">f</span><span class="s">"test</span><span class="si">{</span><span class="n">counter</span><span class="si">}</span><span class="s">.jpg"</span><span class="p">)</span>
<span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
<span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"capture </span><span class="si">{</span><span class="n">counter</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="p">(</span><span class="nb">buffer</span><span class="p">,</span> <span class="p">),</span> <span class="n">metadata</span> <span class="o">=</span> <span class="n">picam2</span><span class="p">.</span><span class="n">capture_buffers</span><span class="p">([</span><span class="s">"main"</span><span class="p">])</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="p">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">write_jpg</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="nb">buffer</span><span class="p">,))</span>
<span class="n">t</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">counter</span> <span class="o">==</span> <span class="mi">20</span><span class="p">:</span>
<span class="k">break</span>
<span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">threading</span><span class="p">.</span><span class="nb">enumerate</span><span class="p">():</span>
<span class="k">if</span> <span class="n">t</span> <span class="o">!=</span> <span class="n">threading</span><span class="p">.</span><span class="n">current_thread</span><span class="p">()</span> <span class="ow">and</span> <span class="n">t</span><span class="p">.</span><span class="n">daemon</span> <span class="ow">is</span> <span class="bp">False</span><span class="p">:</span>
<span class="n">t</span><span class="p">.</span><span class="n">join</span><span class="p">()</span>
<span class="n">end</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="n">end</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span>
</code></pre></div></div>
<p>It takes <code class="language-plaintext highlighter-rouge">2.18</code> seconds to run on 32-bit Raspberry Pi OS and <code class="language-plaintext highlighter-rouge">1.37</code> seconds on 64-bit. Comparing this with result from previous sample demonstrates that optimizing <code class="language-plaintext highlighter-rouge">make_image</code> is critical to improving performance.</p>
<h2 id="convert-asynchronously-and-stream-to-network">Convert asynchronously and stream to network</h2>
<p>This code is similar to the one before but instead of writing to file, it writes data out to a TCP socket</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">picamera2</span> <span class="kn">import</span> <span class="n">Picamera2</span><span class="p">,</span> <span class="n">Preview</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">threading</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="n">picam2</span> <span class="o">=</span> <span class="n">Picamera2</span><span class="p">()</span>
<span class="n">camera_config</span> <span class="o">=</span> <span class="n">picam2</span><span class="p">.</span><span class="n">create_still_configuration</span><span class="p">(</span><span class="n">buffer_count</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">configure</span><span class="p">(</span><span class="n">camera_config</span><span class="p">)</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">def</span> <span class="nf">write_jpg</span><span class="p">(</span><span class="nb">buffer</span><span class="p">):</span>
<span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">sock</span><span class="p">.</span><span class="n">connect</span><span class="p">((</span><span class="s">"192.168.68.60"</span><span class="p">,</span> <span class="mi">8001</span><span class="p">))</span>
<span class="n">out</span> <span class="o">=</span> <span class="n">sock</span><span class="p">.</span><span class="n">makefile</span><span class="p">(</span><span class="s">"wb"</span><span class="p">)</span>
<span class="n">img</span> <span class="o">=</span> <span class="n">picam2</span><span class="p">.</span><span class="n">helpers</span><span class="p">.</span><span class="n">make_image</span><span class="p">(</span><span class="nb">buffer</span><span class="p">,</span> <span class="n">camera_config</span><span class="p">[</span><span class="s">"main"</span><span class="p">])</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">helpers</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">metadata</span><span class="p">,</span> <span class="n">out</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="s">'jpeg'</span><span class="p">)</span>
<span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
<span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"capture </span><span class="si">{</span><span class="n">counter</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="p">(</span><span class="nb">buffer</span><span class="p">,</span> <span class="p">),</span> <span class="n">metadata</span> <span class="o">=</span> <span class="n">picam2</span><span class="p">.</span><span class="n">capture_buffers</span><span class="p">([</span><span class="s">"main"</span><span class="p">])</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="p">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">write_jpg</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="nb">buffer</span><span class="p">,))</span>
<span class="n">t</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">counter</span> <span class="o">==</span> <span class="mi">20</span><span class="p">:</span>
<span class="k">break</span>
<span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">threading</span><span class="p">.</span><span class="nb">enumerate</span><span class="p">():</span>
<span class="k">if</span> <span class="n">t</span> <span class="o">!=</span> <span class="n">threading</span><span class="p">.</span><span class="n">current_thread</span><span class="p">()</span> <span class="ow">and</span> <span class="n">t</span><span class="p">.</span><span class="n">daemon</span> <span class="ow">is</span> <span class="bp">False</span><span class="p">:</span>
<span class="n">t</span><span class="p">.</span><span class="n">join</span><span class="p">()</span>
<span class="n">end</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="n">end</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span>
</code></pre></div></div>
<p>It takes <code class="language-plaintext highlighter-rouge">2.18</code> seconds to run on 32-bit Raspberry Pi OS and <code class="language-plaintext highlighter-rouge">1.37</code> seconds on 64-bit, same as the previous sample.</p>
<p>To receive at the other end</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">threading</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
<span class="k">def</span> <span class="nf">save_jpeg</span><span class="p">(</span><span class="n">args</span><span class="p">):</span>
<span class="n">conn</span> <span class="o">=</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">datetime</span><span class="p">.</span><span class="n">utcnow</span><span class="p">().</span><span class="n">strftime</span><span class="p">(</span><span class="s">'%Y%m%d-%H%M%S_%f'</span><span class="p">)[:</span><span class="o">-</span><span class="mi">3</span><span class="p">]</span><span class="si">}</span><span class="s">.jpg"</span><span class="p">,</span> <span class="s">"wb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">conn</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
<span class="k">if</span> <span class="n">data</span><span class="p">:</span>
<span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">sock</span><span class="p">.</span><span class="n">setsockopt</span><span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">SO_REUSEADDR</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">sock</span><span class="p">.</span><span class="n">bind</span><span class="p">((</span><span class="s">"0.0.0.0"</span><span class="p">,</span> <span class="mi">8001</span><span class="p">))</span>
<span class="n">sock</span><span class="p">.</span><span class="n">listen</span><span class="p">()</span>
<span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">conn</span> <span class="o">=</span> <span class="n">sock</span><span class="p">.</span><span class="n">accept</span><span class="p">()</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="p">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">save_jpeg</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">conn</span><span class="p">,))</span>
<span class="n">t</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
</code></pre></div></div>
<h2 id="capture-raw-image">Capture raw image</h2>
<p>This sample code recovers a raw image buffer and calls <code class="language-plaintext highlighter-rouge">save_dng</code> helper to save a raw image</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">picamera2</span> <span class="kn">import</span> <span class="n">Picamera2</span><span class="p">,</span> <span class="n">Preview</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="n">picam2</span> <span class="o">=</span> <span class="n">Picamera2</span><span class="p">()</span>
<span class="n">camera_config</span> <span class="o">=</span> <span class="n">picam2</span><span class="p">.</span><span class="n">create_still_configuration</span><span class="p">(</span><span class="n">raw</span><span class="o">=</span><span class="p">{})</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">configure</span><span class="p">(</span><span class="n">camera_config</span><span class="p">)</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
<span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"capture </span><span class="si">{</span><span class="n">counter</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="p">(</span><span class="nb">buffer</span><span class="p">,</span> <span class="p">),</span> <span class="n">metadata</span> <span class="o">=</span> <span class="n">picam2</span><span class="p">.</span><span class="n">capture_buffers</span><span class="p">([</span><span class="s">"raw"</span><span class="p">])</span>
<span class="n">picam2</span><span class="p">.</span><span class="n">helpers</span><span class="p">.</span><span class="n">save_dng</span><span class="p">(</span><span class="nb">buffer</span><span class="p">,</span> <span class="n">metadata</span><span class="p">,</span> <span class="n">camera_config</span><span class="p">[</span><span class="s">"raw"</span><span class="p">],</span> <span class="sa">f</span><span class="s">"test</span><span class="si">{</span><span class="n">counter</span><span class="si">}</span><span class="s">.dng"</span><span class="p">)</span>
<span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">counter</span> <span class="o">==</span> <span class="mi">20</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">end</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="n">end</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span>
</code></pre></div></div>
<p>It takes <code class="language-plaintext highlighter-rouge">17.82</code> seconds to run on 32-bit Raspberry Pi OS and <code class="language-plaintext highlighter-rouge">10.46</code> seconds on 64-bit.</p>Picamera2 performanceUse I2C bus with the 38-pin ESP-32 Module2022-09-28T00:00:00+00:002022-09-28T00:00:00+00:00https://tewarid.github.io/2022/09/28/use-i2c-bus-with-the-38-pin-esp-32-module<h1 id="use-i2c-bus-with-the-38-pin-esp-32-module">Use I2C bus with the 38-pin ESP-32 Module</h1>
<p>The I2C bus is a multi-device two-wire half-duplex 8-bit synchronous serial interface. Typical clock speeds are 100 KHz and 400 KHz. Device addresses are 7-bit and usually hardcoded in the device. This necessitates use of separate busses to interface with devices that have the same address.</p>
<p>In this post, we’ll use an ESP-32 38-pin module to interface with the common BME680 environment sensor from Bosch. We’ll use the Arduino IDE to develop our code, and while there are several good Arduino libraries for the BME680, we’ll develop code from scratch to do something as simple as detect BME680 sensor on the I2C bus.</p>
<p>Connect the BME680 sensor module as shown in the wiring diagram below</p>
<p><img src="/assets/img/esp32-38-pin-bme680-i2c.png" alt="Wiring Diagram" /></p>
<p>Here’s an Arduino Sketch that queries the sensor id register <code class="language-plaintext highlighter-rouge">0xD0</code>, and thus senses a BME680 on the I2C bus if the value read is <code class="language-plaintext highlighter-rouge">0x61</code></p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include <Wire.h>
</span>
<span class="cp">#define I2C_ADDRESS 0x76
</span>
<span class="cp">#define I2C_Freq 100000
</span>
<span class="cp">#define I2C_SDA_0 21
#define I2C_SCL_0 22
</span>
<span class="n">TwoWire</span> <span class="n">I2C_0</span> <span class="o">=</span> <span class="n">TwoWire</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
<span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
<span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">Serial</span><span class="p">);</span>
<span class="c1">// Setup I2C interface</span>
<span class="n">I2C_0</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="n">I2C_SDA_0</span><span class="p">,</span> <span class="n">I2C_SCL_0</span><span class="p">,</span> <span class="n">I2C_Freq</span><span class="p">);</span>
<span class="c1">// Read id register 0xD0 and check we can communicate with BME680</span>
<span class="n">I2C_0</span><span class="p">.</span><span class="n">beginTransmission</span><span class="p">(</span><span class="n">I2C_ADDRESS</span><span class="p">);</span>
<span class="n">I2C_0</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="mh">0xD0</span><span class="p">);</span>
<span class="n">I2C_0</span><span class="p">.</span><span class="n">endTransmission</span><span class="p">();</span>
<span class="n">I2C_0</span><span class="p">.</span><span class="n">requestFrom</span><span class="p">(</span><span class="n">I2C_ADDRESS</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">byte</span> <span class="n">id</span> <span class="o">=</span> <span class="n">I2C_0</span><span class="p">.</span><span class="n">read</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">id</span> <span class="o">!=</span> <span class="mh">0x61</span><span class="p">)</span> <span class="p">{</span>
<span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">"Couldn't find BME680!"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Read data from BME680</span>
<span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">();</span>
<span class="n">delay</span><span class="p">(</span><span class="mi">3000</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We define some constants and setup the <code class="language-plaintext highlighter-rouge">TwoWire</code> API to interface with the BME680 using device address <code class="language-plaintext highlighter-rouge">0x76</code>, I2C frequency <code class="language-plaintext highlighter-rouge">100</code> KHz, and I2C bus <code class="language-plaintext highlighter-rouge">0</code> with GPIOs <code class="language-plaintext highlighter-rouge">21</code> (SCL) and <code class="language-plaintext highlighter-rouge">22</code> (SDA).</p>
<p>Note that depending on the wiring of the BME680 module, if you are unable to detect the device, I2C address may need to be changed to <code class="language-plaintext highlighter-rouge">0x77</code>.</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define I2C_ADDRESS 0x76
</span>
<span class="cp">#define I2C_Freq 100000
</span>
<span class="cp">#define I2C_SDA_0 21
#define I2C_SCL_0 22
</span>
<span class="n">TwoWire</span> <span class="n">I2C_0</span> <span class="o">=</span> <span class="n">TwoWire</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
<span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
<span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">Serial</span><span class="p">);</span>
<span class="c1">// Setup I2C interface</span>
<span class="n">I2C_0</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="n">I2C_SDA_0</span><span class="p">,</span> <span class="n">I2C_SCL_0</span><span class="p">,</span> <span class="n">I2C_Freq</span><span class="p">);</span>
</code></pre></div></div>
<p>Whereas the <code class="language-plaintext highlighter-rouge">TwoWire</code> API gives more control, the simpler <code class="language-plaintext highlighter-rouge">Wire</code> API can be used if you are happy to use the default I2C bus and GPIOs. The above code can then be simplified to</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define I2C_ADDRESS 0x76
</span>
<span class="n">TwoWire</span> <span class="n">I2C_0</span> <span class="o">=</span> <span class="n">Wire</span><span class="p">;</span>
<span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
<span class="n">Serial</span><span class="p">.</span><span class="n">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span>
<span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">Serial</span><span class="p">);</span>
<span class="c1">// Setup I2C interface</span>
<span class="n">I2C_0</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span>
</code></pre></div></div>
<p>Next, in the <code class="language-plaintext highlighter-rouge">setup</code> function, we initiate a write to register <code class="language-plaintext highlighter-rouge">0xD0</code>, in preparation to read from it</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">I2C_0</span><span class="p">.</span><span class="n">beginTransmission</span><span class="p">(</span><span class="n">I2C_ADDRESS</span><span class="p">);</span>
<span class="n">I2C_0</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="mh">0xD0</span><span class="p">);</span>
<span class="n">I2C_0</span><span class="p">.</span><span class="n">endTransmission</span><span class="p">();</span>
</code></pre></div></div>
<p>Finally, we send a read request and verify that the value read is <code class="language-plaintext highlighter-rouge">0x61</code></p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">I2C_0</span><span class="p">.</span><span class="n">requestFrom</span><span class="p">(</span><span class="n">I2C_ADDRESS</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">byte</span> <span class="n">id</span> <span class="o">=</span> <span class="n">I2C_0</span><span class="p">.</span><span class="n">read</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">id</span> <span class="o">!=</span> <span class="mh">0x61</span><span class="p">)</span> <span class="p">{</span>
<span class="n">Serial</span><span class="p">.</span><span class="n">println</span><span class="p">(</span><span class="s">"Couldn't find BME680!"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Arduino’s Wire API and hardware handles bus start/stop and ack/nak signaling required by I2C. Here’s a logic analyzer probe of the I2C bus when doing the above operations</p>
<p><img src="/assets/img/esp32-arduino-bme680-i2c.png" alt="I2C bus analysis" /></p>
<p>Multiple devices, as long as they have different addresses, can similarly be connected to and conversed with on the same I2C bus.</p>Use I2C bus with the 38-pin ESP-32 ModuleUser space SPI communication in Linux2022-03-10T00:00:00+00:002022-03-10T00:00:00+00:00https://tewarid.github.io/2022/03/10/user-space-spi-communication-in-linux<h1 id="user-space-spi-communication-in-linux">User space SPI communication in Linux</h1>
<p>Synchronous Peripheral Interface aka SPI is a rather fast and flexible bidirectional serial interface although it is unable to support as many devices per bus as the I2C bus. The Linux SPI interface supports accessing devices from user space applications using <code class="language-plaintext highlighter-rouge">ioctl</code> calls, which is less developer-friendly than the <code class="language-plaintext highlighter-rouge">linux/spi/spi.h</code> API available from kernel space.</p>
<p>In this post, we explore the different means of transferring data using the <code class="language-plaintext highlighter-rouge">linux/spi/spidev.h</code> header and <code class="language-plaintext highlighter-rouge">ioctl</code> call available in <code class="language-plaintext highlighter-rouge">sys/ioctl.h</code>.</p>
<h2 id="open-spi-device">Open SPI device</h2>
<p>The following code snippet shows how to open an SPI device</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="s">"/dev/spidev0.0"</span><span class="p">,</span> <span class="n">O_RDWR</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// error</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Subsequently, the file descriptor can be used to configure the SPI interface, and to transfer data.</p>
<h2 id="configure-spi-interface">Configure SPI interface</h2>
<p>The following code snippet shows how to configure SPI interface characteristics such as transfer mode, bits per word, and clock speed</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">SPI_IOC_WR_MODE</span><span class="p">,</span> <span class="n">SPI_MODE_2</span><span class="p">);</span>
<span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">SPI_IOC_WR_BITS_PER_WORD</span><span class="p">,</span> <span class="mi">8</span><span class="p">);</span>
<span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">SPI_IOC_WR_MAX_SPEED_HZ</span><span class="p">,</span> <span class="mi">8000000U</span><span class="p">);</span>
</code></pre></div></div>
<p>Configuration can also be read via similar <code class="language-plaintext highlighter-rouge">ioctl</code> calls but using different requests such as <code class="language-plaintext highlighter-rouge">SPI_IOC_RD_MODE</code>, <code class="language-plaintext highlighter-rouge">SPI_IOC_RD_BITS_PER_WORD</code>, and <code class="language-plaintext highlighter-rouge">SPI_IOC_RD_MAX_SPEED_HZ</code>.</p>
<h2 id="send-only">Send only</h2>
<p>The following code snippet demonstrates how to send data to a device without receiving any data from it</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">spi_ioc_transfer</span> <span class="n">tr</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="p">{</span>
<span class="p">.</span><span class="n">tx_buf</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">tx</span><span class="p">,</span>
<span class="p">.</span><span class="n">len</span> <span class="o">=</span> <span class="n">len</span><span class="p">,</span>
<span class="p">.</span><span class="n">delay_usecs</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">};</span>
<span class="n">errno</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">SPI_IOC_MESSAGE</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="o">&</span><span class="p">(</span><span class="n">tr</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="n">errno</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// failed</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here, <code class="language-plaintext highlighter-rouge">tx</code> is a preconfigured buffer of length <code class="language-plaintext highlighter-rouge">len</code>, and we want to wait <code class="language-plaintext highlighter-rouge">10</code> microseconds after the last bit has been transferred.</p>
<h2 id="send-and-then-receive">Send and then receive</h2>
<p>The following code snippet demonstrates how to send data to a device such as a command, and then receive data from it</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">spi_ioc_transfer</span> <span class="n">tr</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="p">{</span>
<span class="p">.</span><span class="n">tx_buf</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">tx</span><span class="p">,</span>
<span class="p">.</span><span class="n">len</span> <span class="o">=</span> <span class="n">tx_len</span><span class="p">,</span>
<span class="p">.</span><span class="n">delay_usecs</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="p">.</span><span class="n">rx_buf</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">rx</span><span class="p">,</span>
<span class="p">.</span><span class="n">len</span> <span class="o">=</span> <span class="n">rx_len</span><span class="p">,</span>
<span class="p">.</span><span class="n">delay_usecs</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">};</span>
<span class="n">errno</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">SPI_IOC_MESSAGE</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="o">&</span><span class="p">(</span><span class="n">tr</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="n">errno</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// failed</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="send-and-receive">Send and receive</h2>
<p>The following code snippet demonstrates how to send data to a device and receive data at the same time i.e. transceive</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">spi_ioc_transfer</span> <span class="n">tr</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="p">{</span>
<span class="p">.</span><span class="n">tx_buf</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">tx</span><span class="p">,</span>
<span class="p">.</span><span class="n">rx_buf</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">rx</span><span class="p">,</span>
<span class="p">.</span><span class="n">len</span> <span class="o">=</span> <span class="n">len</span><span class="p">,</span>
<span class="p">.</span><span class="n">delay_usecs</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">};</span>
<span class="n">errno</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">ioctl</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">SPI_IOC_MESSAGE</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="o">&</span><span class="p">(</span><span class="n">tr</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="n">errno</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// failed</span>
<span class="p">}</span>
</code></pre></div></div>User space SPI communication in LinuxUse SPI bus with the Raspberry Pi Pico2022-02-19T00:00:00+00:002022-02-19T00:00:00+00:00https://tewarid.github.io/2022/02/19/use-spi-bus-with-the-raspberry-pi-pico<h1 id="use-spi-bus-with-the-raspberry-pi-pico">Use SPI bus with the Raspberry Pi Pico</h1>
<p>Synchronous Peripheral Interface aka SPI is a rather fast and flexible serial interface although it is unable to support as many devices per bus as the I2C bus. We’ll explore how to use this bus on the Raspberry Pi Pico board, by interfacing it with the commonly available BME680 environmental sensor from Bosch.</p>
<h2 id="parts">Parts</h2>
<p>The list of parts we need is rather short</p>
<ul>
<li>A Raspberry Pi Pico or equivalent module</li>
<li>A micro USB data cable to deliver power to and interface with the board</li>
<li>A grove BME680 sensor from Seeed Studio</li>
</ul>
<p>You’ll need to have the Arduino IDE set up and ready to go on your computer.</p>
<h2 id="wiring-diagram">Wiring Diagram</h2>
<p>We’ll wire the sensor to the board as follows</p>
<p><img src="/assets/img/rp2040-bme680-spi.png" alt="Wiring Diagram" /></p>
<p>Note that the diagram shows the Adafruit BME680 sensor that is equivalent to the grove BME680 sensor from Seeed Studio.</p>
<p>BME680 grove sensor uses the I2C interface by default. To use the SPI interface, you’ll need to physically sever two traces indicated by <em>I2C</em> and <em>0x76</em> labels on the bottom side of the PCB. This can be done by lightly but repeatedly scratching with a pointed cutting tool of some kind.</p>
<h2 id="configure-board-in-arduino-ide">Configure board in Arduino IDE</h2>
<p>Assuming you’ve got the Arduino IDE setup and ready to go, you need to add <code class="language-plaintext highlighter-rouge">https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json</code> in <em>Additional Boards Manager URLs</em>, in <em>Preferences</em>.</p>
<p>Head over to <em>Tools</em> menu, <em>Board</em>, <em>Board Manager</em>, and install <em>Raspberry Pi Pico/RP2040</em> board support.</p>
<h2 id="configure-adafruit-bme680-library">Configure Adafruit BME680 library</h2>
<p>Head over to the <em>Sketch</em> menu, <em>Include Library</em>, <em>Manage Libraries</em>, and install <em>Adafruit BME680 Library</em>.</p>
<h2 id="example-code">Example code</h2>
<p>Head over to <em>File</em> menu, <em>Example</em>, <em>Adafruit BME680 Library</em>, and pick bme680test. This will create a new sketch based on bme680test example.</p>
<h2 id="modify-example">Modify example</h2>
<p>The bme680test example is configured to work over the I2C interface by default.</p>
<p>To get it to work over SPI, you’ll need to modify the following lines</p>
<p>Change value of <code class="language-plaintext highlighter-rouge">BME_CS</code> to GPIO <code class="language-plaintext highlighter-rouge">17</code></p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define BME_CS 17
</span></code></pre></div></div>
<p>Comment creation of I2C interface</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//Adafruit_BME680 bme; // I2C</span>
</code></pre></div></div>
<p>Uncomment creation of SPI hardware interface</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Adafruit_BME680</span> <span class="nf">bme</span><span class="p">(</span><span class="n">BME_CS</span><span class="p">);</span> <span class="c1">// hardware SPI</span>
</code></pre></div></div>
<h2 id="run-example">Run example</h2>
<p>Head over to <em>Tools</em> menu, <em>Board</em>, <em>Raspberry Pi Pico/RP2040</em>, and pick board <em>Raspberry Pi Pico</em>.</p>
<p>Connect the board to your computer, and pick the correct serial port under <em>Tools</em> menu, <em>Port</em>.</p>
<p>Under <em>Sketch</em> menu, select <em>Upload</em>.</p>
<p>Arduino IDE will now build and upload the example code</p>
<pre><code class="language-log">Sketch uses 71336 bytes (3%) of program storage space. Maximum is 2093056 bytes.
Global variables use 11580 bytes (4%) of dynamic memory, leaving 250564 bytes for local variables. Maximum is 262144 bytes.
--------------------------
Compilation complete.
Resetting /dev/cu.usbmodem144143101
Converting to uf2, output size: 156672, start address: 0x2000
Flashing /Volumes/RPI-RP2 (RPI-RP2)
Wrote 156672 bytes to /Volumes/RPI-RP2/NEW.UF2
--------------------------
upload complete.
</code></pre>
<h2 id="example-output">Example output</h2>
<p>To see the output, head over to <em>Tools</em> menu, and select <em>Serial Monitor</em>.</p>
<p>You should see text such as the following print repeatedly</p>
<pre><code class="language-log">Temperature = 32.59 *C
Pressure = 1010.79 hPa
Humidity = 64.26 %Gas = 68.29 KOhms
Approx. Altitude = 20.50 m
</code></pre>Use SPI bus with the Raspberry Pi PicoUse SPI bus with the 38-pin ESP-32 Module2022-02-15T00:00:00+00:002022-02-15T00:00:00+00:00https://tewarid.github.io/2022/02/15/use-spi-bus-with-the-38-pin-esp-32-module<h1 id="use-spi-bus-with-the-38-pin-esp-32">Use SPI bus with the 38-pin ESP-32</h1>
<p>Synchronous Peripheral Interface aka SPI is a rather fast and flexible serial interface although it is unable to support as many devices per bus as the I2C bus. We’ll explore this bus by interfacing with the commonly available BME680 environmental sensor from Bosch.</p>
<h2 id="parts">Parts</h2>
<p>The list of parts we need is rather short</p>
<ul>
<li>A 38-Pin ESP-32 Dev Module</li>
<li>A micro USB data cable to deliver power to and interface with the ESP-32</li>
<li>A grove BME680 sensor from Seeed Studio</li>
</ul>
<p>You’ll need to have the Arduino IDE set up and ready to go on your computer.</p>
<h2 id="wiring-diagram">Wiring Diagram</h2>
<p>We’ll wire the sensor to the ESP-32 as follows</p>
<p><img src="/assets/img/esp32-38-pin-bme680-spi.png" alt="Wiring Diagram" /></p>
<p>Note that the diagram shows the Adafruit BME680 sensor that is equivalent to the grove BME680 sensor from Seeed Studio.</p>
<p>BME680 grove sensor uses the I2C interface by default. To use the SPI interface, you’ll need to physically sever two traces indicated by <em>I2C</em> and <em>0x76</em> labels on the bottom side of the PCB. This can be done by lightly but repeatedly scratching with a pointed cutting tool of some kind.</p>
<h2 id="configure-board-in-arduino-ide">Configure board in Arduino IDE</h2>
<p>Assuming you’ve got the Arduino IDE setup and ready to go, you need to add <code class="language-plaintext highlighter-rouge">https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json</code> in <em>Additional Boards Manager URLs</em>, in <em>Preferences</em>.</p>
<p>Head over to <em>Tools</em> menu, <em>Board</em>, <em>Board Manager</em>, and install esp32 board support.</p>
<h2 id="configure-adafruit-bme680-library">Configure Adafruit BME680 library</h2>
<p>Head over to the <em>Sketch</em> menu, <em>Include Library</em>, <em>Manage Libraries</em>, and install <em>Adafruit BME680 Library</em>.</p>
<h2 id="example-code">Example code</h2>
<p>Head over to <em>File</em> menu, <em>Example</em>, <em>Adafruit BME680 Library</em>, and pick bme680test. This will create a new sketch based on bme680test example.</p>
<h2 id="modify-example">Modify example</h2>
<p>The bme680test example is configured to work over the I2C interface by default.</p>
<p>To get it to work over SPI, you’ll need to modify the following lines</p>
<p>Change value of <code class="language-plaintext highlighter-rouge">BME_CS</code> to GPIO <code class="language-plaintext highlighter-rouge">5</code></p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define BME_CS 5
</span></code></pre></div></div>
<p>Comment creation of I2C interface</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//Adafruit_BME680 bme; // I2C</span>
</code></pre></div></div>
<p>Uncomment creation of SPI hardware interface</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Adafruit_BME680</span> <span class="nf">bme</span><span class="p">(</span><span class="n">BME_CS</span><span class="p">);</span> <span class="c1">// hardware SPI</span>
</code></pre></div></div>
<h2 id="run-example">Run example</h2>
<p>Head over to <em>Tools</em> menu, <em>Board</em>, <em>ESP32 Arduino</em>, and pick board <em>ESP32 Dev Module</em>.</p>
<p>Connect the ESP32 board to your computer, and pick the correct serial port under <em>Tools</em> menu, <em>Port</em>.</p>
<p>Under <em>Sketch</em> menu, select <em>Upload</em>.</p>
<p>Arduino IDE will now build the example code, and wait a while.</p>
<pre><code class="language-log">Sketch uses 265641 bytes (20%) of program storage space. Maximum is 1310720 bytes.
Global variables use 17496 bytes (5%) of dynamic memory, leaving 310184 bytes for local variables. Maximum is 327680 bytes.
--------------------------
Compilation complete.
esptool.py v3.1
Serial port /dev/cu.usbserial-0001
Connecting........_
</code></pre>
<p>Press and hold the BOOT button for a few seconds and release it. After you release the BOOT button, the sketch will be uploaded and run.</p>
<pre><code class="language-log">Chip is ESP32-D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 24:62:ab:f9:02:a8
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Changed.
Configuring flash size...
Flash will be erased from 0x0000e000 to 0x0000ffff...
Flash will be erased from 0x00001000 to 0x00005fff...
Flash will be erased from 0x00010000 to 0x00050fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Compressed 8192 bytes to 47...
Writing at 0x0000e000... (100 %)
Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (effective 452.9 kbit/s)...
Hash of data verified.
Compressed 18528 bytes to 12721...
Writing at 0x00001000... (100 %)
Wrote 18528 bytes (12721 compressed) at 0x00001000 in 0.5 seconds (effective 309.9 kbit/s)...
Hash of data verified.
Compressed 266032 bytes to 148511...
Writing at 0x00010000... (10 %)
Writing at 0x0001ce61... (20 %)
Writing at 0x00024bc2... (30 %)
Writing at 0x0002a358... (40 %)
Writing at 0x0002fa29... (50 %)
Writing at 0x00034f16... (60 %)
Writing at 0x0003d83e... (70 %)
Writing at 0x00045a09... (80 %)
Writing at 0x0004afaf... (90 %)
Writing at 0x00050921... (100 %)
Wrote 266032 bytes (148511 compressed) at 0x00010000 in 2.3 seconds (effective 931.5 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 128...
Writing at 0x00008000... (100 %)
Wrote 3072 bytes (128 compressed) at 0x00008000 in 0.1 seconds (effective 292.6 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
--------------------------
upload complete.
</code></pre>
<h2 id="example-output">Example output</h2>
<p>To see the output, head over to <em>Tools</em> menu, and select <em>Serial Monitor</em>.</p>
<p>You should see text such as the following print repeatedly</p>
<pre><code class="language-log">Temperature = 33.48 *C
Pressure = 1011.16 hPa
Humidity = 58.24 %
Gas = 8.00 KOhms
Approx. Altitude = 17.42 m
</code></pre>
<h2 id="spi-bus">SPI bus</h2>
<p>Let’s drill down into the SPI bus by looking at the following code snippet in the BME680 library code</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">rslt</span> <span class="o">=</span> <span class="n">bme68x_get_regs</span><span class="p">(</span><span class="n">BME68X_REG_CHIP_ID</span><span class="p">,</span> <span class="o">&</span><span class="n">dev</span><span class="o">-></span><span class="n">chip_id</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">dev</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">rslt</span> <span class="o">==</span> <span class="n">BME68X_OK</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dev</span><span class="o">-></span><span class="n">chip_id</span> <span class="o">==</span> <span class="n">BME68X_CHIP_ID</span><span class="p">)</span>
<span class="p">{</span>
</code></pre></div></div>
<p>During initialization, BME680 chip id register is read. If the value returned is <code class="language-plaintext highlighter-rouge">0x61</code>, code proceeds with further initialization steps.</p>
<p>Here’s the code above translated into signals sent and received on the bus</p>
<p><img src="/assets/img/esp32-arduino-bme680-spi.png" alt="SPI bus analysis" /></p>
<p>SPI Enable pin is pulled low by ESP32 to indicate that BME680 sensor is now selected for communication. ESP32 transmits the register read code <code class="language-plaintext highlighter-rouge">0xD0</code>, serially, over the MOSI pin. BME680 transmits value <code class="language-plaintext highlighter-rouge">0x61</code>, a magic number stored in the register, to ESP32 over the MISO pin. Clock pin is used by ESP32 to signal when data is to be read or written to the bus.</p>
<p>SPI bus is full-duplex—data is simultaneously sent and received.</p>Use SPI bus with the 38-pin ESP-32Create a Docker container to build a Linux system using Yocto Project2021-02-06T00:00:00+00:002021-02-06T00:00:00+00:00https://tewarid.github.io/2021/02/06/create-a-docker-container-to-build-a-linux-system-using-yocto-project<h1 id="create-a-docker-container-to-build-a-linux-system-using-yocto-project">Create a Docker container to build a Linux system using Yocto Project</h1>
<p>The last time I posted about <a href="/2014/09/16/embedded-linux-system-for-raspberry-pi-with-yocto-project.html">building a Linux system for a Raspberry Pi using the Yocto project</a>, I used a Linux Virtual machine on macOS. Docker has since become quite popular and is often used to build embedded Linux systems. In this post, I document how to create a Docker container to run the build in the aforementioned post, on macOS, but Linux and Windows should work similarly.</p>
<p>To create the container, we’ll start with the ubuntu:20.04 base image available at Docker Hub</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">--name</span> yocto-raspberry-pi <span class="nt">-it</span> <span class="nt">-v</span> <span class="k">${</span><span class="nv">PWD</span><span class="k">}</span>:/workdir ubuntu:20.04 /bin/bash
</code></pre></div></div>
<p>We’ve mapped <code class="language-plaintext highlighter-rouge">/workdir</code> in the container to the current directory to share files between host and container. You can also use the <a href="https://docs.docker.com/engine/reference/commandline/cp/"><code class="language-plaintext highlighter-rouge">docker cp</code></a> command to copy files/folders between host and container. You cannot build in that directory because Yocto requires a case-sensitive file system.</p>
<p>Now that we’ve created the container and are situated in its terminal, we can install dependencies needed to run Yocto</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt update
apt <span class="nb">install </span>gawk wget git-core diffstat unzip texinfo gcc-multilib build-essential chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev pylint3 xterm python3-subunit mesa-common-dev libpseudo locales vim
</code></pre></div></div>
<p>We’ve also installed vim to be able to edit any files within the container.</p>
<p>Yocto build system requires the system locale to be set to <code class="language-plaintext highlighter-rouge">en_US.UTF-8</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>locale-gen <span class="s2">"en_US.UTF-8"</span>
</code></pre></div></div>
<p>Finally, we need to run the build as a different user because Yocto does not allow building as root</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adduser pi
su pi
<span class="nb">cd</span> /home/pi
</code></pre></div></div>
<p>Now, you should be good to follow the post referenced earlier to build a Linux system for the Raspberry Pi.</p>
<p>To return to the container any time in the future, run</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker start <span class="nt">-ai</span> yocto-raspberry-pi
su pi
<span class="nb">cd</span> /home/pi
</code></pre></div></div>Create a Docker container to build a Linux system using Yocto ProjectHow Google Home iOS app configures Wi-Fi on a new smart speaker2020-10-23T00:00:00+00:002020-10-23T00:00:00+00:00https://tewarid.github.io/2020/10/23/how-google-home-ios-app-configures-wi-fi-on-a-new-smart-speaker<h1 id="how-google-home-ios-app-configures-wi-fi-on-a-new-smart-speaker">How Google Home iOS app configures Wi-Fi on a new smart speaker</h1>
<p>Google Home smart speaker devices are setup using the Google Home app on iOS. While on Android this process is mostly seamless to the user, it is somewhat more laborious on iOS.</p>
<p>To begin with, the smart speaker tells the user to download the Google Home app from the App Store.</p>
<p>The app will attempt to find the speaker over Bluetooth, using Bluetooth LE advertisement packets that look as follows</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Bluetooth HCI Event - LE Meta
Event Code: LE Meta (0x3e)
Parameter Total Length: 36
Sub Event: LE Advertising Report (0x02)
Num Reports: 1
Event Type: Connectable Undirected Advertising (0x00)
Peer Address Type: Random Device Address (0x01)
BD_ADDR: 42:82:82:e8:92:a8 (42:82:82:e8:92:a8)
Data Length: 24
Advertising Data
Flags
Length: 2
Type: Flags (0x01)
000. .... = Reserved: 0x0
...0 .... = Simultaneous LE and BR/EDR to Same Device Capable (Host): false (0x0)
.... 0... = Simultaneous LE and BR/EDR to Same Device Capable (Controller): false (0x0)
.... .0.. = BR/EDR Not Supported: false (0x0)
.... ..1. = LE General Discoverable Mode: true (0x1)
.... ...0 = LE Limited Discoverable Mode: false (0x0)
16-bit Service Class UUIDs
Length: 3
Type: 16-bit Service Class UUIDs (0x03)
UUID 16: Google (0xfea0)
Service Data - 16 bit UUID
Length: 16
Type: Service Data - 16 bit UUID (0x16)
UUID 16: Google (0xfea0)
Service Data: 03fa8fca33e25c6b2020203e00
RSSI: -49dBm
</code></pre></div></div>
<p>Once a speaker is found, a sound is played on it and the app asks the user to confirm that they have heard it. This is probably used to ensure the speaker being configured is the one intended.</p>
<p>Next, the app lists all Wi-Fi networks visible to iOS, asks the user to pick one, and requests a password.</p>
<p>The app sends Wi-Fi configuration over Bluetooth LE, using the GATT services described below. These were discovered using Wireshark from <code class="language-plaintext highlighter-rouge">bluetoothd-hci-latest.pklg</code> file that is available in iOS’s sysdiagnose file.</p>
<p>The following service is used to read the speaker’s temporary public key</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Service UUID: Google (0xfea0)
Characteristic UUID: 197c6160fab211e49fbb0002a5d5c51b
</code></pre></div></div>
<p>The following service is used to write the Wi-Fi configuration</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Service UUID: Google (0xfea0)
Characteristic UUID: 0328fe40002f11e587d00002a5d5c51b
</code></pre></div></div>
<p>Here’s how it appears in Wireshark</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Bluetooth
PacketLogger Sent ACL Data
Bluetooth HCI ACL Packet
.... 0000 0100 0001 = Connection Handle: 0x041
..00 .... .... .... = PB Flag: First Non-automatically Flushable Packet (0)
00.. .... .... .... = BC Flag: Point-To-Point (0)
Data Total Length: 189
Data
[Connect in frame: 4347]
[Disconnect in frame: 7073]
[Source BD_ADDR: Apple_6c:bb:df (a4:d9:31:6c:bb:df)]
[Source Device Name: iPhone]
[Source Role: Unknown (0)]
[Destination BD_ADDR: 5a:c2:ae:cb:7e:77 (5a:c2:ae:cb:7e:77)]
[Destination Device Name: GoogleHome3727]
[Destination Role: Unknown (0)]
[Current Mode: Unknown (-1)]
Bluetooth L2CAP Protocol
Length: 185
CID: Attribute Protocol (0x0004)
Bluetooth Attribute Protocol
Opcode: Prepare Write Request (0x16)
0... .... = Authentication Signature: False
.0.. .... = Command: False
..01 0110 = Method: Prepare Write Request (0x16)
Handle: 0x004e (Google: Unknown)
[Service UUID: Google (0xfea0)]
[UUID: 0328fe40002f11e587d00002a5d5c51b]
Offset: 0
Value: 7b22656e635f706173737764223a224b43307a582b6c4f66…
</code></pre></div></div>
<p>The message packet is encoded in JSON and looks like</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="nl">"enc_passwd"</span><span class="p">:</span><span class="s2">"tt73nJ3oZMHcY</span><span class="se">\/</span><span class="s2">KdadHK9CAQs7BAjiGunkr25nylWUYSr9PLvV2+SKSMq31WBewChxVDBKVhdhNUd3sPdKeCvVIvgrAswNV0tKRE24e+6k1Q+6g1xwSDzXcfXRJe0EVZY+a</span><span class="se">\/</span><span class="s2">AVfMpQDUbotha</span><span class="se">\/</span><span class="s2">U1kQYv8OJ4CtiFsYAlyKDzjSck1fYjF+3vSAm8wcoDFtTc0rrWTtzk6xUavolbAkFyTAbgnG6NkbPqhGoL4XOfZbU</span><span class="se">\/</span><span class="s2">PKRP7OQy3mxy6FbT0n3SxDhsXPYt8PAdkZWnYRdYdNvzCseEiv</span><span class="se">\/</span><span class="s2">c9EHiXRzgAWu18TOBUhDMzzCzLfbp8fxZHhAUMn0t9YD1FxWiS9dslw=="</span><span class="p">,</span><span class="nl">"wpa_auth"</span><span class="p">:</span><span class="mi">7</span><span class="p">,</span><span class="nl">"ssid"</span><span class="p">:</span><span class="s2">"Airport Extreme"</span><span class="p">,</span><span class="nl">"scan_ssid"</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="nl">"wpa_id"</span><span class="p">:</span><span class="mi">0</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>The Wi-Fi password is sent in encrypted form ensuring it cannot be easily obtained.</p>
<p>The speaker then proceeds to get on to the Wi-Fi network, and the app proceeds with the remaining configuration such as giving a new name to the speaker, and adding it to user’s home graph.</p>How Google Home iOS app configures Wi-Fi on a new smart speakerCreate a Windows icon file using ImageMagick2020-09-03T00:00:00+00:002020-09-03T00:00:00+00:00https://tewarid.github.io/2020/09/03/create-a-windows-icon-file-using-imagemagick<h1 id="create-a-windows-icon-file-using-imagemagick">Create a Windows icon file using ImageMagick</h1>
<p>A Windows 10 app <a href="https://docs.microsoft.com/en-us/windows/win32/uxguide/vis-icons">icon</a> file contains four combinations of image size, alpha, bit depth, and colors. I find the open source <a href="https://imagemagick.org">ImageMagick 7</a> particularly helpful, and here’s a recipe for quickly creating an ico file, given a PNG file with enough resolution—I used one with 512x512 pixels, 32-bit depth, and alpha</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>convert <span class="nv">$1</span> <span class="nt">-resize</span> 16x16 <span class="nt">-depth</span> 32 16-32.png
convert <span class="nv">$1</span> <span class="nt">-resize</span> 32x32 <span class="nt">-depth</span> 32 32-32.png
convert <span class="nv">$1</span> <span class="nt">-resize</span> 48x48 <span class="nt">-depth</span> 32 48-32.png
convert <span class="nv">$1</span> <span class="nt">-resize</span> 256x256 <span class="nt">-depth</span> 32 256-32.png
convert 16-32.png 32-32.png 48-32.png 256-32.png <span class="nv">$2</span>
</code></pre></div></div>
<p>Save the above bash script and execute with the PNG file path followed by ico file path</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./convert.sh image.svg icon.ico
</code></pre></div></div>Create a Windows icon file using ImageMagickAnalyze Bluetooth protocols on Windows using Wireshark2020-08-20T00:00:00+00:002020-08-20T00:00:00+00:00https://tewarid.github.io/2020/08/20/analyze-bluetooth-protocols-on-windows-using-wireshark<h1 id="analyze-bluetooth-protocols-on-windows-using-wireshark">Analyze Bluetooth protocols on Windows using Wireshark</h1>
<p>Wireshark for Windows comes with the optional <a href="https://github.com/desowin/usbpcap">USBPcap</a> package that can be used to capture USB traffic. Most computers with Bluetooth, internally use the USB bus, or you can use an off-the-shelf USB dongle. To capture USB traffic, start capture on the USBPcap1 interface or something similar. You can determine if any Bluetooth traffic has been captured, by entering <code class="language-plaintext highlighter-rouge">bluetooth</code> in the filter box. Other useful filter terms are <code class="language-plaintext highlighter-rouge">hci_usb</code>, <code class="language-plaintext highlighter-rouge">bthci_acl</code>, <code class="language-plaintext highlighter-rouge">btl2cap</code>, <code class="language-plaintext highlighter-rouge">btrfcomm</code>, and <code class="language-plaintext highlighter-rouge">btspp</code>.</p>
<p><img src="/assets/img/usbpcap-bluetooth.png" alt="USBPcap" /></p>Analyze Bluetooth protocols on Windows using WiresharkExtend UWP app views into titlebar2020-06-19T00:00:00+00:002020-06-19T00:00:00+00:00https://tewarid.github.io/2020/06/19/extend-uwp-app-views-into-titlebar<h1 id="extend-uwp-app-views-into-titlebar">Extend UWP app views into titlebar</h1>
<p>The following code, added to the constructor of your main page, can help extend your views into the titlebar region</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ApplicationViewTitleBar</span> <span class="n">titleBar</span> <span class="p">=</span> <span class="n">ApplicationView</span><span class="p">.</span><span class="nf">GetForCurrentView</span><span class="p">().</span><span class="n">TitleBar</span><span class="p">;</span>
<span class="n">titleBar</span><span class="p">.</span><span class="n">ButtonBackgroundColor</span> <span class="p">=</span> <span class="n">Colors</span><span class="p">.</span><span class="n">Transparent</span><span class="p">;</span>
<span class="n">titleBar</span><span class="p">.</span><span class="n">ButtonInactiveBackgroundColor</span> <span class="p">=</span> <span class="n">Colors</span><span class="p">.</span><span class="n">Transparent</span><span class="p">;</span>
<span class="n">CoreApplicationViewTitleBar</span> <span class="n">coreTitleBar</span> <span class="p">=</span> <span class="n">CoreApplication</span><span class="p">.</span><span class="nf">GetCurrentView</span><span class="p">().</span><span class="n">TitleBar</span><span class="p">;</span>
<span class="n">coreTitleBar</span><span class="p">.</span><span class="n">ExtendViewIntoTitleBar</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
</code></pre></div></div>
<p>It is a slightly modified version of the original code <a href="https://stackoverflow.com/questions/33440438/how-to-hide-collapse-title-bar-in-a-uwp-app">available from StackOverflow</a>. It makes the background of titlebar buttons transparent in the active state, and inactive state—when the window is out of focus. The titlebar region still allows you to use the mouse to drag the window around.</p>
<p>That may not be enough if you’re using a <code class="language-plaintext highlighter-rouge">Microsoft.UI.Xaml.Controls.NavigationView</code> control from <a href="https://github.com/microsoft/microsoft-ui-xaml/"><code class="language-plaintext highlighter-rouge">Microsoft.UI.Xaml</code></a> version <code class="language-plaintext highlighter-rouge">2.4.2</code>. Although the menu itself extends into the titlebar, its content does not.</p>
<p>One workaround to make the content extend into the titlebar involves setting a negative top margin for the inner frame, like so</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"></winui:NavigationView.MenuItems></span>
<span class="nt"><Frame</span> <span class="na">x:Name=</span><span class="s">"shellFrame"</span> <span class="na">Margin=</span><span class="s">"0, -32, 0, 0"</span> <span class="nt">/></span>
<span class="nt"></winui:NavigationView></span>
</code></pre></div></div>
<p>Another option is to set the <a href="https://docs.microsoft.com/en-us/uwp/api/microsoft.ui.xaml.controls.navigationview.istitlebarautopaddingenabled"><code class="language-plaintext highlighter-rouge">IsTitleBarAutoPaddingEnabled</code></a> property to <code class="language-plaintext highlighter-rouge">False</code>, but then the back button and hamburger menu button are overshadowed by the titlebar region, when they’re located there due to a smaller window size, and cannot be clicked.</p>Extend UWP app views into titlebar