STM32F103 with GY-271 (HMC5883L) sensor and Wire functions : I2c doesn't work

Hello community !

Core : STM32 MCU based board by STMicro v2.2.0
Board : bluepill copy with original STM32F103T8C6

I'm trying since 2 days to use a GY-271 magnetic sensor with a STM32F1.
However, i can't get almost anything from my I2C bus. I tried over I2C1 and I2C2 with no results.

First, i verified the device address with an I2C scanner. I found it at 0X0D on both I2C bus. But that's all. Impossible to write or read on the device (no error generated, just nothing happening).

On I2C1, i also have a SSD1306 Oled screen at 0x68, and this guy's working fine with adafruit library. On the screen, i was able to read a random byte with Wire.requestFrom(). But on the GY-271, nothing... I always read 0, even in registers i just wrote a non-null value.

Here's the full code :

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define COMPAS 0x0D 

volatile uint16_t X = 0, Y = 0, Z = 0;
uint16_t tmp = 0;
boolean test = false;

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


void setup() {
  delay(5000);
  pinMode(PB13, INPUT);


  Serial.begin(9600);
  delay(500);

  Serial.println("Starting the I2C interface.");
  Wire.begin();
  
  Serial.println("Starting Display.");
  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
  display.clearDisplay();
  display.display();


  Wire.beginTransmission(COMPAS);
  byte e = Wire.endTransmission();

  Serial.print("Device ");
  Serial.print(COMPAS, HEX);
  if(e == 0)
    Serial.println(" found.");
  else {
    Serial.print(" not found : ");
    Serial.println(e);
  }

 Wire.beginTransmission(COMPAS);
 Wire.write(0x00); 
 Wire.write(0x70);
 Wire.endTransmission();

 Wire.beginTransmission(COMPAS); 
 Wire.write(0x01);
 Wire.write(0xA0);
 Wire.endTransmission();

 Wire.beginTransmission(COMPAS);
 Wire.write(0x02);
 Wire.write(0x00); // Continuous sampling
 Wire.endTransmission();


 Wire.beginTransmission(COMPAS);
 Wire.write(0x01);
 Wire.endTransmission();
 Wire.requestFrom(COMPAS, 1);
 while(Wire.available() < 1) ;
 Serial.print("I read : ");
 Serial.println(Wire.read());
 
 attachInterrupt(digitalPinToInterrupt(PB13), readCompas, FALLING ); // supposed to call readCompas when data are ready

}

void readCompas() {  // it never enters here. Do nothing if i call the function in the main()

  Serial.println("READCOMPAS"); // NEVER HAPPEN ! No signal observed on DRDY pin
  
  display.setCursor(30, 40); 
  display.print("READING...");
  
  Wire.beginTransmission(COMPAS);
  Wire.write(3); // 3 is the address of X position
  Wire.endTransmission();
  Wire.requestFrom(COMPAS, 6);
  if(6<=Wire.available()) {
    X = Wire.read()<<8; //X msb
    X |= Wire.read(); //X lsb
    Z = Wire.read()<<8; //Z msb
    Z |= Wire.read(); //Z lsb
    Y = Wire.read()<<8; //Y msb
    Y |= Wire.read(); 
  }
}

void loop() {

  tmp = 0;

  display.clearDisplay();
  display.setTextSize(1);     
  display.setTextColor(SSD1306_WHITE); 
  display.cp437(true);         
  
  Wire.beginTransmission(COMPAS);
  Wire.write(0x01); 
  Wire.endTransmission();
  Wire.requestFrom(COMPAS, 1);
  
  uint8_t xxx = Wire.read(); 

  display.setCursor(0, 0); tmp += 9;
  display.print(xxx);

  Serial.println(xxx); // It's always 0 !

  display.setCursor(5, tmp); tmp += 9;
  display.print(X);
  display.setCursor(5, tmp); tmp += 9;
  display.print(Y);
  display.setCursor(5, tmp); tmp += 9;
  display.print(Z);
  
display.setCursor(60, 55); test = !test;
display.print(test ? "|" : "-");
 
display.display();
delay(200);
}

As result on serial monitor :

Starting the I2C interface.
Starting Display.
Device D found.
I read : 0
0
0
0
0
0
0
etc...

At least, shouldn't i get the value i just wrote in the register ???

