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 reading gets stuck sometimes #28

Closed
hapiel opened this issue Jan 28, 2023 · 20 comments
Closed

i2c reading gets stuck sometimes #28

hapiel opened this issue Jan 28, 2023 · 20 comments
Assignees
Labels
question Further information is requested

Comments

@hapiel
Copy link

hapiel commented Jan 28, 2023

Hi, thanks so much for this library!

As you mentioned "Please share your experiences.", I figured I post here about an issue I'm having.

Every now and then (about once every half minute on average) the reading code blocks and stops for 1002ms.
I put this in my loop:

long readAngleTime = millis();

  rEncoderRaw = rAs5600.readAngle();

  if (millis() - readAngleTime > 1){
    Serial.print("angle time: ");
    Serial.println(millis() - readAngleTime);
  }

And it outputs 1002 every now and then.

This is especially problematic as my sensor is on a shaft that spins up to about 3800rpm, and multiple revolutions may pass in this 1 second so I have lost track of the amount of full revolutions it has made.

I am using the sensor on a wiper motor, from this tutorial: https://hackaday.io/project/187675-strong-servo-motor-with-a-wiper-motor
And the code runs on an ESP32.

I am clueless why readAngle() times out, nor do I have any idea on how to reduce the timeout time.

I will attempt to solve this issue by switching to pwm output, as suggested to me on the Arduino discord, but perhaps someone can look into this to help others in the future!

Thanks

@RobTillaart
Copy link
Owner

Thanks for the issue, I will look into it asap.

@RobTillaart RobTillaart added the question Further information is requested label Jan 28, 2023
@RobTillaart RobTillaart self-assigned this Jan 28, 2023
@RobTillaart
Copy link
Owner

Hi, thanks so much for this library!

You're welcome!

The ESP32 might need to attend some background processes (wifi, BLE or timers?) and blocks your code to process these.
Have seen reports of similar "drops" on esp32 / esp8266.

You might try if adding yield() solves this. In short this gives explicit control to pending background processes.

Something like this

void loop()
{
  yield();  // handle background processes   

  uint32_t start = micros();
  rEncoderRaw = rAs5600.readAngle();
  uint32_t duration = micros() - start;
  if (duration > 1000)
  {
    Serial.print("angle time: ");
    Serial.println(duration);
  }
  
  // process measurement
}

If this extra yield() does not solve the issue you might need to dive into RTOS.
Run the RPM measurement explicit on one core and your other code on the other core.


Another option is to have a logic analyzer on the I2C bus to measure the time between I2C request and response.

If the delay is in the sensor you should see a relative long time between request and answer.
If the delay is in the processor, the sensor responses always in more or less constant time.

@RobTillaart
Copy link
Owner

(math note)

This is especially problematic as my sensor is on a shaft that spins up to about 3800rpm

3800 RPM is less than 70 rounds per second ==> 1 rotation per 14 milliseconds.
This implies that the code should make a sample every 3-4 milliseconds to keep track.
This means that the other code in the project should not block for that long.

@hapiel
Copy link
Author

hapiel commented Jan 29, 2023

Thanks for your response!

Sadly, adding the yield time did not solve the problem, but thanks to your updated code I now do have the time in μs: 1001274 to execute the readAngle()...

Also, you are right to note that I need a sample very often, I am hoping to find a mechanical solution for this by putting the encoder on another shaft, but I'm not sure yet if this is possible in my project.

I wish I owned a logic analyser, would love to see what is going on! For now I'm first going to try if the pwm output signal solves my issues...

@RobTillaart
Copy link
Owner

I wish I owned a logic analyser, would love to see what is going on! For now I'm first going to try if the pwm output signal solves my issues...

After a multimeter it is maybe the second most important tool to analyze communication between processor and devices.
I have an 8 channel from - https://www.saleae.com/ - which works very well, and worth the money imho.

or do a project like these - https://www.instructables.com/Arduinolyzerjs-Turn-your-Arduino-into-a-Logic-Anal/

Newer oscilloscopes often have logic analyzers too, but that is another price range.

@RobTillaart
Copy link
Owner

mmm back to the original problem

The readAngle() call blocks for a full second, the extra decimals do not provide more insight (yet).

Can you adjust your code to

void loop()
{
  uint32_t start = micros();
  rEncoderRaw = rAs5600.readAngle();
  uint32_t duration = micros() - start;
  if (duration > 1000)
  {
    Serial.print(millis());
    Serial.println("\t");
    Serial.println(duration);
  }
  
  // process measurement
}

And post a log, What I hope to see is that the blocking call happens e.g. every X seconds.

@hapiel
Copy link
Author

hapiel commented Jan 29, 2023

I am more confused than before. Here is a log, but I think it's irrelevant, I'll explain below:

16279   1001122
23242   1000684
26813   1410
33510   1000710
48505   1001184
65731   1000410
153021  1000945
189548  1000528
193071  1000790
196193  1000351
199766  1000493
203016  1416
204056  1000853
208366  1000392
211128  1000907
212317  1000818
212474  1202
213828  1001121
213932  1055
214982  1001113
215366  1185
216670  1001095
217800  1001030
218824  1000826
232330  1261
234131  1000746
274990  1000504

