Calibrating CMPS12 with arduino nano. (I2C)

Thanks for looking :slight_smile:
im not a complete beginner with arduino but im definitly not a programmer and i realise that its time to ask for help!

I have hit a dead end trying to calibrate my CMPS12, im currently working on an autopilot for my boat and i have it up and running but i sometimes notice that the compass heading shifts, sometimes as much as 90 degrees and thought that i should try and calibrate it, however i cant seem to figure out how to do it!

i know that the compass has auto calibration, what i want to do i save the calibration profile,

The datasheet ive found (https://www.robot-electronics.co.uk/files/cmps12.pdf) says
“To store a profile write the following to the command register 0xF0, 0xF5, 0xF6 with a 20ms delay after each of the three bytes.” i tried that but to verify that it worked i wanted to erase and the read out the calibration profile, then i noticed that i cant seem to do anything other then read the bearing etc, i cant even manage to read software version from the compass! i wrote the following code to read the calibration profile as mentioned in the datasheet but i get nothing on the serial monitor!

what am i doing wrong? i spent the last 4 days trying to learn about i i2c protocol and adressing and bits and bytes and such but i am none the wiser!

#include <Wire.h>
#define i2cAddress 0x60
void setup() {
 
  Wire.begin();                // join i2c bus (address optional for master)
  Serial.begin(9600); 
digitalWrite(A4, LOW);
digitalWrite(A5, LOW);
 
}



void loop() {

  Wire.beginTransmission(i2cAddress);
  Wire.write(0);
  Wire.write(30);      // sets register pointer to echo #1 register (0x1E)
  Wire.endTransmission();      // stop transmitting

  Wire.requestFrom(i2cAddress, 1);    // request 1 bytes from slave device

   byte  reading = Wire.read();  // receive byte
   
       Serial.println(reading);   // print the reading
  //}

  delay(2500);                  // wait a bit

}

i should mention that im using arduino nano and have added external resistors to the i2c bus but no difference in result! i have tried too many different things to write them all but maybe someone with better understanding of the protocols could help me?

Remove these two lines and try again:

digitalWrite(A4, LOW);
digitalWrite(A5, LOW);

i wrote the following code to read the calibration profile as mentioned in the datasheet but i get nothing on the serial monitor!

You get nothing? So the code is not the problem, post a complete wiring diagram of your setup!

Pylon, thank you for you answer!
I have tried it without those lines, still nothing, i added those lines when i added external resistors, ive tried both 5k and 10k external resistors!

i can get heading, pitch, roll and all other parameters from the cmps when i run my usual code so what makes you think that the problem is not in the code?

i cant make a schematic right now but its just
arduino: CMPS12:

5v - 5v

gnd - gnd

SDA - SDA (A4)

SCL - SCL (A5)

With 2 10k resistors from + 5v to both lines!

i do however get 255 on the serial monitor if i pull the ground wire from the cmps?

one thing i noticed is that in the datasheet when running serial communication it says that the cmps responds with "ok" 0x55 after each recived byte but there is no mention of this in i2c mode?

EDIT: After adding a lot of serial prints to the code, one before each step and "ok" after i can see that the loop stops in Wire.endtransmission?

Setup done
wire.begintransmission
OK
wirewrite 0
OK
wirewrite 30
OK
Wire endtransmission

I guess this line is wrong:

  Wire.write(0);

Why do you write a 0? In my interpretation of the documentation this writes 30 into the register 0 (command register). Try to remove this line and post the results.

tried that, still returns nothing!
i have however made progress last night!
by using the code i first copied (without wire.write(0) i can easily read from register 1 (bearing)
and i can also read from all other registrys that send data, bearing, pitch, roll, temperature and so on.
but as soon as i try to read from the command registry 0 or calibration status 30 (0x1E) the skech freezes on end.transmission!

if i use WSWire.h library and add

error = end.transMission();
Serial.print(error");

it returns 6.. timed out while waiting for data to be sent

after trying to read from any if those adresses the cmps is completely frozen until i restart it!

in the end i tried writing the commands used to change the CMPS12:s address and they work, the cmps blinks out its address with the onboard LED so i know it works, then i can assume that the erase and the store commands also work but it would be nice to understand why i cant read out the registry!?

Could it be a knock off cmps causing this or is there something i am missing!
again, thank you for trying to help me!

Could it be a knock off cmps causing this or is there something i am missing!

The documentation for that board isn’t comprehensible enough to go into that detail. It looks like there is a MCU on that board that reads the BNO055 and make the basic calculations to provide angle values to the user. It looks like the firmware of this MCU doesn’t like the reading from register 0 and 30. It then blocks the I2C bus probably by stretching the clock forever.

The documentation doesn’t provide details. For example it doesn’t tell how to read or write a register. It seems to be done the same way as many other devices do it but as the concept of a register isn’t part of the I2C standard a good manual should explain how a register is written or read.

well at least now i know i was not missing somthing obvious :slight_smile:
and i must admint, i have learned alot more on i2c then i think i would have if it had worked the first time around!

i may try during the winter to switch it to serial mode and see if a can get any info that way but for now im just gonna work with what ive got and focus on the rest of the functions in my autopilot since there is only a few good days left before winter!

only one last question for now, i looked at the datasheet for the cmps11 and it actually says:

"I2C communication protocol with the compass module is the same as popular eeprom's such as the 24C04. First send a start bit, the module address with the read/write bit low, then the register number you wish to read. This is followed by a repeated start and the module address again with the read/write bit high."

do you know what they mean by "repeated start" is that done just by sending wire.begintransmission again or do i need a true or false statement? and im guessing that the read/write bit is set by wire.write / wire.read correct?

EDIT, asked too quick, read the wire.endtransmission reference and found the answer! im gonna have to try that now :wink: