I2C current consumption?

I'm making an ultra-low powered arduino vibration sensor. My sensor is the MPU6050 on a GY521 breakout board.

I've managed to get the MPU6050 into it's lowest power mode which is waking every 1.25Hz and reading the accel only. According to the datasheet this should be 10µA.

However when the arduino is reading data from the MPU, it draws 1.74mA! And even when it is not reading data it is drawing 0.14mA!

Does anyone know why it draws more power when the I2C bus is active?

My sketch is below. You can see how the arduino sleeps for 8 seconds every 7 seconds. During the sleep, the current consumption of the MPU is 0.14mA, and when not in sleep it is 1.74mA.

My current meter is between the MPU VCC pin and the 3.3V Pro mini pin.

This is the serial monitor output showing that the MPU is on low current mode:

Enter AT commands:
Initializing I2C devices...
Testing device connections...
MPU6050 connection successful
3a/g: -756 -68 15100 0 0 0
a/g: -804 -76 15012 0 0 0
a/g: -680 -32 15100 0 0 0
sleeping now
a/g: -780 -44 15024 0 0 0
a/g: -712 32 15248 0 0 0
a/g: -756 -28 15092 0 0 0
a/g: -796 -60 15032 0 0 0
sleeping now
// I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class
// 10/7/2011 by Jeff Rowberg <jeff@rowberg.net>
// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib
//
// Changelog:
//      2013-05-08 - added multiple output formats
//                 - added seamless Fastwire support
//      2011-10-07 - initial release


// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
// for both classes must be in the include path of your project
#include "I2Cdev.h"
#include "MPU6050.h"
#include "LowPower.h"

#include <SoftwareSerial.h>

SoftwareSerial BTSerial(12, 11); // RX | TX

// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 accelgyro;
//MPU6050 accelgyro(0x69); // <-- use for AD0 high

int16_t ax, ay, az;
int16_t gx, gy, gz;

long sendcurrmillis;
long sendprevmillis;
int sendinterval = 7000;

long dispcurrmillis;
long dispprevmillis;
int dispinterval = 2000;

uint8_t WakeFreq;



// uncomment "OUTPUT_READABLE_ACCELGYRO" if you want to see a tab-separated
// list of the accel X/Y/Z and then gyro X/Y/Z values in decimal. Easy to read,
// not so easy to parse, and slow(er) over UART.
#define OUTPUT_READABLE_ACCELGYRO

// uncomment "OUTPUT_BINARY_ACCELGYRO" to send all 6 axes of data as 16-bit
// binary, one right after the other. This is very fast (as fast as possible
// without compression or data loss), and easy to parse, but impossible to read
// for a human.
//#define OUTPUT_BINARY_ACCELGYRO


#define LED_PIN 13
bool blinkState = false;



void setup() {
    // join I2C bus (I2Cdev library doesn't do this automatically)
    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
        Wire.begin();
    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
        Fastwire::setup(400, true);
    #endif

    // initialize serial communication
    // (38400 chosen because it works as well at 8MHz as it does at 16MHz, but
    // it's really up to you depending on your project)
    Serial.begin(9600);
    Serial.println("Enter AT commands:");
    BTSerial.begin(9600);  // HC-05 default speed 

    // initialize device
    Serial.println("Initializing I2C devices...");
    accelgyro.initialize();

    // verify connection
    Serial.println("Testing device connections...");
    Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");

    // use the code below to change accel/gyro offset values
    /*
    Serial.println("Updating internal sensor offsets...");
    // -76 -2359 1688 0 0 0
    Serial.print(accelgyro.getXAccelOffset()); Serial.print("\t"); // -76
    Serial.print(accelgyro.getYAccelOffset()); Serial.print("\t"); // -2359
    Serial.print(accelgyro.getZAccelOffset()); Serial.print("\t"); // 1688
    Serial.print(accelgyro.getXGyroOffset()); Serial.print("\t"); // 0
    Serial.print(accelgyro.getYGyroOffset()); Serial.print("\t"); // 0
    Serial.print(accelgyro.getZGyroOffset()); Serial.print("\t"); // 0
    Serial.print("\n");
    accelgyro.setXGyroOffset(220);
    accelgyro.setYGyroOffset(76);
    accelgyro.setZGyroOffset(-85);
    Serial.print(accelgyro.getXAccelOffset()); Serial.print("\t"); // -76
    Serial.print(accelgyro.getYAccelOffset()); Serial.print("\t"); // -2359
    Serial.print(accelgyro.getZAccelOffset()); Serial.print("\t"); // 1688
    Serial.print(accelgyro.getXGyroOffset()); Serial.print("\t"); // 0
    Serial.print(accelgyro.getYGyroOffset()); Serial.print("\t"); // 0
    Serial.print(accelgyro.getZGyroOffset()); Serial.print("\t"); // 0
    Serial.print("\n");
    */

    // configure Arduino LED for
    pinMode(LED_PIN, OUTPUT);

     accelgyro.setWakeFrequency(3); //this sets the wakeup frequency of the accel. 0 = 1.25 HZ, 1 =  2.5hz, 2 = 5hz, 3 = 10hz
     accelgyro.setWakeCycleEnabled(1);    //this puts the mpu6050 into the wake cycle mode, where it sleeps, and then wakes, reads accel (maybe more) and puts in i2c buffer to be read.
     WakeFreq = accelgyro.getWakeFrequency(); //this returns the wakeup frequency that the accel is set to. 0 = 12
     Serial.print(WakeFreq);


BTSerial.write("AT+SLEEP"); 
}


