microReticulumTbeam/exercises/22_compass
2026-04-19 10:22:51 -07:00
..
lib/startup_sd Not fully working, OLED displays, SD card mounts, web not working? 2026-04-17 09:54:46 -07:00
scripts missed this one 2026-04-19 10:22:51 -07:00
src safety -- still not sure what is happening, but I needed to have logging on SD card and web access since running on a USB tether placed the unit close to a maelstrom of magnetic interferences 2026-04-19 10:21:50 -07:00
declination_manual.txt previously meant to include this exercise, too 2026-04-16 10:08:15 -07:00
platformio.ini safety -- still not sure what is happening, but I needed to have logging on SD card and web access since running on a USB tether placed the unit close to a maelstrom of magnetic interferences 2026-04-19 10:21:50 -07:00
README.md Wire vs. Wire1, still needs to be sorted out. ChatGPT has some tests to clarify which I will try after this commit. Magnetometer is on Wire and RTC is on Wire1, this apparently conflicts with the documentation. Getting values, though when I rotated both standing up and sideways, I did not see values ranging from 0-360. 2026-04-17 12:30:56 -07:00

Exercise 22: Compass / Magnetometer Executive Summary

This exercise will target the T-Beam Supreme magnetometer and build toward a real-time compass / field monitor. The manufacturer examples reviewed were:

  • QMC6310_CalibrateExample
  • QMC6310_CompassExample
  • QMC6310_GetDataExample
  • QMC6310_GetPolarExample

What The Sensor Is Useful For

The QMC6310-class sensor is a 3-axis magnetometer. In practical terms, it is useful for:

  • Magnetic heading estimation: a compass that points toward magnetic north.
  • Relative orientation work: detecting yaw changes and turn direction.
  • Magnetic field observation: watching X/Y/Z field components and total field strength change in real time.
  • Sensor fusion: combining magnetometer data with the IMU to stabilize heading over time.
  • Hardware assay: identifying board population differences by probing the magnetometer address and response.

It is not a complete navigation solution by itself. A magnetometer is sensitive to nearby ferrous metal, current-carrying wires, speakers, magnets, and board-level bias. For good heading, it needs calibration and a local magnetic declination correction if the goal is true north rather than magnetic north.

What Each Example Actually Does

QMC6310_GetDataExample

This is the basic bring-up example. It:

  • Uses setupBoards() and the board I2C scan to discover the magnetometer address.
  • Initializes the sensor.
  • Configures continuous measurement mode.
  • Prints compensated values and raw values for X/Y/Z to serial.

This is the best starting point for an exercise whose first goal is "show me live data."

QMC6310_GetPolarExample

This example is the smallest heading-oriented demo. It:

  • Reads magnetometer data.
  • Converts the reading to a polar heading.
  • Applies a declination correction.
  • Prints heading, Gauss, and microtesla values.

This is useful once raw axis data is already trusted.

QMC6310_CompassExample

This is a user-interface example. It:

  • Reads X/Y values.
  • Computes a heading with a hard-coded declination.
  • Draws a compass arrow on the OLED.
  • Also emits debug values on serial.

This demonstrates a real-time visual display, but it is still only as good as the underlying calibration.

QMC6310_CalibrateExample

This example computes hard-iron offsets by watching the min/max raw values while the board is slowly rotated. It then applies:

  • x_offset = (x_max + x_min) / 2
  • y_offset = (y_max + y_min) / 2
  • z_offset = (z_max + z_min) / 2

Those offsets are passed to qmc.setOffset(...), after which subsequent readings are shifted by those values.

This is a runtime calibration aid, not a persistent factory calibration workflow.

Calibration: Does It Need To Happen Every Boot?

Short answer: no, not if you save the offsets somewhere and re-apply them at boot.

Important detail from the local driver:

  • setOffset(...) only stores offsets in the driver object's RAM.
  • The offsets are not written into nonvolatile sensor storage.
  • begin(...) calls initImpl(...), and initImpl(...) performs reset().

Implications:

  • A normal reboot loses the offsets unless your firmware stores them elsewhere.
  • Removing main power loses them.
  • Keeping a battery attached does not make these software offsets persist in the magnetometer.
  • If the board environment has not changed, you do not need to physically recalibrate at every boot, but you do need to reload previously measured offsets from persistent storage.

Recommended practice for this exercise:

  • Add a one-time calibration mode.
  • Save the resulting offsets in persistent storage on the ESP32.
  • Re-apply offsets automatically at startup.
  • Re-run calibration only when the enclosure, mounting, nearby wiring, or magnetic environment changes.

Declination Offset

