55
66SFE_MMC5983MA MMC5983; // global static instance of the sensor
77
8+ #define MGC_TONE_PITCH_H Sound{3000 , 50 }
9+ #define MGC_TONE_PITCH_L Sound{2200 , 50 }
10+ #define MGC_TONE_PITCH_LONG Sound{3000 , 250 }
11+ #define MGC_TONE_PITCH_LONG_L Sound{2000 , 250 }
12+ #define MGC_TONE_WAIT Sound{0 , 50 }
13+ #define MGC_TONE_NOOP Sound{0 , 1 }
14+
15+ Sound mg_calib_rdy[C_MG_LENGTH] = {MGC_TONE_PITCH_H, MGC_TONE_WAIT, MGC_TONE_PITCH_H, MGC_TONE_WAIT, MGC_TONE_PITCH_H};
16+ Sound mg_calib_done[C_MG_LENGTH] = {MGC_TONE_PITCH_LONG, MGC_TONE_WAIT, MGC_TONE_PITCH_H, MGC_TONE_WAIT, MGC_TONE_PITCH_H};
17+ Sound mg_calib_inp[C_MG_LENGTH] = {MGC_TONE_PITCH_H, MGC_TONE_NOOP, MGC_TONE_NOOP, MGC_TONE_NOOP,MGC_TONE_NOOP};
18+ Sound mg_calib_bad[C_MG_LENGTH] = {MGC_TONE_PITCH_LONG_L, MGC_TONE_WAIT, MGC_TONE_PITCH_LONG_L, MGC_TONE_NOOP, MGC_TONE_NOOP};
19+
820ErrorCode MagnetometerSensor::init () {
921 // Checks if sensor is connected
1022 if (!MMC5983.begin (MMC5983_CS)) {
@@ -28,6 +40,110 @@ Magnetometer MagnetometerSensor::read() {
2840 Y = ((double )cy - sf)/sf;
2941 Z = ((double )cz - sf)/sf;
3042
31- Magnetometer reading{X, Y, Z};
43+ // We multiply by 8, which is the full scale of the mag.
44+ // https://github.com/sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library/blob/main/examples/Example4-SPI_Simple_measurement/Example4-SPI_Simple_measurement.ino
45+ Magnetometer reading{X*8 , Y*8 , Z*8 };
3246 return reading;
47+ }
48+
49+ void MagnetometerSensor::begin_calibration (BuzzerController& buzzer) {
50+ Serial.println (" [MAG] Calibration begin" );
51+ buzzer.play_tune (mg_calib_rdy, C_MG_LENGTH);
52+ in_calibration_mode = true ;
53+ _calib_begin_timestamp = millis ();
54+ _calib_beeping = 1 ;
55+ _calib_max_axis = {-INFINITY, -INFINITY, -INFINITY};
56+ _calib_min_axis = {INFINITY, INFINITY, INFINITY};
57+ _calib_magnitude_sum = 0.0 ;
58+ _calib_num_datapoints = 0 ;
59+ }
60+
61+ void MagnetometerSensor::calib_reading (Magnetometer& reading, EEPROMController& eeprom, BuzzerController& buzzer) {
62+ // X reading
63+ if (reading.mx > _calib_max_axis.mx ) { _calib_max_axis.mx = reading.mx ; };
64+ if (reading.mx < _calib_min_axis.mx ) { _calib_min_axis.mx = reading.mx ; };
65+ // Y
66+ if (reading.my > _calib_max_axis.my ) { _calib_max_axis.my = reading.my ; };
67+ if (reading.my < _calib_min_axis.my ) { _calib_min_axis.my = reading.my ; };
68+ // Z
69+ if (reading.mz > _calib_max_axis.mz ) { _calib_max_axis.mz = reading.mz ; };
70+ if (reading.mz < _calib_min_axis.mz ) { _calib_min_axis.mz = reading.mz ; };
71+
72+ // Magnitude
73+ double mag = sqrtf (reading.mx *reading.mx + reading.my *reading.my + reading.mz *reading.mz );
74+ _calib_magnitude_sum += mag;
75+ _calib_num_datapoints++;
76+
77+ // Beeping
78+ if (get_time_since_calibration_start () > 15000 * _calib_beeping && _calib_beeping < 4 ) {
79+ _calib_beeping++;
80+ buzzer.play_tune (mg_calib_inp, C_MG_LENGTH);
81+ }
82+
83+ if (get_time_since_calibration_start () > _calib_time) {
84+ Serial.println (" [MAG] Calibration done." );
85+ commit_calibration (eeprom, buzzer);
86+ }
87+ }
88+
89+ bool MagnetometerSensor::calibration_valid (const Magnetometer& b, const Magnetometer& s) {
90+ constexpr float kSensorEpsilon = 1e-6f ;
91+ constexpr float kSensorMaxShear = 5 .0f ; // probably should tweak this
92+
93+ if (s.mx < kSensorEpsilon || s.my < kSensorEpsilon || s.mz < kSensorEpsilon ) {
94+ // Scale data is too small, so we probably didn't get enough data
95+ return false ;
96+ }
97+
98+ float s_min = fminf (s.mx , fminf (s.my , s.mz )); // Get minimum value of s
99+ float s_max = fmaxf (s.mx , fmaxf (s.my , s.mz )); // Get minimum value of s
100+
101+ if (s_max > kSensorMaxShear * s_min) {
102+ // this is iffy but could indicate strong sensor reference ellipsoid shear
103+ // return false;
104+ }
105+
106+ return true ;
107+ }
108+
109+ void MagnetometerSensor::restore_calibration (EEPROMController& eeprom) {
110+ in_calibration_mode = false ;
111+
112+ // Read back the calibration data from EEPROM:
113+ calibration_bias_softiron = eeprom.data .mmc5983ma_softiron_bias ;
114+ calibration_bias_hardiron = eeprom.data .mmc5983ma_hardiron_bias ;
115+ }
116+
117+ void MagnetometerSensor::commit_calibration (EEPROMController& eeprom, BuzzerController& buzzer) {
118+ in_calibration_mode = false ;
119+
120+ Magnetometer b, s;
121+ double magnitude_tgt = _calib_magnitude_sum / _calib_num_datapoints;
122+
123+ // b: Origin offset
124+ b.mx = (_calib_min_axis.mx + _calib_max_axis.mx ) / 2 ;
125+ b.my = (_calib_min_axis.my + _calib_max_axis.my ) / 2 ;
126+ b.mz = (_calib_min_axis.mz + _calib_max_axis.mz ) / 2 ;
127+
128+ // s: Scale offset
129+ s.mx = (_calib_max_axis.mx - _calib_min_axis.mx ) / (2 *magnitude_tgt);
130+ s.my = (_calib_max_axis.my - _calib_min_axis.my ) / (2 *magnitude_tgt);
131+ s.mz = (_calib_max_axis.mz - _calib_min_axis.mz ) / (2 *magnitude_tgt);
132+
133+ Serial.print (" MAG: " );
134+ Serial.println (magnitude_tgt);
135+
136+ if (!calibration_valid (b, s)) {
137+ Serial.println (" [MAG] Calibration is bad." );
138+ buzzer.play_tune (mg_calib_bad, C_MG_LENGTH);
139+ return ; // The calibration is garbage... this probably shouldn't fail silently but idc rn.
140+ }
141+
142+ calibration_bias_hardiron = b;
143+ calibration_bias_softiron = s;
144+ buzzer.play_tune (mg_calib_done, C_MG_LENGTH);
145+
146+ eeprom.data .mmc5983ma_softiron_bias = calibration_bias_softiron;
147+ eeprom.data .mmc5983ma_hardiron_bias = calibration_bias_hardiron;
148+ eeprom.commit ();
33149}
0 commit comments