To clarify some more about my setup: I am outputting two pwm signals to a motor controller, and controlling the motor speed with a remote that connects over ESP-NOW.

I only realize (or was it perhaps this test session only?) that the glitch only happens in one of the two directions of the motor, when the sensor spins clockwise. This suggests to me that there is something different going on altogether. Despite using yield() and measuring the duration outside of that, might there be something else going on?

In the end of the test, the ESP froze up completely. The motor kept spinning, no more output came to the serial (I was outputting other stuff too at regular intervals, which I removed from the log above)... I let it run for a minute and then shut off the power.

So this of course convinced me that there must be something else really odd with my code, and that the tests I had done earlier must have been misleading, and that I should write a more minimal version of my script for testing...

But then I figured I could replace my pwm signals with some physical switches, and the same thing happened! When I sent pwm signals to the now disconnected pins, the motor reading would not get stuck. When the motor was spinning clockwise (not controlled by the ESP), there was a high likelihood that the reading got stuck. So it's the actual physical spinning of the magnet that causes this issue somehow? Is it not designed to rotate that fast?

I am so confused now.

@RobTillaart
Copy link
Owner

Its getting late here, so a short response for now.
A quick look at the log showed no pattern, although the threshold for "reporting" should have been a little higher.

The primary function of the sensor is to determine the orientation. Derived from that comes angular velocity.

@RobTillaart
Copy link
Owner

I will need to check the datasheet if it mentions measuring rpm. (Coming week)

@RobTillaart
Copy link
Owner

Think I found a possible cause here
espressif/arduino-esp32#5934

If this is the cause you should check the connection

  • wires
  • soldering
  • pull up resistors
    Vibrations can (and will) disturb loose wires.

@RobTillaart
Copy link
Owner

@RobTillaart
Copy link
Owner

RobTillaart commented Jan 30, 2023

What might be part of a solution is add an error check in readReg

uint8_t AS5600::readReg(uint8_t reg)
{
  _wire->beginTransmission(_address);
  _wire->write(reg);
  _error = _wire->endTransmission();

  If (_error != 0) return 0;    <<<<<<<<<

  _wire->requestFrom(_address, (uint8_t)1);
  uint8_t _data = _wire->read();
  return _data;
}

Dont know if it catch errors before the timeout sets in.
Can you test?

@RobTillaart
Copy link
Owner

RobTillaart commented Jan 30, 2023

idem in

uint16_t AS5600::readReg2(uint8_t reg)
{
  _wire->beginTransmission(_address);
  _wire->write(reg);
  _error = _wire->endTransmission();

   If (_error != 0) return 0;.    <<<<<<<<<

  _wire->requestFrom(_address, (uint8_t)2);
  uint16_t _data = _wire->read();
  _data <<= 8;
  _data += _wire->read();
  return _data;
}

in case of error the value 0 is consistent over both readReg() and readReg2()

@RobTillaart
Copy link
Owner

furthermore one need patching of the functions calling readReg() to verify if a 0 return indicates error or not.
But that only makes sense if the timeout is catched by this extra code.

@RobTillaart
Copy link
Owner

Note: Recalled this one - RobTillaart/PCF8575#20

@RobTillaart
Copy link
Owner

A check with isConnected() might also be a workaround. (I have no test setup to verify)

void loop()
{
  if (rAs5600.isConnected() == false)
  {
    Serial.println("Connection problem!");
  }
  else
  {
    uint32_t start = micros();
    rEncoderRaw = rAs5600.readAngle();
    uint32_t duration = micros() - start;
    if (duration > 1500)
    {
      Serial.print(millis());
      Serial.println("\t");
      Serial.println(duration);
    }
    // process measurement
  }
}

@hapiel
Copy link
Author

hapiel commented Jan 30, 2023

Thanks so much for diving into this so thoroughly! This is incredibly helpful.

You found the issue! Of course, my breadboard setup with the vibrating motor, that makes so much sense that the connection fails every now and then at certain motor frequencies.

Sadly, we don't have a solution yet. I mean, I will have a solution for my project, which is that in the end all will be wired up properly (and I might still use PWM instead of i2c just to be safe). However, the two proposed solutions did sadly not work.

I modified the library as you recommended, and I can report that when the esp times out it still returns data, not a 0.
Also, the isConnected() check works perfectly when I pull the wire out all the way, but when I just wiggle the wire I am still able to get the effect where the system times out for exactly 1000ms.

@hapiel
Copy link
Author

hapiel commented Jan 30, 2023

When it does catch an error and returns 0 (which it also does sometimes even when it checked for isConnected() just before) the readAngle() function always takes about 13700us. Which is much more acceptable than the 1000ms of the other issue.

@RobTillaart
Copy link
Owner

Thanks for doing the tests.

So conclusion is that the problem is (1) outside the library scope, and (2) the library cannot fix it as far as we know.
Think that implies this issue can be closed.

(although I am still interested when a working solution pops up)

@hapiel
Copy link
Author

hapiel commented Jan 31, 2023

I agree! Thanks so much for your time, and I'll let you know if I learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants