Skip to content

Mag support (Attempt 2) #458

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jun 12, 2025
24 changes: 24 additions & 0 deletions src/sensors/SensorToggles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,27 @@ bool SensorToggleState::getToggle(SensorToggles toggle) const {
}
return false;
}

void SensorToggleState::onToggleChange(
std::function<void(SensorToggles, bool)>&& callback
) {
this->callback = callback;
}

void SensorToggleState::emitToggleChange(SensorToggles toggle, bool state) const {
if (callback) {
(*callback)(toggle, state);
}
}

const char* SensorToggleState::toggleToString(SensorToggles toggle) {
switch (toggle) {
case SensorToggles::MagEnabled:
return "MagEnabled";
case SensorToggles::CalibrationEnabled:
return "CalibrationEnabled";
case SensorToggles::TempGradientCalibrationEnabled:
return "TempGradientCalibrationEnabled";
}
return "Unknown";
}
9 changes: 9 additions & 0 deletions src/sensors/SensorToggles.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#pragma once

#include <cstdint>
#include <functional>
#include <optional>

#include "../debug.h"

Expand All @@ -38,7 +40,14 @@ class SensorToggleState {
void setToggle(SensorToggles toggle, bool state);
[[nodiscard]] bool getToggle(SensorToggles toggle) const;

void onToggleChange(std::function<void(SensorToggles, bool)>&& callback);

static const char* toggleToString(SensorToggles toggle);

private:
std::optional<std::function<void(SensorToggles, bool)>> callback;
void emitToggleChange(SensorToggles toggle, bool state) const;

bool magEnabled = !USE_6_AXIS;
bool calibrationEnabled = true;
bool tempGradientCalibrationEnabled = true;
Expand Down
7 changes: 7 additions & 0 deletions src/sensors/bno080sensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@ void BNO080Sensor::motionSetup() {
configured = true;
m_tpsCounter.reset();
m_dataCounter.reset();

toggles.onToggleChange([&](SensorToggles toggle, bool) {
if (toggle == SensorToggles::MagEnabled) {
// TODO: maybe handle this more gracefully, I'm sure it's possible
motionSetup();
}
});
}

void BNO080Sensor::motionLoop() {
Expand Down
12 changes: 9 additions & 3 deletions src/sensors/sensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ void Sensor::resetTemperatureCalibrationState() {
printTemperatureCalibrationUnsupported();
};

const char* Sensor::getAttachedMagnetometer() const { return nullptr; }

SlimeVR::Configuration::SensorConfigBits Sensor::getSensorConfigData() {
return SlimeVR::Configuration::SensorConfigBits{
.magEnabled = toggles.getToggle(SensorToggles::MagEnabled),
Expand Down Expand Up @@ -157,12 +159,16 @@ void Sensor::markRestCalibrationComplete(bool completed) {
}

void Sensor::setFlag(SensorToggles toggle, bool state) {
assert(isFlagSupported(toggle));
if (!isFlagSupported(toggle)) {
m_Logger.error(
"Toggle %s isn't supported by this sensor!",
SensorToggleState::toggleToString(toggle)
);
return;
}

toggles.setToggle(toggle, state);

configuration.setSensorToggles(sensorId, toggles);
configuration.save();

motionSetup();
}
5 changes: 5 additions & 0 deletions src/sensors/sensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ class Sensor {
virtual void printDebugTemperatureCalibrationState();
virtual void resetTemperatureCalibrationState();
virtual void saveTemperatureCalibration();
// TODO: currently only for softfusionsensor, bmi160 and others should get
// an overload too
virtual const char* getAttachedMagnetometer() const;
// TODO: realistically each sensor should print its own state instead of
// having 15 getters for things only the serial commands use
bool isWorking() { return working; };
bool getHadData() const { return hadData; };
bool isValid() { return m_hwInterface != nullptr; };
Expand Down
201 changes: 201 additions & 0 deletions src/sensors/softfusion/drivers/icm45base.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include "../../../sensorinterface/RegisterInterface.h"
#include "callbacks.h"
#include "sensors/softfusion/magdriver.h"

namespace SlimeVR::Sensors::SoftFusion::Drivers {

Expand Down Expand Up @@ -104,6 +105,77 @@ struct ICM45Base {

static constexpr uint8_t FifoCount = 0x12;
static constexpr uint8_t FifoData = 0x14;

// Indirect Register Access

static constexpr uint32_t IRegWaitTimeMicros = 4;

enum class Bank : uint8_t {
IMemSram = 0x00,
IPregBar = 0xa0,
IPregSys1 = 0xa4,
IPregSys2 = 0xa5,
IPregTop1 = 0xa2,
};

static constexpr uint8_t IRegAddr = 0x7c;
static constexpr uint8_t IRegData = 0x7e;

// Mag Support

struct IOCPadScenarioAuxOvrd {
static constexpr uint8_t reg = 0x30;
static constexpr uint8_t value = (0b1 << 4) // Enable AUX1 override
| (0b01 << 2) // Enable I2CM master
| (0b1 << 1) // Enable AUX1 enable override
| (0b1 << 0); // Enable AUX1
};

struct I2CMCommand0 {
static constexpr Bank bank = Bank::IPregTop1;
static constexpr uint8_t reg = 0x06;
};

struct I2CMDevProfile0 {
static constexpr Bank bank = Bank::IPregTop1;
static constexpr uint8_t reg = 0x0e;
};

struct I2CMDevProfile1 {
static constexpr Bank bank = Bank::IPregTop1;
static constexpr uint8_t reg = 0x0f;
};

struct I2CMWrData0 {
static constexpr Bank bank = Bank::IPregTop1;
static constexpr uint8_t reg = 0x33;
};

struct I2CMRdData0 {
static constexpr Bank bank = Bank::IPregTop1;
static constexpr uint8_t reg = 0x1b;
};

struct DmpExtSenOdrCfg {
// TODO: todo
};

struct I2CMControl {
static constexpr Bank bank = Bank::IPregTop1;
static constexpr uint8_t reg = 0x16;
};

struct I2CMStatus {
static constexpr Bank bank = Bank::IPregTop1;
static constexpr uint8_t reg = 0x18;

static constexpr uint8_t SDAErr = 0b1 << 5;
static constexpr uint8_t SCLErr = 0b1 << 4;
static constexpr uint8_t SRSTErr = 0b1 << 3;
static constexpr uint8_t TimeoutErr = 0b1 << 2;
static constexpr uint8_t Done = 0b1 << 1;
static constexpr uint8_t Busy = 0b1 << 0;
};
};

#pragma pack(push, 1)
Expand Down Expand Up @@ -149,6 +221,11 @@ struct ICM45Base {
BaseRegs::PwrMgmt0::value
);

m_RegisterInterface.writeReg(
BaseRegs::IOCPadScenarioAuxOvrd::reg,
BaseRegs::IOCPadScenarioAuxOvrd::value
);

read_buffer.resize(FullFifoEntrySize * MaxReadings);

delay(1);
Expand Down Expand Up @@ -231,6 +308,130 @@ struct ICM45Base {
}
}
}

template <typename Reg>
uint8_t readBankRegister() {
uint8_t buffer;
readBankRegister<Reg>(&buffer, sizeof(buffer));
return buffer;
}

template <typename Reg, typename T>
void readBankRegister(T* buffer, size_t length) {
uint8_t data[] = {
static_cast<uint8_t>(Reg::bank),
Reg::reg,
};

auto* bufferBytes = reinterpret_cast<uint8_t*>(buffer);
m_RegisterInterface.writeBytes(BaseRegs::IRegAddr, sizeof(data), data);
delayMicroseconds(BaseRegs::IRegWaitTimeMicros);
for (size_t i = 0; i < length * sizeof(T); i++) {
bufferBytes[i] = m_RegisterInterface.readReg(BaseRegs::IRegData);
delayMicroseconds(BaseRegs::IRegWaitTimeMicros);
}
}

template <typename Reg>
void writeBankRegister() {
writeBankRegister<Reg>(&Reg::value, sizeof(Reg::value));
}

template <typename Reg, typename T>
void writeBankRegister(T* buffer, size_t length) {
auto* bufferBytes = reinterpret_cast<uint8_t*>(buffer);

uint8_t data[] = {
static_cast<uint8_t>(Reg::bank),
Reg::reg,
bufferBytes[0],
};

m_RegisterInterface.writeBytes(BaseRegs::IRegAddr, sizeof(data), data);
delayMicroseconds(BaseRegs::IRegWaitTimeMicros);
for (size_t i = 1; i < length * sizeof(T); i++) {
m_RegisterInterface.writeReg(BaseRegs::IRegData, bufferBytes[i]);
delayMicroseconds(BaseRegs::IRegWaitTimeMicros);
}
}

template <typename Reg>
void writeBankRegister(uint8_t value) {
writeBankRegister<Reg>(&value, sizeof(value));
}

void setAuxId(uint8_t deviceId) {
writeBankRegister<typename BaseRegs::I2CMDevProfile1>(deviceId);
}

uint8_t readAux(uint8_t address) {
writeBankRegister<typename BaseRegs::I2CMDevProfile0>(address);

writeBankRegister<typename BaseRegs::I2CMCommand0>(
(0b1 << 7) // Last transaction
| (0b0 << 6) // Channel 0
| (0b01 << 4) // Read with register
| (0b0001 << 0) // Read 1 byte
);
writeBankRegister<typename BaseRegs::I2CMControl>(
(0b0 << 6) // No restarts
| (0b0 << 3) // Fast mode
| (0b1 << 0) // Start transaction
);

uint8_t lastStatus;
while ((lastStatus = readBankRegister<typename BaseRegs::I2CMStatus>())
& BaseRegs::I2CMStatus::Busy)
;

if (lastStatus != BaseRegs::I2CMStatus::Done) {
m_Logger.error(
"Aux read from address 0x%02x returned status 0x%02x",
address,
lastStatus
);
}

return readBankRegister<typename BaseRegs::I2CMRdData0>();
}

void writeAux(uint8_t address, uint8_t value) {
writeBankRegister<typename BaseRegs::I2CMDevProfile0>(address);
writeBankRegister<typename BaseRegs::I2CMWrData0>(value);
writeBankRegister<typename BaseRegs::I2CMCommand0>(
(0b1 << 7) // Last transaction
| (0b0 << 6) // Channel 0
| (0b01 << 4) // Read with register
| (0b0001 << 0) // Read 1 byte
);
writeBankRegister<typename BaseRegs::I2CMControl>(
(0b0 << 6) // No restarts
| (0b0 << 3) // Fast mode
| (0b1 << 0) // Start transaction
);

uint8_t lastStatus;
while ((lastStatus = readBankRegister<typename BaseRegs::I2CMStatus>())
& BaseRegs::I2CMStatus::Busy)
;

if (lastStatus != BaseRegs::I2CMStatus::Done) {
m_Logger.error(
"Aux write to address 0x%02x with value 0x%02x returned status 0x%02x",
address,
value,
lastStatus
);
}
}

void startAuxPolling(uint8_t dataReg, MagDataWidth dataWidth) {
// TODO:
}

void stopAuxPolling() {
// TODO:
}
};

}; // namespace SlimeVR::Sensors::SoftFusion::Drivers
9 changes: 9 additions & 0 deletions src/sensors/softfusion/imuconsts.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,13 @@ struct IMUConsts {
return IMU::TempTs;
}
}

static constexpr bool SupportsMags = requires(IMU& i) { i.readAux(0x00); };
static constexpr bool Supports9ByteMag = []() constexpr {
if constexpr (requires { IMU::Supports9ByteMag; }) {
return IMU::Supports9ByteMag;
} else {
return true;
}
}();
};
Loading