Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I2C NoAcknowledge on ESP32c3 #73

Closed
Gazedo opened this issue May 17, 2022 · 9 comments
Closed

I2C NoAcknowledge on ESP32c3 #73

Gazedo opened this issue May 17, 2022 · 9 comments

Comments

@Gazedo
Copy link

Gazedo commented May 17, 2022

Hi All,
I'm trying to get a esp32c3 (m5stack c3u board) to communicate with a mpu9250, its connected with:

  • gpio4 = sda
  • gpio5 = scl

But no matter what I keep getting the error:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: I2cError { kind: NoAcknowledge(Unknown), cause: EspError(-1) }', src/main.rs:62:37

I've tried using the write_read function and just directly using a mpu9250 crate so I think the issue lays with the esp_idf_hal crate. I was initially using version 0.37.4 and I tried to go back to version 0.35.2 which also did not work. I've confirmed the mpu9250 is functional by downloading a demo arduino program to the esp32c3 and everything reads just fine with the exact same circuit and hardware.

Below is the test code I've been using. Am I doing something wrong?

use embedded_hal::blocking::i2c::{Read, Write};
use esp_idf_hal::i2c;
use esp_idf_hal::prelude::*;
use esp_idf_hal::units::FromValueType;
use esp_idf_sys as _;
use std::thread;
use std::time::Duration;

fn main() {
    // Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once,
    // or else some patches to the runtime implemented by esp-idf-sys might not link properly.
    esp_idf_sys::link_patches();
    esp_idf_svc::log::EspLogger::initialize_default();
    let per = Peripherals::take().unwrap();
    let sda = per.pins.gpio4.into_input_output().unwrap();
    let scl = per.pins.gpio5.into_output().unwrap();
    let i2c = per.i2c0;

    let config = <i2c::config::MasterConfig as Default>::default().baudrate(400.kHz().into());
    let mut i2cdev =
        i2c::Master::<i2c::I2C0, _, _>::new(i2c, i2c::MasterPins { sda, scl }, config).unwrap();

    loop {
        let mut buff: [u8; 6] = [0; 6];
        i2cdev.write(0x68, &[0x75]).unwrap();
        i2cdev.read(0x68, &mut buff).unwrap();
        log::info!("wai value is {:?}", buff);
        thread::sleep(Duration::from_millis(100));
    }
}

P.S. the line numbers are off because I eliminated a ton of comments. The line that is failing is: i2cdev.write(0x68, &[0x75]).unwrap();

@Gazedo Gazedo changed the title I2C NoAchknowledge on ESP32c3 I2C NoAcknowledge on ESP32c3 May 17, 2022
@ivmarkov
Copy link
Collaborator

Shouldn't you be using write_read? Have you tried this crate?

@Gazedo
Copy link
Author

Gazedo commented May 18, 2022

Shouldn't you be using write_read? Have you tried this crate?

I've tried all of those options, I was trying to reduce the code to the bare minimum to show the error. I get the same error on all of those options because at the base level they all use the write function at some point.

@Dominaezzz
Copy link
Contributor

What does the working Arduino code look like?

@Gazedo
Copy link
Author

Gazedo commented May 18, 2022

This is the Arduino code that works. It is the example from Bolder Flight Systems:

#include "mpu9250.h"

/* Mpu9250 object */
bfs::Mpu9250 imu;

void setup() {
  /* Serial to display data */
  Serial.begin(115200);
  while(!Serial) {}
  /* Start the I2C bus */
  Wire.begin();
  Wire.setClock(400000);
  /* I2C bus,  0x68 address */
  imu.Config(&Wire, bfs::Mpu9250::I2C_ADDR_PRIM);
  /* Initialize and configure IMU */
  if (!imu.Begin()) {
    Serial.println("Error initializing communication with IMU");
    while(1) {}
  }
  /* Set the sample rate divider */
  if (!imu.ConfigSrd(19)) {
    Serial.println("Error configured SRD");
    while(1) {}
  }
}

void loop() {
  /* Check if data read */
  if (imu.Read()) {
    Serial.print(imu.new_imu_data());
    Serial.print("\t");
    Serial.print(imu.new_mag_data());
    Serial.print("\t");
    Serial.print(imu.accel_x_mps2());
    Serial.print("\t");
    Serial.print(imu.accel_y_mps2());
    Serial.print("\t");
    Serial.print(imu.accel_z_mps2());
    Serial.print("\t");
    Serial.print(imu.gyro_x_radps());
    Serial.print("\t");
    Serial.print(imu.gyro_y_radps());
    Serial.print("\t");
    Serial.print(imu.gyro_z_radps());
    Serial.print("\t");
    Serial.print(imu.mag_x_ut());
    Serial.print("\t");
    Serial.print(imu.mag_y_ut());
    Serial.print("\t");
    Serial.print(imu.mag_z_ut());
    Serial.print("\t");
    Serial.print(imu.die_temp_c());
    Serial.print("\n");
  }
}

@MabezDev
Copy link
Member

According to this article, the default sda and scl are pins 8 & 9. So the first question is, does it work if you use pins 8 & 9? Secondly, if it still doesn't work, and you are convinced it really is on pins 4 & 5, what happens if you set Wire.setPins(4, 5); in your arduino example?

@Gazedo
Copy link
Author

Gazedo commented May 19, 2022

I've verified via multimeter that the pins are 4=sda and 5=scl. I also checked the power input to the imu. Unfortunately I don't currently have a multimeter to watch the hardware signals but I may be able to borrow a cheap one. I forgot to modify the example again as I copied and pasted the generic example included with the library. In order to get the pasted example to work, I had to modify Wire.begin(); to be Wire.begin(4, 5);

Things I've tried:

  • MPU9250 library and passing in i2cdev along with delay from Ets
  • using the Write_Read trait
  • Switching to 4=scl and 5=sda
  • using 8 and 9 just to see
  • going back to 33.2 of esp_idf_hal. If you have known good version please let me know.

Things I'd like to confirm:

  • Does the esp32c3 naturally run at a sane clock rate or do I need to specify one to get i2c to work? This does really look like either a configuration issue or a clock speed issue.
  • Why use the command link interface for i2c rather than the simpler and more robust functions outlined in https://github.com/espressif/esp-idf/blob/master/examples/peripherals/i2c/i2c_simple/main/i2c_simple_main.c
  • Was there a version that the i2c is for sure working?
  • Is there a way to enable backtraces on the console? I'm pretty sure my program is failing on line 230 of i2c.rs but I would like that confirmed and I don't know how to pass in environmental variables to a mcu.
  • Should I be trying different versions of the esp-idf-sys crate? Maybe different rust versions?

@Dominaezzz
Copy link
Contributor

Command link is necessary for transactions. There's a strict contract that embedded hal requires for i2c, as to when to receive and send ACKs, which is easy to do with command link.

I currently use the i2c implementation to drive a touch screen so it definitely works.

You should just set clock yourself.

Have a look at the embedded hal i2c contract and see if your device cam comply. .i.e. does it respond with ACKs at the write time?

@Gazedo
Copy link
Author

Gazedo commented May 20, 2022

What versions of esp-idf-hal and esp-idf-sys are you using? According to the docs for the mpu9250 the device does an ack after each write.

This is the MPU 9250 Datasheet and I read the details of the i2c communication on page 34.

@Gazedo
Copy link
Author

Gazedo commented May 22, 2022

Figured it out, you can't do into_output or into_input_output on the pins before passing them into the Master struct. That is confusing and there are no warnings to prevent that.

@Gazedo Gazedo closed this as completed May 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants