<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Jacob Sánchez</title><link>https://jsan.ch/posts/</link><description>Recent content in Posts on Jacob Sánchez</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Fri, 01 Nov 2024 00:00:00 +0100</lastBuildDate><atom:link href="https://jsan.ch/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Method Chaining for View in Swift UI</title><link>https://jsan.ch/posts/2024/11/method-chaining-for-view-in-swift-ui/</link><pubDate>Fri, 01 Nov 2024 00:00:00 +0100</pubDate><guid>https://jsan.ch/posts/2024/11/method-chaining-for-view-in-swift-ui/</guid><description>Acknowledgements Thanks to @Kyokook Hwang who came up with the solution.
TL;DR struct ContentView: View { var action: (() -&amp;gt; Void)? var body: some View { Button(action: { self.action?() }) } func onAction(perform: (() -&amp;gt; Void)?) -&amp;gt; some View { var new = self new.action = perform return new } } Introduction When using the Swift UI API, you have probably realised there are a few ways of passing closures to a View.</description><content type="html"><![CDATA[<h3 id="acknowledgements">Acknowledgements</h3>
<p>Thanks to <a href="https://stackoverflow.com/users/579236/kyokook-hwang">@Kyokook Hwang</a> who came up with the solution.</p>
<h2 id="tldr">TL;DR</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">ContentView</span>: View {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> action: (() -&gt; Void)?
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> body: some View {
</span></span><span style="display:flex;"><span>        Button(action: { <span style="color:#66d9ef">self</span>.action?() })
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">onAction</span>(perform: (() -&gt; Void)?) -&gt; some View {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">var</span> new = <span style="color:#66d9ef">self</span>
</span></span><span style="display:flex;"><span>        new.action = perform
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> new
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="introduction">Introduction</h2>
<p>When using the Swift UI API, you have probably realised there are a few ways
of passing closures to a View. One of them is passing them in the constructor,
either explicitly or using trailing closures, even multiple of them.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span>Button {
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;you pressed me&#34;</span>)
</span></span><span style="display:flex;"><span>} label: {
</span></span><span style="display:flex;"><span>    Image(systemName: <span style="color:#e6db74">&#34;gear&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>But there is another way deeply ingrained within Swift, which can be seen
when setting lifecycle callbacks, such as <code>onAppear</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span>VStack {
</span></span><span style="display:flex;"><span>    Text(<span style="color:#e6db74">&#34;Hello world&#34;</span>)
</span></span><span style="display:flex;"><span>}.onAppear {
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;Text appeared&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>But how can you make use of this technique yourself?</p>
<h2 id="method-chaining">Method chaining</h2>
<p>My first attempt at doing this relied on method chaining, which can be used
to modify an object multiple times within the same statement.
You may have seen this technique used in Alamofire, for instance.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> r = await AF.request(<span style="color:#e6db74">&#34;https://httpbin.org/get&#34;</span>)
</span></span><span style="display:flex;"><span>                .authenticate(username: <span style="color:#e6db74">&#34;user&#34;</span>, password: <span style="color:#e6db74">&#34;pass&#34;</span>)
</span></span><span style="display:flex;"><span>                .validate()
</span></span></code></pre></div><p>Let&rsquo;s take a look at the <a href="https://github.com/Alamofire/Alamofire/blob/master/Source/Core/DataRequest.swift#L145">source code</a> to understand how this works.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span>@discardableResult
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">validate</span>(<span style="color:#66d9ef">_</span> validation: @escaping Validation) -&gt; <span style="color:#66d9ef">Self</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">/// implementation</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">self</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>By returning self within a method, we can call another method on the result,
and so on.</p>
<h2 id="chaining-with-view">Chaining with View</h2>
<p>However, when you attempt to apply method chaining to a View things
get complicated. First of all, you cannot modify self without annotating
the method as <code>mutating</code>, but if you do that the compiler will let you
know that Views are <strong>immutable</strong>, and you cannot use mutating methods
on them.</p>
<p><img src="/images/method-chaining-view-swiftui/compiler-error.png" alt="Views are immutable"></p>
<p>You may try to get around this using <code>@State</code> to hold the closure, but this
won&rsquo;t work either.</p>
<p><a href="https://stackoverflow.com/users/579236/kyokook-hwang">@Kyokook Hwang</a> came up with a solution for this.
Instead of returning self, it is possible to create a <em>copy</em> of self,
modify it, then return it.
The only requirement is for the callback to be a public var.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">ContentView</span>: View {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> action: (() -&gt; Void)?
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">onAction</span>(<span style="color:#66d9ef">_</span> perform: @escaping () -&gt; Void) -&gt; some View {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">var</span> new = <span style="color:#66d9ef">self</span>
</span></span><span style="display:flex;"><span>        new.callback = perform
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> new
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> body: some View {
</span></span><span style="display:flex;"><span>        Button(action: {action})
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>We can even go one step further and support optional callbacks without
requiring them to be wrapped in any way.
Most of the View lifecycle callbacks do this.
Another advantage of this is that you can get rid of <code>@escaping</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">onAction</span>(<span style="color:#66d9ef">_</span> perform: (() -&gt; Void)?) -&gt; some View {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> new = <span style="color:#66d9ef">self</span>
</span></span><span style="display:flex;"><span>    new.callback = perform
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> new
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>From here, you could write a protocol to enforce this interface.</p>
<h2 id="alternative-environment-and-viewmodifier">Alternative: @Environment and ViewModifier</h2>
<p>Originally, I was going to write about this method,
until I found the technique I showed above.
Though verbose, it is also possible to use <code>@Environment</code> to set a closure,
and then use <code>ViewModifier</code> to simplify the syntax.</p>
<p>The only benefit of using this approach is the fact that you can use a
private var.</p>
<p>You can still read about that method on <a href="https://stackoverflow.com/questions/61039657/how-to-implement-function-like-onappear-in-swiftui">StackOverflow</a>.</p>
<h2 id="related-material">Related material</h2>
<ul>
<li>Apple developer documentation: <a href="https://developer.apple.com/documentation/swiftui/environmentkey">environmentkey</a></li>
<li>Stackoverflow: <a href="https://stackoverflow.com/questions/64252145/how-to-implement-custom-callback-action-in-swiftui-similar-to-onappear-function">How to implement custom callback action</a></li>
<li>Stackoverflow: <a href="https://stackoverflow.com/questions/68748549/swiftui-store-closure-in-environment">How to store closure in @Environment</a></li>
<li>Stackoverflow: <a href="https://stackoverflow.com/questions/61039657/how-to-implement-function-like-onappear-in-swiftui">How to implement function like onAppear</a></li>
</ul>
<!-- raw HTML omitted -->
]]></content></item><item><title>How to use Google Sign In with Vue</title><link>https://jsan.ch/posts/2024/03/how-to-use-google-sign-in-with-vue/</link><pubDate>Mon, 25 Mar 2024 00:00:00 +0100</pubDate><guid>https://jsan.ch/posts/2024/03/how-to-use-google-sign-in-with-vue/</guid><description>TL;DR &amp;lt;template&amp;gt; &amp;lt;component is=&amp;#34;script&amp;#34; src=&amp;#34;https://accounts.google.com/gsi/client&amp;#34; @load=&amp;#34;initSignIn&amp;#34; async /&amp;gt; &amp;lt;div id=&amp;#34;gSignInButton&amp;#34; /&amp;gt; &amp;lt;/template&amp;gt; &amp;lt;script setup lang=&amp;#34;ts&amp;#34;&amp;gt; const loginCallback = async (response) =&amp;gt; { // send response to your own server endpoint // and redirect user }; const initSignIn = () =&amp;gt; { google.accounts.id.initialize({ client_id: &amp;#34;YOUR_CLIENT_ID&amp;#34;, callback: loginCallback }); google.accounts.id.renderButton( document.getElementById(&amp;#34;gSignInButton&amp;#34;), { type: &amp;#34;standard&amp;#34;, text: &amp;#34;sign_in_with&amp;#34;, theme: &amp;#34;outline&amp;#34;, size: &amp;#34;large&amp;#34;, width: &amp;#34;400&amp;#34; } ); google.accounts.id.prompt(); }; onMounted(() =&amp;gt; { if (typeof google !</description><content type="html"><![CDATA[<h2 id="tldr">TL;DR</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-jsx" data-lang="jsx"><span style="display:flex;"><span>&lt;<span style="color:#f92672">template</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">component</span> <span style="color:#a6e22e">is</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;script&#34;</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://accounts.google.com/gsi/client&#34;</span> <span style="color:#960050;background-color:#1e0010">@</span><span style="color:#a6e22e">load</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;initSignIn&#34;</span> <span style="color:#a6e22e">async</span> /&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;gSignInButton&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">template</span>&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">setup</span> <span style="color:#a6e22e">lang</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;ts&#34;</span>&gt;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">loginCallback</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">async</span> (<span style="color:#a6e22e">response</span>) =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// send response to your own server endpoint
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#75715e">// and redirect user
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">initSignIn</span> <span style="color:#f92672">=</span> () =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">google</span>.<span style="color:#a6e22e">accounts</span>.<span style="color:#a6e22e">id</span>.<span style="color:#a6e22e">initialize</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">client_id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;YOUR_CLIENT_ID&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">callback</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">loginCallback</span>
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">google</span>.<span style="color:#a6e22e">accounts</span>.<span style="color:#a6e22e">id</span>.<span style="color:#a6e22e">renderButton</span>(
</span></span><span style="display:flex;"><span>    document.<span style="color:#a6e22e">getElementById</span>(<span style="color:#e6db74">&#34;gSignInButton&#34;</span>),
</span></span><span style="display:flex;"><span>    { <span style="color:#a6e22e">type</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;standard&#34;</span>, <span style="color:#a6e22e">text</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;sign_in_with&#34;</span>, <span style="color:#a6e22e">theme</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;outline&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">size</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;large&#34;</span>, <span style="color:#a6e22e">width</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;400&#34;</span> }
</span></span><span style="display:flex;"><span>  );
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">google</span>.<span style="color:#a6e22e">accounts</span>.<span style="color:#a6e22e">id</span>.<span style="color:#a6e22e">prompt</span>();
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">onMounted</span>(() =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#66d9ef">typeof</span> <span style="color:#a6e22e">google</span> <span style="color:#f92672">!==</span> <span style="color:#e6db74">&#39;undefined&#39;</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">initSignIn</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">script</span>&gt;
</span></span></code></pre></div><h2 id="introduction">Introduction</h2>
<p>A common situation when building a website is letting users sign in with their preexisting
Google accounts. Google has made this process rather simple, compared to the traditional
OAuth provider set up, with documentation available <a href="https://developers.google.com/identity/gsi/web/guides/display-button">here</a>.</p>
<p>However, the documentation only covers vanilla JS and HTML (why?).
Making the sign in prompt and button work in Vue took a little bit more work.</p>
<blockquote>
<p>This guide will not go through the process of obtaining a Google API Client ID,
for that you can read Google&rsquo;s documentation.</p>
</blockquote>
<h2 id="first-steps">First Steps</h2>
<p>Right off the bat Google gives us two alternatives: use HTML or JavaScript to display the
login button. The HTML approach actually still uses JavaScript, only that the process is
automated by using a predetermined button <code>id</code>.</p>
<p>Regardless of which approach you use, you must first include Google&rsquo;s Sign In script,
available at <code>https://accounts.google.com/gsi/client</code>.
The naive thing is to try to use the <code>script</code> tag, as it appears in Google&rsquo;s documentation.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://accounts.google.com/gsi/client&#34;</span> <span style="color:#a6e22e">async</span> /&gt;
</span></span></code></pre></div><p>However as of Vue 3, using raw script tags within a component is not allowed.
Instead, you can use the following approach:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">component</span> <span style="color:#a6e22e">is</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;script&#34;</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://accounts.google.com/gsi/client&#34;</span> <span style="color:#a6e22e">async</span> /&gt;
</span></span></code></pre></div><p>With this sorted out, it&rsquo;s time to review our approaches.</p>
<h2 id="html-approach">HTML Approach</h2>
<p>With the <code>script</code> tag adapted to work in Vue 3, we can already create a basic Vue
component using the HTML approach.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">template</span>&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;login-provider-google&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">component</span> <span style="color:#a6e22e">is</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;script&#34;</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://accounts.google.com/gsi/client&#34;</span> <span style="color:#a6e22e">async</span> /&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;g_id_onload&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">data-client_id</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;...&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">data-context</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;signin&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">data-ux_mode</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;popup&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">data-login_uri</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;...&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">data-itp_support</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;true&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;g_id_signin&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">data-type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;standard&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">data-shape</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;rectangular&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">data-theme</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;outline&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">data-text</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;signin_with&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">data-size</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;large&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">data-logo_alignment</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;left&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">template</span>&gt;
</span></span></code></pre></div><p>This component uses a redirect, rather than a callback function.
If you are happy with using a redirect, this is the approach for you.</p>
<p>So why not simply use the <code>data-callback</code> attribute (as per documentation) and pass in
a callback too? I tried to, but I found it hard to directly pass a handler defined within
the component. I&rsquo;m sure it is possible, but I had more important things to do than to find
workarounds for this.</p>
<p>Instead I resorted to the other approach.</p>
<h2 id="js-approach">JS Approach</h2>
<p>In this approach, you are tasked with defining your own google sign in button element
and later initialise it within JavaScript.
We can easily replace the window.onload listener in the documentation with an
<code>onMounted</code> Vue hook.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-jsx" data-lang="jsx"><span style="display:flex;"><span>&lt;<span style="color:#f92672">template</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">component</span> <span style="color:#a6e22e">is</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;script&#34;</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://accounts.google.com/gsi/client&#34;</span> <span style="color:#a6e22e">async</span> /&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;gSignInButton&#34;</span> /&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">template</span>&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">setup</span> <span style="color:#a6e22e">lang</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;ts&#34;</span>&gt;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">initSignIn</span> <span style="color:#f92672">=</span> () =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">google</span>.<span style="color:#a6e22e">accounts</span>.<span style="color:#a6e22e">id</span>.<span style="color:#a6e22e">initialize</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">client_id</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;YOUR_CLIENT_ID&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">callback</span><span style="color:#f92672">:</span> (<span style="color:#a6e22e">response</span>) =&gt; { <span style="color:#75715e">// do something }
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">google</span>.<span style="color:#a6e22e">accounts</span>.<span style="color:#a6e22e">id</span>.<span style="color:#a6e22e">renderButton</span>(
</span></span><span style="display:flex;"><span>    document.<span style="color:#a6e22e">getElementById</span>(<span style="color:#e6db74">&#34;gSignInButton&#34;</span>),
</span></span><span style="display:flex;"><span>    { <span style="color:#a6e22e">type</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;standard&#34;</span>, <span style="color:#a6e22e">text</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;sign_in_with&#34;</span>, <span style="color:#a6e22e">theme</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;outline&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">size</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;large&#34;</span>, <span style="color:#a6e22e">width</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;400&#34;</span> }
</span></span><span style="display:flex;"><span>  );
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">google</span>.<span style="color:#a6e22e">accounts</span>.<span style="color:#a6e22e">id</span>.<span style="color:#a6e22e">prompt</span>();
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">onMounted</span>(() =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">initSignIn</span>();
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">script</span>&gt;
</span></span></code></pre></div><p>This works for the most part. But there will be times when the script has not loaded
by the time the onMounted hook is executed. Even removing the async attribute won&rsquo;t fix
it. This leads to the very confusing effect of having a login page with no button.
This may be somewhat hard to catch too. I was only able to trigger this by logging in,
then visiting a few pages, reloading, and then signing out. When I was redirected
to the login page, as clockwork, there was no Google Sign In button.</p>
<p>To account for this, we can take advantage of the onLoad event, which triggers once a script
is finally loaded.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-jsx" data-lang="jsx"><span style="display:flex;"><span>&lt;<span style="color:#f92672">component</span> <span style="color:#a6e22e">is</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;script&#34;</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://accounts.google.com/gsi/client&#34;</span> <span style="color:#960050;background-color:#1e0010">@</span><span style="color:#a6e22e">load</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;initSignIn&#34;</span> <span style="color:#a6e22e">async</span> /&gt;
</span></span></code></pre></div><p>Note that we have to leave the <code>onMounted</code> hook in place, for if the script has already loaded
the event will not trigger. To avoid any errors, we can first check if the library has already
loaded.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#a6e22e">onMounted</span>(() =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#66d9ef">typeof</span> <span style="color:#a6e22e">google</span> <span style="color:#f92672">!==</span> <span style="color:#e6db74">&#39;undefined&#39;</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">initSignIn</span>();
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>That should cover everything needed to use the Google Sign In button in Vue.
You could add your own logic to easily switch between the log-in and sign-up layouts, amongst
other things.</p>
<h2 id="faq">FAQ</h2>
<ul>
<li>Why not execute the script on the fly, for instance by adding the script element
after the fact?</li>
</ul>
<p>It just seemed a bit of a hack, and there was ultimately no need to.</p>
<ul>
<li>Why not use &lsquo;vue-plugin-load-script&rsquo; instead?</li>
</ul>
<p>Same as last question. Besides, it hasn&rsquo;t seen a commit in several years.
The last thing you want is to have unmaintained dependencies in an active project.</p>
<!-- raw HTML omitted -->
]]></content></item><item><title>Programming an nRF52 with the Raspberry Pi Debug Probe</title><link>https://jsan.ch/posts/2023/09/programming-an-nrf52-with-the-raspberry-pi-debug-probe/</link><pubDate>Thu, 07 Sep 2023 00:00:00 +0100</pubDate><guid>https://jsan.ch/posts/2023/09/programming-an-nrf52-with-the-raspberry-pi-debug-probe/</guid><description>TL;DR west build -- -DOPENOCD_NRF5_INTERFACE=cmsis-dap west flash --runner openocd should have you covered, as long as you can run OpenOCD without sudo.
Acknowledgements Thanks to Jeremy Bentham from iosoft for responding to my query.
Updates I have added information on how to erase the flash memory using the openocd runner (2023-09-07). Introduction Earlier this year I was designing a board which would be powered by the nRF52833. Like all other SoC&amp;rsquo;s in the nRF52 series, it can be programmed through a SWD interface.</description><content type="html"><![CDATA[<h2 id="tldr">TL;DR</h2>
<pre tabindex="0"><code>west build -- -DOPENOCD_NRF5_INTERFACE=cmsis-dap
west flash --runner openocd
</code></pre><p>should have you covered, as long as you can run OpenOCD <a href="#no-sudo" title="Running OpenOCD without sudo">without sudo</a>.</p>
<h2 id="acknowledgements">Acknowledgements</h2>
<p>Thanks to Jeremy Bentham from iosoft for responding to my query.</p>
<h2 id="updates">Updates</h2>
<ol>
<li>I have added information on how to erase the flash memory using the openocd runner (2023-09-07).</li>
</ol>
<h2 id="introduction">Introduction</h2>
<p>Earlier this year I was designing a board which would be powered by the <a href="https://www.nordicsemi.com/products/nrf52833">nRF52833</a>.
Like all other SoC&rsquo;s in the nRF52 series, it can be programmed through a SWD interface.
Since the project had a short timeline, I could not afford to explore other options, and so
I rushed to order an <a href="https://www.nordicsemi.com/Products/Development-hardware/nRF52833-DK">nRF52833 development kit</a>, which costed me £47.54 with VAT
from Digi-Key.
Fortunately, the development kit worked quite well at programming the board I had designed for the
duration of the project. I will not delve into the specific setup I used for this right now.</p>
<p>However, in the middle of development of that specific project, the Raspberry Pi foundation released
the <a href="https://www.raspberrypi.com/news/raspberry-pi-debug-probe-a-plug-and-play-debug-kit-for-12/">Debug Probe</a> for just £10 (or $12 in the US). As soon as I found out it offered SWD
support, I knew I had to give it a shot.
The opportunity would not come until I visited Cambridge, and the official Raspberry Pi store
(a few weeks ago), where I was able to get my hands on one.</p>
<h2 id="hardware-setup">Hardware Setup</h2>
<p><strong>WARNING:</strong>
The Pi Debug Probe is only meant to program boards which run between 1.8v to 3.3v.
Do not connect the debug probe to a board running at a higher voltage, such as 5v,
it may damage the probe.</p>
<p>
    <img src="https://www.raspberrypi.com/documentation/microcontrollers/images/the-probe.png"  alt="The debug probe and its accessories"  class="center"  style="border-radius: 8px;margin:20px 0 20px 0;"  />


The debug probe comes out of the box with three different cables that serve the same purpose.
You should choose the one which is more convenient for you. My board exposed the programming
interface as male pins so I used the cable with female headers.
Connect the plug end to the <em>DBUG</em> port, or the one in the right in this illustration.
Then, connect the other end as outlined in this table.</p>
<table>
<thead>
<tr>
<th>Debug Probe</th>
<th>nRF52</th>
</tr>
</thead>
<tbody>
<tr>
<td>SC (Orange)</td>
<td>SWDCLK</td>
</tr>
<tr>
<td>SD (Yellow)</td>
<td>SWDIO</td>
</tr>
<tr>
<td>GND</td>
<td>GND</td>
</tr>
</tbody>
</table>
<p>Make sure your debug probe is connected to the host computer, and that your board has a power source.</p>
<h2 id="software-setup">Software Setup</h2>
<p>You will need a working installation of the <a href="https://www.nordicsemi.com/Products/Development-software/nrf-connect-sdk">nRF Connect SDK</a>,
which can be installed with the <a href="https://www.nordicsemi.com/Products/Development-tools/nRF-Connect-for-Desktop/Download">nRF Connect for Desktop</a> app.</p>
<h3 id="openocd">OpenOCD</h3>
<p>Also known as the <a href="https://openocd.org/">open on-chip debugger</a>, is a tool created by Dominic Rath,
which &lsquo;provides on-chip programming and debugging support&rsquo;, and supports
different interfaces such as telnet, TCL, and GDB.
To work with the Raspberry Pi Debug Probe, or other Raspberry Pi boards,
you should download the fork of openocd maintained by the Raspberry Pi Foundation.
Instructions for that are available <a href="https://www.raspberrypi.com/documentation/microcontrollers/debug-probe.html#installing-openocd">here</a>.
On Linux, the following will do:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ sudo apt install automake autoconf build-essential texinfo libtool libftdi-dev libusb-1.0-0-dev
</span></span><span style="display:flex;"><span>$ git clone https://github.com/raspberrypi/openocd.git --branch rp2040-v0.12.0 --depth<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> --no-single-branch
</span></span><span style="display:flex;"><span>$ cd openocd
</span></span><span style="display:flex;"><span>$ ./bootstrap
</span></span><span style="display:flex;"><span>$ ./configure
</span></span><span style="display:flex;"><span>$ make -j4
</span></span><span style="display:flex;"><span>$ sudo make install
</span></span></code></pre></div><p>The main thing to know about openocd is that it operates through commands.
Commands may be passed directly with the <code>-c &quot;command here&quot;</code> argument,
or instead, commands can be placed in a file, which are usually <code>.cfg</code> files.
These are passed to openocd with the <code>-f file.cfg</code> argument.</p>
<h2 id="building-and-flashing">Building and Flashing</h2>
<p>To illustrate, I will use the <em>blinky</em> sample from Zephyr, which should
exist in your nRF Connect SDK installation under <code>zephyr/samples/basic/blinky</code>.
This sample only makes the board LED blink (huh).
Inside a terminal with access to the SDK, navigate to the project directory.
If you are using the blinky sample, I suggest working on a copy of it.</p>
<p>Then, issue this command, where <em>X</em> is the name of your board.
If you are using an nRF development kit, this might be something like
&ldquo;nrf52833dk_nrf52833&rdquo;.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>west build --board X <span style="color:#75715e"># Replace with the board you will program</span>
</span></span></code></pre></div><p>The build process should create a <code>zephyr.elf</code> file under <code>build/zephyr</code>,
which will be uploaded to the board with openocd.
The following command will load the configuration file needed to interact with the
debug probe, which uses the CMSIS-DAP protocol, it will also load the configuration
needed to program the nRF52 SoC. Finally, it will issue the command to
upload the program, reset the board, and exit the session.
The description for these and other commands can be found in the <a href="https://openocd.org/doc/html/General-Commands.html">documentation</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo openocd -f interface/cmsis-dap.cfg -f target/nrf52.cfg -c <span style="color:#e6db74">&#34;program build/zephyr/zephyr.elf verify reset exit&#34;</span>
</span></span></code></pre></div><p>The configuration files are included with the RPi installation of openocd, so the command
shoud work as it is. To view the source of the configuration files:
<a href="https://github.com/raspberrypi/openocd/blob/rp2040-v0.12.0/tcl/target/nrf52.cfg">nRF52.cfg</a> and <a href="https://github.com/raspberrypi/openocd/blob/rp2040-v0.12.0/tcl/interface/cmsis-dap.cfg">cmsis-dap.cfg</a>.</p>
<h3 id="integration-with-west">Integration with west</h3>
<p>Since I am used to programming the board with <code>west flash</code> when using the development kit,
I also wanted to do so with the debug probe. This is possible due to the fact that west is highly
configurable, and can use different &ldquo;runners&rdquo; to flash a board, one of which is openocd
To investigate this possibility, let&rsquo;s take a look at the board.cmake file for the nRF52833dk, or
the board of your preference.</p>
<p><code>zephyr/boards/arm/nrf52833dk_nrf52833/board.cmake</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmake" data-lang="cmake"><span style="display:flex;"><span><span style="color:#75715e"># SPDX-License-Identifier: Apache-2.0
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>board_runner_args(<span style="color:#e6db74">jlink</span> <span style="color:#e6db74">&#34;--device=nRF52833_xxAA&#34;</span> <span style="color:#e6db74">&#34;--speed=4000&#34;</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>board_runner_args(<span style="color:#e6db74">pyocd</span> <span style="color:#e6db74">&#34;--target=nrf52833&#34;</span> <span style="color:#e6db74">&#34;--frequency=4000000&#34;</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>include(<span style="color:#f92672">${</span>ZEPHYR_BASE<span style="color:#f92672">}</span><span style="color:#e6db74">/boards/common/nrfjprog.board.cmake</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>include(<span style="color:#f92672">${</span>ZEPHYR_BASE<span style="color:#f92672">}</span><span style="color:#e6db74">/boards/common/jlink.board.cmake</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>include(<span style="color:#f92672">${</span>ZEPHYR_BASE<span style="color:#f92672">}</span><span style="color:#e6db74">/boards/common/pyocd.board.cmake</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>include(<span style="color:#f92672">${</span>ZEPHYR_BASE<span style="color:#f92672">}</span><span style="color:#e6db74">/boards/common/openocd-nrf5.board.cmake</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p>Pay attention to the last line in this file, it includes the file below:</p>
<p><code>zephyr/boards/common/openocd-nrf5.board.cmake</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmake" data-lang="cmake"><span style="display:flex;"><span><span style="color:#75715e"># SPDX-License-Identifier: Apache-2.0
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#75715e"># Infer nrf51 vs nrf52 etc from the BOARD name. This enforces a board
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"># naming convention: &#34;nrf5x&#34; must appear somewhere in the board name
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"># for this to work.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"># Boards which don&#39;t meet this convention can set this variable before
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"># including this script.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>if (<span style="color:#e6db74">NOT</span> <span style="color:#e6db74">DEFINED</span> <span style="color:#e6db74">OPENOCD_NRF5_SUBFAMILY</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>  string(<span style="color:#e6db74">REGEX</span> <span style="color:#e6db74">MATCH</span> <span style="color:#e6db74">nrf5.</span> <span style="color:#e6db74">OPENOCD_NRF5_SUBFAMILY</span> <span style="color:#e6db74">&#34;${BOARD}&#34;</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>endif()<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>if(<span style="color:#e6db74">&#34;${OPENOCD_NRF5_SUBFAMILY}&#34;</span> <span style="color:#e6db74">STREQUAL</span> <span style="color:#e6db74">&#34;&#34;</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>  message(<span style="color:#e6db74">FATAL_ERROR</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Can&#39;t match nrf5 subfamily from BOARD name. &#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;To fix, set CMake variable OPENOCD_NRF5_SUBFAMILY.&#34;</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>endif()<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>if (<span style="color:#e6db74">NOT</span> <span style="color:#e6db74">DEFINED</span> <span style="color:#e6db74">OPENOCD_NRF5_INTERFACE</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>  set(<span style="color:#e6db74">OPENOCD_NRF5_INTERFACE</span> <span style="color:#e6db74">&#34;jlink&#34;</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>endif()<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span><span style="color:#75715e"># We can do the right thing for supported subfamilies using a generic
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"># script, at least for openocd 0.10.0 and the openocd shipped with
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"># Zephyr SDK 0.10.3.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>set(<span style="color:#e6db74">pre_init_cmds</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;set WORKAREASIZE 0x4000&#34;</span>	<span style="color:#75715e"># 16 kB RAM used for flashing
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#e6db74">&#34;source [find interface/${OPENOCD_NRF5_INTERFACE}.cfg]&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;transport select swd&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;source [find target/${OPENOCD_NRF5_SUBFAMILY}.cfg]&#34;</span>
</span></span><span style="display:flex;"><span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>foreach(<span style="color:#e6db74">cmd</span> <span style="color:#f92672">${</span>pre_init_cmds<span style="color:#f92672">}</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>  board_runner_args(<span style="color:#e6db74">openocd</span> <span style="color:#e6db74">--cmd-pre-init</span> <span style="color:#e6db74">&#34;${cmd}&#34;</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>endforeach()<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>include(<span style="color:#f92672">${</span>ZEPHYR_BASE<span style="color:#f92672">}</span><span style="color:#e6db74">/boards/common/openocd.board.cmake</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p>You might have noticed that this file configures the arguments that
will be passed to openocd, including an interface and a target:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmake" data-lang="cmake"><span style="display:flex;"><span>set(<span style="color:#e6db74">pre_init_cmds</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;set WORKAREASIZE 0x4000&#34;</span>	<span style="color:#75715e"># 16 kB RAM used for flashing
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#e6db74">&#34;source [find interface/${OPENOCD_NRF5_INTERFACE}.cfg]&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;transport select swd&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;source [find target/${OPENOCD_NRF5_SUBFAMILY}.cfg]&#34;</span>
</span></span><span style="display:flex;"><span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p>The target is already defined to be the nRF52.
The interface is the only thing left to change, which by default is set to jlink.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmake" data-lang="cmake"><span style="display:flex;"><span>if (<span style="color:#e6db74">NOT</span> <span style="color:#e6db74">DEFINED</span> <span style="color:#e6db74">OPENOCD_NRF5_INTERFACE</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>  set(<span style="color:#e6db74">OPENOCD_NRF5_INTERFACE</span> <span style="color:#e6db74">&#34;jlink&#34;</span>)<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010"></span>endif()<span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p>This can be overriden by setting the <code>$OPENOCD_NRF5_INTERFACE</code> cmake variable.
The variable can be set when issuing the <code>west build</code> command.
This brings us back to where we started:</p>
<pre tabindex="0"><code>west build -- -DOPENOCD_NRF5_INTERFACE=cmsis-dap
west flash --runner openocd
</code></pre><p><a href="https://docs.zephyrproject.org/latest/develop/west/build-flash-debug.html">See documentation</a>.</p>
<p>However, you must make sure that the current user can access cmsis-dap devices
without sudo. I have adapted these instructions from <a href="https://forgge.github.io/theCore/guides/running-openocd-without-sudo.html">here</a>.</p>
<h3 id="no-sudo">Running OpenOCD without sudo</h3>
<ol>
<li>Add user to <em>plugdev</em>:</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo useradd -G plugdev <span style="color:#66d9ef">$(</span>whoami<span style="color:#66d9ef">)</span>
</span></span></code></pre></div><ol start="2">
<li>Create a custom udev rule to change the file mode of CMSIS-DAP devices.
To do this, create a file at <code>/etc/udev/rules.d/99-openocd.rules</code>.
Creating the file will require root privileges.</li>
</ol>
<pre tabindex="0"><code>ACTION!=&#34;add|change&#34;, GOTO=&#34;openocd_rules_end&#34;
SUBSYSTEM!=&#34;usb|tty|hidraw&#34;, GOTO=&#34;openocd_rules_end&#34;

# CMSIS-DAP compatible adapters
ATTRS{product}==&#34;*CMSIS-DAP*&#34;, MODE=&#34;664&#34;, GROUP=&#34;plugdev&#34;

LABEL=&#34;openocd_rules_end&#34;
</code></pre><p>This udev rule will only allow non-root access to cmsis-dap devices.
If you want generalised access to openocd without being root, please read <a href="https://forgge.github.io/theCore/guides/running-openocd-without-sudo.html">this</a>,
but keep in mind it is better to restrict unnecessary access.</p>
<h3 id="update-erasing-the-flash-memory-2023-09-07">Update: Erasing the flash memory (2023-09-07)</h3>
<p>I commonly need to run <code>west flash --erase</code> to erase the nRF52 flash memory, which can contain
Bluetooth bonding metadata. This works only with the jlink runner, with the openocd
runner, you will need to run this instead:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>west flash --runner openocd --cmd-pre-load <span style="color:#e6db74">&#34;reset halt; nrf5 mass_erase&#34;</span>
</span></span></code></pre></div><p>This adds a custom openocd command after running <code>init</code> but before loading the program
to the chip. First, the board is reset and halted. Then, it runs a specific routine that will
erase the code memory and user configuration registers of the chip <a href="https://openocd.org/doc/html/Flash-Commands.html">(documentation)</a>.</p>
<h2 id="closing-thoughts">Closing thoughts</h2>
<ul>
<li>
<p>If you want to use a different Raspberry Pi board, such as a RPi 2, Lean2 covered
that <a href="https://iosoft.blog/2019/09/28/arm-gcc-lean-nordic-nrf52/">here</a>. Also, it might be worth looking at <a href="https://github.com/raspberrypi/openocd/blob/rp2040-v0.12.0/tcl/interface/raspberrypi-native.cfg">this config file</a>
from the raspberry pi fork of openocd.</p>
</li>
<li>
<p>If you want to program an nRF51 device instead, I would try to use the <code>target/nrf51.cfg</code>
target, but alas I have no way of testing this.</p>
</li>
</ul>
<h2 id="related-material">Related material</h2>
<ul>
<li><a href="https://www.raspberrypi.com/documentation/microcontrollers/debug-probe.html">Raspberry Pi Debug Probe Documentation</a></li>
<li><a href="https://iosoft.blog/2019/09/28/arm-gcc-lean-nordic-nrf52/">Programming an nRF52 using a Raspberry Pi 2</a></li>
<li><a href="https://iosoft.blog/2019/01/28/raspberry-pi-openocd/">Using OpenOCD in the Raspberry Pi</a></li>
<li><a href="https://openocd.org/">OpenOCD official website</a></li>
</ul>
<!-- raw HTML omitted -->
]]></content></item></channel></rss>