ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
bmm150_aux.h
Go to the documentation of this file.
1#pragma once
2#include <cmath>
3#include <cstdint>
5
6// BMM150 magnetometer definitions - the chip that can optionally be wired to the BMI270's
7// auxiliary (secondary I2C master) interface. Lives in its own header/namespace because this
8// is BMM150-specific driver knowledge, not BMI270 knowledge - it just can't be a standalone
9// ESPHome component, since the BMM150 has no address of its own on the main I2C bus and is only
10// reachable by having the BMI270 proxy register reads/writes through its aux interface.
12
13static constexpr uint8_t BMM150_DEFAULT_I2C_ADDRESS = 0x10;
14static constexpr uint8_t BMM150_REG_CHIP_ID = 0x40;
15static constexpr uint8_t BMM150_REG_DATA_X_LSB = 0x42;
16static constexpr uint8_t BMM150_REG_POWER_CONTROL = 0x4B;
17static constexpr uint8_t BMM150_REG_OP_MODE = 0x4C;
18static constexpr uint8_t BMM150_CHIP_ID_VALUE = 0x32;
19static constexpr uint8_t BMM150_CMD_POWER_ON = 0x01; // power control bit: suspend -> sleep (3 ms start-up)
20static constexpr uint8_t BMM150_CMD_SOFT_RESET = 0x83; // soft reset bits 7 and 1, power control bit kept set
21static constexpr uint8_t BMM150_CMD_NORMAL_MODE_ODR_30HZ = 0x38; // data rate 111b (30 Hz), opmode 00b (normal)
22static constexpr uint32_t BMM150_START_UP_TIME_MS = 3;
23static constexpr uint32_t BMM150_SOFT_RESET_TIME_MS = 1;
24
25// Factory trim registers 0x5D-0x71, read once at setup and used to compensate every sample.
26static constexpr uint8_t BMM150_REG_TRIM_START = 0x5D;
27static constexpr uint8_t BMM150_TRIM_BLOCK_LEN = 8; // one aux burst
28static constexpr uint8_t BMM150_TRIM_BLOCKS = 3; // 0x5D, 0x65, 0x6D -> covers 0x5D-0x74
29static constexpr uint8_t BMM150_TRIM_X1 = 0x5D;
30static constexpr uint8_t BMM150_TRIM_Y1 = 0x5E;
31static constexpr uint8_t BMM150_TRIM_Z4 = 0x62; // 16-bit, LSB first
32static constexpr uint8_t BMM150_TRIM_X2 = 0x64;
33static constexpr uint8_t BMM150_TRIM_Y2 = 0x65;
34static constexpr uint8_t BMM150_TRIM_Z2 = 0x68; // 16-bit
35static constexpr uint8_t BMM150_TRIM_Z1 = 0x6A; // 16-bit
36static constexpr uint8_t BMM150_TRIM_XYZ1 = 0x6C; // 16-bit, 15 valid bits
37static constexpr uint8_t BMM150_TRIM_Z3 = 0x6E; // 16-bit
38static constexpr uint8_t BMM150_TRIM_XY2 = 0x70;
39static constexpr uint8_t BMM150_TRIM_XY1 = 0x71;
40static constexpr uint16_t BMM150_TRIM_XYZ1_MASK = 0x7FFF;
41
42// Raw ADC values that mean "overflow" per the datasheet.
43static constexpr int16_t BMM150_XY_OVERFLOW_ADCVAL = -4096;
44static constexpr int16_t BMM150_Z_OVERFLOW_ADCVAL = -16384;
45
46// Result of a BMM150 magnetometer reading, in µT. An axis is NaN when the sensor reported overflow.
47struct BMM150Data {
48 float x;
49 float y;
50 float z;
51};
52
53// Factory trim values (BMM150 datasheet, compensation section).
54struct BMM150Trim {
55 int8_t x1, y1, x2, y2, xy2;
56 uint8_t xy1;
57 int16_t z2, z3, z4;
58 uint16_t z1, xyz1;
59};
60
61// A little-endian 16-bit trim word, addressed by its register
62inline uint16_t bmm150_trim_word(const uint8_t *regs, uint8_t reg) {
63 const uint8_t offset = reg - BMM150_REG_TRIM_START;
64 return encode_uint16(regs[offset + 1], regs[offset]);
65}
66
67// Decodes the trim registers; `regs` holds the bytes of registers 0x5D onwards (at least 21).
68inline BMM150Trim bmm150_parse_trim(const uint8_t *regs) {
69 BMM150Trim trim;
70 trim.x1 = static_cast<int8_t>(regs[BMM150_TRIM_X1 - BMM150_REG_TRIM_START]);
71 trim.y1 = static_cast<int8_t>(regs[BMM150_TRIM_Y1 - BMM150_REG_TRIM_START]);
72 trim.z4 = static_cast<int16_t>(bmm150_trim_word(regs, BMM150_TRIM_Z4));
73 trim.x2 = static_cast<int8_t>(regs[BMM150_TRIM_X2 - BMM150_REG_TRIM_START]);
74 trim.y2 = static_cast<int8_t>(regs[BMM150_TRIM_Y2 - BMM150_REG_TRIM_START]);
75 trim.z2 = static_cast<int16_t>(bmm150_trim_word(regs, BMM150_TRIM_Z2));
76 trim.z1 = bmm150_trim_word(regs, BMM150_TRIM_Z1);
77 trim.xyz1 = bmm150_trim_word(regs, BMM150_TRIM_XYZ1) & BMM150_TRIM_XYZ1_MASK;
78 trim.z3 = static_cast<int16_t>(bmm150_trim_word(regs, BMM150_TRIM_Z3));
79 trim.xy2 = static_cast<int8_t>(regs[BMM150_TRIM_XY2 - BMM150_REG_TRIM_START]);
80 trim.xy1 = regs[BMM150_TRIM_XY1 - BMM150_REG_TRIM_START];
81 return trim;
82}
83
84// Compensates an X or Y sample; returns µT, or NaN on overflow. Port of the Bosch BMM150 API (float variant).
85inline float bmm150_compensate_xy(const BMM150Trim &trim, int8_t trim_a, int8_t trim_b, int16_t raw, uint16_t rhall) {
86 if (raw == BMM150_XY_OVERFLOW_ADCVAL || rhall == 0 || trim.xyz1 == 0)
87 return NAN;
88 float retval = (float) trim.xyz1 * 16384.0f / (float) rhall - 16384.0f;
89 float comp1 = (float) trim.xy2 * (retval * retval / 268435456.0f);
90 float comp2 = comp1 + retval * (float) trim.xy1 / 16384.0f;
91 float comp3 = (float) trim_b + 160.0f;
92 float comp4 = (float) raw * ((comp2 + 256.0f) * comp3);
93 return ((comp4 / 8192.0f) + (float) trim_a * 8.0f) / 16.0f;
94}
95
96// Compensates a Z sample; returns µT, or NaN on overflow. Port of the Bosch BMM150 API (float variant).
97inline float bmm150_compensate_z(const BMM150Trim &trim, int16_t raw, uint16_t rhall) {
98 if (raw == BMM150_Z_OVERFLOW_ADCVAL || trim.z2 == 0 || trim.z1 == 0 || trim.xyz1 == 0 || rhall == 0)
99 return NAN;
100 float comp0 = (float) raw - (float) trim.z4;
101 float comp1 = (float) rhall - (float) trim.xyz1;
102 float comp2 = (float) trim.z3 * comp1;
103 float comp3 = (float) trim.z1 * (float) rhall / 32768.0f;
104 float comp4 = (float) trim.z2 + comp3;
105 float comp5 = comp0 * 131072.0f - comp2;
106 return (comp5 / (comp4 * 4.0f)) / 16.0f;
107}
108
109// Converts 8 raw bytes from the BMM150 data registers (X, Y, Z, RHALL; 0x42-0x49) to compensated µT.
110inline BMM150Data bmm150_convert(const BMM150Trim &trim, const uint8_t *raw) {
111 // X/Y are 13-bit (LSB byte bits 7:3), Z is 15-bit (bits 7:1), RHALL is 14-bit (bits 7:2).
112 int16_t x = (int16_t) (((int16_t) (int8_t) raw[1] * 32) | (raw[0] >> 3));
113 int16_t y = (int16_t) (((int16_t) (int8_t) raw[3] * 32) | (raw[2] >> 3));
114 int16_t z = (int16_t) (((int16_t) (int8_t) raw[5] * 128) | (raw[4] >> 1));
115 uint16_t rhall = (uint16_t) (((uint16_t) raw[7] << 6) | (raw[6] >> 2));
116 return BMM150Data{
117 .x = bmm150_compensate_xy(trim, trim.x1, trim.x2, x, rhall),
118 .y = bmm150_compensate_xy(trim, trim.y1, trim.y2, y, rhall),
119 .z = bmm150_compensate_z(trim, z, rhall),
120 };
121}
122
123} // namespace esphome::bmi270::bmm150
uint8_t raw[35]
Definition bl0939.h:0
bool z
Definition msa3xx.h:1
BMM150Data bmm150_convert(const BMM150Trim &trim, const uint8_t *raw)
Definition bmm150_aux.h:110
float bmm150_compensate_z(const BMM150Trim &trim, int16_t raw, uint16_t rhall)
Definition bmm150_aux.h:97
uint16_t bmm150_trim_word(const uint8_t *regs, uint8_t reg)
Definition bmm150_aux.h:62
BMM150Trim bmm150_parse_trim(const uint8_t *regs)
Definition bmm150_aux.h:68
float bmm150_compensate_xy(const BMM150Trim &trim, int8_t trim_a, int8_t trim_b, int16_t raw, uint16_t rhall)
Definition bmm150_aux.h:85
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:980
static void uint32_t
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6