ESP8266 I2C problem

So all, on to something new.
I got my hands on a node MCU V3. Managed to make it connect to a WIFI network etc. Wonderful. Then I was trying to interface one of my I2C sensors (3V3 device) with my new gadget. It communicates with the device since I get data, but the data is way off and not comparable with the values I get on my Arduino UNO. I have tried the Wire.setClockStretchLimit(40000); trick I read here on the forum. But it does not do anything. Does anyone have experience with this? I am using the basic Wire.h library to interface with the sensor.

Thanks in advance!

Full code, a schematic of how the sensor is connected and details of the sensor please

/*
 * I2C example sketch for ESP8266
 */


#include <Wire.h>


const int DevID = 0x68;
const int PWR_MGT1_REG = 0x6B;

const int DataReg_AX = 0x3B;
const int DataReg_AY = 0x3D;
const int DataReg_AZ = 0x3F;
const int AccConfigReg = 0x1C;
const int GyrConfigReg = 0x1B;
const int SelfTestReg = 0x0D;

const int TempReg = 0x41;

const int DataReg_GX = 0x43;
const int DataReg_GY = 0x45;
const int DataReg_GZ = 0x47;

const int CalibrationConfigAcc = 0xF0;//set this value for calibration
const int CalibrationConfigGyr = 0xE0;//set this value for calibration
const int NormalConfigAcc = 0x00;//set this value for calibration 2G
const int NormalConfigGyr = 0x00;//set this value for calibration 250dps

/*
  int16_t Acc_X;
  int16_t Acc_Y;
  int16_t Acc_Z;
*/
float Acc_X, accAngleX, accAngleY, Yaw_Angle;
float Acc_Y;
float Acc_Z;

int8_t X_selfTest;
int8_t Y_selfTest;
int8_t Z_selfTest;
int8_t Acc_X_Y_Z_selfTest;

int16_t Temp;
/*
  int16_t Gyro_X;
  int16_t Gyro_Y;
  int16_t Gyro_Z;
*/
float Gyro_X;
float Gyro_Y;
float Gyro_Z;

int16_t Gyro_X_selfTest;
int16_t Gyro_Y_selfTest;
int16_t Gyro_Z_selfTest;

unsigned long elapsedTime, previousTime, currentTime;

void setup() {// put your setup code here, to run once:
  Serial.begin(115200);
  Wire.begin ();
  //Wire.setClockStretchLimit(40000);
  Wire.beginTransmission(DevID);
  Wire.write(PWR_MGT1_REG);
  Wire.write(0x0);// wake up the sensor
  Wire.endTransmission();
  CalibrationProcedure();// enter calibration procedure
}

void loop() {// put your main code here, to run repeatedly:

previousTime = currentTime;
currentTime = millis();
 elapsedTime = (currentTime - previousTime) / 1000;


  Wire.beginTransmission(DevID);
  Wire.write(DataReg_AX);
  Wire.endTransmission();

  Wire.requestFrom(DevID, 14);
  if (Wire.available() <= 14) {
    Acc_X = ((Wire.read() << 8) | (Wire.read())) / 16384.0;// divide by 16384 for 2G setting according to datasheet
    Acc_Y = ((Wire.read() << 8) | (Wire.read())) / 16384.0;
    Acc_Z = ((Wire.read() << 8) | (Wire.read())) / 16384.0;
    Temp = (Wire.read() << 8) | (Wire.read());
    Gyro_X = ((Wire.read() << 8) | (Wire.read()))  / 131.0; // divide by 131.0 according to datasheet for setting 250 dps
    Gyro_Y = ((Wire.read() << 8) | (Wire.read())) / 131.0;
    Gyro_Z = ((Wire.read() << 8) | (Wire.read())) / 131.0;
    delay(500);
  }

  //The formulas below have been extracted from another program and uses mathematical
  //values for calulation of pitch roll and yaw. For the moment only pitch is calibrated
  //The next purpose of this program is follow a calibration procedure to take approx 200 samples
  //and average the total value to correct the pitch roll and yaw value
  accAngleX = (atan(Acc_Y / sqrt(pow(Acc_X, 2) + pow(Acc_Z, 2))) * 180 / PI) + 0.32;
  accAngleY = (atan(-1 * Acc_X / sqrt(pow(Acc_Y, 2) + pow(Acc_Z, 2))) * 180 / PI);
  Yaw_Angle = Yaw_Angle + Gyro_Z * elapsedTime; 
  
  float Gyro_X_axis = (float)Gyro_X / 131.0;
  float Gyro_Y_axis = (float)Gyro_Y / 131.0;
  float Gyro_Z_axis = (float)Gyro_Z / 131.0;
  float Temperature = (Temp / (340.0)) + 36.53;
  Serial.println ("Angle X");
  Serial.println(accAngleX);
  Serial.println ("Angle Y");
  Serial.println (accAngleY);
  Serial.println("Yaw Angle");
  Serial.println(Yaw_Angle);
  delay(500);
}

