mirror of
https://github.com/ALLFATHER-BV/wadamesh.git
synced 2026-09-25 15:34:39 +00:00
Firmware
- variants/thinknode_m9/M9Compass.{h,cpp}: QMC6309 at 0x7C on the peripheral
bus. Probe by chip id, soft reset with the explicit clear, CTRL2 0x20
(50 Hz, +/-32 G, set/reset on), CTRL1 0x61 (normal, OSR 8/8), read-back
verified. Synchronous read from the Lua host bridge, 1 s cache, lazy
re-probe while the rail-powered part is still in POR, OVFL kept + flagged +
logged. HAS_M9_COMPASS=1 in the env -> new hardware gate CAP_COMPASS.
- src/helpers/WadaNmeaLocationProvider.h: Wadamesh-owned copy of the core
MicroNMEALocationProvider that also exposes RMC speed/course (the core
keeps its parser private; no libdeps patch). M9 target.cpp builds on it,
HAS_GPS_MOTION=1, wadaGpsMotion() for UITask.
- wada.sys.gps(): + alt (m), + speed_kmh/course where the board provides
them (course only while moving -- an empty RMC course parses as 0), and
nil while the user has GPS switched off. wada.sys.compass(): {x,y,z,ovfl}
Gauss, sensor frame, uncalibrated, registered only where CAP_COMPASS;
caps().compass. Calibration, axis mapping and the heading maths live in
the app so they can be adjusted per user without a firmware cut.
- Host: luaHostContactAt reads the RTC once per contacts() walk instead of
once per contact (an I2C transaction each on the M9); pressCb reports
press coordinates in body content space (scroll offset folded in).
App
- deploy/apps/gpscompass/1.0 + apps.json: rotating rose with a fixed index,
live fix readout, bearing/range to a selected contact, magnetometer
heading with hard-iron calibration (C), frame rotate/mirror (O/F, the
M9's sensor orientation is undocumented), GPS-course fallback on every
other board, saturation warning. Not baked into lua_builtin.h on purpose
(CAP_BUILTIN_LUA_APPS also removes the Store > Apps tab).
Verified: M9, V4, V4-R8, T-Deck compile (M9 flash +2 KB); two adversarial
review passes, all confirmed findings fixed; host Lua harness (vendored
Lua, LUA_32BITS) -- calibration recovers a simulated bias exactly, heading
error 0 deg, worst tick ~10k of the 100k budget. Hardware validation list
(axis orientation, bias magnitude, 0x7C ACK) in M9_PORT.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
48 lines
2.4 KiB
C++
48 lines
2.4 KiB
C++
#pragma once
|
|
|
|
// ThinkNode M9 magnetometer — QST QMC6309 on the peripheral I2C bus (Wire,
|
|
// SDA=7 / SCL=6) at 7-bit address 0x7C. The chip sits on the GPIO18 peripheral
|
|
// rail with the LCD/GPS, which ThinkNodeM9Board::begin() claims for the whole
|
|
// runtime, so it is powered whenever the firmware runs.
|
|
//
|
|
// This is deliberately a dumb sensor reader: it hands out the raw field vector
|
|
// in Gauss, sensor frame, uncalibrated. Hard-iron calibration, the sensor-to-
|
|
// screen axis mapping (not documented anywhere — Meshtastic's own M9 driver
|
|
// marks its heading offset "must be verified on real hardware" and never uses
|
|
// it) and the heading maths live in the consumer (the GPS Compass Lua app),
|
|
// where they can be adjusted and persisted per user without a firmware cut.
|
|
//
|
|
// Register map (QMC6309 datasheet Rev A, cross-checked against SlimeVR,
|
|
// madflight and the emfcamp Tildagon drivers — NOT against SensorLib, whose
|
|
// setOutputDataRate() writes the ODR into the wrong register):
|
|
// 0x00 chip id (0x90) 0x01..0x06 X/Y/Z int16 little-endian
|
|
// 0x09 status: bit0 DRDY (cleared by reading 0x09), bit1 OVFL
|
|
// 0x0A CTRL1: OSR2[7:5] OSR1[4:3] MODE[1:0] (00 suspend, 01 normal, 11 cont.)
|
|
// 0x0B CTRL2: SOFT_RST[7] ODR[6:4] RNG[3:2] SET/RESET[1:0]
|
|
// Sensitivity at the ±32 G range used here: 1000 LSB/G (1 mG per count).
|
|
#if defined(HAS_M9_COMPASS) && defined(ESP32)
|
|
|
|
#include <stdint.h>
|
|
|
|
class TwoWire;
|
|
|
|
/** Probe + configure the chip on `w`. Safe to call when the chip is absent or
|
|
* not yet out of power-on reset: the read path re-probes on its own. Logs one
|
|
* line to Serial either way (matches the keyboard bring-up style). */
|
|
void m9CompassBegin(TwoWire& w);
|
|
|
|
/** True once the chip has answered with its id and taken the configuration. */
|
|
bool m9CompassPresent();
|
|
|
|
/** Latest field vector in Gauss, sensor frame, uncalibrated. Reads the chip
|
|
* synchronously when a new sample is ready (three short I2C transactions,
|
|
* well under 1 ms at 100 kHz on a healthy bus), otherwise returns the cached
|
|
* sample while it is younger than a second. False = no chip, bus error, or
|
|
* nothing fresh. `overflow` (optional) is set when the chip flagged the
|
|
* sample as saturated (an axis beyond ±32000 counts): the values are still
|
|
* returned so a consumer can show "away from magnets" rather than "no
|
|
* compass", but they are not a usable heading. */
|
|
bool m9CompassRead(float* x_gauss, float* y_gauss, float* z_gauss, bool* overflow = nullptr);
|
|
|
|
#endif
|