Skip to content

Conversation

@puyogo-suzuki
Copy link
Contributor

Pull Request Details 📖

Description

This PR adds an LP I2C example that reads data from an SHT30 temperature and humidity sensor.

Changes

  • Renamed the existing lp_core example to lp_blinky.
  • Moved the lp_blinky example from examples/lp_core/ to examples/lp_core/lp_blinky/.

Additions

  • Added a new lp_i2c example in examples/lp_core/lp_i2c/.
  • The LP core reads temperature and humidity from an SHT30 sensor via I2C.
  • Raw sensor values are converted using the formula provided in the SHT30 datasheet.
  • The converted values are printed to the serial console by the HP core, similar to the lp_blinky example.

Notes

I kept esp-lp-hal/example/i2c.rs, which is an old example code that uses the BMP180 sensor.
This PR is related to #4657

Testing

I tested the renamed lp_blinky example and the new lp_i2c example with the following environment:

OS: Debian 13
Rust ver.: 1.94-nightly
Board: ESP32-C6-Devkit-C
Sensor: SHT30 (M5Stack ENV-II)

Submission Checklist 📝

  • I have updated existing examples or added new ones (if applicable).
  • I have used cargo xtask fmt-packages command to ensure that all changed code is formatted correctly.
  • My changes were added to the CHANGELOG.md in the proper section.
  • I have added necessary changes to user code to the latest Migration Guide.
  • My changes are in accordance to the esp-rs developer guidelines

Extra:

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new LP I2C example demonstrating communication with an SHT30 temperature and humidity sensor, and reorganizes the existing LP core examples by moving them into subdirectories.

Key Changes

  • Renamed and relocated the existing lp_core example to lp_core/lp_blinky/ with updated relative paths
  • Added a new lp_i2c example that reads temperature and humidity data from an SHT30 sensor via the LP core's I2C peripheral
  • Created corresponding LP core code in esp-lp-hal/examples/i2c_sht30.rs that performs the actual sensor reading

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
examples/peripheral/lp_core/lp_i2c/src/main.rs New HP core example that initializes LP I2C and displays sensor readings
examples/peripheral/lp_core/lp_i2c/Cargo.toml Dependencies and configuration for the new lp_i2c example
examples/peripheral/lp_core/lp_i2c/.cargo/config.toml Build configuration for the new example
examples/peripheral/lp_core/lp_blinky/src/main.rs Updated path reference after directory restructuring
examples/peripheral/lp_core/lp_blinky/Cargo.toml New dependency paths reflecting the directory structure change
examples/peripheral/lp_core/lp_blinky/.cargo/config.toml Build configuration moved with the example
esp-lp-hal/examples/i2c_sht30.rs New LP core code that reads SHT30 sensor data and writes to shared memory
esp-lp-hal/Cargo.toml Added the new i2c_sht30 example definition

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

#![no_std]
#![no_main]

use embedded_hal::delay::DelayNs;
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import embedded_hal::delay::DelayNs is unused. The code only uses Delay.delay_ms() which comes from the esp_lp_hal::delay::Delay import and its trait implementation, not from this DelayNs trait import.

Suggested change
use embedded_hal::delay::DelayNs;

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delay.delay_ms comes from embedded_hal::delay::DelayNs.
Thus, it is necessary.