void CalibrationProcedure() {
  Serial.println("Keep sensor steady until calibration process is complete!");
  Wire.beginTransmission(DevID);
  Wire.write(AccConfigReg);//8 g
  Wire.write(CalibrationConfigAcc);
  Wire.endTransmission();
  delay(250);
  Wire.beginTransmission(DevID);
  Wire.write(GyrConfigReg);
  Wire.write(CalibrationConfigGyr);//250 deg/s
  Wire.endTransmission();
  delay(500);
  Serial.println("Selftest enabled, reading data...");

  Wire.beginTransmission(DevID);
  Wire.write(SelfTestReg);
  Wire.endTransmission();

  Wire.requestFrom(DevID, 4);
  if (Wire.available() <= 4) {
    X_selfTest = Wire.read();
    Y_selfTest = Wire.read();
    Z_selfTest =  Wire.read();
    Acc_X_Y_Z_selfTest = Wire.read();
    delay(500);
  }

  //extracting factory trim values after self test
  //unsigned integers for accelero variables
  uint8_t XA_selfTest;
  uint8_t YA_selfTest;
  uint8_t ZA_selfTest;
  //unsigned integers for gyro variables
  uint8_t GX_selfTest;
  uint8_t GY_selfTest;
  uint8_t GZ_selfTest;
  //Factory Trim variables
  float FTGX;
  float FTGY;
  float FTGZ;
  float FTAX;
  float FTAY;
  float FTAZ;

  float SelfTestPercentage_GX;
  float SelfTestPercentage_GY;
  float SelfTestPercentage_GZ;
  float SelfTestPercentage_AX;
  float SelfTestPercentage_AY;
  float SelfTestPercentage_AZ;

  GX_selfTest = X_selfTest & 0x1F; // 0x1F is a bit mask to extract the first 5 bits. unsigned format
  GY_selfTest = Y_selfTest & 0x1F;
  GZ_selfTest = Z_selfTest & 0x1F;
  XA_selfTest = (X_selfTest >> 3) | (Acc_X_Y_Z_selfTest & 0x30) >> 4; //shift the first 3 bits and extract bits with a mask depending on the LSB postion in the last received mixed byte
  YA_selfTest = (Y_selfTest >> 3) | (Acc_X_Y_Z_selfTest & 0x0C) >> 2;
  ZA_selfTest = (Z_selfTest >> 3) | (Acc_X_Y_Z_selfTest & 0x03);

  Serial.println ("XA_selfTest");
  Serial.println (X_selfTest, HEX);
  Serial.println("YA_selfTest");
  Serial.println(Y_selfTest, HEX);
  Serial.println("ZA_selfTest");
  Serial.println(Z_selfTest, HEX);
  Serial.println("Acc_X_Y_Z_selfTest");
  Serial.println(Acc_X_Y_Z_selfTest, HEX);

  //calculate the factory trim settings
  FTGX = (25.0 * 131.0) * (pow(1.046, ((float)GX_selfTest - 1.0)));// Factortrim Gyro X
  FTGY = (-25.0 * 131.0) * (pow(1.046, ((float)GY_selfTest - 1.0)));// Factortrim Gyro Y
  FTGZ = (25.0 * 131.0) * (pow(1.046, ((float)GZ_selfTest - 1.0)));// Factortrim Gyro Z
  FTAX = (4096.0 * 0.34) * (pow((0.92 / 0.34), (((float)XA_selfTest - 1.0) / 30.0)));
  FTAY = (4096.0 * 0.34) * (pow((0.92 / 0.34), (((float)YA_selfTest - 1.0) / 30.0)));
  FTAZ = (4096.0 * 0.34) * (pow((0.92 / 0.34), (((float)ZA_selfTest - 1.0) / 30.0)));

  // get data outputs with self test enabled
  Wire.beginTransmission(DevID);
  Wire.write(DataReg_AX);
  Wire.endTransmission();

  Wire.requestFrom(DevID, 14);
  if (Wire.available() <= 14) {
    Acc_X = (Wire.read() << 8) | (Wire.read());
    Acc_Y = (Wire.read() << 8) | (Wire.read());
    Acc_Z = (Wire.read() << 8) | (Wire.read());
    Temp = (Wire.read() << 8) | (Wire.read());
    Gyro_X = (Wire.read() << 8) | (Wire.read());
    Gyro_Y = (Wire.read() << 8) | (Wire.read());
    Gyro_Z = (Wire.read() << 8) | (Wire.read());
    delay(500);
  }
  delay(500);

  //setup the device for normal OPS
  Wire.beginTransmission(DevID);
  Wire.write(AccConfigReg);
  Wire.write(NormalConfigAcc);
  Wire.endTransmission();
  delay(50);
  Wire.beginTransmission(DevID);
  Wire.write(GyrConfigReg);
  Wire.write(NormalConfigGyr);
  Wire.endTransmission();
  delay(50);

  //request data with self test not enabled
  Wire.beginTransmission(DevID);
  Wire.write(DataReg_AX);
  Wire.endTransmission();

  Wire.requestFrom(DevID, 14);
  if (Wire.available() <= 14) {
    Acc_X = (Wire.read() << 8) | (Wire.read());
    Acc_Y = (Wire.read() << 8) | (Wire.read());
    Acc_Z = (Wire.read() << 8) | (Wire.read());
    Temp = (Wire.read() << 8) | (Wire.read());
    Gyro_X = (Wire.read() << 8) | (Wire.read());
    Gyro_Y = (Wire.read() << 8) | (Wire.read());
    Gyro_Z = (Wire.read() << 8) | (Wire.read());
    delay(5000);
  }

}