void loop() {
    // read raw accel/gyro measurements from device
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

    sendcurrmillis = millis();
if(sendcurrmillis - sendprevmillis > sendinterval)
  {
    entersleep();
   sendprevmillis = millis();
  }

 dispcurrmillis = millis();
if(dispcurrmillis - dispprevmillis > dispinterval)
  {
   
        Serial.print("a/g:\t");
        Serial.print(ax); Serial.print("\t");
        Serial.print(ay); Serial.print("\t");
        Serial.print(az); Serial.print("\t");
        Serial.print(gx); Serial.print("\t");
        Serial.print(gy); Serial.print("\t");
        Serial.println(gz);
    dispprevmillis = millis();
  }
    // these methods (and a few others) are also available
    //accelgyro.getAcceleration(&ax, &ay, &az);
    //accelgyro.getRotation(&gx, &gy, &gz);



}

void entersleep() {

   Serial.println("sleeping now");
   Serial.flush();
   LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
   //delay(2000);
  
}

void BTthroughput()
{

  // Keep reading from HC-05 and send to Arduino Serial Monitor
  if (BTSerial.available())
    Serial.write(BTSerial.read());

  // Keep reading from Arduino Serial Monitor and send to HC-05
  if (Serial.available())
    BTSerial.write(Serial.read());
}

void BTsendrecv()
{

  // Keep reading from HC-05 and send to Arduino Serial Monitor
  if (BTSerial.available())
    Serial.write(BTSerial.read());

  // Keep reading from Arduino Serial Monitor and send to HC-05
  if (Serial.available())
    BTSerial.write(Serial.read());
}

Why is the I2C data transfer using so much power? Does anyone know?

Here's my wiring diagram:

I've been thinking, and the 4.7Kohm pull up resistors on the I2C lines....on a 3.3v supply they would pull 0.7mA each when the bus is low, so 2 of them would make 1.4mA.....

Is that what's going on?

Surely someone can help steer me in the right direction?

I've found a blog where the total current consumption for his entire smart watch is 120uA, and that has a MPU6050 on it! https://bengoncalves.net/2015/10/02/arduino-power-down-mode-with-accelerometer-compass-and-pressure-sensor/

Why is my i2c interface being such a power hog?

So after a lot of searching, I think I've found that the high power consumption happens when the bus is being held low for a long time during communication.

Can anyone help me make sure that my i2c comms link is efficient and doesn't just hold the bus in the low state?

At this rate I'm gonna have to create a new post but leave my code out of it just to get people interested......

I2C libraries, AFAIK, enable the internal pull up resistors. That might be enough pull up current.

The extra 4k7 pull up resistors might not be needed if both I2C devices are very close to each other.
Leo..

Wawa:
I2C libraries, AFAIK, enable the internal pull up resistors. That might be enough pull up current.

The extra 4k7 pull up resistors might not be needed if both I2C devices are very close to each other.
Leo..

The wire length is 40mm, so I'm guessing I'll be okay to remove them. Although I've just read that the internal pullup is 50K, is that small enough?

And is it also possible to control when the bus is low? That way it doesn't matter what the configuration is, and no power will be wasted.

With the internal and external pull ups in parallel you have around 5K. With just the internal you will have 50k. That should be a reduction in current of about 90%....So try to temporarily take the on board resistors out of the circuit (cut the track cleanly with a scapel or hobby knife - then if it doesn't work a small blob of solder can bridge the cut to connect them back again).

Or connect the pullup resistors to a spare pin. Drive that pin HIGH when the bus is active and then let it float or go LOW when you're not reading I2C.

Be aware this may change the address of the chip. Some "clever" I2C chips will look at the state of SDA and SCL on power-up and change their addresses. I don't think the MPU6050 is one of those, fortunately.

I don't think there is anything much you can do about the time that the bus is low. If you look at the I2C lines on an oscilloscope, you won't see lots of dead time before and after the data bytes. It is really very efficient from that point of view.

If the internal pullups are ineffective, try adding just a little conductance - maybe 20K resistors? I would expect that the internal pullups would be quite adequate for 40mm of wire.

4k7 should be enough for two meters of (Cat-6) wire.
I doubt that the internal pull-up resistors have any problem with 40mm of wire.
Leo..

MorganS:
I don't think there is anything much you can do about the time that the bus is low. If you look at the I2C lines on an oscilloscope, you won't see lots of dead time before and after the data bytes. It is really very efficient from that point of view.

Are you (or anybody else of course) sure that there's nothing else I can be doing to reduce the total average power consumption?. In my sketch aren't I constantly using the bus for several seconds even though it may not be transmitting any data? Which means that both lines are being held low?

I've also seen Wire.endTransmission(); used a lot in really low power implementations of this MPU6050. Is it used to completely shut down the bus and stop power wastage?

I2C lines are normally HIGH (= no current throught the pull-up resistors), and only LOW during data transfer.
I don't know what the program does.
Leo..

Wire.endTransmission() is the function which actually transmits the data over the wire. Wire.write() just adds data to the buffer waiting to transmit.

dave_sausages:
Are you (or anybody else of course) sure that there's nothing else I can be doing to reduce the total average power consumption?.

Did you even try what I suggested in post #7 ???

If that does not work (it should) then you do have the option of lowering the clock frequency and voltage to the arduino. But this is not for beginners and will be tricky to set up.