MEMSIC MMC5883MA-B interface using Arduino Uno and I2C

Hello all,

First of all, I am new user to arduino but I have already programmed on other developpment kit.
In this project, I would like to interface the magnetic sensor from MEMSIC the MMC5883MA-B.
This device looks very precise and easy to interface but I am stuck with arduino Wire librairy.

I looks like I cannot read from any register from the device.
I found the wire librairy a bit complicated to understand. I looked other several example on the forum and the iterface for the same sensor on gitHub ( https://github.com/pigtwn/Driver_MMC5883MA) but I cannot find a solution to my problem.

From the datasheet of the MMC5883MA-B (https://www.mouser.fr/datasheet/2/821/MEMSIC_03052018_MMC5883MA-B%20User%20Guide-1310797.pdf) the example on page 11 for example, It looks like I have to send a START condition then SDA line should be pulled low. Then I can program the Control Register and read x,y,z magnetic values.

My tests showed that I correctly created a START condition as I found a low value on the osciloscope on SDA line. But then I can't read any register, the value obtained is always 255, even on the DEVICE_ID register.

  #include <Wire.h>
  
  #define MMC5883MA_OUT 0x00
  #define MMC5883MA_XOUT 0x00
  #define MMC5883MA_XOUT_LOW 0x00
  #define MMC5883MA_XOUT_HIGH 0x01
  #define MMC5883MA_YOUT 0x02
  #define MMC5883MA_YOUT_LOW 0x02
  #define MMC5883MA_YOUT_HIGH 0x03
  #define MMC5883MA_ZOUT 0x04
  #define MMC5883MA_ZOUT_LOW 0x04
  #define MMC5883MA_ZOUT_HIGH 0x05
  #define MMC5883MA_TEMPERATURE 0x06
  #define MMC5883MA_STATUS 0x07
  #define MMC5883MA_INTERNAL_CONTROL_0 0x08
  #define MMC5883MA_INTERNAL_CONTROL_1 0x09
  #define MMC5883MA_INTERNAL_CONTROL_2 0x0A
  #define MMC5883MA_X_THRESHOLD 0x0B
  #define MMC5883MA_Y_THRESHOLD 0x0C
  #define MMC5883MA_Z_THRESHOLD 0x0D
  #define MMC5883MA_PRODUCT_ID 0x2F
  #define MMC5883MA_ADDR_WRITE 0x60
  #define MMC5883MA_ADDR_READ 0x61
  
  #define MMC5883MA_DYNAMIC_RANGE 16
  #define MMC5883MA_RESOLUTION 65536

void setup() {

  Wire.begin();
  Serial.begin(9600);
  delay(100);
  
  Wire.beginTransmission(MMC5883MA_ADDR); //Adress of I2C device
  Wire.write(MMC5883MA_INTERNAL_CONTROL_0);//Selecting  I2C CONTROL_0_Register
  Wire.write(0x01);// Value to be written
  Wire.endTransmission();

  Wire.beginTransmission(MMC5883MA_ADDR); //Adress of I2C device
  Wire.write(MMC5883MA_PRODUCT_ID);// Selecting I2C ID_Register
  Wire.endTransmission();

}

void loop() {

  //////////////// GET PRODUCT ID ///////////////
  Wire.requestFrom(MMC5883MA_ADDR_READ, 1);
  byte data1 = Wire.read(); //Get Product ID
  Serial.println(data1); // value here is always 255

  delay(1000);
}

I have to mention that I correctly wired the MEMSIC development kit (VDA and VDD to 3.3V of arduino, ground , and SDA and SCL linked to each others).

Does anyone has a hint to solve my problem ?

Thank you all for your help.

The posted code does not compile! Please don't waste our time looking at code that is rejected by the compiler.

  #define MMC5883MA_ADDR_WRITE 0x60
  #define MMC5883MA_ADDR_READ 0x61

For the Arduino Wire library you provide the I2C address not the address byte. The I2C address consists of 7bits and for the MMC5883MA it's 0x30. You use the same address for read and write operations, the library is responsible to set the corresponding bit in the address byte correctly.

This sensor is a 3.3V device, according to the datasheet the voltage on the I2C interface should not exceed 3.6V. If you connect that device directly to the UNO you might damage it.

  Wire.beginTransmission(MMC5883MA_ADDR); //Adress of I2C device
  Wire.write(MMC5883MA_PRODUCT_ID);// Selecting I2C ID_Register
  Wire.endTransmission();

According to the datasheet a stop condition should not happen between the register address and the read request (that's called a repeated start condition), so in this code part you should give Wire.endTransmission a false parameter.

void loop() {

  //////////////// GET PRODUCT ID ///////////////
  Wire.requestFrom(MMC5883MA_ADDR_READ, 1);
  byte data1 = Wire.read(); //Get Product ID
  Serial.println(data1); // value here is always 255

  delay(1000);
}

The datasheet doesn't say anything about repeated read requests with the specification of a register address in-between. This might or might no work.

You should check the result codes of Wire.endTransmission() and Wire.requestFrom() for errors.

If you still get wrong results, post a complete wiring diagram of your setup.

Yup I made a mistake while writing the previous post, adding comments etc... the code wasn't compiling due to the MEMSIC adress that was not defined.

Anyway, thank you for your reply, I did have a wrong I2C address, tried with 0x30 and I am now able to read the right I product ID of the device : 0x0C with this code :

  #include <Wire.h>
  
  #define MMC5883MA_OUT 0x00
  #define MMC5883MA_XOUT 0x00
  #define MMC5883MA_XOUT_LOW 0x00
  #define MMC5883MA_XOUT_HIGH 0x01
  #define MMC5883MA_YOUT 0x02
  #define MMC5883MA_YOUT_LOW 0x02
  #define MMC5883MA_YOUT_HIGH 0x03
  #define MMC5883MA_ZOUT 0x04
  #define MMC5883MA_ZOUT_LOW 0x04
  #define MMC5883MA_ZOUT_HIGH 0x05
  #define MMC5883MA_TEMPERATURE 0x06
  #define MMC5883MA_STATUS 0x07
  #define MMC5883MA_INTERNAL_CONTROL_0 0x08
  #define MMC5883MA_INTERNAL_CONTROL_1 0x09
  #define MMC5883MA_INTERNAL_CONTROL_2 0x0A
  #define MMC5883MA_X_THRESHOLD 0x0B
  #define MMC5883MA_Y_THRESHOLD 0x0C
  #define MMC5883MA_Z_THRESHOLD 0x0D
  #define MMC5883MA_PRODUCT_ID 0x2F
  #define MMC5883MA_ADDR 0x30
  
  #define MMC5883MA_DYNAMIC_RANGE 16
  #define MMC5883MA_RESOLUTION 65536
  
  uint8_t id;
  
void setup() {
  
  Wire.begin();
  Serial.begin(9600);
  delay(100);
}

void loop() {
  
  Wire.beginTransmission(MMC5883MA_ADDR);
  Wire.write(MMC5883MA_PRODUCT_ID);
  Wire.requestFrom(MMC5883MA_ADDR, 1);
  id = Wire.read(); 
  Wire.endTransmission();
  
  Serial.println(id); //Value here is always 12
}

So this parts now works well.

But I have another problem... when I try to read measurement values, I cannot get a reliable value.

Here is my code for the control register settings and value reading for temperature. It does the same with magnetic x,y,z values :

  #include <Wire.h>
  
  #define MMC5883MA_OUT 0x00
  #define MMC5883MA_XOUT 0x00
  #define MMC5883MA_XOUT_LOW 0x00
  #define MMC5883MA_XOUT_HIGH 0x01
  #define MMC5883MA_YOUT 0x02
  #define MMC5883MA_YOUT_LOW 0x02
  #define MMC5883MA_YOUT_HIGH 0x03
  #define MMC5883MA_ZOUT 0x04
  #define MMC5883MA_ZOUT_LOW 0x04
  #define MMC5883MA_ZOUT_HIGH 0x05
  #define MMC5883MA_TEMPERATURE 0x06
  #define MMC5883MA_STATUS 0x07
  #define MMC5883MA_INTERNAL_CONTROL_0 0x08
  #define MMC5883MA_INTERNAL_CONTROL_1 0x09
  #define MMC5883MA_INTERNAL_CONTROL_2 0x0A
  #define MMC5883MA_X_THRESHOLD 0x0B
  #define MMC5883MA_Y_THRESHOLD 0x0C
  #define MMC5883MA_Z_THRESHOLD 0x0D
  #define MMC5883MA_PRODUCT_ID 0x2F
  #define MMC5883MA_ADDR 0x30
  
  #define MMC5883MA_DYNAMIC_RANGE 16
  #define MMC5883MA_RESOLUTION 65536
  
  uint8_t id;
  uint8_t iStatus;
  uint8_t temp;
  uint8_t result[6];
  
void setup() {
  
  Wire.begin();
  Serial.begin(9600);
  delay(100);
  
  //// SET
  Wire.beginTransmission(MMC5883MA_ADDR); //Adress of I2C device
  Wire.write(MMC5883MA_INTERNAL_CONTROL_0);// I2C Register
  Wire.write(0x08);// Value
  Wire.endTransmission();

  // SET A TEMPERATURE MEASUREMENT
  Wire.beginTransmission(MMC5883MA_ADDR); //Adress of I2C device
  Wire.write(MMC5883MA_INTERNAL_CONTROL_0);// I2C Register
  Wire.write(0x02);// Value
  Wire.endTransmission();
}

void loop() {
  Wire.beginTransmission(MMC5883MA_ADDR); //Adress of I2C device
  Wire.write(MMC5883MA_STATUS);// I2C Status Register
  Wire.requestFrom(MMC5883MA_ADDR, 1);
  iStatus = Wire.read(); //Status here is 127
  Wire.endTransmission(false);

  Wire.beginTransmission(MMC5883MA_ADDR); //Adress of I2C device
  Wire.write(MMC5883MA_TEMPERATURE);// I2C Register
  Wire.requestFrom(MMC5883MA_ADDR, 1); 
  temp = Wire.read(); //TEMP value is 18 (-62°C...)
  Wire.endTransmission(false);
  
  Serial.println(temp);
}

There is clearly something that I am doing wrong. I tried with SET and RESET parts, still the same. I also tried to put the loop code into the setup so it does it once, it is not acting better.
Also I did not notice any problems with the returned values from Wire.endTransmission() and Wire.requestFrom.
I finnaly tried to remove the false param form endTransmisssion on each read parts. or just remove the line without success.

I did not try to write into control register 2 as I was refering to the example on the datasheet. But I'll try and post results ASAP :wink:

Anyone has an idea to fix my problem ?

Thank you again,

But I have another problem... when I try to read measurement values, I cannot get a reliable value.

What's a reliable value? Or better, what's an unreliable value?

Did you calibrate the thing? All magnetometers need to be calibrated.

Hi, I have been having a similar problem with the MMC5883MA-B sensor, and I was wondering if you ever figured out how to get accurate readings? All the magnetic field readings I get seem to be ~10000 mG off from my predicted readings with regards to the earths magnetic field. Thanks for your time!