The code is quite raw still. I am using an MPU6050 on a GY 521 breakout board. The board has pull up resistors on board and a voltage regulator for 5V VCC. The thing is that the board does not have level shifters on board which were needed for the UNO but the Node MCU is 3.3V.
What happens is this. With the code above on the UNO I get a steady X axis value of +/- 0 degrees. When I check out the same situation with the Node MCU the values for X axis are 45 degrees and then jumping back to 0. Basically the go all the way around.
The schematic attached is not the greatest but I hope its clear.

Your schematic is exactly what we like to see. This is far more clear than any Fritzing pretty picture.

The SCL and SDA lines need a 4k7 pullup on each. Else you will get random erratic data.

https://learn.sparkfun.com/tutorials/i2c/i2c-at-the-hardware-level

The following sketch works well for UNO; but, it gives constant (222.79) value for the temperature reading in ESP9266/NodeMCU regardless of MPU6050's Vcc at 5V or 3.3V. The ESP does not recognize the sensor.

#include<Wire.h>
#define MPUaddr 0x68  //keyed in Li AD0 is open

void setup() 
{
  Serial.begin(9600);
  Wire.begin(5, 16);//SDA = D1, SCL = D0
  Wire.beginTransmission(MPUaddr);                                                //Set the register bits as 00000000 to activate the gyro.
  byte busStatus = Wire.endTransmission();
  if(busStatus !=0)
  {
    Serial.print("Sensor not found.");
    while(1);    
  }
}

void loop()
{
  Wire.beginTransmission(MPUaddr);                        //Start communication with the MPU-6050.
  Wire.write(0x6B);                                            //We want to write to the PWR_MGMT_1 register (6B hex).
  Wire.write(0x00);   //enable temp sensor                                           //Set the register bits as 00000000 to activate the gyro.
  Wire.endTransmission();      

  //----pointing temp sensor-----------------
  Wire.beginTransmission(MPUaddr);                        //Start communication with the MPU-6050.
  Wire.write(0x41);      //pointing Temp_Out_High Reg                                           //Set the register bits as 00000000 to activate the gyro.
  Wire.endTransmission(); 
  
  Wire.requestFrom(MPUaddr, 2); //two-byte temp data                                  //End the transmission with the gyro.
  byte x1 = Wire.read();
  byte x2 = Wire.read();
  int x = (int)(x1<<8)|(int)x2;
  
  //------compute temp from x-----
  float mpuTemp = (float)(x/340.0 + 36.53);  //formula from data sheets
  Serial.print("Temp = ");
  Serial.print(mpuTemp, 2);  //2-digit after decimal point
  Serial.println(" degC");
  delay(1000);
}

