Replies: 11 comments 1 reply
-
|
Great that your working on this @vidma I actually have a couple of 42688's laying around, but did not get around to actually build the driver. So yes, please go ahead and create a PR for 45686 (hopefully in combo with 42688). I'll gladly merge it. I prefer to copy/paste external code, to keep this project with minimal external dependencies which could potentially break in the future. I was also looking at ArduPilot/Betaflight/INAV in order to "steal" their gyro drivers. Last week I ported the ArduPilot uBlox gps driver to madflight, but it was more work than expected. So maybe starting from scratch with the TDK code is the easier route. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
|
There is a diagram of a similar board where the magnetometer
I'll rewrite it for |
Beta Was this translation helpful? Give feedback.
-
|
Hello @qqqlab
I figured out how to connect the board😀 Now I can connect via both I'd like to connect like this:
or
Could you help with this? |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
|
Sketch code for obtaining /*
*
* Copyright (c) [2020] by InvenSense, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "ICM45686.h"
// attempt to integrate SlimeVR code - no success
// #include "QMC6309_SlimeVR.h"
// attempt to integrate Mad Flight code - no success
// #include "mag_mf.h"
// rewrote MF for my own use - success
#include "mag_qmc6309_mf.h"
/* If you don't specify the I2C address, it defaults to 0x68, and then you need to pull the SDO pin on the board to ground.
But you can explicitly specify the address below */
// 0x68 SDO LOW
// 0x69 SDO HIGH
// #define ADDR_0x68
// Instantiate an ICM456XX with LSB address set to 0
// ICM456xx IMU(Wire,0);
ICM456xx IMU(Wire,0x69);
// I2c addr 0x7C
QMC6309 magSensor;
float Mx, My, Mz;
void setup() {
int ret;
Serial.begin(115200);
while(!Serial) {}
#ifdef ADDR_0x68
pinMode(GPIO_NUM_38, OUTPUT);
digitalWrite(GPIO_NUM_38, LOW);
#endif
// Initializing the ICM456XX
ret = IMU.begin();
if (ret != 0) {
Serial.print("ICM456xx initialization failed: ");
Serial.println(ret);
while(1);
}
// Accel ODR = 100 Hz and Full Scale Range = 16G
IMU.startAccel(100,16);
// Gyro ODR = 100 Hz and Full Scale Range = 2000 dps
IMU.startGyro(100,2000);
// Wait IMU to start
delay(100);
if (!magSensor.begin()) {
Serial.println("Failed to initialize QMC6309 sensor!");
while (1) {
delay(1000);
}
} else {
Serial.println("QMC6309 initialized successfully.");
}
}
void loop() {
inv_imu_sensor_data_t imu_data;
// Read registers
IMU.getDataFromRegisters(imu_data);
// https://github.com/tdk-invn-oss/motion.mcu.icm45686.driver/blob/16e45db85ba817ad1ec401c602eccc09e4f4e82f/examples/basic_read_registers/basic_read_registers.c#L123
// accel_g[0] = (float)(d.accel_data[0] * 4 /* gee */) / 32768;
// accel_g[1] = (float)(d.accel_data[1] * 4 /* gee */) / 32768;
// accel_g[2] = (float)(d.accel_data[2] * 4 /* gee */) / 32768;
// gyro_dps[0] = (float)(d.gyro_data[0] * 2000 /* dps */) / 32768;
// gyro_dps[1] = (float)(d.gyro_data[1] * 2000 /* dps */) / 32768;
// gyro_dps[2] = (float)(d.gyro_data[2] * 2000 /* dps */) / 32768;
// Format data for Serial Plotter
Serial.print("AccelX:");
Serial.print(imu_data.accel_data[0]);
Serial.print(",");
Serial.print("AccelY:");
Serial.print(imu_data.accel_data[1]);
Serial.print(",");
Serial.print("AccelZ:");
Serial.print(imu_data.accel_data[2]);
Serial.print(",");
Serial.print("GyroX:");
Serial.print(imu_data.gyro_data[0]);
Serial.print(",");
Serial.print("GyroY:");
Serial.print(imu_data.gyro_data[1]);
Serial.print(",");
Serial.print("GyroZ:");
Serial.print(imu_data.gyro_data[2]);
Serial.print(",");
Serial.print("Temperature:");
// Serial.print(imu_data.temp_data);
Serial.print((float)25 + ((float)imu_data.temp_data / 128));
Serial.println("");
if (magSensor.read(Mx, My, Mz)) {
Serial.printf("Magnetometer (uT): X=%.2f Y=%.2f Z=%.2f\n", Mx, My, Mz);
} else {
Serial.println("Data not ready or overflow.");
}
// Run @ ODR 10Hz
delay(100);
}
Header file /*==========================================================================================
MIT License
Copyright (c) 2023-2025 https://madflight.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
===========================================================================================*/
#ifndef MAG_QMC6309_MF_H
#define MAG_QMC6309_MF_H
#include <Arduino.h>
#include <Wire.h>
// Регистры и константы QMC6309
#define QMC6309_WAI_REG 0x00
#define QMC6309_WAI_VALUE 0x90
#define QMC6309_OUTX_REG 0x01
#define QMC6309_STAT_REG 0x09
#define QMC6309_STAT_DRDY 0x01
#define QMC6309_STAT_OVFL 0x02
#define QMC6309_CTRL1_REG 0x0A
#define QMC6309_CTRL2_REG 0x0B
#define QMC6309_CTRL1_MD_CONTINUOUS 0b11
#define QMC6309_CTRL1_OSR_8 (0b00 << 3)
#define QMC6309_CTRL1_LPF_16 (0b100 << 5)
#define QMC6309_CTRL2_SET_RESET_ON 0b00
#define QMC6309_CTRL2_RNG_8G (0b10 << 2)
#define QMC6309_CTRL2_ODR_100Hz (0b011 << 4)
#define QMC6309_CTRL2_SOFT_RESET_START 0x80
#define QMC6309_CTRL2_SOFT_RESET_STOP 0x00
class QMC6309 {
private:
TwoWire *wire;
uint8_t address = 0x7C; // 7-bit I2C address
float scale_uT = 0.025f;
float magneticDeclinationDegrees = 0.0f; // user-adjustable magnetic declination
int16_t mx = 0, my = 0, mz = 0;
uint8_t readRegister(uint8_t reg) {
wire->beginTransmission(address);
wire->write(reg);
wire->endTransmission(false);
wire->requestFrom(address, (uint8_t)1);
if (wire->available()) {
return wire->read();
}
return 0xFF;
}
bool readRegisters(uint8_t reg, uint8_t *buffer, uint8_t len) {
wire->beginTransmission(address);
wire->write(reg);
if (wire->endTransmission(false) != 0) return false;
uint8_t count = wire->requestFrom(address, len);
if (count != len) return false;
for (uint8_t i = 0; i < len; i++) {
buffer[i] = wire->read();
}
return true;
}
bool writeRegister(uint8_t reg, uint8_t val) {
wire->beginTransmission(address);
wire->write(reg);
wire->write(val);
return (wire->endTransmission() == 0);
}
public:
QMC6309(TwoWire &wirePort = Wire) : wire(&wirePort) {}
bool begin() {
wire->setClock(400000);
wire->begin();
// WHO_AM_I Check
uint8_t wai = readRegister(QMC6309_WAI_REG);
if (wai != QMC6309_WAI_VALUE) {
Serial.printf("QMC6309: Wrong WHO_AM_I value: 0x%02X\n", wai);
return false;
}
// configuration CTRL2 и CTRL1
uint8_t ctrl2_val = QMC6309_CTRL2_SET_RESET_ON | QMC6309_CTRL2_RNG_8G | QMC6309_CTRL2_ODR_100Hz;
uint8_t ctrl1_val = QMC6309_CTRL1_MD_CONTINUOUS | QMC6309_CTRL1_OSR_8 | QMC6309_CTRL1_LPF_16;
if (!writeRegister(QMC6309_CTRL2_REG, ctrl2_val)) return false;
if (!writeRegister(QMC6309_CTRL1_REG, ctrl1_val)) return false;
delay(10);
return true;
}
bool updateRaw() {
uint8_t stat = readRegister(QMC6309_STAT_REG);
if ((stat & QMC6309_STAT_DRDY) == 0 || (stat & QMC6309_STAT_OVFL) != 0) return false;
uint8_t data[6];
if (!readRegisters(QMC6309_OUTX_REG, data, 6)) return false;
mx = (int16_t)(data[0] | (data[1] << 8));
my = (int16_t)(data[2] | (data[3] << 8));
mz = (int16_t)(data[4] | (data[5] << 8));
return true;
}
bool read(float &x, float &y, float &z, bool raw = false) {
if (!updateRaw()) return false;
if (raw) {
// Return raw data without scaling
x = mx;
y = my;
z = mz;
} else {
// Returning calibrated values
x = mx * scale_uT;
y = my * scale_uT;
z = mz * scale_uT;
}
return true;
}
// New azimuth calculation function with calibration
int getAzimuth360calib(float y, float x) {
float heading = -atan2(y, x) * 180.0 / PI;
heading += magneticDeclinationDegrees; // use an internal class variable
// normalization in range [0, 360)
heading = fmod(heading, 360.0);
if (heading < 0) heading += 360.0;
return (int)heading;
}
// set magnetic declination (for calibration)
void setDeclination(float degrees) {
magneticDeclinationDegrees = degrees;
}
};
#endif // MAG_QMC6309_MF_H
I'd like to fully understand the |
Beta Was this translation helpful? Give feedback.
-
|
Hello @qqqlab 😀 |
Beta Was this translation helpful? Give feedback.
-
|
@brightproject Sorry, I have no interest to program this feature, as it would be for only this specific combination of chips. |
Beta Was this translation helpful? Give feedback.
-
|
Sorry for wasting your time. |
Beta Was this translation helpful? Give feedback.
-
#include <Arduino.h>
#include <Wire.h>
#define SERIAL_BAUD 115200
#define MY_WIRE Wire
#define I2C_FREQ (400000)
#define SDA_I2C (GPIO_NUM_6)
#define SCL_I2C (GPIO_NUM_7)
// Registers for passthrough (User Bank 0)
#define REG_SCENARIO_AUX_OVRD 0x30
#define ICM_ADDR 0x69 // Address ICM45686
#define QMC_ADDR 0x7C // Address QMC6309
void writeReg(uint8_t addr, uint8_t reg, uint8_t data) {
Wire.beginTransmission(addr);
Wire.write(reg);
Wire.write(data);
Wire.endTransmission();
}
void setup() {
Serial.begin(SERIAL_BAUD);
Wire.begin(SDA_I2C, SCL_I2C, I2C_FREQ);
// Enable|Disable passthrough mode - saves icm45686 to memory
writeReg(ICM_ADDR, REG_SCENARIO_AUX_OVRD, 0x18); // AUX1_MODE_OVRD, AUX1 in I2CM Bypass
// writeReg(ICM_ADDR, REG_SCENARIO_AUX_OVRD, 0x00); // disable
delay(50); // Wait for the configuration to take effect
} // SETUP
void loop() {
} //LOOP
|
Beta Was this translation helpful? Give feedback.






Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I'm adding ICM45686 based on https://github.com/tdk-invn-oss/motion.arduino.ICM45686
Seems like that's the best <5-10eur IMU out there.
@qqqlab
Status:
https://store.kouno.xyz/products/icm-45686-ist8306-module
P.S. for #28 #29 probably one could use https://github.com/finani/ICM42688 (lol I even mixed up sensor name and started, hacking this one first to find out it was wrong driver from my sensor :D so I can tell APIs are rather similar, but a bit different )
or Ardupilot code [1] which support multiple sensors (but I guess might be more work to adapt it?):
[1] https://github.com/ArduPilot/ardupilot/blob/master/libraries/AP_InertialSensor/AP_InertialSensor_Invensensev3.cpp
https://github.com/ArduPilot/ardupilot/blob/master/libraries/AP_InertialSensor/AP_InertialSensor_Invensensev3.cpp
Beta Was this translation helpful? Give feedback.
All reactions