Diametrical Magnet Orientation
Travel Area: ±2.5 mm (±170 px). N (dark charcoal) / S (gold) mark the magnet poles; the north side tracks magnetic orientation.
Thumbstick Signal & Calibration
Live α/β angles and field strengths from the twisted magnet demo. Units: mT
Gain & Calibration Config
a1 = atan2( √(z1² + (k1·(y1 − o11·z1))²), (x1 − o12·z1) )
b1 = atan2( √(z1² + (k2·(x1 − o21·z1))²), (y1 − o22·z1) )
Angles in degrees; calibration constants update live.
How the α/β calibration works here
The raw readouts are plain atan2 over the magnet reply. The calibrated ones run the
same Desmos model as the Voxdale page — the shared angle_calib.js module.
The formula above rewrites the α and β angles so each of them sees two components:
an in-plane (tangential) one X/Y and a perpendicular Z one, with the cross-axis mixing
scaled out by k and the orthogonality terms.
What feeds the formula (signal mapping by mode):
- 1px mode (units: mT) — pixel 0’s absolute field: x1 = Bx, y1 = By, z1 = Bz. α lives in the X–Z plane, β in the Y–Z plane.
- 2px mode (units: mT/mm) — the differential channels between adjacent pixels: x1 = X02, y1 = Y13, and z1 = Z02 for α / Z13 for β. Here the “companion” inside a1’s square root is the perpendicular gradient, so tilts along one axis are measured against the opposite pair’s gradient channel.
The six constants:
- k1, k2 — K-factors. Scale the cross-axis component feeding each angle: 1.0 keeps it as measured, >1 amplifies it, <1 attenuates it. Use these when one axis reads stronger/weaker than the geometry really is (gain trim).
- o11, o12 — α orthogonality terms; o21, o22 — β orthogonality terms. Each subtracts the Z feedthrough from an in-plane axis (lower term, o12/o22, acting on x1/y1) or the in-plane feedthrough from the Z leg (upper term, o11/o21, acting inside the square root). Non-zero values cancel mounting or sensitivity crosstalk.
Identity = raw. With k = 1 and every o = 0 the calibrated readout is the pure
atan2 (no cross-axis mixing), which keeps the two columns equal until you enter
constants. Values normalize live; non-finite inputs never break the loop.
Frame Health
Everything on this tab is the unmodified reply from the three transfers the loop issues each cycle: 0xFC000 and 0x03F00 for the twelve absolute pixels, 0x000FC for the six differential channels. Nothing is averaged, scaled or clamped.
| Source | -- |
|---|---|
| Status byte | -- |
| Function | -- |
| Data ready | -- |
| Flags | -- |
| CRC | -- |
| Loop period | -- |
Absolute Pixels — raw LSB
Signed 12-bit counts, full scale ±2048. The averages are the four-pixel means the thumbstick knob reads from.
| Channel | Count |
|---|---|
| X0 | -- |
| Y0 | -- |
| Z0 | -- |
| X1 | -- |
| Y1 | -- |
| Z1 | -- |
| X2 | -- |
| Y2 | -- |
| Z2 | -- |
| X3 | -- |
| Y3 | -- |
| Z3 | -- |
| Average X | -- |
| Average Y | -- |
| Average Z | -- |
| Peak |count| | -- |
Differential Channels — raw LSB
The chip's own inter-pixel measurements, read in a single transfer. They populate this tab only — the thumbstick knob is driven by the 4-pixel absolute average again, so there is no gradient/field source switch anymore.
| Channel | Count |
|---|---|
| X02 | -- |
| Y02 | -- |
| Z02 | -- |
| X13 | -- |
| Y13 | -- |
| Z13 | -- |
Knob Output
What the thumbstick tab is drawing, after the 4-pixel absolute average and the joystick scale are applied.
| Position source | Absolute field (avg X, Y) |
|---|---|
| Knob X | -- |
| Knob Y | -- |
| Angle | -- |