I tried to change the BUS to 1 to 2, i changed the the GY271 module (i've many of them)...

Any help welcome !

Tank you

A requestFrom() always assumes the requested bytes received even if the slave does not send anything. The slave has no means to abort a request. It only may reject (NAK) the address byte so you should check the requestFrom() result. A busy slave typically returns 0xFF (all pullup) while your received 0 means a really provided output.

For later: you're doing way too much in the ISR. No I/O may work inside an ISR. Why do you want to use interrupts at all?

1 Like

Thank you very much for your answer DrDiettrich.

I tried this in the main loop (with same init) :

  Wire.beginTransmission(COMPAS);
 Wire.write(0x01);
 Wire.write(0x70); // it's supposed to set register 0X01 at 0x70 isn't it ?
 byte et = Wire.endTransmission();

  Serial.print("endTransmission after writing returned ");
  Serial.println(et); 
  
  Wire.beginTransmission(COMPAS);
  Wire.write(0x01); 
  et = Wire.endTransmission();
  byte rf = Wire.requestFrom(COMPAS, 1);

  uint8_t v =  Wire.read();
  
  Serial.print("endTransmission returned ");
  Serial.println(et); 

  Serial.print("requestFrom returned ");
  Serial.println(rf); 

  Serial.print("Value = ");
  Serial.println(v); 

result :

Starting the I2C interface.
Starting Display.
Device D found.

endTransmission after writing returned 0
endTransmission returned 0
requestFrom returned 1
Value = 0

The requestFrom return 2 if i request 2 bytes.

Shouldn't i get 0x70 in my v variable ? (btw, any register i try to read return 0)

Chip's datasheet confirm it's a R/W register :

00 Configuration Register A Read/Write
01 Configuration Register B Read/Write
02 Mode Register Read/Write

About the ISR, i put the code here while i was testing I2C I/O in my main loop. Of course, serial/screen/i2c operations will be removed and i'll just raise a flag to execute this code in my main loop.

Please show a full sketch.
There is a website for that: https://snippets-r-us.com/

If the sensor would not acknowledge to its I2C address, then the Wire.endTransmission() would return an error.
If the sensor would not return data, then 0xFF would be read.
It seems as if the sensor is returning 0x00.

Can you try to read the three identification registers ?
You might have a other magnetometer on the board or a fake or counterfeit or clone, that is probably not a real HMC5883L.

The full sketch is in the main message. My 2nd message is the same thing but with error testing in the main loop. I'll post it as tested below.
The address i use is the one i get through the I2C scanner (cf 1st post).

I tried to read the ID register (in hexa)

  Wire.beginTransmission(COMPAS); 
  Wire.write(10); //select Identification register A
  Wire.endTransmission();
  Wire.requestFrom(COMPAS, 3);
  
 char a = Wire.read();
 char b = Wire.read();
 char c = Wire.read();

  Serial.print("Identification register : ");
  Serial.print(a, HEX); Serial.print(" ");
  Serial.print(b, HEX); Serial.print(" ");
  Serial.print(c, HEX); Serial.print(" ");
 Serial.println("");

result :

Identification register : 0 0 1

About the board, all i can "say" is

I tried 2 different boards.

Thank you for your help.

Full sketch :

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define COMPAS 0x0D 

volatile uint16_t X = 0, Y = 0, Z = 0;
uint16_t tmp = 0;
boolean test = false;

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


void setup() {
  delay(5000);
  pinMode(PB13, INPUT);


  Serial.begin(9600);
  delay(500);

  Serial.println("Starting the I2C interface.");
  Wire.begin();
  
  Serial.println("Starting Display.");
  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
  display.clearDisplay();
  display.display();


  Wire.beginTransmission(COMPAS);
  byte e = Wire.endTransmission();

  Serial.print("Device ");
  Serial.print(COMPAS, HEX);
  if(e == 0)
    Serial.println(" found.");
  else {
    Serial.print(" not found : ");
    Serial.println(e);
  }

 Wire.beginTransmission(COMPAS);
 Wire.write(0x00); 
 Wire.write(0x70);
 Wire.endTransmission();

 Wire.beginTransmission(COMPAS); 
 Wire.write(0x01);
 Wire.write(0xA0);
 Wire.endTransmission();

 Wire.beginTransmission(COMPAS);
 Wire.write(0x02);
 Wire.write(0x00); // Continuous sampling
 Wire.endTransmission();


 Wire.beginTransmission(COMPAS);
 Wire.write(0x01);
 Wire.endTransmission();
 Wire.requestFrom(COMPAS, 1);

 Serial.print("I read : ");
 Serial.println(Wire.read());
 
 attachInterrupt(digitalPinToInterrupt(PB13), readCompas, FALLING ); // supposed to call readCompas when data are ready

}



void readCompas() { 

Serial.println("READCOMPAS"); // NEVER HAPPEN ! No signal observed on DRDY pin
  
  display.setCursor(30, 40); 
  display.print("READING...");
  
  Wire.beginTransmission(COMPAS);
  Wire.write(3); // 3 is the address of X position
  Wire.endTransmission();
  Wire.requestFrom(COMPAS, 6);
  if(6<=Wire.available()) {
    X = Wire.read()<<8; //X msb
    X |= Wire.read(); //X lsb
    Z = Wire.read()<<8; //Z msb
    Z |= Wire.read(); //Z lsb
    Y = Wire.read()<<8; //Y msb
    Y |= Wire.read(); 
  }
}




void loop() {

  tmp = 0;

  display.clearDisplay();
  display.setTextSize(1);     
  display.setTextColor(SSD1306_WHITE); 
  display.cp437(true);         


 Wire.beginTransmission(COMPAS);
 Wire.write(0x01);
 Wire.write(0x70);
 byte et = Wire.endTransmission();

  Serial.print("endTransmission after writing returned ");
  Serial.println(et); 
  
  Wire.beginTransmission(COMPAS);
  Wire.write(0x01); 
  et = Wire.endTransmission();
  byte rf = Wire.requestFrom(COMPAS, 1);

  uint8_t v =  Wire.read();
  
  Serial.print("endTransmission returned ");
  Serial.println(et); 

  Serial.print("requestFrom returned ");
  Serial.println(rf); 

  Serial.print("Value = ");
  Serial.println(v); 
  



  display.setCursor(0, 0); tmp += 9;
  display.print(v);

  

  display.setCursor(5, tmp); tmp += 9;
  display.print(X);
  display.setCursor(5, tmp); tmp += 9;
  display.print(Y);
  display.setCursor(5, tmp); tmp += 9;
  display.print(Z);
  


display.setCursor(60, 55); test = !test;
display.print(test ? "|" : "-");
 

display.display();
delay(200);
}







The I2C library is interrupt driven. You can not start a I2C session from a interrupt.
Make the sketch ten times simpler, without interrupt, and show the new sketch please.

The interrupt is never triggered, that's why i put the code i don't use there.

I remove everything useless for demonstration :

#include <Wire.h>
#define COMPAS 0x0D 

void setup() {
  delay(5000);

  Serial.begin(9600);
  delay(500);

  Serial.println("Starting the I2C interface.");
  Wire.begin();

  Wire.beginTransmission(COMPAS);
  byte e = Wire.endTransmission();
  Serial.print("Device ");
  Serial.print(COMPAS, HEX);
  if(e == 0)
    Serial.println(" found.");
  else {
    Serial.print(" not found : ");
    Serial.println(e);
  }

  Wire.beginTransmission(COMPAS);
  Wire.write(0x00); 
  Wire.write(0x70);
  Wire.endTransmission();
  
  Wire.beginTransmission(COMPAS); 
  Wire.write(0x01);
  Wire.write(0xA0);
  Wire.endTransmission();
  
  Wire.beginTransmission(COMPAS);
  Wire.write(0x02);
  Wire.write(0x00); // Set continuous sampling mode
  Wire.endTransmission();
}


void loop() {

  Wire.beginTransmission(COMPAS); 
  Wire.write(10); //select Identification register A
  Wire.endTransmission();
  Wire.requestFrom(COMPAS, 3);
  
  char a = Wire.read();
  char b = Wire.read();
  char c = Wire.read();

  Serial.print("Identification register (decimal) : ");
  Serial.print(a, DEC); Serial.print(" ");
  Serial.print(b, DEC); Serial.print(" ");
  Serial.print(c, DEC); Serial.print(" ");
  Serial.println("");

  Wire.beginTransmission(COMPAS);
  Wire.write(0x01);
  Wire.write(0x70);
  byte et = Wire.endTransmission();
  
  Serial.print("endTransmission after writing returned ");
  Serial.println(et); 
    
  Wire.beginTransmission(COMPAS);
  Wire.write(0x01); 
  et = Wire.endTransmission();
  byte rf = Wire.requestFrom(COMPAS, 1);

  uint8_t v =  Wire.read();
  
  Serial.print("endTransmission returned ");
  Serial.println(et); 

  Serial.print("requestFrom returned ");
  Serial.println(rf); 

  Serial.print("Value = ");
  Serial.println(v); 

  delay(1000);
}

Result :

Starting the I2C interface.
Device D found.
Identification register (decimal) : 0 0 1
endTransmission after writing returned 0
endTransmission returned 0
requestFrom returned 1
Value = 0

The HMC5883L has 0x1E as I2C address.
The ID registers are: 0x48, 0x34, 0x33 (spelling "H43" in ASCII).

The 0x0D is a QMC5883L. Sorry that I did not mention that earlier, the 0x0D should ring a bell.
A QMC5883L has often "DA 5883" written on the chip. Maybe you have a fake copy of a counterfeit of a clone. Maybe you have something that has no sensor inside.

You need a different library for the QMC. Although it is used as a replacement, its registers are not the same. Can you find a library for the QMC ?

If you want real products, then buy from real sellers: the Arduino Store, Adafruit, Sparkfun, Pololu. They make an effort to sell something good.

Thank you very much for your help !

I'll order a real one and try again !

And i'll also try to understand what i bought :sob:

Ok, i downloaded the right datasheet...
The register where i was trying to write were read only and the register to configure the chip were far away. Once enabled with the right address, i was abale to read values from the 6 first registers.
Those values changed a lot when i put a magnet next to the board.

With the good datasheet, it's so easy :smiley:

Thank you very much, i was going to give up !

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.