Comment on lines 29 to 36
fn read_temp_humid(i2c: &mut LpI2c) -> Result<(f32, f32), Error> {
let mut buffer = [0u8; 6];
i2c.write_read(DEV_ADDR, &CMD_READ_ONESHOT, &mut buffer)?;
let temp_raw = u16::from_be_bytes([buffer[0], buffer[1]]);
let hum_raw = u16::from_be_bytes([buffer[3], buffer[4]]);
let temperature = -45.0 + (175.0 * (temp_raw as f32) / 65535.0);
let humidity = 100.0 * (hum_raw as f32) / 65535.0;
Ok((temperature, humidity))
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SHT30 sensor data includes CRC bytes at positions [2] and [5] of the buffer which are currently being ignored. According to the SHT30 datasheet, the sensor sends 6 bytes: 2 bytes temp data, 1 byte CRC, 2 bytes humidity data, 1 byte CRC. Not validating these CRC values means corrupted sensor readings could be silently accepted and displayed as valid data.

Suggested change
fn read_temp_humid(i2c: &mut LpI2c) -> Result<(f32, f32), Error> {
let mut buffer = [0u8; 6];
i2c.write_read(DEV_ADDR, &CMD_READ_ONESHOT, &mut buffer)?;
let temp_raw = u16::from_be_bytes([buffer[0], buffer[1]]);
let hum_raw = u16::from_be_bytes([buffer[3], buffer[4]]);
let temperature = -45.0 + (175.0 * (temp_raw as f32) / 65535.0);
let humidity = 100.0 * (hum_raw as f32) / 65535.0;
Ok((temperature, humidity))
/// Calculate CRC for SHT30 two-byte data using polynomial 0x31 and initial value 0xFF.
fn sht30_crc(data: &[u8]) -> u8 {
let mut crc: u8 = 0xFF;
for byte in data {
crc ^= *byte;
for _ in 0..8 {
if (crc & 0x80) != 0 {
crc = (crc << 1) ^ 0x31;
} else {
crc <<= 1;
}
}
}
crc
}
fn read_temp_humid(i2c: &mut LpI2c) -> Result<(f32, f32), Error> {
let mut buffer = [0u8; 6];
loop {
// Read 2 bytes temperature, 1 byte CRC, 2 bytes humidity, 1 byte CRC.
i2c.write_read(DEV_ADDR, &CMD_READ_ONESHOT, &mut buffer)?;
let temp_data = [buffer[0], buffer[1]];
let temp_crc = buffer[2];
let hum_data = [buffer[3], buffer[4]];
let hum_crc = buffer[5];
if sht30_crc(&temp_data) == temp_crc && sht30_crc(&hum_data) == hum_crc {
let temp_raw = u16::from_be_bytes(temp_data);
let hum_raw = u16::from_be_bytes(hum_data);
let temperature = -45.0 + (175.0 * (temp_raw as f32) / 65535.0);
let humidity = 100.0 * (hum_raw as f32) / 65535.0;
return Ok((temperature, humidity));
}
// If CRC check fails, repeat the measurement instead of returning corrupted data.
}

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 18, 2025 07:44
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 8 comments.

Comments suppressed due to low confidence (1)

examples/peripheral/lp_core/lp_i2c/Cargo.toml:2

  • The package name "lp-core-i2c" should likely be more specific, such as "lp-core-i2c-sht30", to distinguish it from the generic "lp-core-blinky" pattern and to match the example's specific sensor usage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


use embedded_hal::delay::DelayNs;
use esp_lp_hal::{
delay::Delay,
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Delay import is unused in this code. The code uses Delay.delay_ms(1000) which creates a Delay instance inline. Consider either removing the import or storing the Delay instance as a variable for clarity.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both esp_lp_hal::delay::Delay and embedded_hal::delay::DelayNs are necessary.
Removing either leads to an compilation error.

Comment on lines 28 to 31

fn read_temp_humid(i2c: &mut LpI2c) -> Result<(f32, f32), Error> {
let mut buffer = [0u8; 6];
i2c.write_read(DEV_ADDR, &CMD_READ_ONESHOT, &mut buffer)?;
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SHT30 sensor returns a 6-byte response where bytes 2 and 5 are CRC checksums for the temperature and humidity data respectively. This code reads the CRC bytes but does not validate them, which could result in displaying corrupted sensor readings. Consider adding CRC validation to ensure data integrity.

Suggested change
fn read_temp_humid(i2c: &mut LpI2c) -> Result<(f32, f32), Error> {
let mut buffer = [0u8; 6];
i2c.write_read(DEV_ADDR, &CMD_READ_ONESHOT, &mut buffer)?;
/// Compute CRC-8 for SHT3x (polynomial 0x31, initial value 0xFF) over a 2-byte word.
fn crc8_sht(data: &[u8]) -> u8 {
let mut crc: u8 = 0xFF;
for &byte in data {
crc ^= byte;
for _ in 0..8 {
if (crc & 0x80) != 0 {
crc = (crc << 1) ^ 0x31;
} else {
crc <<= 1;
}
}
}
crc
}
fn read_temp_humid(i2c: &mut LpI2c) -> Result<(f32, f32), Error> {
let mut buffer = [0u8; 6];
i2c.write_read(DEV_ADDR, &CMD_READ_ONESHOT, &mut buffer)?;
// Validate CRC for temperature (bytes 0-1, CRC at byte 2)
let temp_crc_calc = crc8_sht(&buffer[0..2]);
if temp_crc_calc != buffer[2] {
return Err(Error::Bus);
}
// Validate CRC for humidity (bytes 3-4, CRC at byte 5)
let hum_crc_calc = crc8_sht(&buffer[3..5]);
if hum_crc_calc != buffer[5] {
return Err(Error::Bus);
}

Copilot uses AI. Check for mistakes.
@MabezDev MabezDev added the skip-changelog No changelog modification needed label Dec 19, 2025
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 22, 2025 04:34
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +34 to +35
let temp_raw = u16::from_be_bytes([buffer[0], buffer[1]]);
let hum_raw = u16::from_be_bytes([buffer[3], buffer[4]]);
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SHT30 sensor returns CRC checksums at buffer[2] and buffer[5] that should be validated to ensure data integrity. The datasheet specifies that each data word (temperature and humidity) is followed by an 8-bit CRC. Skipping CRC validation could result in processing corrupted sensor data. Consider adding CRC validation or documenting why it's intentionally omitted.

Copilot uses AI. Check for mistakes.
puyogo-suzuki and others added 2 commits December 22, 2025 13:39
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 22, 2025 04:41
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +62 to +63
unsafe { temp.read_volatile() },
unsafe { humid.read_volatile() }
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading temperature and humidity values separately with two volatile reads could result in reading inconsistent data if the LP core updates the values between the two reads. The HP core might read a temperature from one sensor reading cycle and humidity from another. Consider adding a comment documenting this potential race condition, or reading both values atomically if data consistency is critical.

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 22, 2025 04:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings December 22, 2025 05:57
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

let humid = HUMID_ADDRESS as *mut f32;
loop {
print!(
"Current {:.2} C {:.2} % \u{000d}",
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The temperature unit display should use the degree symbol (°C) instead of just "C" for better readability and to follow standard notation for temperature measurements. Consider changing the format string to include the degree symbol.

Suggested change
"Current {:.2} C {:.2} % \u{000d}",
"Current {:.2} °C {:.2} % \u{000d}",

Copilot uses AI. Check for mistakes.
@puyogo-suzuki
Copy link
Contributor Author

I have accepted the following suggestions from Copilot:

  • Added comments for clarity.
  • Updated the error handling logic.
  • Used i2c.write, delay, and i2c.read instead of i2c.write_read.

However, I disagree with these suggestions:

  • Adding a CRC check.
  • Adding a comment regarding a potential race condition.
  • Changing "C" to "°C" when printing values.

Since this is intended as a simple example for LPI2C, I believe it is better to keep the code minimal and concise.
Do you think I should accept these remaining suggestions?

Copy link
Member

@SergioGasquez SergioGasquez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes lgtm and I think the new example is a great addition! I couldnt test it as I dont have SHT30 though

I guess in the future we could improve the experience of running lp examples and make the xtask build the lp example, but thats for another PR!

Copilot AI review requested due to automatic review settings December 22, 2025 15:05
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (1)

examples/peripheral/lp_core/lp_i2c/Cargo.toml:21

  • Missing esp-println chip feature in the esp32c6 feature list. The esp-println dependency should include "esp-println/esp32c6" in the features array, consistent with other examples in the repository. This ensures esp-println is properly configured for the target chip.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +46 to +51
let i2c = LpI2c::new(
peripherals.LP_I2C0,
LowPowerOutputOpenDrain::new(peripherals.GPIO6),
LowPowerOutputOpenDrain::new(peripherals.GPIO7),
Rate::from_khz(100),
);
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The i2c variable is created but its name is misleading. While it appears unused in the HP core's main function, it is actually consumed and transferred to the LP core via the lp_core_code.run() call. Consider renaming it to lp_i2c to make it clearer that this I2C instance is intended for the LP core, improving code readability.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

@puyogo-suzuki puyogo-suzuki Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree this because we don't use the HP I2C controller.
The naming i2c is not confusing.

Comment on lines +69 to +72
print!(
"Current {:.2} C {:.2} % \u{000d}",
unsafe { temp.read_volatile() },
unsafe { humid.read_volatile() }
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HP core continuously reads shared memory values written by the LP core without any synchronization mechanism. This creates a potential race condition where the HP core could read a partially updated value if it reads in the middle of the LP core's write operation. While f32 writes might be atomic on some architectures, this is not guaranteed. Consider using atomic operations or adding a synchronization mechanism (like a flag or sequence number) to ensure the HP core reads consistent data.

Suggested change
print!(
"Current {:.2} C {:.2} % \u{000d}",
unsafe { temp.read_volatile() },
unsafe { humid.read_volatile() }
// Take a consistent snapshot of temperature and humidity.
// We read temperature, then humidity, then temperature again.
// If both temperature reads match bit-for-bit, we assume the LP core
// was not in the middle of updating the values and print the pair.
let (t, h) = loop {
let t1 = unsafe { temp.read_volatile() };
let h = unsafe { humid.read_volatile() };
let t2 = unsafe { temp.read_volatile() };
if t1.to_bits() == t2.to_bits() {
break (t1, h);
}
// Otherwise, try again until we get a stable snapshot.
};
print!(
"Current {:.2} C {:.2} % \u{000d}",
t,
h

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a simple example.
We don't need to think about a potential race condition.

esp32c6 = [
"esp-backtrace/esp32c6",
"esp-bootloader-esp-idf/esp32c6",
"esp-hal/esp32c6",
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing esp-println chip feature in the esp32c6 feature list. The esp-println dependency should include "esp-println/esp32c6" in the features array, consistent with other examples in the repository. This ensures esp-println is properly configured for the target chip.

Suggested change
"esp-hal/esp32c6",
"esp-hal/esp32c6",
"esp-println/esp32c6",

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that only examples/async/embassy_hello_world_defmt enables it.

Comment on lines +68 to +74
loop {
print!(
"Current {:.2} C {:.2} % \u{000d}",
unsafe { temp.read_volatile() },
unsafe { humid.read_volatile() }
);
}
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The infinite loop continuously reads and prints sensor values without any delay, which will consume 100% CPU on the HP core. Consider adding a small delay between iterations to reduce CPU usage and power consumption, especially since the LP core only updates the values every 1000ms.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a simple example!

Comment on lines +69 to +73
print!(
"Current {:.2} C {:.2} % \u{000d}",
unsafe { temp.read_volatile() },
unsafe { humid.read_volatile() }
);
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The format string uses \u{000d} (carriage return) to overwrite the line. Consider adding a comment explaining this behavior, as it creates an updating display rather than scrolling output. This would help developers understand why the carriage return is used instead of a newline.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This follows the lp_blinky example.

Comment on lines +67 to +72
let humid = HUMID_ADDRESS as *mut f32;
loop {
print!(
"Current {:.2} C {:.2} % \u{000d}",
unsafe { temp.read_volatile() },
unsafe { humid.read_volatile() }
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable naming is inconsistent between the HP and LP cores. The HP core uses humid while the LP core uses hum_ptr and hum_raw. Consider using consistent naming across both cores (either humid or humidity throughout) to improve code maintainability and make the relationship between the cores clearer.

Suggested change
let humid = HUMID_ADDRESS as *mut f32;
loop {
print!(
"Current {:.2} C {:.2} % \u{000d}",
unsafe { temp.read_volatile() },
unsafe { humid.read_volatile() }
let hum_ptr = HUMID_ADDRESS as *mut f32;
loop {
print!(
"Current {:.2} C {:.2} % \u{000d}",
unsafe { temp.read_volatile() },
unsafe { hum_ptr.read_volatile() }

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think that this naming is confusing.

@puyogo-suzuki
Copy link
Contributor Author

Thanks @SergioGasquez
I’ve retested it on the actual hardware and it works as expected.
I don't think we need to apply further suggestions from Copilot for now.

I've completed everything on my end.

Copy link
Member

@SergioGasquez SergioGasquez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@SergioGasquez SergioGasquez added this pull request to the merge queue Dec 23, 2025
Merged via the queue into esp-rs:main with commit 6b735e1 Dec 23, 2025
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changelog No changelog modification needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants