lupyuen.org/articles/rusti2c.html

1095 lines
No EOL
69 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="rustdoc">
<title>Rust talks I2C on Apache NuttX RTOS</title>
<!-- Begin scripts/articles/*-header.html: Article Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<meta property="og:title"
content="Rust talks I2C on Apache NuttX RTOS"
data-rh="true">
<meta property="og:description"
content="Reading the Bosch BME280 I2C Sensor with Rust Embedded HAL... On BL602 RISC-V SoC and Apache NuttX RTOS"
data-rh="true">
<meta property="og:image"
content="https://lupyuen.github.io/images/rusti2c-title.jpg">
<meta property="og:type"
content="article" data-rh="true">
<link rel="canonical" href="https://lupyuen.org/articles/rusti2c.html" />
<!-- End scripts/articles/*-header.html -->
<!-- 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="../dark.css">
<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");
}
</style>
<!-- End scripts/rustdoc-header.html -->
</head>
<body class="rustdoc">
<!--[if lte IE 8]>
<div class="warning">
This old browser is unsupported and will most likely display funky
things.
</div>
<![endif]-->
<!-- Begin scripts/rustdoc-before.html: Pre-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
<!-- Begin Theme Picker -->
<div class="theme-picker" style="left: 0"><button id="theme-picker" aria-label="Pick another theme!"><img src="../brush.svg"
width="18" alt="Pick another theme!"></button>
<div id="theme-choices"></div>
</div>
<!-- Theme Picker -->
<!-- End scripts/rustdoc-before.html -->
<h1 class="title">Rust talks I2C on Apache NuttX RTOS</h1>
<nav id="rustdoc"><ul>
<li><a href="#read-sensor-data-from-bme280" title="Read Sensor Data from BME280">1 Read Sensor Data from BME280</a><ul></ul></li>
<li><a href="#connect-bme280" title="Connect BME280">2 Connect BME280</a><ul></ul></li>
<li><a href="#run-bme280-app" title="Run BME280 App">3 Run BME280 App</a><ul></ul></li>
<li><a href="#rust-driver-for-bme280" title="Rust Driver for BME280">4 Rust Driver for BME280</a><ul></ul></li>
<li><a href="#nuttx-embedded-hal" title="NuttX Embedded HAL">5 NuttX Embedded HAL</a><ul></ul></li>
<li><a href="#read-i2c-register" title="Read I2C Register">6 Read I2C Register</a><ul></ul></li>
<li><a href="#write-i2c-register" title="Write I2C Register">7 Write I2C Register</a><ul></ul></li>
<li><a href="#whats-next" title="Whats Next">8 Whats Next</a><ul></ul></li>
<li><a href="#notes" title="Notes">9 Notes</a><ul></ul></li>
<li><a href="#appendix-read-i2c-register-in-embedded-hal" title="Appendix: Read I2C Register in Embedded HAL">10 Appendix: Read I2C Register in Embedded HAL</a><ul>
<li><a href="#nuttx-types-and-constants" title="NuttX Types and Constants">10.1 NuttX Types and Constants</a><ul></ul></li>
<li><a href="#into-embedded-hal" title="Into Embedded HAL">10.2 Into Embedded HAL</a><ul></ul></li></ul></li>
<li><a href="#appendix-write-i2c-register-in-embedded-hal" title="Appendix: Write I2C Register in Embedded HAL">11 Appendix: Write I2C Register in Embedded HAL</a><ul></ul></li>
<li><a href="#appendix-read-i2c-register-in-c" title="Appendix: Read I2C Register in C">12 Appendix: Read I2C Register in C</a><ul>
<li><a href="#c-types-and-constants" title="C Types and Constants">12.1 C Types and Constants</a><ul></ul></li></ul></li>
<li><a href="#appendix-build-flash-and-run-nuttx" title="Appendix: Build, Flash and Run NuttX">13 Appendix: Build, Flash and Run NuttX</a><ul>
<li><a href="#download-nuttx" title="Download NuttX">13.1 Download NuttX</a><ul></ul></li>
<li><a href="#configure-nuttx" title="Configure NuttX">13.2 Configure NuttX</a><ul></ul></li>
<li><a href="#configure-rust-target" title="Configure Rust Target">13.3 Configure Rust Target</a><ul></ul></li>
<li><a href="#build-nuttx" title="Build NuttX">13.4 Build NuttX</a><ul></ul></li>
<li><a href="#flash-nuttx" title="Flash NuttX">13.5 Flash NuttX</a><ul></ul></li>
<li><a href="#run-nuttx" title="Run NuttX">13.6 Run NuttX</a><ul></ul></li></ul></li></ul></nav><p>📝 <em>22 Mar 2022</em></p>
<p><img src="https://lupyuen.github.io/images/rusti2c-title.jpg" alt="Bosch BME280 Sensor connected to Pine64 PineCone BL602 RISC-V Board" /></p>
<p><em><a href="https://www.bosch-sensortec.com/products/environmental-sensors/humidity-sensors-bme280/">Bosch BME280 Sensor</a> connected to <a href="https://lupyuen.github.io/articles/pinecone">Pine64 PineCone BL602 RISC-V Board</a></em></p>
<p><a href="https://en.wikipedia.org/wiki/I%C2%B2C"><strong>I2C</strong></a> is a great way to connect all kinds of <strong>Sensor Modules</strong> when were creating an <strong>IoT Gadget</strong>. Like sensors for temperature, light, motion, spectroscopy, soil moisture, GPS, LIDAR, … <a href="https://www.sparkfun.com/categories/tags/i2c"><strong>and many more!</strong></a></p>
<p><em>But where will we get the Software Drivers for the I2C Sensors?</em></p>
<p><a href="https://github.com/rust-embedded/awesome-embedded-rust#driver-crates"><strong>Embedded Rust</strong></a> has a large collection of drivers for I2C Sensors. And they will work on <a href="https://github.com/rust-embedded/awesome-embedded-rust#hal-implementation-crates"><strong>many platforms!</strong></a></p>
<p>Today we shall experiment with the Rust Driver for <a href="https://www.bosch-sensortec.com/products/environmental-sensors/humidity-sensors-bme280/"><strong>Bosch BME280 Sensor</strong></a> (Temperature / Humdity / Air Pressure). And learn how we made it work on the (Linux-like) <a href="https://lupyuen.github.io/articles/nuttx"><strong>Apache NuttX RTOS</strong></a>.</p>
<p>Well run this on the <a href="https://lupyuen.github.io/articles/pinecone"><strong>BL602 RISC-V SoC</strong></a> (pic above), though it should work fine on ESP32 and other NuttX platforms.</p>
<p>Lets dive into our <strong>Rust I2C App for NuttX</strong></p>
<ul>
<li><a href="https://github.com/lupyuen/rust-i2c-nuttx"><strong>lupyuen/rust-i2c-nuttx</strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/rusti2c-code10a.png" alt="Read Sensor Data from BME280" /></p>
<p><a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/rust/src/bme280.rs">(Source)</a></p>
<h1 id="read-sensor-data-from-bme280"><a class="doc-anchor" href="#read-sensor-data-from-bme280">§</a>1 Read Sensor Data from BME280</h1>
<p>Heres how we read the <strong>Temperature, Humidity and Air Pressure</strong> from the BME280 Sensor: <a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/rust/src/bme280.rs">rust/src/bme280.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// Read Temperature, Pressure and Humidity from BME280 Sensor over I2C
</span><span class="kw">pub fn </span>read_bme280() {
<span class="comment">// Open I2C Port
</span><span class="kw">let </span>i2c = nuttx_embedded_hal::I2c::new(
<span class="string">"/dev/i2c0"</span>, <span class="comment">// I2C Port
</span><span class="number">400000</span>, <span class="comment">// I2C Frequency: 400 kHz
</span>).expect(<span class="string">"open failed"</span>);</code></pre></div>
<p>We begin by opening the I2C Port “<strong>/dev/i2c0</strong>”, configured for 400 kHz.</p>
<p>(This halts with an error if the I2C Port doesnt exist)</p>
<p><em>Whats nuttx_embedded_hal?</em></p>
<p>Thats the <strong>Hardware Abstraction Layer</strong> (HAL) for NuttX, coded in Rust. (More about this in a while)</p>
<p>Next we <strong>initialise the BME280 Driver</strong></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Init the BME280 Driver
</span><span class="kw">let </span><span class="kw-2">mut </span>bme280 = bme280::BME280::new(
i2c, <span class="comment">// I2C Port
</span><span class="number">0x77</span>, <span class="comment">// I2C Address of BME280
</span>nuttx_embedded_hal::Delay <span class="comment">// Delay Interface
</span>);</code></pre></div>
<p><strong>BME280</strong> comes from the BME280 Driver Crate. (As well see soon)</p>
<p>Before reading the BME280 Sensor, we <strong>initialise the sensor</strong></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Init the BME280 Sensor
</span>bme280.init()
.expect(<span class="string">"init failed"</span>);</code></pre></div>
<p>(This halts with an error if the initialisation fails)</p>
<p>Were ready to read the <strong>Temperature, Humidity and Air Pressure</strong> from the BME280 Sensor…</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Measure Temperature, Pressure and Humidity
</span><span class="kw">let </span>measurements = bme280.measure()
.expect(<span class="string">"measure failed"</span>);</code></pre></div>
<p>Finally we <strong>print the Sensor Data</strong></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Print the measurements
</span><span class="macro">println!</span>(<span class="string">"Relative Humidity = {}%"</span>,
measurements.humidity);
<span class="macro">println!</span>(<span class="string">"Temperature = {} deg C"</span>,
measurements.temperature);
<span class="macro">println!</span>(<span class="string">"Pressure = {} pascals"</span>,
measurements.pressure);
}</code></pre></div>
<p>Thats all we need to read the Sensor Data from the BME280 Sensor!</p>
<p><em>Where is println defined?</em></p>
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/macros.rs"><strong>println</strong></a> comes from our NuttX Embedded HAL. We import it at the top…</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Import Libraries
</span><span class="kw">use </span>nuttx_embedded_hal::{ <span class="comment">// NuttX Embedded HAL
</span>println, <span class="comment">// Print a formatted message
</span>};</code></pre></div>
<p>Before running our Rust App, lets connect the BME280 Sensor.</p>
<p><img src="https://lupyuen.github.io/images/bme280-connect.jpg" alt="Bosch BME280 Sensor connected to Pine64 PineCone BL602 RISC-V Board" /></p>
<h1 id="connect-bme280"><a class="doc-anchor" href="#connect-bme280">§</a>2 Connect BME280</h1>
<p>We connect BME280 to Pine64s <a href="https://lupyuen.github.io/articles/pinecone"><strong>PineCone BL602 Board</strong></a> as follows (pic above)…</p>
<div><table><thead><tr><th style="text-align: center">BL602 Pin</th><th style="text-align: center">BME280 Pin</th><th style="text-align: left">Wire Colour</th></tr></thead><tbody>
<tr><td style="text-align: center"><strong><code>GPIO 3</code></strong></td><td style="text-align: center"><code>SDA</code></td><td style="text-align: left">Green</td></tr>
<tr><td style="text-align: center"><strong><code>GPIO 4</code></strong></td><td style="text-align: center"><code>SCL</code></td><td style="text-align: left">Blue</td></tr>
<tr><td style="text-align: center"><strong><code>3V3</code></strong></td><td style="text-align: center"><code>3.3V</code></td><td style="text-align: left">Red</td></tr>
<tr><td style="text-align: center"><strong><code>GND</code></strong></td><td style="text-align: center"><code>GND</code></td><td style="text-align: left">Black</td></tr>
</tbody></table>
</div>
<p>The <strong>I2C Pins</strong> on BL602 are defined here: <a href="https://github.com/lupyuen/nuttx/blob/rusti2c/boards/risc-v/bl602/bl602evb/include/board.h#L85-L88">board.h</a></p>
<div class="example-wrap"><pre class="language-c"><code>/* I2C Configuration */
#define BOARD_I2C_SCL \
(GPIO_INPUT | GPIO_PULLUP | GPIO_FUNC_I2C | \
GPIO_PIN4)
#define BOARD_I2C_SDA \
(GPIO_INPUT | GPIO_PULLUP | GPIO_FUNC_I2C | \
GPIO_PIN3)</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/expander#pin-functions">(Which pins can be used? See this)</a></p>
<p>We disabled the <strong>UART1 Port</strong> because it uses the same pins as I2C: <a href="https://github.com/lupyuen/nuttx/blob/rusti2c/boards/risc-v/bl602/bl602evb/include/board.h#L63-L68">board.h</a></p>
<div class="example-wrap"><pre class="language-c"><code>#ifdef TODO /* Remember to check for duplicate pins! */
#define BOARD_UART_1_RX_PIN \
(GPIO_INPUT | GPIO_PULLUP | GPIO_FUNC_UART | \
GPIO_PIN3)
#define BOARD_UART_1_TX_PIN \
(GPIO_INPUT | GPIO_PULLUP | GPIO_FUNC_UART | \
GPIO_PIN4)
#endif /* TODO */</code></pre></div>
<p>(UART0 is used by the Serial Console)</p>
<p><em>What if were connecting to ESP32?</em></p>
<p><strong>For ESP32:</strong> The GPIO Pin Numbers for the I2C Port (I2C0) are defined in <a href="https://github.com/lupyuen/nuttx/blob/rusti2c/arch/xtensa/src/esp32/Kconfig#L797-L805">Kconfig</a> and menuconfig…</p>
<div class="example-wrap"><pre class="language-text"><code>config ESP32_I2C0_SCLPIN
int &quot;I2C0 SCL Pin&quot;
default 22
range 0 39
config ESP32_I2C0_SDAPIN
int &quot;I2C0 SDA Pin&quot;
default 23
range 0 39</code></pre></div>
<p><em>Do we need Pull-Up Resistors?</em></p>
<p>Were using the <a href="https://learn.sparkfun.com/tutorials/sparkfun-bme280-breakout-hookup-guide/all"><strong>SparkFun BME280 Breakout Board</strong></a>, which has <strong>Pull-Up Resistors</strong>. (So we dont need to add our own)</p>
<blockquote>
<p><img src="https://lupyuen.github.io/images/rusti2c-run2a.png" alt="Run BME280 App" /></p>
</blockquote>
<h1 id="run-bme280-app"><a class="doc-anchor" href="#run-bme280-app">§</a>3 Run BME280 App</h1>
<p>Were ready to run our Rust App on NuttX!</p>
<ol>
<li>
<p>Follow these steps to <strong>build, flash and run NuttX</strong></p>
<p><a href="https://lupyuen.github.io/articles/rusti2c#appendix-build-flash-and-run-nuttx"><strong>“Build, Flash and Run NuttX”</strong></a></p>
</li>
<li>
<p>At the NuttX Shell, enter this command to list the <strong>NuttX Devices</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>ls /dev</code></pre></div></li>
<li>
<p>We should see our <strong>I2C Port</strong> thats connected to BME280…</p>
<div class="example-wrap"><pre class="language-text"><code>/dev:
i2c0
...</code></pre></div></li>
<li>
<p>To <strong>read the BME280 Sensor</strong>, enter this command…</p>
<div class="example-wrap"><pre class="language-bash"><code>rust_i2c</code></pre></div></li>
<li>
<p>We should see the <strong>Relative Humidity, Temperature and Air Pressure</strong></p>
<div class="example-wrap"><pre class="language-text"><code>read_bme280
Relative Humidity = 89.284164%
Temperature = 29.942907 deg C
Pressure = 100483.04 pascals
Done!</code></pre></div>
<p><a href="https://github.com/lupyuen/rust-i2c-nuttx#test-rust-driver-for-bme280">(See the complete log)</a></p>
</li>
</ol>
<p>The Rust Driver for BME280 runs successfully on NuttX!</p>
<blockquote>
<p><img src="https://lupyuen.github.io/images/rusti2c-bme280.png" alt="Rust Driver for BME280" /></p>
</blockquote>
<h1 id="rust-driver-for-bme280"><a class="doc-anchor" href="#rust-driver-for-bme280">§</a>4 Rust Driver for BME280</h1>
<p><em>We ran the Rust Driver for BME280 on NuttX… Without any code changes?</em></p>
<p>Yeah amazing right? Earlier we saw this: <a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/rust/src/bme280.rs">rust/src/bme280.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Init the BME280 Driver
</span><span class="kw">let </span><span class="kw-2">mut </span>bme280 = bme280::BME280
::new( ... );</code></pre></div>
<p><strong>BME280</strong> comes from the Rust Embedded Driver for BME280 (pic above)…</p>
<ul>
<li><a href="https://crates.io/crates/bme280"><strong>crates.io/bme280</strong></a></li>
</ul>
<p>That we have added to our <a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/rust/Cargo.toml"><strong>Cargo.toml</strong></a></p>
<div class="example-wrap"><pre class="language-text"><code>## External Rust libraries used by this module. See crates.io.
[dependencies]
## BME280 Driver: https://crates.io/crates/bme280
bme280 = &quot;0.2.1&quot;
## NuttX Embedded HAL: https://crates.io/crates/nuttx-embedded-hal
nuttx-embedded-hal = &quot;1.0.10&quot;
## Rust Embedded HAL: https://crates.io/crates/embedded-hal
embedded-hal = &quot;0.2.7&quot; </code></pre></div>
<p>The Rust Driver for BME280 works on NuttX because of <strong>NuttX Embedded HAL</strong>. Lets look inside.</p>
<p><a href="https://crates.io/crates/nuttx-embedded-hal">(BTW: Always use the latest version of NuttX Embedded HAL)</a></p>
<p><img src="https://lupyuen.github.io/images/rusti2c-arch2.jpg" alt="NuttX Embedded HAL" /></p>
<h1 id="nuttx-embedded-hal"><a class="doc-anchor" href="#nuttx-embedded-hal">§</a>5 NuttX Embedded HAL</h1>
<p><em>Whats NuttX Embedded HAL?</em></p>
<p><strong>NuttX Embedded HAL</strong> (Hardware Abstraction Layer) is the Rust Library that exposes a Standard Rust Interface for the <strong>Input / Output Ports on NuttX</strong>: GPIO, I2C, SPI, …</p>
<ul>
<li>
<p><a href="https://crates.io/crates/nuttx-embedded-hal"><strong>crates.io/nuttx-embedded-hal</strong></a></p>
</li>
<li>
<p><a href="https://docs.rs/nuttx-embedded-hal/"><strong>Documentation for nuttx-embedded-hal</strong></a></p>
</li>
</ul>
<p>Earlier we called NuttX Embedded HAL to <strong>open the I2C Port</strong>: <a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/rust/src/bme280.rs">bme280.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Open I2C Port
</span><span class="kw">let </span>i2c = nuttx_embedded_hal::I2c::new(
<span class="string">"/dev/i2c0"</span>, <span class="comment">// I2C Port
</span><span class="number">400000</span>, <span class="comment">// I2C Frequency: 400 kHz
</span>).expect(<span class="string">"open failed"</span>);</code></pre></div>
<p>And we passed it to the BME280 Driver…</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Init the BME280 Driver
</span><span class="kw">let </span><span class="kw-2">mut </span>bme280 = bme280::BME280::new(
i2c, <span class="comment">// I2C Port
</span><span class="number">0x77</span>, <span class="comment">// I2C Address of BME280
</span>nuttx_embedded_hal::Delay <span class="comment">// Delay Interface
</span>);</code></pre></div>
<p>(<a href="https://docs.rs/nuttx-embedded-hal/latest/nuttx_embedded_hal/struct.Delay.html"><strong>Delay</strong></a> also comes from NuttX Embedded HAL)</p>
<p><em>But BME280 Driver doesnt know anything about NuttX?</em></p>
<p>Thats OK because the BME280 Driver and NuttX Embedded HAL both talk through the same interface: <a href="https://docs.rs/embedded-hal/latest/embedded_hal/"><strong>Rust Embedded HAL</strong></a>. (Pic above)</p>
<p>Rust Embedded HAL is the standard interface used by <a href="https://github.com/rust-embedded/awesome-embedded-rust#driver-crates"><strong>Rust Embedded Drivers</strong></a> (like the BME280 Driver) to talk to the GPIO / I2C / SPI ports.</p>
<p><a href="https://lupyuen.github.io/articles/rust2#rust-driver-for-lora-sx1262">(Rust Driver for LoRa SX1262 works on NuttX too)</a></p>
<p><em>Thats why Rust Embedded Drivers can run on many platforms?</em></p>
<p>Yep because the Rust Embedded HAL has been implemented on <a href="https://github.com/rust-embedded/awesome-embedded-rust#hal-implementation-crates"><strong>many platforms</strong></a>: Linux, FreeBSD, nRF52, STM32 Blue Pill, …</p>
<p>And now NuttX! (As NuttX Embedded HAL)</p>
<p><img src="https://lupyuen.github.io/images/rusti2c-code6a.png" alt="Call NuttX Embedded HAL to read I2C register" /></p>
<p><a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/rust/src/test.rs#L29-L62">(Source)</a></p>
<h1 id="read-i2c-register"><a class="doc-anchor" href="#read-i2c-register">§</a>6 Read I2C Register</h1>
<p><em>Can we call NuttX Embedded HAL in our own Rust Programs?</em></p>
<p>Yes we can! This is how we call NuttX Embedded HAL to <strong>read an I2C Register</strong> on BME280: <a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/rust/src/test.rs#L29-L62">test.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// Read an I2C Register
</span><span class="kw">pub fn </span>test_hal_read() {
<span class="comment">// Open I2C Port
</span><span class="kw">let </span><span class="kw-2">mut </span>i2c = nuttx_embedded_hal::I2c::new(
<span class="string">"/dev/i2c0"</span>, <span class="comment">// I2C Port
</span><span class="number">400000</span>, <span class="comment">// I2C Frequency: 400 kHz
</span>).expect(<span class="string">"open failed"</span>);</code></pre></div>
<p>This opens the I2C Port “<strong>/dev/i2c0</strong>” at 400 kHz. (Weve seen this earlier)</p>
<p>Next we prepare a one-byte <strong>Receive Buffer</strong> that will receive the Register Value…</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Buffer for received Register Value (1 byte)
</span><span class="kw">let </span><span class="kw-2">mut </span>buf = [<span class="number">0 </span>; <span class="number">1</span>]; <span class="comment">// Init to 0</span></code></pre></div>
<p>Then we call NuttX Embedded HAL to <strong>read Register <code>0xD0</code></strong> from I2C Address <code>0x77</code></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Read I2C Register
</span>i2c.write_read(
<span class="number">0x77</span>, <span class="comment">// I2C Address
</span><span class="kw-2">&amp;</span>[<span class="number">0xD0</span>], <span class="comment">// Register ID
</span><span class="kw-2">&amp;mut </span>buf <span class="comment">// Buffer to be received (Register Value)
</span>).expect(<span class="string">"read register failed"</span>);</code></pre></div>
<p>Our Receive Buffer now contains the <strong>Register Value</strong> <code>0x60</code></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Register Value must be BME280 Device ID (0x60)
</span><span class="macro">assert_eq!</span>(buf[<span class="number">0</span>], <span class="number">0x60</span>);
}</code></pre></div>
<p>Thats how we call NuttX Embedded HAL to read an I2C Register!</p>
<p><img src="https://lupyuen.github.io/images/rusti2c-arch.jpg" alt="NuttX Embedded HAL" /></p>
<p><em>How did we implement I2C in the NuttX Embedded HAL?</em></p>
<p>NuttX Embedded HAL accesses the I2C Port by calling the <strong>NuttX I2C Interface</strong>: open(), ioctl() and close(). (Pic above)</p>
<p>Check out the details in the Appendix…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/rusti2c#appendix-read-i2c-register-in-embedded-hal"><strong>“Read I2C Register in Embedded HAL”</strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/rusti2c-code7a.png" alt="Write I2C Register" /></p>
<p><a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/rust/src/test.rs#L64-L116">(Source)</a></p>
<h1 id="write-i2c-register"><a class="doc-anchor" href="#write-i2c-register">§</a>7 Write I2C Register</h1>
<p><em>What about writing to I2C Registers?</em></p>
<p>This code calls NuttX Embedded HAL to <strong>write an I2C Register</strong>: <a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/rust/src/test.rs#L64-L116">test.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// Write an I2C Register
</span><span class="kw">pub fn </span>test_hal_write() {
<span class="comment">// Open I2C Port
</span><span class="kw">let </span><span class="kw-2">mut </span>i2c = nuttx_embedded_hal::I2c::new(
<span class="string">"/dev/i2c0"</span>, <span class="comment">// I2C Port
</span><span class="number">400000</span>, <span class="comment">// I2C Frequency: 400 kHz
</span>).expect(<span class="string">"open failed"</span>);
<span class="comment">// Write 0xA0 to Register 0xF5
</span>i2c.write(
<span class="number">0x77</span>, <span class="comment">// I2C Address
</span><span class="kw-2">&amp;</span>[<span class="number">0xF5</span>, <span class="number">0xA0</span>] <span class="comment">// Register ID and value
</span>).expect(<span class="string">"write register failed"</span>);</code></pre></div>
<p>The implementation of <strong>i2c.write</strong> in NuttX Embedded HAL is explained here…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/rusti2c#appendix-write-i2c-register-in-embedded-hal"><strong>“Write I2C Register in Embedded HAL”</strong></a></li>
</ul>
<p>When we connect a <a href="https://lupyuen.github.io/images/bme280-logic2.jpg"><strong>Logic Analyser</strong></a>, well see something unusual…</p>
<div class="example-wrap"><pre class="language-text"><code>Setup Write to [0xEE] + ACK
0xF5 + ACK
0xA0 + ACK
Setup Read to [0xEF] + ACK
0xA0 + NAK</code></pre></div>
<p><img src="https://lupyuen.github.io/images/rusti2c-logic3a.png" alt="Write 0xA0 to Register 0xF4" /></p>
<p>Theres an <strong>extra I2C Read</strong> at the end, right after writing the Register ID <code>0xF5</code> and Register Value <code>0xA0</code>.</p>
<p>But its harmless. NuttX Embedded HAL does this to work around the I2C quirks on BL602…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/bme280#appendix-quirks-in-bl602-nuttx-i2c-driver"><strong>“Quirks in BL602 NuttX I2C Driver”</strong></a></li>
</ul>
<p><em>What about GPIO and SPI on NuttX Embedded HAL?</em></p>
<p>Yep they have been implemented in NuttX Embedded HAL…</p>
<ul>
<li>
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal#gpio-output"><strong>NuttX GPIO</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal#spi"><strong>NuttX SPI</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal#delay"><strong>NuttX Delay</strong></a></p>
</li>
</ul>
<blockquote>
<p><img src="https://lupyuen.github.io/images/rusti2c-run1.png" alt="Rust I2C on NuttX" /></p>
</blockquote>
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>8 Whats Next</h1>
<p>I had lots of fun running Rust on NuttX, I hope youll enjoy it too!</p>
<p>If youre keen to make <strong>Rust on NuttX</strong> better, or if theres something I should port to Rust on NuttX, please lemme know! 🙏</p>
<p>Many Thanks to my <a href="https://lupyuen.github.io/articles/sponsor"><strong>GitHub Sponsors</strong></a> for supporting my work! This article wouldnt have been possible without your support.</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/sponsor">Sponsor me a coffee</a></p>
</li>
<li>
<p><a href="https://www.reddit.com/r/rust/comments/tj9a2s/rust_talks_i2c_on_apache_nuttx_rtos/">Discuss this article on Reddit</a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/book">Read “The RISC-V BL602 / BL604 Book”</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>
<p><em>Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…</em></p>
<p><a href="https://github.com/lupyuen/lupyuen.github.io/blob/master/src/rusti2c.md"><code>lupyuen.github.io/src/rusti2c.md</code></a></p>
<h1 id="notes"><a class="doc-anchor" href="#notes">§</a>9 Notes</h1>
<ol>
<li>This article is the expanded version of <a href="https://twitter.com/MisterTechBlog/status/1502823263121989634">this Twitter Thread</a></li>
</ol>
<p><img src="https://lupyuen.github.io/images/rusti2c-arch.jpg" alt="NuttX Embedded HAL" /></p>
<h1 id="appendix-read-i2c-register-in-embedded-hal"><a class="doc-anchor" href="#appendix-read-i2c-register-in-embedded-hal">§</a>10 Appendix: Read I2C Register in Embedded HAL</h1>
<p><em>How was NuttX Embedded HAL implemented in Rust?</em></p>
<p>NuttX Embedded HAL accesses the I2C Port by calling the <strong>NuttX I2C Interface</strong>: open(), ioctl() and close(). (Pic above)</p>
<p>To understand why, lets look at a NuttX Rust Program that <strong>reads an I2C Register</strong> on BME280: <a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/rust/src/test.rs#L117-L193">rust/src/test.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// Read an I2C Register
</span><span class="kw">pub fn </span>test_i2c() {
<span class="comment">// Open I2C Port
</span><span class="kw">let </span>i2c = <span class="kw">unsafe </span>{
open(<span class="string">b"/dev/i2c0\0"</span>.as_ptr(), O_RDWR)
};
<span class="macro">assert!</span>(i2c &gt; <span class="number">0</span>);</code></pre></div>
<p>We begin by calling <strong>open()</strong> to open the I2C Port.</p>
<p>This is flagged as “<code>unsafe</code>” because were calling a C Function. <a href="https://lupyuen.github.io/articles/rust2#rust-meets-nuttx">(See this)</a></p>
<p>Next we prepare the buffers that will be <strong>sent and received</strong> over I2C…</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Send the Register ID 0xD0 (1 byte)
</span><span class="kw">let </span><span class="kw-2">mut </span>start = [<span class="number">0xD0 </span>; <span class="number">1</span>];
<span class="comment">// Receive the Register Value (1 byte)
</span><span class="kw">let </span><span class="kw-2">mut </span>buf = [<span class="number">0 </span>; <span class="number">1</span>];</code></pre></div>
<p>Well read the I2C Register in 2 steps…</p>
<ol>
<li>
<p>Send the <strong>Register ID</strong> <code>0xD0</code></p>
</li>
<li>
<p>Receive the <strong>Register Value</strong></p>
</li>
</ol>
<p>We define the First Step: Send the <strong>Register ID</strong></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Compose I2C Transfers
</span><span class="kw">let </span>msg = [
<span class="comment">// First I2C Message: Send Register ID
</span>i2c_msg_s {
frequency: <span class="number">400000</span>, <span class="comment">// I2C Frequency: 400 kHz
</span>addr: <span class="number">0x77</span>, <span class="comment">// I2C Address
</span>buffer: start.as_mut_ptr(), <span class="comment">// Buffer to be sent (Register ID)
</span>length: start.len() <span class="kw">as </span>ssize_t, <span class="comment">// Length of the buffer in bytes
// For BL602: Register ID must be passed as I2C Sub Address
</span><span class="attr">#[cfg(target_arch = <span class="string">"riscv32"</span>)] </span><span class="comment">// If architecture is RISC-V 32-bit...
</span>flags: I2C_M_NOSTOP, <span class="comment">// I2C Flags: Send I2C Sub Address
// Otherwise pass Register ID as I2C Data
</span><span class="attr">#[cfg(not(target_arch = <span class="string">"riscv32"</span>))] </span><span class="comment">// If architecture is not RISC-V 32-bit...
</span>flags: <span class="number">0</span>, <span class="comment">// I2C Flags: None
// TODO: Check for BL602 specifically, not just RISC-V 32-bit
</span>},</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/bme280#appendix-quirks-in-bl602-nuttx-i2c-driver">(<strong>I2C_M_NOSTOP</strong> is needed because of a BL602 quirk)</a></p>
<p>And heres the Second Step: Receive the <strong>Register Value</strong></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Second I2C Message: Receive Register Value
</span>i2c_msg_s {
frequency: <span class="number">400000</span>, <span class="comment">// I2C Frequency: 400 kHz
</span>addr: <span class="number">0x77</span>, <span class="comment">// I2C Address
</span>buffer: buf.as_mut_ptr(), <span class="comment">// Buffer to be received
</span>length: buf.len() <span class="kw">as </span>ssize_t, <span class="comment">// Length of the buffer in bytes
</span>flags: I2C_M_READ, <span class="comment">// I2C Flags: Read from I2C Device
</span>},
];</code></pre></div>
<p>Finally we execute the two steps by calling <strong>ioctl()</strong></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Compose ioctl Argument
</span><span class="kw">let </span>xfer = i2c_transfer_s {
msgv: msg.as_ptr(), <span class="comment">// Array of I2C messages for the transfer
</span>msgc: msg.len() <span class="kw">as </span>size_t, <span class="comment">// Number of messages in the array
</span>};
<span class="comment">// Execute I2C Transfers
</span><span class="kw">let </span>ret = <span class="kw">unsafe </span>{
ioctl(
i2c, <span class="comment">// I2C Port
</span>I2CIOC_TRANSFER, <span class="comment">// I2C Transfer
</span><span class="kw-2">&amp;</span>xfer <span class="comment">// I2C Messages for the transfer
</span>)
};
<span class="macro">assert!</span>(ret &gt;= <span class="number">0</span>);</code></pre></div>
<p>The <strong>Register Value</strong> appears in our Receive Buffer…</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code> <span class="comment">// Register Value must be BME280 Device ID (0x60)
</span><span class="macro">assert!</span>(buf[<span class="number">0</span>] == <span class="number">0x60</span>);
<span class="comment">// Close the I2C Port
</span><span class="kw">unsafe </span>{ close(i2c); }
}</code></pre></div>
<p><a href="https://github.com/lupyuen/rust-i2c-nuttx#test-i2c-port">(See the Output Log)</a></p>
<p>The above Rust code looks highly similar to the C version…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/rusti2c#appendix-read-i2c-register-in-c"><strong>“Read I2C Register in C”</strong></a></li>
</ul>
<p>Lets look at the NuttX Types and Constants that we have ported from C to Rust.</p>
<p><img src="https://lupyuen.github.io/images/rusti2c-code2a.png" alt="Read I2C Register" /></p>
<p><a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/rust/src/test.rs#L117-L193">(Source)</a></p>
<h2 id="nuttx-types-and-constants"><a class="doc-anchor" href="#nuttx-types-and-constants">§</a>10.1 NuttX Types and Constants</h2>
<p><em>What are i2c_msg_s and i2c_transfer_s in the code above?</em></p>
<p>They are <strong>NuttX I2C Types</strong> that we have ported from C to Rust.</p>
<p><strong>i2c_msg_s</strong> is the <strong>I2C Message Struct</strong> that defines each message that will be sent or received over I2C: <a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/lib.rs#L125-L153">nuttx-embedded-hal/src/lib.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// I2C Message Struct: I2C transaction segment beginning with a START. A number of these can
/// be transferred together to form an arbitrary sequence of write/read
/// transfer to an I2C device.
/// Ported from C: https://github.com/lupyuen/nuttx/blob/rusti2c/include/nuttx/i2c/i2c_master.h#L208-L215
</span><span class="attr">#[repr(C)]
</span><span class="kw">pub struct </span>i2c_msg_s {
<span class="doccomment">/// I2C Frequency
</span><span class="kw">pub </span>frequency: u32,
<span class="doccomment">/// I2C Address
</span><span class="kw">pub </span>addr: u16,
<span class="doccomment">/// I2C Flags (I2C_M_*)
</span><span class="kw">pub </span>flags: u16,
<span class="doccomment">/// Buffer to be transferred
</span><span class="kw">pub </span>buffer: <span class="kw-2">*mut </span>u8,
<span class="doccomment">/// Length of the buffer in bytes
</span><span class="kw">pub </span>length: ssize_t,
}</code></pre></div>
<p><strong>i2c_transfer_s</strong> contains an <strong>array of I2C Message Structs</strong> that will be sent / received when we call ioctl() to execute the I2C Transfer…</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// I2C Transfer Struct: This structure is used to communicate with the I2C character driver in
/// order to perform IOCTL transfers.
/// Ported from C: https://github.com/lupyuen/nuttx/blob/rusti2c/include/nuttx/i2c/i2c_master.h#L231-L235
</span><span class="attr">#[repr(C)]
</span><span class="kw">pub struct </span>i2c_transfer_s {
<span class="doccomment">/// Array of I2C messages for the transfer
</span><span class="kw">pub </span>msgv: <span class="kw-2">*const </span>i2c_msg_s,
<span class="doccomment">/// Number of messages in the array
</span><span class="kw">pub </span>msgc: size_t,
}</code></pre></div>
<p><em>What about I2C_M_NOSTOP, I2C_M_READ and I2CIOC_TRANSFER?</em></p>
<p><strong>I2C_M_NOSTOP</strong>, <strong>I2C_M_READ</strong> and <strong>I2CIOC_TRANSFER</strong> are <strong>NuttX I2C Constants</strong> that we have ported from C to Rust.</p>
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/lib.rs#L105-L124">(See this)</a></p>
<p><img src="https://lupyuen.github.io/images/rusti2c-code3a.png" alt="NuttX I2C Types" /></p>
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/lib.rs#L125-L153">(Source)</a></p>
<h2 id="into-embedded-hal"><a class="doc-anchor" href="#into-embedded-hal">§</a>10.2 Into Embedded HAL</h2>
<p>The code above goes into <strong>NuttX Embedded HAL</strong> like so…</p>
<p><img src="https://lupyuen.github.io/images/rusti2c-code5a.png" alt="Into Embedded HAL" /></p>
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L97-L160">(Source)</a></p>
<p>This conforms to the I2C Interface thats expected by <strong>Rust Embedded HAL</strong></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// NuttX Implementation of I2C WriteRead
</span><span class="kw">impl </span>i2c::WriteRead <span class="kw">for </span>I2c {
...
<span class="doccomment">/// Write `wbuf` to I2C Port and read `rbuf` from I2C Port.
/// We assume this is a Read I2C Register operation, with Register ID at `wbuf[0]`.
/// TODO: Handle other kinds of I2C operations
</span><span class="kw">fn </span>write_read(
<span class="kw-2">&amp;mut </span><span class="self">self</span>, <span class="comment">// I2C Bus
</span>addr: u8, <span class="comment">// I2C Address
</span>wbuf: <span class="kw-2">&amp;</span>[u8], <span class="comment">// Buffer to be sent (Register ID)
</span>rbuf: <span class="kw-2">&amp;mut </span>[u8] <span class="comment">// Buffer to be received
</span>) -&gt; <span class="prelude-ty">Result</span>&lt;(), <span class="self">Self</span>::Error&gt; <span class="comment">// In case of error, return an error code
</span>{ ... }</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L97-L160">(Source)</a></p>
<p><em>What about the calls to open() and close()?</em></p>
<p>We moved open() into the <strong>new()</strong> constructor: <a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L340-L351">hal.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// NuttX Implementation of I2C Bus
</span><span class="kw">impl </span>I2c {
<span class="doccomment">/// Create an I2C Bus from a Device Path (e.g. "/dev/i2c0")
</span><span class="kw">pub fn </span>new(path: <span class="kw-2">&amp;</span>str, frequency: u32) -&gt; <span class="prelude-ty">Result</span>&lt;<span class="self">Self</span>, i32&gt; {
<span class="comment">// Open the NuttX Device Path (e.g. "/dev/i2c0") for read-write
</span><span class="kw">let </span>fd = open(path, O_RDWR);
<span class="kw">if </span>fd &lt; <span class="number">0 </span>{ <span class="kw">return </span><span class="prelude-val">Err</span>(fd) }
<span class="comment">// Return the I2C Bus
</span><span class="prelude-val">Ok</span>(<span class="self">Self </span>{ fd, frequency })
}
}</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L492-L516">(<strong>open</strong> is defined here)</a></p>
<p>And we moved close() into the <strong>drop()</strong> destructor: <a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L414-L420">hal.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// NuttX Implementation of I2C Bus
</span><span class="kw">impl </span>Drop <span class="kw">for </span>I2c {
<span class="doccomment">/// Close the I2C Bus
</span><span class="kw">fn </span>drop(<span class="kw-2">&amp;mut </span><span class="self">self</span>) {
<span class="kw">unsafe </span>{ close(<span class="self">self</span>.fd) };
}
}</code></pre></div>
<p><strong>I2c</strong> Struct contains a NuttX File Descriptor and the I2C Frequency: <a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L454-L461">hal.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// NuttX I2C Bus
</span><span class="kw">pub struct </span>I2c {
<span class="doccomment">/// NuttX File Descriptor
</span>fd: i32,
<span class="doccomment">/// I2C Frequency in Hz
</span>frequency: u32,
}</code></pre></div>
<p><a href="https://github.com/lupyuen/rust-i2c-nuttx#test-i2c-hal">(See the Output Log)</a></p>
<p><img src="https://lupyuen.github.io/images/rusti2c-code8a.png" alt="Write I2C Register in Embedded HAL" /></p>
<p><a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L33-L96">(Source)</a></p>
<h1 id="appendix-write-i2c-register-in-embedded-hal"><a class="doc-anchor" href="#appendix-write-i2c-register-in-embedded-hal">§</a>11 Appendix: Write I2C Register in Embedded HAL</h1>
<p>BL602 has a peculiar I2C Port that uses <strong>I2C Sub Addresses</strong></p>
<ul>
<li><a href="https://lupyuen.github.io/articles/bme280#appendix-quirks-in-bl602-nuttx-i2c-driver"><strong>“Quirks in BL602 I2C Driver”</strong></a></li>
</ul>
<p>We tried <a href="https://lupyuen.github.io/images/rusti2c-lottery1.png"><strong>all sequences</strong></a> of I2C Read / Write / Sub Address. Only this strange sequence works for writing to I2C Registers…</p>
<ol>
<li>
<p>Write I2C <strong>Register ID and Register Value</strong> together as I2C Sub Address</p>
</li>
<li>
<p>Followed by <strong>Read I2C Data</strong></p>
</li>
</ol>
<p>Heres the implementation in NuttX Embedded HAL: <a href="https://github.com/lupyuen/nuttx-embedded-hal/blob/main/src/hal.rs#L33-L96">hal.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="doccomment">/// NuttX Implementation of I2C Write
</span><span class="kw">impl </span>i2c::Write <span class="kw">for </span>I2c {
<span class="doccomment">/// Error Type
</span><span class="kw">type </span>Error = i32;
<span class="doccomment">/// Write `buf` to I2C Port.
/// We assume this is a Write I2C Register operation, with Register ID at `buf[0]`.
/// TODO: Handle other kinds of I2C operations
</span><span class="kw">fn </span>write(<span class="kw-2">&amp;mut </span><span class="self">self</span>, addr: u8, buf: <span class="kw-2">&amp;</span>[u8]) -&gt; <span class="prelude-ty">Result</span>&lt;(), <span class="self">Self</span>::Error&gt; {
<span class="comment">// Copy to local buffer because we need a mutable reference
</span><span class="kw">let </span><span class="kw-2">mut </span>buf2 = [<span class="number">0 </span>; <span class="number">64</span>];
<span class="macro">assert!</span>(buf.len() &lt;= buf2.len());
buf2[..buf.len()].copy_from_slice(buf);
<span class="comment">// Buffer for received I2C data
</span><span class="kw">let </span><span class="kw-2">mut </span>rbuf = [<span class="number">0 </span>; <span class="number">1</span>];
<span class="comment">// Compose I2C Transfer
</span><span class="kw">let </span>msg = [
<span class="comment">// First I2C Message: Send Register ID and I2C Data as I2C Sub Address
</span>i2c_msg_s {
frequency: <span class="self">self</span>.frequency, <span class="comment">// I2C Frequency
</span>addr: addr <span class="kw">as </span>u16, <span class="comment">// I2C Address
</span>buffer: buf2.as_mut_ptr(), <span class="comment">// Buffer to be sent
</span>length: buf.len() <span class="kw">as </span>ssize_t, <span class="comment">// Number of bytes to send
// For BL602: Register ID must be passed as I2C Sub Address
</span><span class="attr">#[cfg(target_arch = <span class="string">"riscv32"</span>)] </span><span class="comment">// If architecture is RISC-V 32-bit...
</span>flags: <span class="kw">crate</span>::I2C_M_NOSTOP, <span class="comment">// I2C Flags: Send I2C Sub Address
// Otherwise pass Register ID as I2C Data
</span><span class="attr">#[cfg(not(target_arch = <span class="string">"riscv32"</span>))] </span><span class="comment">// If architecture is not RISC-V 32-bit...
</span>flags: <span class="number">0</span>, <span class="comment">// I2C Flags: None
// TODO: Check for BL602 specifically (by target_abi?), not just RISC-V 32-bit
</span>},
<span class="comment">// Second I2C Message: Read I2C Data, because this forces BL602 to send the first message correctly
</span>i2c_msg_s {
frequency: <span class="self">self</span>.frequency, <span class="comment">// I2C Frequency
</span>addr: addr <span class="kw">as </span>u16, <span class="comment">// I2C Address
</span>buffer: rbuf.as_mut_ptr(), <span class="comment">// Buffer to be received
</span>length: rbuf.len() <span class="kw">as </span>ssize_t, <span class="comment">// Number of bytes to receive
</span>flags: I2C_M_READ, <span class="comment">// I2C Flags: Read I2C Data
</span>},
];
<span class="comment">// Compose ioctl Argument to write I2C Registers
</span><span class="kw">let </span>xfer = i2c_transfer_s {
msgv: msg.as_ptr(), <span class="comment">// Array of I2C messages for the transfer
</span>msgc: msg.len() <span class="kw">as </span>size_t, <span class="comment">// Number of messages in the array
</span>};
<span class="comment">// Execute I2C Transfer to write I2C Registers
</span><span class="kw">let </span>ret = <span class="kw">unsafe </span>{
ioctl(
<span class="self">self</span>.fd, <span class="comment">// I2C Port
</span>I2CIOC_TRANSFER, <span class="comment">// I2C Transfer
</span><span class="kw-2">&amp;</span>xfer <span class="comment">// I2C Messages for the transfer
</span>)
};
<span class="macro">assert!</span>(ret &gt;= <span class="number">0</span>);
<span class="prelude-val">Ok</span>(())
}
}</code></pre></div>
<p>Our Logic Analyser shows that BL602 writes correctly to the I2C Register (with a harmless I2C Read at the end)…</p>
<div class="example-wrap"><pre class="language-text"><code>Setup Write to [0xEE] + ACK
0xF5 + ACK
0xA0 + ACK
Setup Read to [0xEF] + ACK
0xA0 + NAK</code></pre></div>
<p><img src="https://lupyuen.github.io/images/rusti2c-logic3a.png" alt="BL602 writes correctly to the I2C Register! With a harmless I2C Read at the end" /></p>
<p><a href="https://github.com/lupyuen/rust-i2c-nuttx#fix-i2c-write">(See the Output Log)</a></p>
<p><img src="https://lupyuen.github.io/images/rusti2c-code1.png" alt="Read I2C Register in C" /></p>
<p><a href="https://github.com/lupyuen/bme280-nuttx/blob/main/driver.c#L155-L183">(Source)</a></p>
<h1 id="appendix-read-i2c-register-in-c"><a class="doc-anchor" href="#appendix-read-i2c-register-in-c">§</a>12 Appendix: Read I2C Register in C</h1>
<p>This is how we read an I2C Register in C from a NuttX App…</p>
<div class="example-wrap"><pre class="language-c"><code>static int bme280_reg_read(const struct device *priv,
uint8_t start, uint8_t *buf, int size)
{
DEBUGASSERT(priv != NULL);
DEBUGASSERT(buf != NULL);
struct i2c_msg_s msg[2];
int ret;
msg[0].frequency = priv-&gt;freq;
msg[0].addr = priv-&gt;addr;
#ifdef CONFIG_BL602_I2C0
// For BL602: Register ID must be passed as I2C Sub Address
msg[0].flags = I2C_M_NOSTOP;
#else
// Otherwise pass Register ID as I2C Data
msg[0].flags = 0;
#endif // CONFIG_BL602_I2C0
msg[0].buffer = &amp;start;
msg[0].length = 1;
msg[1].frequency = priv-&gt;freq;
msg[1].addr = priv-&gt;addr;
msg[1].flags = I2C_M_READ;
msg[1].buffer = buf;
msg[1].length = size;
ret = I2C_TRANSFER(priv-&gt;i2c, msg, 2);</code></pre></div>
<p><a href="https://github.com/lupyuen/bme280-nuttx/blob/main/driver.c#L155-L183">(Source)</a></p>
<p>How do we call <strong>I2C_TRANSFER</strong> from a NuttX App? Thanks to the I2C Demo App we have the answer…</p>
<div class="example-wrap"><pre class="language-c"><code>int i2ctool_get(FAR struct i2ctool_s *i2ctool, int fd, uint8_t regaddr,
FAR uint16_t *result)
{
struct i2c_msg_s msg[2];
...
int ret = i2cdev_transfer(fd, msg, 2);</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx-apps/blob/rusti2c/system/i2c/i2c_get.c#L158-L206">(Source)</a></p>
<p><strong>i2cdev_transfer</strong> is defined as…</p>
<div class="example-wrap"><pre class="language-c"><code>int i2cdev_transfer(int fd, FAR struct i2c_msg_s *msgv, int msgc)
{
struct i2c_transfer_s xfer;
/* Set up the IOCTL argument */
xfer.msgv = msgv;
xfer.msgc = msgc;
/* Perform the IOCTL */
return ioctl(fd, I2CIOC_TRANSFER, (unsigned long)((uintptr_t)&amp;xfer));
}</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx-apps/blob/rusti2c/system/i2c/i2c_devif.c#L117-L129">(Source)</a></p>
<p>We ported the code above to NuttX Embedded HAL. <a href="https://lupyuen.github.io/articles/rusti2c#into-embedded-hal">(See this)</a></p>
<h2 id="c-types-and-constants"><a class="doc-anchor" href="#c-types-and-constants">§</a>12.1 C Types and Constants</h2>
<p>Earlier weve seen <strong>i2c_msg_s</strong> and <strong>i2c_transfer_s</strong>. They are defined as…</p>
<div class="example-wrap"><pre class="language-c"><code>struct i2c_msg_s
{
uint32_t frequency; /* I2C frequency */
uint16_t addr; /* Slave address (7- or 10-bit) */
uint16_t flags; /* See I2C_M_* definitions */
FAR uint8_t *buffer; /* Buffer to be transferred */
ssize_t length; /* Length of the buffer in bytes */
};</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx/blob/rusti2c/include/nuttx/i2c/i2c_master.h#L208-L215">(Source)</a></p>
<div class="example-wrap"><pre class="language-c"><code>struct i2c_transfer_s
{
FAR struct i2c_msg_s *msgv; /* Array of I2C messages for the transfer */
size_t msgc; /* Number of messages in the array. */
};</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx/blob/rusti2c/include/nuttx/i2c/i2c_master.h#L231-L235">(Source)</a></p>
<p><strong>I2CIOC_TRANSFER</strong> is defined as…</p>
<div class="example-wrap"><pre class="language-c"><code>#define I2CIOC_TRANSFER _I2CIOC(0x0001)</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx/blob/rusti2c/include/nuttx/i2c/i2c_master.h#L105-L129">(Source)</a></p>
<p>_<strong>I2CIOC</strong> is defined as…</p>
<div class="example-wrap"><pre class="language-c"><code>#define _I2CIOC(nr) _IOC(_I2CBASE,nr)</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx/blob/rusti2c/include/nuttx/fs/ioctl.h#L467-L468">(Source)</a></p>
<p>_<strong>IOC</strong> and _<strong>I2CBASE</strong> are defined as…</p>
<div class="example-wrap"><pre class="language-c"><code>#define _IOC(type,nr) ((type)|(nr))</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx/blob/rusti2c/include/nuttx/fs/ioctl.h#L107">(Source)</a></p>
<div class="example-wrap"><pre class="language-c"><code>#define _I2CBASE (0x2100) /* I2C driver commands */</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx/blob/rusti2c/include/nuttx/fs/ioctl.h#L73">(Source)</a></p>
<p>We ported these C Types and Constants to NuttX Embedded HAL. <a href="https://lupyuen.github.io/articles/rusti2c#nuttx-types-and-constants">(See this)</a></p>
<h1 id="appendix-build-flash-and-run-nuttx"><a class="doc-anchor" href="#appendix-build-flash-and-run-nuttx">§</a>13 Appendix: Build, Flash and Run NuttX</h1>
<p><em>(For BL602, BL604 and ESP32)</em></p>
<p>Below are the steps to build, flash and run NuttX on BL602, BL604 and ESP32.</p>
<p>The instructions below will work on <strong>Linux (Ubuntu)</strong>, <strong>WSL (Ubuntu)</strong> and <strong>macOS</strong>.</p>
<p><a href="https://nuttx.apache.org/docs/latest/quickstart/install.html">(Instructions for other platforms)</a></p>
<p><a href="https://popolon.org/gblog3/?p=1977&amp;lang=en">(See this for Arch Linux)</a></p>
<h2 id="download-nuttx"><a class="doc-anchor" href="#download-nuttx">§</a>13.1 Download NuttX</h2>
<p>Download the modified source code for <strong>NuttX OS and NuttX Apps</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>mkdir nuttx
cd nuttx
git clone --recursive --branch rusti2c https://github.com/lupyuen/nuttx nuttx
git clone --recursive --branch rusti2c https://github.com/lupyuen/nuttx-apps apps</code></pre></div>
<p>Or if we prefer to <strong>add the Rust Library and App</strong> to our NuttX Project, follow these instructions…</p>
<ol>
<li>
<p><a href="https://github.com/lupyuen/rust-nuttx"><strong>“Install Rust Library”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/rust-i2c-nuttx"><strong>“Install Rust I2C App”</strong></a></p>
</li>
</ol>
<p><a href="https://lupyuen.github.io/articles/pinedio2#appendix-bundled-features">(<strong>For PineDio Stack BL604:</strong> The Rust Library and App are already preinstalled)</a></p>
<h2 id="configure-nuttx"><a class="doc-anchor" href="#configure-nuttx">§</a>13.2 Configure NuttX</h2>
<p>Now we configure our NuttX project…</p>
<ol>
<li>
<p>Install the build prerequisites…</p>
<p><a href="https://lupyuen.github.io/articles/nuttx#install-prerequisites"><strong>“Install Prerequisites”</strong></a></p>
</li>
<li>
<p>Install Rust from <a href="https://rustup.rs"><strong>rustup.rs</strong></a></p>
</li>
<li>
<p>Configure the build…</p>
<div class="example-wrap"><pre class="language-bash"><code>cd nuttx
## For BL602: Configure the build for BL602
./tools/configure.sh bl602evb:nsh
## For PineDio Stack BL604: Configure the build for BL604
./tools/configure.sh bl602evb:pinedio
## For ESP32: Configure the build for ESP32.
## TODO: Change &quot;esp32-devkitc&quot; to our ESP32 board.
./tools/configure.sh esp32-devkitc:nsh
## Edit the Build Config
make menuconfig </code></pre></div></li>
<li>
<p>Enable our <strong>Rust Library</strong></p>
<p>Check the box for <strong>“Library Routines”</strong><strong>“Rust Library”</strong></p>
<p>Hit <strong>“Exit”</strong> until the Top Menu appears. (“NuttX/x64_64 Configuration”)</p>
</li>
<li>
<p>Enable our <strong>Rust I2C App</strong></p>
<p>Check the box for <strong>“Application Configuration”</strong><strong>“Examples”</strong><strong>“Rust I2C App”</strong></p>
<p>Hit <strong>“Exit”</strong> until the Top Menu appears. (“NuttX/x64_64 Configuration”)</p>
</li>
<li>
<p>Enable <strong>I2C0 Port</strong></p>
<p><strong>For BL602 / BL604:</strong> Check the box for <strong>“System Type”</strong><strong>“BL602 Peripheral Support”</strong><strong>“I2C0”</strong></p>
<p><strong>For ESP32:</strong> Check the box for <strong>“System Type”</strong><strong>“ESP32 Peripheral Select”</strong><strong>“I2C 0”</strong></p>
<p>Hit <strong>“Exit”</strong> until the Top Menu appears. (“NuttX/x64_64 Configuration”)</p>
<p><img src="https://lupyuen.github.io/images/bme280-config1.jpg" alt="Enable the I2C Port and I2C Character Driver" /></p>
</li>
<li>
<p>Enable <strong>I2C Character Driver</strong></p>
<p>Check the box for <strong>“Device Drivers”</strong><strong>“I2C Driver Support”</strong><strong>“I2C Character Driver”</strong></p>
<p>Hit <strong>“Exit”</strong> until the Top Menu appears. (“NuttX/x64_64 Configuration”)</p>
</li>
<li>
<p>Enable <strong>ls</strong> command…</p>
<p>Select <strong>“Application Configuration”</strong><strong>“NSH Library”</strong><strong>“Disable Individual commands”</strong></p>
<p>Uncheck <strong>“Disable ls”</strong></p>
<p>Hit <strong>“Exit”</strong> until the Top Menu appears. (“NuttX/x64_64 Configuration”)</p>
</li>
<li>
<p>Enable <strong>Logging and Assertion Checks</strong></p>
<p>Select <strong>“Build Setup”</strong><strong>“Debug Options”</strong></p>
<p>Check the boxes for the following…</p>
<div class="example-wrap"><pre class="language-text"><code>Enable Debug Features
Enable Error Output
Enable Warnings Output
Enable Informational Debug Output
Enable Debug Assertions
I2C Debug Features
I2C Error Output
I2C Warnings Output
I2C Informational Output </code></pre></div>
<p>Hit <strong>“Exit”</strong> until the Top Menu appears. (“NuttX/x64_64 Configuration”)</p>
</li>
<li>
<p>Save the configuration and exit menuconfig</p>
<p><a href="https://gist.github.com/lupyuen/85550f16517202b7978e592da976c4e7">(See the .config for BL602)</a></p>
</li>
</ol>
<h2 id="configure-rust-target"><a class="doc-anchor" href="#configure-rust-target">§</a>13.3 Configure Rust Target</h2>
<p><strong>For BL602 / BL604</strong>: Skip to the next section</p>
<p><strong>For ESP32-C3 (RISC-V)</strong>:</p>
<ol>
<li>
<p>Run this command to install the Rust Target…</p>
<div class="example-wrap"><pre class="language-bash"><code>rustup target add riscv32imc-unknown-none-elf</code></pre></div></li>
<li>
<p>Edit <a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/run.sh"><strong>apps/examples/rust_i2c/run.sh</strong></a></p>
</li>
<li>
<p>Set “rust_build_target” and “rust_build_target_folder” to…</p>
<p><strong>riscv32imc-unknown-none-elf</strong></p>
</li>
<li>
<p>Remove “-Z build-std=core” from “rust_build_options”</p>
</li>
</ol>
<p><strong>For ESP32 (Xtensa)</strong>:</p>
<ol>
<li>
<p>Install the Rust compiler fork with Xtensa support. <a href="https://github.com/jessebraham/esp-hal/tree/main/esp32-hal">(See this)</a></p>
</li>
<li>
<p>Edit <a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/run.sh"><strong>apps/examples/rust_i2c/run.sh</strong></a></p>
</li>
<li>
<p>Set “rust_build_target” and “rust_build_target_folder” to…</p>
<p><strong>xtensa-esp32-none-elf</strong></p>
</li>
<li>
<p>Remove “-Z build-std=core” from “rust_build_options”</p>
</li>
</ol>
<p><a href="https://lupyuen.github.io/articles/rust2#appendix-rust-build-script-for-nuttx">(<strong>run.sh</strong> is explained here)</a></p>
<p><a href="https://lupyuen.github.io/articles/rust2#rust-target">(More about Rust Targets)</a></p>
<h2 id="build-nuttx"><a class="doc-anchor" href="#build-nuttx">§</a>13.4 Build NuttX</h2>
<p>Follow these steps to build NuttX for BL602, BL604 or ESP32…</p>
<ol>
<li>
<p>To build NuttX with Rust, run the Rust Build Script <a href="https://github.com/lupyuen/rust-i2c-nuttx/blob/main/run.sh"><strong>run.sh</strong></a></p>
<div class="example-wrap"><pre class="language-bash"><code>pushd apps/examples/rust_i2c
./run.sh
popd</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/rust2#appendix-rust-build-script-for-nuttx">(<strong>run.sh</strong> is explained here)</a></p>
</li>
<li>
<p>We should see…</p>
<div class="example-wrap"><pre class="language-text"><code>LD: nuttx
CP: nuttx.hex
CP: nuttx.bin</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/9bfd71f7029bb66e327f89c8a58f450d">(See the complete log for BL602 / BL604)</a></p>
</li>
<li>
<p>Ignore the errors at the <strong>“Flash NuttX”</strong> and <strong>“Run NuttX”</strong> steps</p>
</li>
<li>
<p><strong>For WSL:</strong> Copy the <strong>NuttX Firmware</strong> to the <strong>c:\blflash</strong> directory in the Windows File System…</p>
<div class="example-wrap"><pre class="language-bash"><code>## /mnt/c/blflash refers to c:\blflash in Windows
mkdir /mnt/c/blflash
cp nuttx.bin /mnt/c/blflash</code></pre></div>
<p>For WSL we need to run <strong>blflash</strong> under plain old Windows CMD (not WSL) because it needs to access the COM port.</p>
</li>
<li>
<p>In case of problems, refer to the <strong>NuttX Docs</strong></p>
<p><a href="https://nuttx.apache.org/docs/latest/platforms/risc-v/bl602/index.html"><strong>“BL602 NuttX”</strong></a></p>
<p><a href="https://nuttx.apache.org/docs/latest/platforms/xtensa/esp32/index.html"><strong>“ESP32 NuttX”</strong></a></p>
<p><a href="https://nuttx.apache.org/docs/latest/quickstart/install.html"><strong>“Installing NuttX”</strong></a></p>
</li>
</ol>
<blockquote>
<p><img src="https://lupyuen.github.io/images/nuttx-build2.png" alt="Building NuttX" /></p>
</blockquote>
<h2 id="flash-nuttx"><a class="doc-anchor" href="#flash-nuttx">§</a>13.5 Flash NuttX</h2>
<p><strong>For ESP32:</strong> <a href="https://nuttx.apache.org/docs/latest/platforms/xtensa/esp32/index.html#flashing"><strong>See instructions here</strong></a> <a href="https://popolon.org/gblog3/?p=1977&amp;lang=en">(Also check out this article)</a></p>
<p><strong>For BL602 / BL604:</strong> Follow these steps to install <strong>blflash</strong></p>
<ol>
<li>
<p><a href="https://lupyuen.github.io/articles/flash#install-rustup"><strong>“Install rustup”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/flash#download-and-build-blflash"><strong>“Download and build blflash”</strong></a></p>
</li>
</ol>
<p>We assume that our Firmware Binary File <strong>nuttx.bin</strong> has been copied to the <strong>blflash</strong> folder.</p>
<p>Set BL602 / BL604 to <strong>Flashing Mode</strong> and restart the board…</p>
<p><strong>For PineDio Stack BL604:</strong></p>
<ol>
<li>
<p>Set the <strong>GPIO 8 Jumper</strong> to <strong>High</strong> <a href="https://lupyuen.github.io/images/pinedio-high.jpg">(Like this)</a></p>
</li>
<li>
<p>Disconnect the USB cable and reconnect</p>
<p>Or use the Improvised Reset Button <a href="https://lupyuen.github.io/articles/pinedio#appendix-improvised-reset-button-for-pinedio-stack">(Heres how)</a></p>
</li>
</ol>
<p><strong>For PineCone BL602:</strong></p>
<ol>
<li>
<p>Set the <strong>PineCone Jumper (IO 8)</strong> to the <strong><code>H</code> Position</strong> <a href="https://lupyuen.github.io/images/pinecone-jumperh.jpg">(Like this)</a></p>
</li>
<li>
<p>Press the Reset Button</p>
</li>
</ol>
<p><strong>For BL10:</strong></p>
<ol>
<li>
<p>Connect BL10 to the USB port</p>
</li>
<li>
<p>Press and hold the <strong>D8 Button (GPIO 8)</strong></p>
</li>
<li>
<p>Press and release the <strong>EN Button (Reset)</strong></p>
</li>
<li>
<p>Release the D8 Button</p>
</li>
</ol>
<p><strong>For <a href="https://docs.ai-thinker.com/en/wb2">Ai-Thinker Ai-WB2</a>, Pinenut and MagicHome BL602:</strong></p>
<ol>
<li>
<p>Disconnect the board from the USB Port</p>
</li>
<li>
<p>Connect <strong>GPIO 8</strong> to <strong>3.3V</strong></p>
</li>
<li>
<p>Reconnect the board to the USB port</p>
</li>
</ol>
<p>Enter these commands to flash <strong>nuttx.bin</strong> to BL602 / BL604 over UART…</p>
<div class="example-wrap"><pre class="language-bash"><code>## For Linux: Change &quot;/dev/ttyUSB0&quot; to the BL602 / BL604 Serial Port
blflash flash nuttx.bin \
--port /dev/ttyUSB0
## For macOS: Change &quot;/dev/tty.usbserial-1410&quot; to the BL602 / BL604 Serial Port
blflash flash nuttx.bin \
--port /dev/tty.usbserial-1410 \
--initial-baud-rate 230400 \
--baud-rate 230400
## For Windows: Change &quot;COM5&quot; to the BL602 / BL604 Serial Port
blflash flash c:\blflash\nuttx.bin --port COM5</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/9c0dbd75bb6b8e810939a36ffb5c399f">(See the Output Log)</a></p>
<p>For WSL: Do this under plain old Windows CMD (not WSL) because <strong>blflash</strong> needs to access the COM port.</p>
<p><a href="https://github.com/apache/nuttx/issues/4336">(Flashing WiFi apps to BL602 / BL604? Remember to use <strong>bl_rfbin</strong>)</a></p>
<p><a href="https://lupyuen.github.io/articles/flash#flash-the-firmware">(More details on flashing firmware)</a></p>
<p><img src="https://lupyuen.github.io/images/nuttx-flash2.png" alt="Flashing NuttX" /></p>
<h2 id="run-nuttx"><a class="doc-anchor" href="#run-nuttx">§</a>13.6 Run NuttX</h2>
<p><strong>For ESP32:</strong> Use Picocom to connect to ESP32 over UART…</p>
<div class="example-wrap"><pre class="language-bash"><code>picocom -b 115200 /dev/ttyUSB0</code></pre></div>
<p><a href="https://popolon.org/gblog3/?p=1977&amp;lang=en">(More about this)</a></p>
<p><strong>For BL602 / BL604:</strong> Set BL602 / BL604 to <strong>Normal Mode</strong> (Non-Flashing) and restart the board…</p>
<p><strong>For PineDio Stack BL604:</strong></p>
<ol>
<li>
<p>Set the <strong>GPIO 8 Jumper</strong> to <strong>Low</strong> <a href="https://lupyuen.github.io/images/pinedio-low.jpg">(Like this)</a></p>
</li>
<li>
<p>Disconnect the USB cable and reconnect</p>
<p>Or use the Improvised Reset Button <a href="https://lupyuen.github.io/articles/pinedio#appendix-improvised-reset-button-for-pinedio-stack">(Heres how)</a></p>
</li>
</ol>
<p><strong>For PineCone BL602:</strong></p>
<ol>
<li>
<p>Set the <strong>PineCone Jumper (IO 8)</strong> to the <strong><code>L</code> Position</strong> <a href="https://lupyuen.github.io/images/pinecone-jumperl.jpg">(Like this)</a></p>
</li>
<li>
<p>Press the Reset Button</p>
</li>
</ol>
<p><strong>For BL10:</strong></p>
<ol>
<li>Press and release the <strong>EN Button (Reset)</strong></li>
</ol>
<p><strong>For <a href="https://docs.ai-thinker.com/en/wb2">Ai-Thinker Ai-WB2</a>, Pinenut and MagicHome BL602:</strong></p>
<ol>
<li>
<p>Disconnect the board from the USB Port</p>
</li>
<li>
<p>Connect <strong>GPIO 8</strong> to <strong>GND</strong></p>
</li>
<li>
<p>Reconnect the board to the USB port</p>
</li>
</ol>
<p>After restarting, connect to BL602 / BL604s UART Port at 2 Mbps like so…</p>
<p><strong>For Linux:</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>screen /dev/ttyUSB0 2000000</code></pre></div>
<p><strong>For macOS:</strong> Use CoolTerm (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
<p><strong>For Windows:</strong> Use <code>putty</code> (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
<p><strong>Alternatively:</strong> Use the Web Serial Terminal (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
<p>Press Enter to reveal the <strong>NuttX Shell</strong></p>
<div class="example-wrap"><pre class="language-text"><code>NuttShell (NSH) NuttX-10.2.0-RC0
nsh&gt;</code></pre></div>
<p>Congratulations NuttX is now running on BL602 / BL604!</p>
<p><a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">(More details on connecting to BL602 / BL604)</a></p>
<p><img src="https://lupyuen.github.io/images/nuttx-boot2.png" alt="Running NuttX" /></p>
<p><img src="https://lupyuen.github.io/images/rusti2c-title2.jpg" alt="Bosch BME280 Sensor connected to Pine64 PineCone BL602 RISC-V Board" /></p>
<p><em><a href="https://www.bosch-sensortec.com/products/environmental-sensors/humidity-sensors-bme280/">Bosch BME280 Sensor</a> connected to <a href="https://lupyuen.github.io/articles/pinecone">Pine64 PineCone BL602 RISC-V Board</a></em></p>
<!-- Begin scripts/rustdoc-after.html: Post-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
<!-- Begin Theme Picker and Prism Theme -->
<script src="../theme.js"></script>
<script src="../prism.js"></script>
<!-- Theme Picker and Prism Theme -->
<!-- End scripts/rustdoc-after.html -->
</body>
</html>