Hi Steve,

Thanks for your reply. The pull ups were not drawn on the schematic since they are already on the breakout board. I measured 2k2 which to me seems a normal value for a 3V3 dataline?

Hi

GolamMostafa:
The following sketch works well for UNO; but, it gives constant (222.79) value for the temperature reading in ESP9266/NodeMCU regardless of MPU6050's Vcc at 5V or 3.3V. The ESP does not recognize the sensor.

#include<Wire.h>

#define MPUaddr 0x68  //keyed in Li AD0 is open

void setup()
{
  Serial.begin(9600);
  Wire.begin(5, 16);//SDA = D1, SCL = D0
  Wire.beginTransmission(MPUaddr);                                                //Set the register bits as 00000000 to activate the gyro.
  byte busStatus = Wire.endTransmission();
  if(busStatus !=0)
  {
    Serial.print("Sensor not found.");
    while(1);   
  }
}

void loop()
{
  Wire.beginTransmission(MPUaddr);                        //Start communication with the MPU-6050.
  Wire.write(0x6B);                                            //We want to write to the PWR_MGMT_1 register (6B hex).
  Wire.write(0x00);  //enable temp sensor                                          //Set the register bits as 00000000 to activate the gyro.
  Wire.endTransmission();

//----pointing temp sensor-----------------
  Wire.beginTransmission(MPUaddr);                        //Start communication with the MPU-6050.
  Wire.write(0x41);      //pointing Temp_Out_High Reg                                          //Set the register bits as 00000000 to activate the gyro.
  Wire.endTransmission();
 
  Wire.requestFrom(MPUaddr, 2); //two-byte temp data                                  //End the transmission with the gyro.
  byte x1 = Wire.read();
  byte x2 = Wire.read();
  int x = (int)(x1<<8)|(int)x2;
 
  //------compute temp from x-----
  float mpuTemp = (float)(x/340.0 + 36.53);  //formula from data sheets
  Serial.print("Temp = ");
  Serial.print(mpuTemp, 2);  //2-digit after decimal point
  Serial.println(" degC");
  delay(1000);
}

The code I posted works for the UNO. A 0 reading for a level sensor is correct. The problem is that the same code I posted does not work on my Node MCU. Because the output I get from the sensor jumps around. While this is not the case on my UNO...

I think my problem is not very clearly described...I was a bit tired yesterday. So when I upload the code posted above I seem to have valid readings from the MPU6050 breakout board. When the sensor is flat I have 0 degrees. When I put the same code in my Node MCU V3 I get outputs, but when the sensor is flat, sometimes it reads 45 degrees and sometimes 0. I have seen people with the same issue, but I did not find the solution for this...I am breaking my head over this one. Anyone that can help me?

How reliable is the communication with the sensor ?

Run a I2C Scanner and let it run for a while. It should be 100% okay.

In your sketch, these lines "if (Wire.available() <= 14) {" after a Wire.requestFrom() do nothing. The return value of Wire.available() is always 14 or lower, even if the sensor was not connected at all.

To check if you got data from the sensor, you can check with "if (Wire.available() == 14)", but you also should know when something was wrong:

Wire.requestFrom(DevID, 14);
if (Wire.available() != 14) 
{
  Serial.println("I2C bus error");
}
else
{
  Acc_X = ((Wire.read() << 8) | (Wire.read())) / 16384.0;// divide by 16384 for 2G setting according to datasheet
  Acc_Y = ((Wire.read() << 8) | (Wire.read())) / 16384.0;
  ...
}

You should do that every time the Wire.requestFrom() is called when your I2C bus is not reliable.

Thanks for your reply. I connected another sensor. And I try to run the standard I2C scanner from the Arduino examples. It gives me answer but very strange replies:

Scanning...
Unknown error at address 0x01
Unknown error at address 0x02
I2C device found at address 0x09 !
Unknown error at address 0x12
Unknown error at address 0x23
Unknown error at address 0x24
Unknown error at address 0x32
Unknown error at address 0x33
I2C device found at address 0x36 !
Unknown error at address 0x39
I2C device found at address 0x42 !
Unknown error at address 0x49
I2C device found at address 0x4F !
I2C device found at address 0x50 !
I2C device found at address 0x54 !
Unknown error at address 0x58
I2C device found at address 0x61 !
Unknown error at address 0x64
I2C device found at address 0x65 !
Unknown error at address 0x72

