<li><ahref="#bl602-i2c-hardware-abstraction-layer-high-level-vs-low-level"title="BL602 I2C Hardware Abstraction Layer: High Level vs Low Level">1 BL602 I2C Hardware Abstraction Layer: High Level vs Low Level</a><ul></ul></li>
<li><ahref="#connect-bl602-to-bme280-i2c-sensor"title="Connect BL602 to BME280 I2C Sensor">2 Connect BL602 to BME280 I2C Sensor</a><ul>
<li><ahref="#i2c-protocol-for-bme280"title="I2C Protocol for BME280">2.1 I2C Protocol for BME280</a><ul></ul></li></ul></li>
<li><ahref="#assign-i2c-pins-and-set-i2c-frequency"title="Assign I2C Pins and set I2C Frequency">3.2 Assign I2C Pins and set I2C Frequency</a><ul></ul></li>
<li><ahref="#define-i2c-message-and-buffer"title="Define I2C Message and Buffer">4.1 Define I2C Message and Buffer</a><ul></ul></li>
<li><ahref="#set-i2c-operation-and-buffer"title="Set I2C Operation and Buffer">4.2 Set I2C Operation and Buffer</a><ul></ul></li>
<li><ahref="#set-i2c-device-address-and-register-address"title="Set I2C Device Address and Register Address">4.3 Set I2C Device Address and Register Address</a><ul></ul></li>
<li><ahref="#get-i2c-message-and-interrupt-reason"title="Get I2C Message and Interrupt Reason">6.1 Get I2C Message and Interrupt Reason</a><ul></ul></li>
<li><ahref="#i2c-data-received"title="I2C Data Received">6.2 I2C Data Received</a><ul></ul></li>
<li><ahref="#i2c-transfer-end"title="I2C Transfer End">6.3 I2C Transfer End</a><ul></ul></li>
<li><ahref="#i2c-no-acknowledge"title="I2C No Acknowledge">6.4 I2C No Acknowledge</a><ul></ul></li>
<li><ahref="#i2c-data-transmitted"title="I2C Data Transmitted">6.5 I2C Data Transmitted</a><ul></ul></li>
<li><ahref="#appendix-how-to-troubleshoot-risc-v-exceptions"title="Appendix: How to Troubleshoot RISC-V Exceptions">13 Appendix: How to Troubleshoot RISC-V Exceptions</a><ul></ul></li>
<li><ahref="#appendix-test-bme280-with-bus-pirate"title="Appendix: Test BME280 with Bus Pirate">14 Appendix: Test BME280 with Bus Pirate</a><ul></ul></li></ul></nav><p>📝 <em>29 Jan 2021</em></p>
<p><strong><ahref="https://lupyuen.github.io/articles/pinecone">PineCone BL602</a> (<ahref="https://wiki.pine64.org/wiki/Nutcracker#Pinenut-01S_Module_information_and_schematics">and Pinenut</a>)</strong> is an awesome RISC-V Microcontroller Board with WiFi and Bluetooth LE Networking.</p>
<p><strong>Low Level HAL <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_i2c.c"><code>bl_i2c.c</code></a></strong>: This runs on BL602 Bare Metal.</p>
<p><strong>High Level HAL <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_i2c.c"><code>hal_i2c.c</code></a></strong>: This calls the Low Level HAL, and uses the Device Tree and FreeRTOS.</p>
<p>Today we shall use the <strong>Low Level I2C HAL <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_i2c.c"><code>bl_i2c.c</code></a></strong> because…</p>
<p>In the next article we’ll port the Low Level I2C HAL to Mynewt. And hopefully the PineCone BL602 Community will port it to Arduino, RIOT, Zephyr, …</p>
<p><ahref="https://github.com/nandojve/zephyr/blob/bouffalo/boards/riscv/dt_bl10_devkit/doc/index.rst"><strong>UPDATE: Check out Zephyr for BL602</strong></a></p>
<p>(Yes we said that BL602 will <em>talk to I2C Sensors today</em>… Though we won’t be able to <em>use the sensor data meaningfully yet</em>)</p>
<p>We’ll see in a while that the Low Level HAL requires an Embedded Operating System to function properly. (Which is beyond the scope of this article)</p>
<p>We shall test BL602 I2C with this BL602 Command-Line Firmware (modded from BL602 IoT SDK): <ahref="https://github.com/lupyuen/bl_iot_sdk/tree/master/customer_app/sdk_app_i2c"><code>sdk_app_i2c</code></a></p>
<p>Let’s connect BL602 to the <ahref="https://learn.sparkfun.com/tutorials/sparkfun-bme280-breakout-hookup-guide"><strong>Bosch BME280 I2C Sensor for Temperature, Humidity and Air Pressure</strong></a></p>
<p>(Air Pressure is very useful for sensing which level of a building we’re on!)</p>
<p>Connect BL602 to BME280 according to the pic above…</p>
<p>The Low Level I2C HAL assigns GPIO 3 and 4 to the I2C Port on BL602. (See <strong>“Section 3.2.8: GPIO Function”</strong> in the <ahref="https://github.com/bouffalolab/bl_docs/tree/main/BL602_RM/en"><strong>BL602 Reference Manual</strong></a>)</p>
<p>(If we’re using the High Level I2C HAL, the I2C Pins are defined in the Device Tree)</p>
<p>(BME280 may be configured as Device ID <code>0x76</code> or <code>0x77</code>. <ahref="https://www.sparkfun.com/products/13676">SparkFun BME280</a> in the pic above uses <code>0x77</code>)</p>
<p>To sum up: We need to reproduce on BL602 the two <code>[Start] ... [Stop]</code> transactions. Which includes sending 3 bytes (<code>0xEE</code>, <code>0xD0</code>, <code>0xEF</code>) and receiving 1 byte (<code>0x60</code>).</p>
<p>Remember our Command-Line Firmware <ahref="https://github.com/lupyuen/bl_iot_sdk/tree/master/customer_app/sdk_app_i2c"><code>sdk_app_i2c</code></a> for testing I2C on BL602?</p>
<p>Let’s discover how this command calls the Low Level I2C HAL to initialise the I2C Port: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_i2c/sdk_app_i2c/demo.c#L343-L369"><code>sdk_app_i2c/demo.c</code></a></p>
<h2id="select-i2c-port"><aclass="doc-anchor"href="#select-i2c-port">§</a>3.1 Select I2C Port</h2><divclass="example-wrap"><preclass="language-c"><code>/// Init I2C Port. Based on hal_i2c_init in hal_i2c.c
<h2id="assign-i2c-pins-and-set-i2c-frequency"><aclass="doc-anchor"href="#assign-i2c-pins-and-set-i2c-frequency">§</a>3.2 Assign I2C Pins and set I2C Frequency</h2><divclass="example-wrap"><preclass="language-c"><code> // Init I2C Port 0 to GPIO 3 and 4
<p>The I2C Port triggers <strong>I2C Interrupts</strong> after sending and receiving queued data, also when an error occurs. So we need to enable I2C Interrupts…</p>
<p>Here we register the function <code>test_i2c_interrupt_entry</code> as the Interrupt Handler Function for I2C Interrupts. (More about this function in a while)</p>
<p><code>gpstmsg</code> points to the <strong>current I2C Message</strong> being sent or received, so that the Interrupt Handler knows which Message Buffer to use for sending and receiving data.</p>
<p>The following functions are defined in the <strong>Low Level I2C HAL</strong>: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_i2c.c"><code>bl_i2c.c</code></a></p>
<p>These functions are defined in the <strong>BL602 Interrupt HAL</strong>: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_irq.c"><code>bl_irq.c</code></a></p>
<p>And these functions are defined in the <strong>BL602 Standard Driver</strong>: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602_std/bl602_std/StdDriver/Src/bl602_i2c.c"><code>bl602_i2c.c</code></a></p>
<p>Here’s how we create an I2C Message: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_i2c/sdk_app_i2c/demo.c#L424-L442"><code>sdk_app_i2c/demo.c</code></a></p>
<h2id="set-i2c-operation-and-buffer"><aclass="doc-anchor"href="#set-i2c-operation-and-buffer">§</a>4.2 Set I2C Operation and Buffer</h2><divclass="example-wrap"><preclass="language-c"><code>// Set the I2C operation
<p>Then we assign the data buffer <code>read_buf</code> to <code>read_msg</code> and set the number of bytes to be read (1).</p>
<p><code>idex</code> is the index into the buffer <code>read_buf</code>. Our I2C Interrupt Handler will increment this index as it populates the buffer upon receiving data.</p>
<h2id="set-i2c-device-address-and-register-address"><aclass="doc-anchor"href="#set-i2c-device-address-and-register-address">§</a>4.3 Set I2C Device Address and Register Address</h2>
<p>The I2C Register Address feature is <strong>not available</strong> on STM32 Blue Pill, Nordic nRF52, GigaDevice GD32 VF103 (RISC-V), ESP32, … Not even on Raspberry Pi Pico!</p>
<p>(Though it seems to be supported on <ahref="https://mcuxpresso.nxp.com/api_doc/dev/116/group__i2c.html">NXP Microcontrollers</a> as “I2C Subaddress”)</p>
<p>The I2C Documentation in the <ahref="https://github.com/bouffalolab/bl_docs/tree/main/BL602_RM/en">BL602 Reference Manual</a> appears somewhat confusing because of the I2C Register Address feature. <ahref="https://lupyuen.github.io/images/i2c-confuse.png">See this</a></p>
<p>Let’s find out what happens inside that command: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_i2c/sdk_app_i2c/demo.c#L420-L448"><code>sdk_app_i2c/demo.c</code></a></p>
<p>(For I2C Write Operation <code>I2C_M_WRITE</code>: The Message buffer field <code>buf</code> should point to a byte array that contains the I2C Data that will be written to the I2C Register)</p>
<p><code>i2c_transfer_start</code> is defined in the <strong>Low Level I2C HAL</strong>: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_i2c.c"><code>bl_i2c.c</code></a></p>
<h2id="get-i2c-message-and-interrupt-reason"><aclass="doc-anchor"href="#get-i2c-message-and-interrupt-reason">§</a>6.1 Get I2C Message and Interrupt Reason</h2>
<p>According to the <ahref="https://github.com/bouffalolab/bl_docs/tree/main/BL602_RM/en"><strong>BL602 Reference Manual</strong></a> there are 6 kinds of I2C Interrupts…</p>
<p>This condition flows through to the end of our Interrupt Handler, and calls <code>test_i2c_transferbytes</code> to copy the received data into our Message Buffer and receive more data.</p>
<p>This is bad… We encounter I2C No Acknowledge usually when the I2C Device Address is misconfigured (say <code>0x76</code> instead of <code>0x77</code>).</p>
<p>This condition flows through to the end of our Interrupt Handler, and calls <code>test_i2c_transferbytes</code> to transmit the next 4 bytes of data from our Message Buffer.</p>
<p>Here’s how it works for I2C Write and I2C Read Operations: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_i2c/sdk_app_i2c/demo.c#L249-L271"><code>sdk_app_i2c/demo.c</code></a></p>
<p>In an I2C Write Operation, we handle the I2C Data Transmitted Interrupt by <strong>transmitting the next 4 bytes from the Message Buffer</strong>…</p>
<p><code>do_write_data</code> is defined in the <strong>Low Level I2C HAL</strong>: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_i2c.c"><code>bl_i2c.c</code></a></p>
<p>In an I2C Read Operation, we handle the I2C Data Received Interrupt by <strong>copying the received bytes into the Message Buffer, 4 bytes at a time</strong>…</p>
<p><code>do_read_data</code> is defined in the <strong>Low Level I2C HAL</strong>: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_i2c.c"><code>bl_i2c.c</code></a></p>
<p>(FYI: <code>test_i2c_transferbytes</code> is the fixed version of <code>i2c_transferbytes</code> from the High Level I2C HAL <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_i2c.c"><code>hal_i2c.c</code></a>. <ahref="https://lupyuen.github.io/images/i2c-transferbytes.png">Here’s the fix</a>)</p>
<p>This command calls <code>test_i2c_stop</code> to close the I2C Port: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_i2c/sdk_app_i2c/demo.c#L450-L460"><code>sdk_app_i2c/demo.c</code></a></p>
<p><code>test_i2c_stop</code> closes the I2C Port like so: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_i2c/sdk_app_i2c/demo.c#L236-L247"><code>sdk_app_i2c/demo.c</code></a></p>
<p><code>i2c_clear_status</code> is defined in the <strong>Low Level I2C HAL</strong>: <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_i2c.c"><code>bl_i2c.c</code></a></p>
<p>We’ve read the I2C code… Let’s download, flash and run the modded <ahref="https://github.com/lupyuen/bl_iot_sdk/tree/master/customer_app/sdk_app_i2c"><code>sdk_app_i2c</code></a> firmware!</p>
<p>Alternatively, we may build the Firmware Binary File <code>sdk_app_i2c.bin</code> from the <ahref="https://github.com/lupyuen/bl_iot_sdk/tree/master/customer_app/sdk_app_i2c">source code</a>…</p>
<p>Set the <strong>PineCone Jumper (IO 8)</strong> to the <strong><code>H</code> Position</strong><ahref="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>
<p>Set the <strong>PineCone Jumper (IO 8)</strong> to the <strong><code>L</code> Position</strong><ahref="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>
<p><strong>Alternatively:</strong> Use the Web Serial Terminal (<ahref="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
<p>Aha Something Different! We have encountered <strong>one interrupt for Data Received</strong> (Rx Ready), because BME280 has returned some I2C data to BL602…</p>
<h1id="why-we-need-an-embedded-os-for-i2c"><aclass="doc-anchor"href="#why-we-need-an-embedded-os-for-i2c">§</a>10 Why we need an Embedded OS for I2C</h1>
<p>The I2C data transfer happens in the background, executed by the Interrupt Handler. The Foreground Task isn’t notified when the data transfer is complete.</p>
<p><strong>Solution:</strong> Our Interrupt Handler should use a <strong>Semaphore or a Message Queue</strong> to notify the Foreground Task when the data transfer is done.</p>
<p><strong>Solution:</strong> Our program should use a <strong>Semaphore or a Mutex Lock</strong> to prevent concurrent updates to the shared variables.</p>
<p>When we implement these two Solutions in FreeRTOS… We’ll get the <strong>High Level I2C HAL!</strong> (See <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_i2c.c"><code>hal_i2c.c</code></a>)</p>
<div><table><thead><tr><thstyle="text-align: left">Function In <br> This Article</th><thstyle="text-align: left">Function In <br> High Level HAL</th></tr></thead><tbody>
<p><ahref="https://help.aliyun.com/document_detail/161064.html?spm=a2c4g.11186623.6.577.492c4564jOCOjU">See the Chinese docs for High Level I2C HAL</a></p>
<p><ahref="https://github.com/bouffalolab/bl_iot_sdk/tree/master/customer_app/sdk_app_i2c">See the original (unmodified) High Level I2C HAL Demo</a></p>
<p>Yes! We may implement the two Solutions with any Embedded Operating System that supports <strong>Task Synchronisation</strong> features (Semaphore, Mutex, Message Queue).</p>
<p>Thus to do meaningful work with I2C (like reading I2C Sensor Data periodically and processing the data), we need to use the <strong>Low Level I2C HAL together with an Embedded Operating System</strong>.</p>
<p><ahref="https://twitter.com/MisterTechBlog/status/1354776244018057218?s=20">(I have received ST7789 SPI displays for testing… Many thanks to my Generous Sponsor! 😀)</a></p>
<p>There’s plenty more code in the <ahref="https://github.com/bouffalolab/bl_iot_sdk"><strong>BL602 IoT SDK</strong></a> to be deciphered and documented: <strong>ADC, DAC, WiFi, Bluetooth LE,</strong> …</p>
<p><ahref="https://wiki.pine64.org/wiki/Nutcracker"><strong>Come Join Us… Make BL602 Better!</strong></a></p>
<p><ahref="https://www.reddit.com/r/embedded_oc/comments/l7d469/pinecone_bl602_talks_to_i2c_sensors/?utm_source=share&utm_medium=web2x&context=3">Discuss this article on Reddit</a></p>
<p>Check out the <strong><ahref="https://github.com/pine64/ArduinoCore-bouffalo/blob/main/libraries/Wire/src/Wire.cpp">BL602 I2C HAL for Arduino</a></strong></p>
<p>Because Mynewt exposes an I2C API that <strong>controls the I2C Stop Bit explicitly</strong>. <ahref="https://mynewt.apache.org/latest/os/modules/hal/hal_i2c/hal_i2c.html#c.hal_i2c_master_write">(See this <code>last_op</code> parameter)</a></p>
<p>When porting BL602 I2C to Mynewt, we need to reconcile the two styles of I2C coding: <strong>Register Address vs Stop Bit.</strong></p>
<p>The code should be similar. The demo program contains code for writing to I2C Registers, but it hasn’t been tested. And it needs cleaning up. <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_i2c/sdk_app_i2c/demo.c#L376-L418">See this</a></p>
<p>DMA for I2C (and SPI) sounds overkill for an IoT Gadget. We should keep the firmware simple and easy to maintain. (Until we have more maintainers)</p>
<p><strong>BL602 SPI</strong> doesn’t have a Low Level HAL… It only comes as a High Level HAL with FreeRTOS. Which will be a challenging exploration. <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_spi.c">See this</a></p>
<p>This article is the expanded version of <ahref="https://twitter.com/MisterTechBlog/status/1352937390776545281?s=19"><strong>this Twitter Thread</strong></a></p>
<p>(From High Level I2C HAL <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_i2c.c"><code>hal_i2c.c</code></a>)</p>
<h1id="appendix-how-to-troubleshoot-risc-v-exceptions"><aclass="doc-anchor"href="#appendix-how-to-troubleshoot-risc-v-exceptions">§</a>13 Appendix: How to Troubleshoot RISC-V Exceptions</h1>
<p>When our program <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_i2c/"><code>sdk_app_i2c</code></a> is sending I2C data, the program crashes with the RISC-V Exception shown above…</p>
<p><strong><code>mcause</code> (Machine Cause Register)</strong>: Tells us the reason for the exception. <ahref="http://www.five-embeddev.com/riscv-isa-manual/latest/machine.html#sec:mcause">More details</a></p>
<p>The Exception Code is 7 (Store/AMO Access Fault), which means that we have accessed an invalid memory address.</p>
<p>(Probably a bad pointer)</p>
<p><ahref="http://www.five-embeddev.com/riscv-isa-manual/latest/machine.html#sec:mcause">List of RISC-V Exception Codes</a></p>
</li>
<li>
<p><strong><code>mepc</code> (Machine Exception Program Counter)</strong>: The address of the code that caused the exception. <ahref="http://www.five-embeddev.com/riscv-isa-manual/latest/machine.html#machine-exception-program-counter-mepc">More details</a></p>
<p><strong><code>mtval</code> (Machine Trap Value Register)</strong>: The invalid address that was accessed. <ahref="http://www.five-embeddev.com/riscv-isa-manual/latest/machine.html#machine-trap-value-register-mtval">More details</a></p>
<p>Our program attempted to access the invalid address <code>0x000 00014</code> and crashed.</p>
<p>According to the RISC-V Disassembly <ahref="https://github.com/lupyuen/bl_iot_sdk/releases/download/v1.0.1/sdk_app_i2c.S"><code>sdk_app_i2c.S</code></a>, the code address <code>0x2300 8fe2</code> is located in the I2C Interrupt Handler of the BL602 I2C HAL (See pic)</p>
<li><ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_i2c.c#L97-L133"><code>i2c_interrupt_entry</code> in <code>hal_i2c.c</code></a></li>
<p>It comes from the High Level HAL <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_i2c.c"><code>hal_i2c.c</code></a>, but we’re actually using the Low Level HAL <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_i2c.c"><code>bl_i2c.c</code></a>.</p>
<li><ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_i2c/sdk_app_i2c/main.c#L159-L199"><code>aos_loop_proc</code> in <code>main.c</code></a></li>
<p>Not quite. When we comment out <code>hal_i2c_init</code>, we disable the High Level I2C HAL functions in our demo firmware <ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_i2c/"><code>sdk_app_i2c</code></a></p>
<p><ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_i2c/"><strong><code>i2c</code> Branch</strong></a> is used for testing Low Level I2C HAL</p>
<p><ahref="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_i2c/"><strong><code>master</code> Branch</strong></a> is used for testing High Level I2C HAL</p>
</li>
</ul>
<p>(The proper fix is to create a new command that calls <code>hal_i2c_init</code>)</p>
<p><em>What are the <code>aos</code> functions in the code above?</em></p>
<p>The <code>aos</code> functions are defined in <ahref="https://github.com/alibaba/AliOS-Things">AliOS</a>. Remember that the High Level I2C HAL is called by AliOS Firmware.</p>
<h1id="appendix-test-bme280-with-bus-pirate"><aclass="doc-anchor"href="#appendix-test-bme280-with-bus-pirate">§</a>14 Appendix: Test BME280 with Bus Pirate</h1>
<p><ahref="http://dangerousprototypes.com/docs/Bus_Pirate"><strong>Bus Pirate</strong></a> is a useful gadget for verifying whether our BME280 Sensor works OK. And for checking the I2C bytes that should be sent down the wire to BME280.</p>
<p><ahref="http://dangerousprototypes.com/docs/I2C">(Bus Pirate also works as a simple Protocol Analyser for sniffing I2C data)</a></p>
<p>I2C uses the even / odd address convention to indicate whether we’re writing or reading data. So our BME280 at address <code>0x77</code> appears as two Read / Write aliases…</p>
<p><strong>In the First I2C Transaction:</strong> Bus Pirate sends <strong><code>0xEE</code></strong> to indicate a Write Transaction (for address <code>0x77</code>).</p>
<p><strong>In the Second I2C Transaction:</strong> Bus Pirate sends <strong><code>0xEF</code></strong> to indicate a Read Transaction (for address <code>0x77</code>).</p>
<p>BME280 returns the value of the Chip ID Register, indicated by <strong><code>r</code></strong></p>
</li>
</ol>
<p>To sum up: Bus Pirate initiates two <code>[ ... ]</code> transactions. The transactions will send 3 bytes (<code>0xEE</code>, <code>0xD0</code>, <code>0xEF</code>) and receive 1 byte (<code>0x60</code>).</p>