Declination is separate from magnetometer calibration.

  • Calibration removes local sensor bias and board-level hard-iron offset.
  • Declination converts magnetic north into true north for a specific latitude, longitude, and date.

So yes: if you want true heading, the user should specify a coordinate, obtain the current declination for that coordinate, and store that value with provenance.

For this exercise, the practical design is:

  1. Choose the operating coordinates.
  2. Obtain declination on the host, not on the ESP32.
  3. Save the fetched value in a simple text file such as declination.txt.
  4. Load that value into firmware at build time or into ESP32 persistent storage at provisioning time.
  5. Re-fetch when the unit is moved a meaningful distance or after enough time has passed that the stored date is stale.

Why host-side instead of device-side:

  • The ESP32 firmware should not depend on live network access to a geomagnetic service.
  • Declination changes slowly, so caching is appropriate.
  • A text file with comments gives you traceability for how the value was obtained.

Recommended file shape:

# procured: April 16, 2026
# for coordinates: 44.93642012667761, -123.02203699545396
# source: NOAA/NCEI geomagnetic declination calculator
# sign convention: east positive, west negative
declination_deg=14.47

For your current manually captured value:

2026-04-16      14.47° E  ± 0.36°  changing by 0.11° W per year

the appropriate build-time value is:

MAG_DECLINATION_DEG = 14.47

using the convention:

  • East declination is positive.
  • West declination is negative.

That convention should be written directly into the firmware and README so there is no hidden sign ambiguity.

Operational guidance:

  • For a fixed station, one fetched value is usually sufficient and should simply be refreshed occasionally.
  • For a mobile unit used across a region, a single fixed declination is still acceptable for an early exercise, but it should be understood as an approximation.
  • For this project stage, storing one deployment-specific declination value is the right tradeoff.

Multi-Unit Assay For Seven Boards

Lewis He's current note says T-Beam Supreme units may contain QMC6310N, QMC6310U, or QMC6309, each with a different I2C address.

What the local LilyGo code currently supports:

  • 0x1C -> treated as QMC6310U
  • 0x3C -> treated as QMC6310N after distinguishing it from the OLED at the same address family

What is missing locally:

  • I do not see QMC6309 support anywhere in the local clone.
  • The referenced GitHub commit is not present in the local repository, so the current local examples cannot yet identify QMC6309.

Practical assay recommendation for units A through G:

  1. Build a small probe sketch for a selected PlatformIO environment such as cy.
  2. Run the same board scan that setupBoards() already performs.
  3. Print the detected magnetometer I2C address.
  4. Print getChipID().
  5. Record the result per unit label: AMY, BOB, CY, DAN, ED, FLO, GUY.

Expected result set:

  • If the unit answers at 0x1C, classify it as QMC6310U.
  • If the unit answers at 0x3C, classify it as QMC6310N.
  • If a unit answers at some third address, that is the candidate path for QMC6309, but the local code will need to be extended to name it explicitly.

The clean design is a real-time serial-first field monitor, with OLED support as a secondary display.

Suggested output in real time:

  • Unit identifier, for example CY
  • Detected magnetometer address
  • Chip ID
  • Build UTC tag
  • Raw X/Y/Z
  • Offset-corrected X/Y/Z
  • Declination in degrees and its source date
  • Heading in degrees
  • Total field magnitude
  • Calibration status

Suggested workflow:

  1. Boot and identify the attached board and detected magnetometer address.
  2. Load saved calibration offsets if present.
  3. Stream readings continuously over serial for capture and inspection.
  4. Optionally mirror heading and a simple arrow on the OLED.
  5. Provide a serial command or compile-time mode to enter calibration.
  6. Save newly computed offsets for future boots.
  7. Apply stored declination before reporting true heading.

Bottom Line

The examples are enough to establish that this sensor is suitable for a live heading and magnetic field display exercise. The only caution is that the example labeled "calibration" is session-local unless we explicitly save in non-volatile memory the offsets. The first useful deliverable for this exercise should therefore be a per-unit magnetometer assay plus a serial real-time monitor, followed by persistent calibration storage and then an OLED compass view.

Local Helper Scripts

Exercise 22 now includes:

  • scripts/set_build_epoch.py copied from Exercise 18 so the same unique build timestamp mechanism is available here.
  • scripts/fetch_declination.pl to fetch a declination value on the host and write a provenance-rich declination.txt.
  • platformio.ini scaffolded with the build-stamp hook and manual declination build defines.

The NOAA fetch helper remains available, but given the current registration requirement, the recommended workflow is manual lookup plus a checked-in text record such as declination_manual.txt.