mirror of
https://github.com/lupyuen/lupyuen.github.io.git
synced 2025-01-13 09:08:30 +08:00
1148 lines
No EOL
78 KiB
HTML
1148 lines
No EOL
78 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
|
||
<head>
|
||
<link rel="stylesheet" type="text/css" href="/_static/css/banner-styles.css?v=bsmaklHF" />
|
||
<link rel="stylesheet" type="text/css" href="/_static/css/iconochive.css?v=qtvMKcIJ" />
|
||
<link rel="canonical" href="https://lupyuen.org/articles/porting-druid-rust-widgets-to-pinetime-smart-watch.html" />
|
||
<!-- End Wayback Rewrite JS Include -->
|
||
<title data-rh="true">Porting [druid] Rust Widgets to PineTime Smart Watch</title>
|
||
<meta data-rh="true" charset="utf-8" />
|
||
<meta data-rh="true" name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
|
||
<meta data-rh="true" name="theme-color" content="#000000" />
|
||
<meta data-rh="true" property="og:type" content="article" />
|
||
<meta data-rh="true" property="article:published_time" content="2020-01-02T10:06:01.676Z" />
|
||
<meta data-rh="true" name="title" content="Porting [druid] Rust Widgets to PineTime Smart Watch" />
|
||
<meta data-rh="true" property="og:title" content="Porting [druid] Rust Widgets to PineTime Smart Watch" />
|
||
<meta data-rh="true" property="twitter:title" content="Porting [druid] Rust Widgets to PineTime Smart Watch" />
|
||
<meta data-rh="true" name="description"
|
||
content="A button that responds to our tapping and increments a counter… That’s what we shall accomplish today on the PineTime Smart Watch… In [druid], UI controls are known as Widgets. Our app contains two…" />
|
||
<meta data-rh="true" property="og:description"
|
||
content="A button that responds to our tapping and increments a counter… That’s what we shall accomplish today on the PineTime Smart Watch…" />
|
||
<meta data-rh="true" property="twitter:description"
|
||
content="A button that responds to our tapping and increments a counter… That’s what we shall accomplish today on the PineTime Smart Watch…" />
|
||
<meta data-rh="true" name="twitter:card" content="summary_large_image" />
|
||
<meta data-rh="true" name="twitter:creator" content="@MisterTechBlog" />
|
||
<meta data-rh="true" name="author" content="Lup Yuen Lee 李立源" />
|
||
<meta data-rh="true" name="robots" content="index,follow" />
|
||
<meta data-rh="true" name="referrer" content="unsafe-url" />
|
||
<meta data-rh="true" name="twitter:label1" value="Reading time" />
|
||
<meta data-rh="true" name="twitter:data1" value="12 min read" />
|
||
<meta property="og:image"
|
||
content="https://lupyuen.github.io/images/legacy/g1.png">
|
||
|
||
<!-- Begin scripts/rustdoc-header.html: Header for Custom Markdown files processed by rustdoc, like chip8.md -->
|
||
<link rel="alternate" type="application/rss+xml" title="RSS Feed for lupyuen" href="/rss.xml" />
|
||
<link rel="stylesheet" type="text/css" href="../normalize.css">
|
||
<link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle">
|
||
|
||
<link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle">
|
||
<link rel="stylesheet" type="text/css" href="../prism.css">
|
||
<script src="../storage.js"></script><noscript>
|
||
<link rel="stylesheet" href="../noscript.css"></noscript>
|
||
<link rel="shortcut icon" href="../favicon.ico">
|
||
<style type="text/css">
|
||
#crate-search {
|
||
background-image: url("../down-arrow.svg");
|
||
}
|
||
a {
|
||
color: #77d;
|
||
}
|
||
</style>
|
||
<!-- End scripts/rustdoc-header.html -->
|
||
|
||
</head>
|
||
|
||
<body>
|
||
|
||
<div id="root">
|
||
<div class="a b c">
|
||
<article>
|
||
<section class="cj ck cl cm ak cn ce n co"></section><span class="r"></span>
|
||
<div>
|
||
<div class="cp u cq cr cs ct"></div>
|
||
<section class="cu cv cw cx cy">
|
||
<div class="cz ak">
|
||
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/g1.png" /></p>
|
||
|
||
<p><em>PineTime Smart Watch running a [druid] app with
|
||
a Label Widget and a Button Widget</em></p>
|
||
|
||
</div>
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<div>
|
||
<div id="d2a3" class="ea eb ec bk ed b ee ef eg eh ei ej ek el em en eo">
|
||
<h1 class="ed b ee ep eg eq ei er ek es em et ec">Porting [druid] Rust Widgets to PineTime Smart
|
||
Watch</h1>
|
||
</div>
|
||
<div class="eu">
|
||
<div class="n ev ew ex ey">
|
||
<div class="o n">
|
||
<div><a rel="noopener"
|
||
href="https://lupyuen.github.io">
|
||
<div class="dd ez fa">
|
||
<div class="bs n fb o p cp fc fd fe ff fg ct"></div>
|
||
|
||
</div>
|
||
</a></div>
|
||
<div class="fi ak r">
|
||
<div class="n">
|
||
<div style="flex:1"><span class="bj b bk bl bm bn r ec q">
|
||
<div class="fj n o fk"><span class="bj dy ds bl di fl fm fn fo fp ec"><a
|
||
class="at au av aw ax ay az ba bb bc fq bf bg bh bi" rel="noopener"
|
||
href="https://lupyuen.github.io">Lup
|
||
Yuen Lee 李立源</a></span>
|
||
<div class="fr r ar h">
|
||
</div>
|
||
</div>
|
||
</span></div>
|
||
</div><span class="bj b bk bl bm bn r bo bp"><span class="bj dy ds bl di fl fm fn fo fp bo">
|
||
<div><a class="at au av aw ax ay az ba bb bc fq bf bg bh bi" rel="noopener"
|
||
href="https://lupyuen.github.io">
|
||
14 Dec 2019</a> <!-- -->·
|
||
<!-- -->
|
||
<!-- -->12
|
||
<!-- --> min read
|
||
</div>
|
||
</span></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<p>
|
||
UPDATE: This code in this article has been archived in the <a href="https://github.com/lupyuen/pinetime-rust-mynewt/tree/pre-lvgl">pre-lvgl</a> branch of pinetime-rust-mynewt.
|
||
The pinetime-rust-mynewt firmware has been revamped to support <a href="https://lupyuen.github.io/pinetime-rust-mynewt/articles/watchface">Rust Watch Faces on LVGL</a>.
|
||
<a href="https://lupyuen.github.io/pinetime-rust-mynewt/articles/watchface">Check out the updates </a>
|
||
</p>
|
||
|
||
<p id="410b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">A button that responds to our
|
||
tapping and increments a counter… That’s what we shall accomplish today on the <a
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener"
|
||
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot">PineTime
|
||
Smart Watch</a>…</p>
|
||
</div>
|
||
</div>
|
||
<div class="cz ak">
|
||
|
||
<div class="dl r dd">
|
||
<div class="hm r"><iframe
|
||
src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F7nXJdW_GkEw%3Ffeature%3Doembed&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D7nXJdW_GkEw&image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F7nXJdW_GkEw%2Fhqdefault.jpg&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=youtube"
|
||
allowfullscreen="" frameborder="0" height="480" width="854" title="PineTime [druid] touch widgets"
|
||
class="cp t u dh ak" scrolling="auto"></iframe></div>
|
||
</div>
|
||
|
||
</div>
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<p id="fe15" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Watch App</a> we see in the video
|
||
was created with the [<a
|
||
href="https://crates.io/crates/druid"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">druid</a>] Crate in Rust.</p>
|
||
<p id="7843" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">In [druid], UI controls are known
|
||
as Widgets. Our app contains two Widgets: A Label Widget that displays the counter, and a Button
|
||
Widget that increments the counter.</p>
|
||
<p id="183d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Lemme explain the code in the <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">[druid] app</a>…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/cf8de0355513e5a450a975839e7b7870.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
|
||
</em></p>
|
||
|
||
<p id="274d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">When the app is launched, we
|
||
create a new window, passing it the <code class="dm ho hp hq hr b">ui_builder</code> function that
|
||
will return a list of Widgets and their layouts. (More about <code
|
||
class="dm ho hp hq hr b">ui_builder</code> later.)</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/2e042fc196fb9cb7fe28ff836d08336c.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
|
||
</em></p>
|
||
|
||
<p id="bcf5" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here we set the Application State
|
||
to <code class="dm ho hp hq hr b">0</code>. <em class="hs">What’s this Application State?</em></p>
|
||
<p id="dadc" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Remember the counter value that’s
|
||
incremented every time we tap the button? This counter value is stored in the [druid] <strong
|
||
class="gr ht">Application State</strong>. Widgets will interact with the Application State: Our
|
||
Button Widget updates the state and our Label Widget displays the state.</p>
|
||
<p id="8005" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">That’s why [druid] is known as a
|
||
<a href="https://crates.io/crates/druid"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Data-Oriented Rust UI</a>… [druid]
|
||
Widgets are bound to data values in the Application State.</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/63e343144093f02599dc75f6b76798b8.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
|
||
</em></p>
|
||
|
||
<p id="e28f" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Recall that <code
|
||
class="dm ho hp hq hr b">main_window</code> contains a list of Widgets and their layouts. We’ll pass
|
||
this to the AppLauncher to launch the app window. The app shall be launched with <code
|
||
class="dm ho hp hq hr b">data</code>, the Application State, set to 0. If the launch fails, we’ll
|
||
stop with an error <code class="dm ho hp hq hr b">launch failed</code>.</p>
|
||
<p id="b14e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Now let’s look at <code
|
||
class="dm ho hp hq hr b">ui_builder</code> and how it creates the Widgets…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/b1829f1ec252fb6a45fbdd2c5d5bb23b.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
|
||
</em></p>
|
||
|
||
<p id="c0e1" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">First we create a <code
|
||
class="dm ho hp hq hr b">text</code> string that contains the counter value. How shall we get the
|
||
value of the <code class="dm ho hp hq hr b">text</code> string? Through this Rust Closure…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/a15ffce536a92befa5bc5088b3774de5.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
|
||
</em></p>
|
||
|
||
<p id="1e7e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">What’s a Rust
|
||
Closure?</em> Think of it as a Rust Function without a name. The Closure above is equivalent to this
|
||
verbose Rust Function…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/eaf6512835189ceb785a44304268ffe9.js"></script></p>
|
||
|
||
|
||
|
||
<p id="a14a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">So it’s simpler to use the
|
||
Closure form. The Closure simply returns the value of the counter in <code
|
||
class="dm ho hp hq hr b">data</code> (the Application State) as the value of the <code
|
||
class="dm ho hp hq hr b">text</code> string. The <code class="dm ho hp hq hr b">into</code> part
|
||
converts the returned value from number to string.</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/2be64d6b3cc1cc2d661059f7480e1608.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
|
||
</em></p>
|
||
|
||
<p id="e7af" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Now that we have the counter
|
||
value stored in our <code class="dm ho hp hq hr b">text</code> string, let’s display it with a Label
|
||
Widget.</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/234494d01b9520199db07daf21b70d02.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
|
||
</em></p>
|
||
|
||
<p id="61ee" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here we create a button labelled
|
||
increment that calls this Rust Closure whenever the button is tapped…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/fcfab489865c453417cd6180c925fc60.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
|
||
</em></p>
|
||
|
||
<p id="881c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This Closure is equivalent to the
|
||
Rust Function…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/8a067757c1816fa101b2a2d60b6057b8.js"></script></p>
|
||
|
||
|
||
|
||
<p id="1a92" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm ho hp hq hr b">data</code> contains our Application State, which is our counter value. The
|
||
Closure simply increments our counter value by 1, every time the button is tapped.</p>
|
||
<p id="e00b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">Hey Presto!</em>
|
||
That’s the magic behind our Data-Oriented UI… The button increments the counter in our Application
|
||
State, the label displays the value of the counter in our Application State!</p>
|
||
<p id="d6f2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">[druid] completes the magic act
|
||
by wiring up the Widgets to the Application State… When our Application State changes (upon tapping
|
||
the button), [druid] automagically refreshes our Label Widget to show the new counter value.</p>
|
||
<p id="e398" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Let’s tell [druid] how to layout
|
||
our Label and Button Widgets for display…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/42f63217ec8547d3040d587f23eefb41.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
|
||
</em></p>
|
||
|
||
<p id="a402" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">We’ll display the Label and
|
||
Button Widgets in a single column <code class="dm ho hp hq hr b">col</code>. The Label Widget goes on
|
||
top, centered horizontally, with a padding of 5 pixels.</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/3e10975902d1a13ba41858c8b87f5f82.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
|
||
</em></p>
|
||
|
||
<p id="ff7e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The Button Widget appears next in
|
||
<code class="dm ho hp hq hr b">col</code>. Also with a padding of 5 pixels.</p>
|
||
<p id="73d3" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Note that the second argument to
|
||
<code class="dm ho hp hq hr b">add_child</code> is the same for both the label and the button: <code
|
||
class="dm ho hp hq hr b">1.0</code>. This means that the label and button shall occupy equal
|
||
vertical spacing, i.e. the label shall fill the top half of the screen, the button shall fill the
|
||
bottom half. This is similar to the <code
|
||
class="dm ho hp hq hr b"><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">flexbox</a></code><a
|
||
href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"> concept in CSS</a>.</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/4451cacfd5a46a1cb89bbd9e2441c578.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
|
||
</em></p>
|
||
|
||
<p id="f5f0" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">And lastly, we return the <code
|
||
class="dm ho hp hq hr b">col</code> to the caller. To recap: <code
|
||
class="dm ho hp hq hr b">col</code> contains the Label and Button Widgets, the Closures for
|
||
displaying and incrementing the Application State counter, and the layout of the Widgets.</p>
|
||
<p id="bc8a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">That’s the clever simplicity of
|
||
[druid] apps. [druid] apps have a Declarative UI like <a
|
||
href="https://developer.apple.com/tutorials/swiftui/creating-a-watchos-app"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">SwiftUI</a> and <a
|
||
href="https://flutterbyexample.com/reusable-custom-card-widget"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Flutter</a>… Without the legacy
|
||
baggage of iOS and Android. That’s why I think [druid] is perfect for building Watch Apps for
|
||
PineTime!</p>
|
||
<p id="d5de" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here’s the complete [druid] Watch
|
||
App for PineTime…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/55496eb6c8b78a6b036b13508a1d79be.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
|
||
</em></p>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="d632" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">Powering [druid] with Type
|
||
Inference</h1>
|
||
<p id="6c5c" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">The above Rust source code for
|
||
the watch app looks so trivial (compared with SwiftUI and Flutter)… <em class="hs">Surely something
|
||
must be missing in that app?</em></p>
|
||
<p id="6eb3" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Oh yes, plenty is missing from
|
||
the app… <em class="hs">But the Rust Compiler performs </em><a
|
||
href="https://doc.rust-lang.org/stable/rust-by-example/types/inference.html"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><em class="hs">Type
|
||
Inference</em></a><em class="hs"> and fills in all the missing bits for us!</em> Here’s how it
|
||
looks with the missing bits filled in…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/fe85063d052c0544e35e7fd18cc0b27d.js"></script></p>
|
||
|
||
|
||
|
||
<p id="bbdc" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Note that the Widgets have been
|
||
expanded as <code class="dm ho hp hq hr b">Label<u32></code> and <code
|
||
class="dm ho hp hq hr b">Button<u32></code>. That’s because <code
|
||
class="dm ho hp hq hr b">Label</code> and <code class="dm ho hp hq hr b">Button</code> are actually
|
||
Generic Types in Rust.</p>
|
||
<p id="4334" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Quick recap of Generic Types:
|
||
<code class="dm ho hp hq hr b">Vec</code> is a Generic Type that represents a vector (array) of
|
||
values. When we write <code class="dm ho hp hq hr b">Vec<i32></code>, it refers to a vector of
|
||
integer values (<code class="dm ho hp hq hr b">i32</code>).</p>
|
||
<p id="deee" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Thus <code
|
||
class="dm ho hp hq hr b">Label<u32></code> and <code
|
||
class="dm ho hp hq hr b">Button<u32></code> are Generic Widgets that are backed by <code
|
||
class="dm ho hp hq hr b">u32</code>, our Application State that contains the counter. <em
|
||
class="hs">Reminds us that [druid] is really a Data-Oriented UI!</em></p>
|
||
<p id="b70b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Some types (like <code
|
||
class="dm ho hp hq hr b">WindowDesc</code>) are Generic Types with two parameters. So this line of
|
||
code in our app…</p>
|
||
<pre
|
||
class="hh hi hj hk hl iu iv iw"><span id="132f" class="ix ie ec bk hr b ds iy iz r ja">let main_window = WindowDesc::new(ui_builder);</span></pre>
|
||
<p id="ffe2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Actually expands to this
|
||
complicated mess…</p>
|
||
<pre
|
||
class="hh hi hj hk hl iu iv iw"><span id="b0db" class="ix ie ec bk hr b ds iy iz r ja">let main_window = WindowDesc::<u32,Flex<u32>>::new(ui_builder);</span></pre>
|
||
<p id="be4c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">What a bloody
|
||
clever compiler!</em> The Rust Compiler makes it easy for us to write Watch Apps. Under the hood,
|
||
things are a lot more complicated with the Generic Types. Read on to learn how [druid] Widgets are
|
||
implemented on PineTime…</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="55ec" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">Downsizing [druid] from Desktop
|
||
to Embedded</h1>
|
||
<p id="d91d" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">[druid] is a GUI toolkit that
|
||
produces desktop apps for Windows, macOS and Linux. It doesn’t support Embedded Platforms like
|
||
PineTime. So I had to change some features to make [druid] work on PineTime. <em class="hs">(Only the
|
||
[druid] implementation was changed, not the [druid] APIs… The same [druid] desktop application code
|
||
can be compiled for PineTime without any code changes! Compare the above [druid] code for PineTime
|
||
with </em><a
|
||
href="https://github.com/xi-editor/druid/blob/master/druid/examples/hello.rs"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><em class="hs">the original
|
||
version</em></a><em class="hs">)</em></p>
|
||
<p id="011a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">On Embedded Rust we declare our
|
||
programs as <code
|
||
class="dm ho hp hq hr b">#![<a href="https://rust-embedded.github.io/book/intro/no-std.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">no_std</a>]</code>.
|
||
This means that our embedded programs will call the <a
|
||
href="https://doc.rust-lang.org/core/index.html"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong class="gr ht">Rust Core
|
||
Library</strong></a>, instead of the usual <a
|
||
href="https://doc.rust-lang.org/std/index.html"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong class="gr ht">Rust
|
||
Standard Library</strong></a> which has many more features. Any code in [druid] that uses the
|
||
following features from the Rust Standard Library will <em class="hs">NOT</em> compile on PineTime…
|
||
</p>
|
||
<ol class="">
|
||
<li id="5e50" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc jb jc jd"><code
|
||
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/vec/index.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Vec</a></code>
|
||
vectors, because it uses <strong class="gr ht">Heap Memory</strong> to support resizable vectors.
|
||
And Heap Memory is not available on embedded platforms (by default).</li>
|
||
<li id="9320" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><code
|
||
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/string/index.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">String</a></code>
|
||
(though <code class="dm ho hp hq hr b">&str</code> is supported), because it also uses <strong
|
||
class="gr ht">Heap Memory</strong>.</li>
|
||
<li id="0f06" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><code
|
||
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/fmt/" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">format</a>!</code>,
|
||
because internally it uses a <code class="dm ho hp hq hr b">String</code> to write formatted
|
||
strings.</li>
|
||
<li id="8d8e" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><code
|
||
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/boxed/index.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Box</a></code>,
|
||
<code
|
||
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/cell/struct.RefCell.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">RefCell</a></code>,
|
||
<code
|
||
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/sync/struct.Arc.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Arc</a></code>
|
||
(Atomically Reference Counted) and <code
|
||
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/borrow/enum.Cow.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Cow</a></code>
|
||
(Clone On Write), because they use <strong class="gr ht">Heap Memory</strong> as well.</li>
|
||
</ol>
|
||
<p id="5d5b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">Why isn’t Heap
|
||
Memory available on Embedded Rust?</em> Embedded platforms like PineTime often have little RAM.
|
||
(Only 64 KB RAM for PineTime’s Nordic nRF52832 microcontroller) When RAM is limited, we prefer to
|
||
budget our memory requirements in advance… Just to be sure that our Smart Watch doesn’t allocate too
|
||
many Widgets at runtime and crash.</p>
|
||
<p id="a867" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Hence all space for Widgets
|
||
should be preallocated in Static Memory before the Watch App starts. <em class="hs">(There is a way to
|
||
implement Heap Memory on embedded platforms, </em><a
|
||
href="https://doc.rust-lang.org/core/alloc/trait.GlobalAlloc.html"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><em class="hs">check this for
|
||
details</em></a><em class="hs">)</em></p>
|
||
<p id="579c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">So many features
|
||
are missing from Embedded Rust… Is it still possible to run [druid] apps on PineTime?</em> Yes, I
|
||
used some tricks to make a quick (and somewhat dirty) port of [druid] to PineTime… Which you can also
|
||
use for porting Rust desktop libraries to embedded platforms! Read on to learn more…</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="24ff" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">Porting Vectors, Strings and
|
||
format! to Embedded</h1>
|
||
<p id="810e" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">The <code
|
||
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/vec/index.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Vec</a></code>
|
||
and <code
|
||
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/string/index.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">String</a></code>
|
||
types are used in many Rust libraries. On embedded platforms we can’t allow vectors and strings to be
|
||
resized on demand… But we can create vectors and strings that have a maximum size.</p>
|
||
<p id="c009" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here’s the quickest (and
|
||
dirtiest) way to use the [<a
|
||
href="https://crates.io/crates/heapless"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">heapless</a>] crate to replace
|
||
standard types <code class="dm ho hp hq hr b">Vec</code> and <code
|
||
class="dm ho hp hq hr b">String</code> by <a
|
||
href="https://docs.rs/heapless/0.5.1/heapless/struct.Vec.html"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">vectors</a> and <a
|
||
href="https://docs.rs/heapless/0.5.1/heapless/struct.String.html"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">strings</a> with fixed size
|
||
limits…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/9a0430d44a7df8c082ad71c0bcad1e89.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/localization.rs#L87-L91"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/localization.rs#L87-L91</a>
|
||
</em></p>
|
||
|
||
<p id="7ce2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">(Yep told you this
|
||
would be dirty… Should never override standard Rust types!)</em></p>
|
||
<p id="6289" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Most of the surrounding code that
|
||
calls <code class="dm ho hp hq hr b">Vec</code> and <code class="dm ho hp hq hr b">String</code>
|
||
should compile with this simple modification. (We may need to replace <code
|
||
class="dm ho hp hq hr b">&str</code> by <code class="dm ho hp hq hr b">String</code>) Watch out
|
||
for the extra copies of vectors and strings that our program will now be copying… Keep them small!</p>
|
||
<p id="5036" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Once the code works on our
|
||
embedded gadget, <em class="hs">Do The Right Thing</em> and rename <code
|
||
class="dm ho hp hq hr b">Vec</code> and <code class="dm ho hp hq hr b">String</code> to something
|
||
sensible, like <code class="dm ho hp hq hr b">FixedVec</code> and <code
|
||
class="dm ho hp hq hr b">FixedString</code>.</p>
|
||
<p id="22c0" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">There’s a similar trick for
|
||
porting <code
|
||
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/macro.format.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">format</a>!</code>
|
||
to embedded platforms. Let’s say we are converting a number v to a text string…</p>
|
||
<pre
|
||
class="hh hi hj hk hl iu iv iw"><span id="ffb5" class="ix ie ec bk hr b ds iy iz r ja">format!("{}", v)</span></pre>
|
||
<p id="55ff" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This fails to compile because
|
||
<code class="dm ho hp hq hr b">format!</code> uses an internal <code
|
||
class="dm ho hp hq hr b">String</code> to <a
|
||
href="https://doc.rust-lang.org/src/alloc/fmt.rs.html#561"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">store the formatted result</a>.
|
||
The [heapless] solution uses the <code
|
||
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/core/macro.write.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">write</a>!</code>
|
||
macro like this…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/3601e17aba48b9e6eda4e77d7edfcfdc.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/argvalue.rs#L63-L66"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/argvalue.rs#L63-L66</a>
|
||
</em></p>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="43cb" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">Boxing up Widgets and Windows
|
||
</h1>
|
||
<p id="a7fe" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">Without Heap Memory, [druid]
|
||
can’t use <code class="dm ho hp hq hr b">Box</code> to allocate Widgets on the heap…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/33ae6027e8aa716636c5d6756ebdfcd1.js"></script></p>
|
||
|
||
|
||
<p><em>Original [druid] code that allocates Widgets
|
||
on the heap. From <a
|
||
href="https://github.com/xi-editor/druid/blob/master/druid/src/widget/flex.rs#L103-L151"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/xi-editor/druid/blob/master/druid/src/widget/flex.rs#L103-L151</a>
|
||
</em></p>
|
||
|
||
<p id="660d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">Why does [druid]
|
||
use </em><code class="dm ho hp hq hr b"><em class="hs">Box</em></code><em class="hs"> Widgets?</em>
|
||
Because [druid] has Container Widgets (<code class="dm ho hp hq hr b">Flex</code> / <code
|
||
class="dm ho hp hq hr b">Row</code> / <code class="dm ho hp hq hr b">Column</code>) that contain
|
||
references to Child Widgets, like we see above.</p>
|
||
<p id="ec69" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">In our embedded version, [druid]
|
||
uses a custom <code
|
||
class="dm ho hp hq hr b"><a href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">WidgetBox</a></code>
|
||
type that pretends to be a <code class="dm ho hp hq hr b">Widget</code> in a <code
|
||
class="dm ho hp hq hr b">Box</code>…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/9d83be6ad4b20e808e2646df97ed796c.js"></script></p>
|
||
|
||
|
||
<p><em>Modified [druid] code that uses static
|
||
WidgetBox. From <a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/flex.rs#L126-L151"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/flex.rs#L126-L151</a>
|
||
</em></p>
|
||
|
||
<p id="b906" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here’s a crude implementation of
|
||
<code class="dm ho hp hq hr b">WidgetBox</code>… Instead of the heap, <code
|
||
class="dm ho hp hq hr b">WidgetBox</code> stores Widgets in an array in Static Memory. <code
|
||
class="dm ho hp hq hr b">WidgetBox</code> then returns the array index (Widget ID) at which the
|
||
Widget is stored….</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/d76314005e47a91be45caea2274c5638.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs</a>
|
||
</em></p>
|
||
|
||
<p id="bda2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm ho hp hq hr b">WidgetBox</code> implements the Traits (features) of a Widget by forwarding
|
||
the function calls to the Widget stored in Static Memory…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/5c725d5f288fb6b9e9793885673c9b4b.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs</a>
|
||
</em></p>
|
||
|
||
<p id="dc64" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Storing Widgets statically can be
|
||
tricky… Our Widgets are Generic Types that depend on the data type of the Application State. Earlier
|
||
we have seen that the Rust Compiler expands our Watch App with labels and buttons as <code
|
||
class="dm ho hp hq hr b">Label<u32></code> and <code
|
||
class="dm ho hp hq hr b">Button<u32></code></p>
|
||
<p id="645a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The solution is to use a <a
|
||
href="https://users.rust-lang.org/t/how-to-use-specialization-features/6023"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong class="gr ht">Specialised
|
||
Trait</strong></a> like this…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/a4946daf650c85e16708d9028522ec9b.js"></script></p>
|
||
|
||
|
||
<p><em>From <a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs</a>
|
||
</em></p>
|
||
|
||
<p id="2c55" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Note the pattern for coding a
|
||
Specialised Trait…</p>
|
||
<ol class="">
|
||
<li id="7bdc" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc jb jc jd">Declare the Trait
|
||
across all types: <br /><code
|
||
class="dm ho hp hq hr b">trait GlobalWidgets<<strong class="gr ht">D</strong>> { fn f(arg: <strong class="gr ht">D</strong>); ...</code>
|
||
</li>
|
||
<li id="ba7f" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd">Implement the Default
|
||
Trait: <br /><code
|
||
class="dm ho hp hq hr b">impl<<strong class="gr ht">D</strong>> GlobalWidgets<<strong class="gr ht">D</strong>> { <strong class="gr ht">default</strong> fn f(arg: <strong class="gr ht">D</strong>) { ...</code>
|
||
</li>
|
||
<li id="68c7" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd">Implement the
|
||
Specialised Trait for each specific type like <code class="dm ho hp hq hr b">u32</code>: <br /><code
|
||
class="dm ho hp hq hr b">impl GlobalWidgets<<strong class="gr ht">u32</strong>> { fn f(arg: <strong class="gr ht">u32</strong>) { ...</code>
|
||
</li>
|
||
</ol>
|
||
<p id="7b92" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The same trick is used to port
|
||
[druid] Windows to PineTime. Whenever we need to <code class="dm ho hp hq hr b">Box</code> up a
|
||
[druid] <code class="dm ho hp hq hr b">Window</code>, we use a <code
|
||
class="dm ho hp hq hr b"><a href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/windowbox.rs" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">WindowBox</a></code>
|
||
instead. Windows and Window Handlers are now <a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/win_handler.rs#L52-L264"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">stored in a static array</a> just
|
||
like Widgets.</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/g2.png" /></p>
|
||
|
||
|
||
<p><em>[druid] Dependencies</em></p>
|
||
|
||
<h1 id="8d2e" class="id ie ec bk bj if ee jl eg jm ii jn ik jo im jp io">Connect [druid] to PineTime’s
|
||
Display and Touch Controllers</h1>
|
||
<p id="3c20" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">Now that [druid] compiles
|
||
successfully on PineTime, let’s connect our fork of [druid] to the PineTime display and touch
|
||
hardware…</p>
|
||
<div class="jq jr js jt ju jv"><a
|
||
href="https://github.com/lupyuen/druid-embedded"
|
||
rel="noopener nofollow">
|
||
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
|
||
<div class="ko n co p kp kq">
|
||
<h2 class="bj if kr bl ec">
|
||
<div class="di jw fm fn jx fp">lupyuen/druid-embedded</div>
|
||
</h2>
|
||
</div>
|
||
<div class="ku r">
|
||
<div class="kv r kw kx ky ku kz la lb"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
<p id="818d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">[druid] calls the [<a
|
||
href="https://crates.io/crates/piet"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">piet</a>] crate to render graphics
|
||
and text. <a class="at cg hd he hf hg" target="_blank" rel="noopener"
|
||
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot">In
|
||
our first PineTime article</a>, we have created a Rust display driver based on the [<a
|
||
href="https://crates.io/crates/embedded-graphics"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">embedded-graphics</a>] and [<a
|
||
href="https://crates.io/crates/st7735-lcd"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">st7735-lcd</a>] crates.</p>
|
||
<p id="357a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Hence the solution: Adapt [piet]
|
||
to render graphics and text with [embedded-graphics]. Here’s the resulting fork…</p>
|
||
<div class="jq jr js jt ju jv"><a
|
||
href="https://github.com/lupyuen/piet-embedded/tree/master/piet-embedded-graphics"
|
||
rel="noopener nofollow">
|
||
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
|
||
<div class="ko n co p kp kq">
|
||
<h2 class="bj if kr bl ec">
|
||
<div class="di jw fm fn jx fp">lupyuen/piet-embedded</div>
|
||
</h2>
|
||
</div>
|
||
<div class="ku r">
|
||
<div class="lc r kw kx ky ku kz la lb"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
<p id="b850" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here’s a peek of [piet] adapted
|
||
for [embedded-graphics]: Rendering a Bezier path with [embedded-graphics] primitives (so that we can
|
||
have rounded buttons)…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/17fc071db37d5b1d496b9c8ae7f10126.js"></script></p>
|
||
|
||
|
||
<p><em>Rendering Widgets with [embedded-graphics].
|
||
From <a
|
||
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/context.rs#L67-L199"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/context.rs#L67-L199</a>
|
||
</em></p>
|
||
|
||
<p id="c523" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The display driver from our first
|
||
PineTime article <code class="dm ho hp hq hr b">display.rs</code> has been <a
|
||
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/display.rs"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">relocated into the [piet]
|
||
library</a>.</p>
|
||
<blockquote class="ld le lf">
|
||
<p id="1b65" class="gp gq ec hs gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="bk">💎</em><strong
|
||
class="gr ht">Advanced Topic</strong>: The [embedded-graphics] version of [piet] make take a few
|
||
seconds to <a
|
||
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/context.rs#L87-L96"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">blank the screen</a> or <a
|
||
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/context.rs#L124-L147"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">fill a region with colour</a>
|
||
(e.g. for buttons). I may enhance the display driver [st7735-lcd] to use SPI with DMA for such
|
||
operations.</p>
|
||
</blockquote>
|
||
<p id="6826" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">What about
|
||
touch?</em> <a class="at cg hd he hf hg" target="_blank" rel="noopener"
|
||
href="https://lupyuen.github.io/articles/building-a-rust-driver-for-pinetimes-touch-controller">In
|
||
our second PineTime article</a>, we have created a Rust Touch Controller Driver. <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/touch_sensor.rs#L81-L101"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">This Touch Controller Driver has
|
||
been wired up</a> to send touch events to [druid]. [druid] doesn’t support touch events yet, so we
|
||
simulate the mouse down + mouse up events instead…</p>
|
||
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/0c57c85af177dbe9ceed957e37f08db2.js"></script></p>
|
||
|
||
|
||
<p><em>Handling Touchable Widgets. From <a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/win_handler.rs#L62-L90"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/win_handler.rs#L62-L90</a>
|
||
</em></p>
|
||
|
||
<blockquote class="ld le lf">
|
||
<p id="f7d9" class="gp gq ec hs gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="bk">💎</em><strong
|
||
class="gr ht">Advanced Topic</strong>: If you check the video demo, most taps of the button
|
||
respond within 1 second, but some taps on the button don’t seem to increment the counter, and some
|
||
taps appear to increment the counter twice. That’s because the Touch Controller Driver has not been
|
||
implemented completely.</p>
|
||
<p id="0202" class="gp gq ec hs gr b gs gt gu gv gw gx gy gz ha hb hc"><a class="at cg hd he hf hg"
|
||
target="_blank" rel="noopener"
|
||
href="https://lupyuen.github.io/articles/building-a-rust-driver-for-pinetimes-touch-controller">The
|
||
touchscreen controller supports multitouch</a>, so the controller tracks each finger and reports
|
||
whether each finger is a press (down) or release (up) action. The current Touch Controller Driver <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/touch_sensor.rs#L81-L101"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">interprets only the first finger
|
||
detected</a>, and always assume it’s a press (down) action not a release (up) action.</p>
|
||
</blockquote>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="11b4" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">Other Features Downsized in
|
||
[druid] and [kurbo]</h1>
|
||
<p id="088b" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">The above fork of [druid] has
|
||
some features commented out…</p>
|
||
<ol class="">
|
||
<li id="76d5" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc jb jc jd"><strong
|
||
class="gr ht">Localization</strong>: Our [druid] app uses a <a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/localization.rs"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Localized String</a> <code
|
||
class="dm ho hp hq hr b">hello-counter</code>… This actually refers to a <a
|
||
href="https://github.com/xi-editor/druid/blob/master/druid/resources/i18n/en-US/builtin.ftl"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Resource File</a> that specifies
|
||
the displayed text as <code class="dm ho hp hq hr b">Current value is <count></code>. This is
|
||
probably overkill for a proof-of-concept, so it has been stubbed out and replaced by <a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/argvalue.rs"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">a simpler version</a> that
|
||
displays only the first argument.</li>
|
||
<li id="e48f" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><strong
|
||
class="gr ht">Themes and Environments</strong>: [druid] supports UI themes and configurable
|
||
environment settings. For embedded platforms they should probably be implemented as compile-time
|
||
constants. So Themes and Environments have been <a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/env.rs#L40-L83"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">stubbed out for now</a>.</li>
|
||
<li id="52ec" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/event.rs#L127-L137"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong
|
||
class="gr ht">Menus</strong></a><strong class="gr ht"> and </strong><a
|
||
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/event.rs#L82-L94"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong
|
||
class="gr ht">Keyboards</strong></a>: Since they are not necessary for Watch Apps</li>
|
||
</ol>
|
||
<p id="fdaa" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">[druid] and [piet] use the [<a
|
||
href="https://crates.io/crates/kurbo"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">kurbo</a>] crate for computing
|
||
Bezier curves, which are necessary for drawing rounded buttons. [kurbo] has been downsized to work
|
||
with PineTime and <code class="dm ho hp hq hr b">#![no_std]</code>…</p>
|
||
<ol class="">
|
||
<li id="0d47" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc jb jc jd"><strong
|
||
class="gr ht">Bezier paths</strong> are limited to <a
|
||
href="https://github.com/lupyuen/kurbo-embedded/blob/master/src/bezpath.rs#L15-L20"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">16 points</a></li>
|
||
<li id="6be4" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><strong
|
||
class="gr ht">Double-precision floating-point (</strong><code
|
||
class="dm ho hp hq hr b"><strong class="gr ht">f64</strong></code><strong class="gr ht">) math
|
||
functions</strong> seem to be missing from the Rust Core Library, so we are using the [<a
|
||
href="https://crates.io/crates/libm"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">libm</a>] crate instead. We
|
||
could switch to single-precision floating-point (f32) and use [<a
|
||
href="https://crates.io/crates/micromath"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">micromath</a>] instead, because
|
||
it’s optimised for embedded platforms.</li>
|
||
</ol>
|
||
<p id="62bc" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here is my fork of [kurbo] for
|
||
PineTime…</p>
|
||
<div class="jq jr js jt ju jv"><a
|
||
href="https://github.com/lupyuen/kurbo-embedded"
|
||
rel="noopener nofollow">
|
||
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
|
||
<div class="ko n co p kp kq">
|
||
<h2 class="bj if kr bl ec">
|
||
<div class="di jw fm fn jx fp">lupyuen/kurbo-embedded</div>
|
||
</h2>
|
||
</div>
|
||
<div class="ku r">
|
||
<div class="lg r kw kx ky ku kz la lb"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="5b74" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">PineTime ROM Usage for [druid]
|
||
and Friends</h1>
|
||
<p id="6f6a" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">[druid] + [piet] + [kurbo] +
|
||
[embedded-graphics] + [st7735-lcd] + Rust Core Library + Mynewt OS… <em class="hs">Will this long and
|
||
exciting Conga Line even run on PineTime’s Nordic nRF52832 microcontroller with </em><strong
|
||
class="gr ht"><em class="hs">512 KB ROM</em></strong><em class="hs"> and </em><strong
|
||
class="gr ht"><em class="hs">64 KB RAM</em></strong><em class="hs">?</em></p>
|
||
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/g3.png" /></p>
|
||
|
||
|
||
|
||
<p id="890e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">Yes it does!</em>
|
||
Total ROM used is only <strong class="gr ht">237 KB</strong>, less than half of the available ROM
|
||
space!</p>
|
||
<p id="4c74" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">And this was built in <strong
|
||
class="gr ht">Debug Mode</strong>, not optimised Release Mode! <em class="hs">(Which is hard to
|
||
debug and learn)</em></p>
|
||
<p id="647d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">What about ROM
|
||
space for firmware upgrades?</em> Well we have an additional <strong class="gr ht">4 MB SPI
|
||
Flash</strong> available on PineTime!</p>
|
||
<p id="7e01" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Total RAM used is <strong
|
||
class="gr ht">46 KB</strong>, which includes 32 KB of stack space <em class="hs">(Probably too
|
||
much)</em></p>
|
||
<p id="11e1" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">So a functional
|
||
Rust Widget UI for Watch Apps looks mighty feasible on PineTime!</em></p>
|
||
<p id="9b62" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">I think it also helps when I
|
||
replaced the <code class="dm ho hp hq hr b">Box</code> references in [druid] by <code
|
||
class="dm ho hp hq hr b">WidgetBox</code> and <code class="dm ho hp hq hr b">WindowBox</code>… The
|
||
Rust Compiler does an incredible job of removing dead code when everything is statically defined.</p>
|
||
<p id="6dae" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">We can analyse the ROM bloat by
|
||
loading this <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/logs/my_sensor_app.elf.map#L32538"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Linker Output Map</a> into a
|
||
Memory Map Google Sheet that’s <a class="at cg hd he hf hg" target="_blank" rel="noopener"
|
||
href="https://lupyuen.github.io/articles/stm32-blue-pill-analyse-and-optimise-your-ram-and-rom">explained
|
||
in this article</a>…</p>
|
||
</div>
|
||
</div>
|
||
<div class="cz ak">
|
||
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/g4.png" /></p>
|
||
|
||
|
||
<p><em>Functions that occupy the most ROM space, sorted
|
||
by ROM size (in bytes). From <a
|
||
href="https://docs.google.com/spreadsheets/d/1Lb217jqZGM7NlnOVCxpaLAI0CQcOTPnbMpksDSPVyiw/edit#gid=381366828&fvid=1643056349"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://docs.google.com/spreadsheets/d/1Lb217jqZGM7NlnOVCxpaLAI0CQcOTPnbMpksDSPVyiw/edit#gid=381366828&fvid=1643056349</a>
|
||
</em></p>
|
||
|
||
</div>
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<p id="d9b5" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><a
|
||
href="https://docs.google.com/spreadsheets/d/1Lb217jqZGM7NlnOVCxpaLAI0CQcOTPnbMpksDSPVyiw/edit#gid=381366828"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">The Google Sheet is here</a>. It
|
||
shows the functions that occupy the most ROM space, sorted by ROM size (in bytes). Some observations…
|
||
</p>
|
||
<ol class="">
|
||
<li id="1731" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc jb jc jd">[<a
|
||
href="https://crates.io/crates/libm"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">libm</a>], the floating-point
|
||
math library, seems to be taking the most ROM space. We could replace this by [<a
|
||
href="https://crates.io/crates/micromath"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">micromath</a>], a smaller math
|
||
library for embedded platforms</li>
|
||
<li id="49b1" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><code
|
||
class="dm ho hp hq hr b">core::num::flt2dec::strategy</code> needs closer study. And why <code
|
||
class="dm ho hp hq hr b">dragon</code> and his friend <code class="dm ho hp hq hr b">grisu</code>
|
||
are hiding in my watch!</li>
|
||
<li id="7d8a" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd">[druid] seems to take
|
||
a fair amount of ROM space. Which is expected since [druid] was designed for desktop use. The ROM
|
||
usage will probably drop drastically once we enable size optimisation in the Rust Release Build.
|
||
</li>
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="f64c" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">What’s Next?</h1>
|
||
<p id="974e" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">Here are the other articles in
|
||
the PineTime series…</p>
|
||
<div class="jq jr js jt ju jv"><a rel="noopener"
|
||
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot">
|
||
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
|
||
<div class="ko n co p kp kq">
|
||
<h2 class="bj if kr bl ec">
|
||
<div class="di jw fm fn jx fp">Sneak Peek of PineTime Smart Watch… And why it’s perfect for
|
||
teaching IoT</div>
|
||
</h2>
|
||
</div>
|
||
<div class="ku r">
|
||
<div class="lw r kw kx ky ku kz la lb"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
<div class="jq jr js jt ju jv"><a rel="noopener"
|
||
href="https://lupyuen.github.io/articles/building-a-rust-driver-for-pinetimes-touch-controller">
|
||
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
|
||
<div class="ko n co p kp kq">
|
||
<h2 class="bj if kr bl ec">
|
||
<div class="di jw fm fn jx fp">Building a Rust Driver for PineTime’s Touch Controller</div>
|
||
</h2>
|
||
</div>
|
||
<div class="ku r">
|
||
<div class="lx r kw kx ky ku kz la lb"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
<div class="jq jr js jt ju jv"><a rel="noopener"
|
||
href="https://lupyuen.github.io/articles/optimising-pinetimes-display-driver-with-rust-and-mynewt">
|
||
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
|
||
<div class="ko n co p kp kq">
|
||
<h2 class="bj if kr bl ec">
|
||
<div class="di jw fm fn jx fp">Optimising PineTime’s Display Driver with Rust and Mynewt</div>
|
||
</h2>
|
||
</div>
|
||
<div class="ku r">
|
||
<div class="ly r kw kx ky ku kz la lb"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
<p id="26f3" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">I hope the maintainers of
|
||
[druid], [piet], [kurbo], [embedded-graphics], [st7735-lcd] will consider incorporating some of the
|
||
tweaks I have covered in this article. <em class="hs">Together we can do so much more when the Rust
|
||
Ecosystem embraces Embedded Platforms!</em></p>
|
||
<p id="161c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">And I hope you agree with me that
|
||
this is clearly the better way to code Watch Apps. There’s more work to be done to flesh this out
|
||
fully for PineTime… Lemme know if you’re keen to help!</p>
|
||
<p id="c82c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The source code in this article
|
||
may be found in the <code class="dm ho hp hq hr b">pinetime</code> branch of this repository…</p>
|
||
<div class="jq jr js jt ju jv"><a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/pinetime"
|
||
rel="noopener nofollow">
|
||
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
|
||
<div class="ko n co p kp kq">
|
||
<h2 class="bj if kr bl ec">
|
||
<div class="di jw fm fn jx fp">lupyuen/stm32bluepill-mynewt-sensor</div>
|
||
</h2>
|
||
</div>
|
||
<div class="ku r">
|
||
<div class="lz r kw kx ky ku kz la lb"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
<p id="9eb7" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The macOS build files for
|
||
[druid], [piet], [kurbo], Embedded Rust and Mynewt OS for PineTime are available here…</p>
|
||
<div class="jq jr js jt ju jv"><a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/releases/tag/v13.0.0"
|
||
rel="noopener nofollow">
|
||
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
|
||
<div class="ko n co p kp kq">
|
||
<h2 class="bj if kr bl ec">
|
||
<div class="di jw fm fn jx fp">lupyuen/stm32bluepill-mynewt-sensor</div>
|
||
</h2>
|
||
</div>
|
||
<div class="ku r">
|
||
<div class="ma r kw kx ky ku kz la lb"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
<p id="3eb4" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">I’ll be packaging a proper
|
||
release when PineTime is available to the general public. Meanwhile if you have trouble building the
|
||
source files (<a class="at cg hd he hf hg" target="_blank" rel="noopener"
|
||
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot">as
|
||
explained in the first article</a>), please drop me a note! The PineTime build, flash and debug
|
||
steps on Visual Studio Code look like this…</p>
|
||
</div>
|
||
</div>
|
||
<div class="cz ak">
|
||
|
||
<div class="dl r dd">
|
||
<div class="mb r"><iframe
|
||
src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FAMVYXOSPjIg%3Ffeature%3Doembed&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DAMVYXOSPjIg&image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FAMVYXOSPjIg%2Fhqdefault.jpg&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=youtube"
|
||
allowfullscreen="" frameborder="0" height="300" width="400"
|
||
title="Rust and Mynewt OS on PineTime Smart Watch" class="cp t u dh ak" scrolling="auto"></iframe>
|
||
</div>
|
||
</div>
|
||
<p><em>PineTime build, flash and debug steps on Visual
|
||
Studio Code</em></p>
|
||
|
||
</div>
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<p id="b9e4" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">UPDATE: <a
|
||
href="https://store.pine64.org/?product=pinetime-dev-kit"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">PineTime is now available for
|
||
purchase by general public</a>! I’m creating a <a
|
||
href="https://github.com/lupyuen/pinetime-rust-mynewt"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">new repository here</a> that will
|
||
have a prebuilt firmware image for PineTime. I’ll also include instructions for flashing the firmware
|
||
to your PineTime with a Raspberry Pi.</p>
|
||
<div class="jq jr js jt ju jv"><a
|
||
href="https://github.com/lupyuen/pinetime-rust-mynewt"
|
||
rel="noopener nofollow">
|
||
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
|
||
<div class="ko n co p kp kq">
|
||
<h2 class="bj if kr bl ec">
|
||
<div class="di jw fm fn jx fp">lupyuen/pinetime-rust-mynewt</div>
|
||
</h2>
|
||
</div>
|
||
<div class="ku r">
|
||
<div class="mc r kw kx ky ku kz la lb"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
</div>
|
||
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://github.com/sponsors/lupyuen">Sponsor me a coffee</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io">Check out my articles</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/rss.xml">RSS Feed</a></p>
|
||
</li>
|
||
</ul>
|
||
|
||
</body>
|
||
|
||
</html>
|
||
<!--
|
||
FILE ARCHIVED ON 04:48:08 Jan 09, 2020 AND RETRIEVED FROM THE
|
||
INTERNET ARCHIVE ON 10:32:54 Feb 21, 2021.
|
||
JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
|
||
|
||
ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
|
||
SECTION 108(a)(3)).
|
||
-->
|
||
<!--
|
||
playback timings (ms):
|
||
exclusion.robots.policy: 0.385
|
||
captures_list: 230.53
|
||
exclusion.robots: 0.399
|
||
RedisCDXSource: 0.705
|
||
PetaboxLoader3.resolve: 218.895 (4)
|
||
esindex: 0.012
|
||
CDXLines.iter: 27.074 (3)
|
||
LoadShardBlock: 198.076 (3)
|
||
PetaboxLoader3.datanode: 63.453 (4)
|
||
load_resource: 135.773
|
||
--> |