Today I try to acquire orientation from CPMS03 compass reading PWM signal with Arduino ATmega2560 in a simple way.
I start by saying that the best way to read this sensor is to use the I2C bus connection and the Arduino wire.h library functions. We can find many document in Internet about this way to use CPMS03 compass. But during the construction of our soccer player robots to compete in the RoboCup Junior light, we found an incompatibility between this sensor and the motor shield Adafruit v2.3 that manages its 4 DC motors, because probably both use the same pins SDA and SCL to communicate with Arduino. Because of this, the values ​​provided by the compass are not correct. But the compass is very important for the robot to keep in memory the direction of the goal!!!
So I choose to acquire orientation from CPMS03 compass reading PWM signal provided by its pin 4 and reading by Arduino pulseIn() function. The pulseIn() waits for the pin to go HIGH, starts timing, then waits for the pin to go LOW and stops timing. So returns the length of the pulse in microseconds.
The PWM connection between CPMS03 compass and ATmega2560 is more simple than the I2C bus connection, as you can see in the next photo. In the schematic I connect CMPS03 pin 4 to ATmega2560 digital pin 15, CMPS03 pin 1 to 5V and CMPS03 pin 9 to GND. If you use Auduino Uno, you can do the same connection and use the same sketches I propose, but change digital pin to another number (for example pin 3 instead of pin 15).

So I write this simple sketch in whitch values read from compass are printed on Arduino IDE serial monitor:

// The sketch acquires CMPS03 using pulseIn Function
#define PWM_PIN  15
 
unsigned int pwm_value;
 
void setup() {
  pinMode(PWM_PIN, INPUT);
  Serial.begin(9600);
}
 
void loop() {
  pwm_value = pulseIn(PWM_PIN, HIGH);
  Serial.println(pwm_value);
}

Next, I create a simple sketch to calibrate the range of values ​​read with pulseIn() from the PWM signal of the compass, to solve the problem of orientation calibration, so I can determine the value that the compass assigns north and the  value provided after one complete revolution:

/* CMPS03 with pulseIn Function
 * CMPS03 pin 4 is connected to ATmega2560 digital pin 15
 * CMPS03 pin 1 to 5V and pin 9 to GND
 */
#define PWM_PIN  15       

/* pulseIn returns a max value that exceded the Arduino
  max value of an integer = 32767 so we need to use an
  unsigned int */
unsigned int pwm_value, pwm_value_max, pwm_value_min;
 
void setup() {
  pinMode(PWM_PIN, INPUT);
  Serial.begin(9600);
  pwm_value_max = pulseIn(PWM_PIN, HIGH);
  pwm_value_min = pwm_value_max;
}
 
void loop() {
  pwm_value = pulseIn(PWM_PIN, HIGH);
  /* Calibrate Compass to find the maximum and the minimum (NORTH).  
   */
  if (pwm_value > pwm_value_max)
    pwm_value_max=pwm_value;  
  if (pwm_value < pwm_value_min)
    pwm_value_min=pwm_value;  
    
  Serial.print("min: ");
  Serial.print(pwm_value_min);
  Serial.print(" value: ");
  Serial.print(pwm_value);
  Serial.print(" max: ");
  Serial.print(pwm_value_max);
 
  /* I rotated a CMPS03 sensor of mine slowly in a circle, and I measured that the
   *  provided values are in range from 1018 (that I assumed NORTH) to 36383
   *  and I stimated the cardinal signs:
   *  NORTH = 1018
   *  WEST = 35365*90/360 = 8841
   *  SOUTH = 35365*180/360 = 17682
   *  EAST = 35365*270/360 = 26524
   *  
   */
 }