It seems a communication issue somehow. I do not have a scope at my disposal unfortunately. So I cannot do any debugging in that way.

Please post images of the actual wiring.

So a quick test with the I2C scanner and the GY 521 breakout (MPU6050). With this I do get a valid address back without any issue. Only the values from the sensor are not correct in comparision with the same code on my Uno. See the hardware setup attached.
I had to use the 5V from the UNO board since the GY521 has a voltage regulator, and I was afraid that if I supplied 3.3V the voltage drop would be too big to make the board work properly.
I will re-build my setup for the BMP388 breakout board.
Thanks for having a look at it...

Attached my other setup to check the I2C bus with the ESP8266 NodeMCU and the I2C scanner strange outputs. This sensor has a 5V voltage regulator with levelshifters on board. So I needed to levelshift the logic levels back to 3V3 to be able to communicate with the node MCU. I have tried different power supplies but all results in the same issue...

Koepel:
How reliable is the communication with the sensor ?

Run a I2C Scanner and let it run for a while. It should be 100% okay.

In your sketch, these lines "if (Wire.available() <= 14) {" after a Wire.requestFrom() do nothing. The return value of Wire.available() is always 14 or lower, even if the sensor was not connected at all.

To check if you got data from the sensor, you can check with "if (Wire.available() == 14)", but you also should know when something was wrong:

Wire.requestFrom(DevID, 14);

if (Wire.available() != 14)
{
  Serial.println("I2C bus error");
}
else
{
  Acc_X = ((Wire.read() << 8) | (Wire.read())) / 16384.0;// divide by 16384 for 2G setting according to datasheet
  Acc_Y = ((Wire.read() << 8) | (Wire.read())) / 16384.0;
  ...
}



You should do that every time the Wire.requestFrom() is called when your I2C bus is not reliable.

I changed my code and the value is still 45 degrees when it should read 0 degrees. I am overlooking something really stupid I am afraid...

I connected another sensor (QMC5883L). I2C scanner works but the same problem regarding data. What comes out does not make any sense. It works for a few seconds then it just returns garbage. Any other thoughts?

The voltage regulator is a low-drop voltage regulator. You can power it with 3.3V and it might drop to 3.2V for the sensor. That should be no problem.

Is the black wire connected to XCL ?
Can you remove it ? Just leave AD0 open.

Can you replace all the wires with other wires and put everything in an other location on the breadboard ? Sometimes a breadboard has bad contacts and sometimes a wire is broken.

When you tried the I2C Scanner sketch, did you change Wire.begin() to use the specific pins ?

Thank you for your reply and your help.
Attached a new setup with the 3V3 supply, new wires etc etc.
Also with AD0 removed basically no changes. X and Y axis output values do not make sense at all.

Update:
I managed to get valid data using this library:

According to me there must be a difference with the I2Cdev library and the way the wire.h library is implemented in the code.

I will investigate this further. Since more sensors have similar issues I guess it could be useful for other nodeMCU users.
Any tips or suggestions are more than welcome!

I didn't know that you were using the I2Cdev, I would have certainly mentioned that it has problems. This is just one of the many problems: Wrong use of Wire library · Issue #265 · jrowberg/i2cdevlib · GitHub.

The first goal is to have a I2C Scanner working, but I would like to see the I2C Scanner that you use.
Is that working now ?

Is your own sketch working ?

The ElectronicCats version of the MPU6050 is a fork of I2Cdev, and it has those problems as well. For the 'dmp' sketches, you need a interrupt signal.

I'm out of ideas. I hope you get better result with code that is know to work for a ESP8266.

Thanks a lot for your effort and reply! Really appreciated. I think I caused a misunderstanding. The code in my opening post is the code I have problems with because the data is invalid. (cq. reading 79 degrees instead of 0).
Using the I2C scanner from this link: Arduino Playground - I2cScanner
It can find the address and there are no issues regarding that.

Now I was trying a library from GitHub in the post above and that for a miraculous reason does give me valid data.(reading the amount of degrees properly) That implementation however, I do not really understand properly so I will not be able to use that for other sensors I want to connect to my NodeMCU.
IMHO my device should work properly with the code I wrote, since it does work properly on my Arduino Uno.

It seems a bit of an assumption, but as far as it looks like, the Wire.h library does not seem to be working for NodeMCU devices? Or there seems to be a timing issue with it...I will keep trying.