Compare commits

...
Sign in to create a new pull request.

10 commits

11 changed files with 1510 additions and 74 deletions

3
.gitignore vendored
View file

@ -11,3 +11,6 @@ script
build
LilyGoLoRaBoard
record.*
*log
.codex

View file

@ -26,6 +26,9 @@
* @author Lewis He (lewishe@outlook.com)
* @date 2026-01-26
*
* Modified 2026-04-21 to compute mean-based offsets from CALRAW samples
* instead of midpoint-of-extrema offsets.
*
*/
#include <Wire.h>
#include <SPI.h>
@ -89,13 +92,20 @@ void calibrate()
int32_t z_min = 65535;
int32_t z_max = -65535;
int32_t range = 1000;
int32_t range = 2000;
int32_t i = 0;
int32_t x = 0, y = 0, z = 0;;
int32_t raw_x = 0, raw_y = 0, raw_z = 0;
int16_t x_offset = 0;
int16_t y_offset = 0;
int16_t z_offset = 0;
uint32_t sample_counter = 0;
// Mean-based accumulation of raw calibration samples.
int64_t x_sum = 0;
int64_t y_sum = 0;
int64_t z_sum = 0;
MagnetometerData data;
while (i < range) {
i += 1;
@ -103,48 +113,89 @@ void calibrate()
if (magnetometer.isDataReady()) {
magnetometer.readData(data);
sample_counter++;
x = (data.raw.x + x) / 2;
y = (data.raw.y + y) / 2;
z = (data.raw.z + z) / 2;
if (x < x_min) {
x_min = x;
i = 0;
}
if (x > x_max) {
x_max = x;
i = 0;
}
if (y < y_min) {
y_min = y;
i = 0;
}
if (y > y_max) {
y_max = y;
i = 0;
}
if (z < z_min) {
z_min = z;
i = 0;
}
if (z > z_max) {
z_max = z;
i = 0;
}
int j = round(10 * i / range);
raw_x = data.raw.x;
raw_y = data.raw.y;
raw_z = data.raw.z;
Serial.print("[");
for (int k = 0; k < j; ++k) {
Serial.print("*");
x_sum += raw_x;
y_sum += raw_y;
z_sum += raw_z;
Serial.print("CALRAW ");
Serial.print("X:");
Serial.print(raw_x);
Serial.print(" Y:");
Serial.print(raw_y);
Serial.print(" Z:");
Serial.println(raw_z);
// Track extrema on the direct raw sample, not on a running average.
if (raw_x < x_min) {
x_min = raw_x;
i = 0;
}
if (raw_x > x_max) {
x_max = raw_x;
i = 0;
}
if (raw_y < y_min) {
y_min = raw_y;
i = 0;
}
if (raw_y > y_max) {
y_max = raw_y;
i = 0;
}
if (raw_z < z_min) {
z_min = raw_z;
i = 0;
}
if (raw_z > z_max) {
z_max = raw_z;
i = 0;
}
if ((sample_counter % 100) == 0) {
Serial.print("CALSTAT i=");
Serial.print(i);
Serial.print(" samples=");
Serial.print(sample_counter);
Serial.print(" x_min=");
Serial.print(x_min);
Serial.print(" x_max=");
Serial.print(x_max);
Serial.print(" y_min=");
Serial.print(y_min);
Serial.print(" y_max=");
Serial.print(y_max);
Serial.print(" z_min=");
Serial.print(z_min);
Serial.print(" z_max=");
Serial.println(z_max);
}
Serial.println("]");
}
delay(5);
}
x_offset = (x_max + x_min) / 2;
y_offset = (y_max + y_min) / 2;
z_offset = (z_max + z_min) / 2;
if (sample_counter == 0) {
Serial.println("No calibration samples were collected.");
return;
}
// Mean/centroid-based offsets.
x_offset = (int16_t)lround((double)x_sum / (double)sample_counter);
y_offset = (int16_t)lround((double)y_sum / (double)sample_counter);
z_offset = (int16_t)lround((double)z_sum / (double)sample_counter);
// Also compute the old midpoint offsets for comparison/debugging.
int16_t x_midpoint_offset = (x_max + x_min) / 2;
int16_t y_midpoint_offset = (y_max + y_min) / 2;
int16_t z_midpoint_offset = (z_max + z_min) / 2;
Serial.print("samples:");
Serial.println(sample_counter);
Serial.print("x_min:");
Serial.println(x_min);
@ -164,21 +215,30 @@ void calibrate()
Serial.print("z_max:");
Serial.println(z_max);
Serial.print("x_offset:");
Serial.print("x_midpoint_offset:");
Serial.println(x_midpoint_offset);
Serial.print("y_midpoint_offset:");
Serial.println(y_midpoint_offset);
Serial.print("z_midpoint_offset:");
Serial.println(z_midpoint_offset);
Serial.print("x_mean_offset:");
Serial.println(x_offset);
Serial.print("y_offset:");
Serial.print("y_mean_offset:");
Serial.println(y_offset);
Serial.print("z_offset:");
Serial.print("z_mean_offset:");
Serial.println(z_offset);
// Set the calibration value and the user calculates the deviation
// Set the mean-based calibration values.
magnetometer.setOffset(x_offset, y_offset, z_offset);
Serial.println();
Serial.println("Calibration complete!");
Serial.println("Offsets applied using raw-sample means.");
Serial.println("Check if Magnetic Strength is ~25-65 uT");
Serial.println("If too low, repeat calibration with better rotation");
}
@ -232,45 +292,65 @@ void setup()
while (1);
}
// Calibration algorithm reference from
// https://github.com/CoreElectronics/CE-PiicoDev-QMC6310-MicroPython-Module
calibrate();
Serial.println("Calibration done.");
delay(5000);
SensorInfo info = magnetometer.getSensorInfo();
Serial.print("Manufacturer: "); Serial.println(info.manufacturer);
Serial.print("Model: "); Serial.println(info.model);
Serial.print("I2C Address: 0x"); Serial.println(info.i2c_address, HEX);
Serial.print("Version: "); Serial.println(info.version);
Serial.print("UID: 0x"); Serial.println(info.uid);
Serial.print("Type: "); Serial.println(SensorUtils::typeToString(info.type));
SensorConfig cfg = magnetometer.getConfig();
Serial.print("DataRate: "); Serial.println(cfg.sample_rate);
Serial.print("FullScaleRange: "); Serial.println(cfg.range);
Serial.print("Mode: "); Serial.println((uint8_t)cfg.mode);
Serial.println();
//Find the magnetic declination : https://www.magnetic-declination.com/
float declination_deg = MagnetometerUtils::dmsToDecimalDegrees(-3, 20); // -3.3333
// Print Sensor ID and basic configuration.
SensorInfo info = magnetometer.getSensorInfo();
Serial.print("Manufacturer: ");
Serial.println(info.manufacturer);
magnetometer.setDeclination(declination_deg);
Serial.print("Model: ");
Serial.println(info.model);
Serial.print("I2C Address: 0x");
Serial.println(info.i2c_address, HEX);
Serial.print("Version: ");
Serial.println(info.version);
Serial.print("UID: 0x");
Serial.println(info.uid, HEX);
Serial.print("Type: ");
Serial.print("Type: "); Serial.println(SensorUtils::typeToString(info.type));
Serial.println();
/* Print Sensor settings. */
// TODO: Add getSensorSettings() method to retrieve current sensor settings and print them here.
// auto settings = magnetometer.getSensorSettings();
// Serial.print("DataRate: ");
// Serial.println(settings.outputDataRate);
// Serial.print("FullScaleRange: ");
// Serial.println(settings.fullScaleRange);
// Serial.print("Mode: ");
// Serial.println(settings.mode);
// Serial.println();
// User's location magnetic declination offset.
//float declination_offset_deg = -3.33f; // Lewis's
float declination_offset_deg = 14.28f; // Salem, Oregon
magnetometer.setDeclination(declination_offset_deg);
Serial.print(" Magnetic Declination: ");
Serial.print(declination_deg, 2);
Serial.print(declination_offset_deg);
Serial.println("°");
// Print current sensitivity.
// Full scale 2G: 0.000067 Gauss/LSB
// Full scale 8G: 0.000267 Gauss/LSB
// Full scale 12G: 0.000400 Gauss/LSB
// Full scale 30G: 0.001000 Gauss/LSB
Serial.print(" Sensitivity: ");
Serial.print(magnetometer.getSensitivity(), 6);
Serial.println(" Gauss/LSB");
delay(3000);
Serial.println("Read data now...");
}
void loop()
{
MagnetometerData data;
@ -300,12 +380,31 @@ void loop()
Serial.print(" Z:");
Serial.print(data.raw.z);
Serial.print(" Heading (rad): ");
Serial.print(data.heading, 6);
//Serial.print(" Heading (rad): ");
//Serial.print(data.heading, 6);
//Serial.print(" rad");
//Serial.print(" Heading (deg): ");
//Serial.print(data.heading_degrees, 2);
//Serial.print("°");
// Use raw values (already offset-corrected by calibration)
float Xc = data.raw.x;
float Yc = data.raw.y;
// Compute corrected heading
//float heading_rad = atan2(Yc, Xc); // this was mirrored
float heading_rad = atan2(-Yc, Xc);
// Convert to degrees
float heading_deg = heading_rad * 180.0 / PI;
if (heading_deg < 0) heading_deg += 360;
// Print corrected values
Serial.print(" HeadingCorr (rad): ");
Serial.print(heading_rad, 6);
Serial.print(" rad");
Serial.print(" Heading (deg): ");
Serial.print(data.heading_degrees, 2);
Serial.print(" HeadingCorr (deg): ");
Serial.print(heading_deg, 2);
Serial.print("°");
float strength = MagnetometerUtils::calculateMagneticStrength(data);

View file

@ -0,0 +1,424 @@
/**
*
* @license MIT License
*
* Copyright (c) 2026 lewis he
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @file QMC6310_CalibrateExample.ino
* @author Lewis He (lewishe@outlook.com)
* @date 2026-01-26
*
* Modified 2026-04-21 to compute mean-based offsets from CALRAW samples
* instead of midpoint-of-extrema offsets.
*
*/
#include <Wire.h>
#include <SPI.h>
#include <Arduino.h>
#include "SensorQMC6310.hpp"
#define ARDUINO_T_BEAM_S3_SUPREME
#ifdef ARDUINO_T_BEAM_S3_SUPREME
#include <XPowersAXP2101.tpp> //PMU Library https://github.com/lewisxhe/XPowersLib.git
#endif
#ifndef SENSOR_SDA
#define SENSOR_SDA 17
#endif
#ifndef SENSOR_SCL
#define SENSOR_SCL 18
#endif
SensorQMC6310 magnetometer;
void beginPower()
{
#if defined(ARDUINO_T_BEAM_S3_SUPREME)
XPowersAXP2101 power;
power.begin(Wire1, AXP2101_SLAVE_ADDRESS, 42, 41);
power.disableALDO1();
power.disableALDO2();
delay(250);
power.setALDO1Voltage(3300);
power.enableALDO1();
power.setALDO2Voltage(3300);
power.enableALDO2();
#endif
}
void calibrate()
{
if (!magnetometer.setOutputDataRate(200.0f)) {
Serial.println("Failed to set output data rate");
return ;
}
Serial.println("========================================");
Serial.println("Calibration Instructions:");
Serial.println("1. Rotate sensor in FIGURE-8 pattern");
Serial.println("2. Cover all axes (X, Y, Z directions)");
Serial.println("3. Rotate slowly and completely");
Serial.println("4. Wait for progress bar to complete");
Serial.println("5. Expected: Magnetic Strength ~25-65 uT");
Serial.println("========================================");
Serial.println();
Serial.println("Place the sensor on the plane and slowly rotate the sensor...");
Serial.println("Rotate in FIGURE-8 pattern to cover all directions!");
Serial.println();
int32_t x_min = 65535;
int32_t x_max = -65535;
int32_t y_min = 65535;
int32_t y_max = -65535;
int32_t z_min = 65535;
int32_t z_max = -65535;
int32_t range = 2000;
int32_t i = 0;
int32_t raw_x = 0, raw_y = 0, raw_z = 0;
int16_t x_offset = 0;
int16_t y_offset = 0;
int16_t z_offset = 0;
uint32_t sample_counter = 0;
// Mean-based accumulation of raw calibration samples.
int64_t x_sum = 0;
int64_t y_sum = 0;
int64_t z_sum = 0;
MagnetometerData data;
while (i < range) {
i += 1;
if (magnetometer.isDataReady()) {
magnetometer.readData(data);
sample_counter++;
raw_x = data.raw.x;
raw_y = data.raw.y;
raw_z = data.raw.z;
x_sum += raw_x;
y_sum += raw_y;
z_sum += raw_z;
Serial.print("CALRAW ");
Serial.print("X:");
Serial.print(raw_x);
Serial.print(" Y:");
Serial.print(raw_y);
Serial.print(" Z:");
Serial.println(raw_z);
// Track extrema on the direct raw sample, not on a running average.
if (raw_x < x_min) {
x_min = raw_x;
i = 0;
}
if (raw_x > x_max) {
x_max = raw_x;
i = 0;
}
if (raw_y < y_min) {
y_min = raw_y;
i = 0;
}
if (raw_y > y_max) {
y_max = raw_y;
i = 0;
}
if (raw_z < z_min) {
z_min = raw_z;
i = 0;
}
if (raw_z > z_max) {
z_max = raw_z;
i = 0;
}
if ((sample_counter % 100) == 0) {
Serial.print("CALSTAT i=");
Serial.print(i);
Serial.print(" samples=");
Serial.print(sample_counter);
Serial.print(" x_min=");
Serial.print(x_min);
Serial.print(" x_max=");
Serial.print(x_max);
Serial.print(" y_min=");
Serial.print(y_min);
Serial.print(" y_max=");
Serial.print(y_max);
Serial.print(" z_min=");
Serial.print(z_min);
Serial.print(" z_max=");
Serial.println(z_max);
}
}
delay(5);
}
if (sample_counter == 0) {
Serial.println("No calibration samples were collected.");
return;
}
// Mean/centroid-based offsets.
x_offset = (int16_t)lround((double)x_sum / (double)sample_counter);
y_offset = (int16_t)lround((double)y_sum / (double)sample_counter);
z_offset = (int16_t)lround((double)z_sum / (double)sample_counter);
// Also compute the old midpoint offsets for comparison/debugging.
int16_t x_midpoint_offset = (x_max + x_min) / 2;
int16_t y_midpoint_offset = (y_max + y_min) / 2;
int16_t z_midpoint_offset = (z_max + z_min) / 2;
Serial.print("samples:");
Serial.println(sample_counter);
Serial.print("x_min:");
Serial.println(x_min);
Serial.print("x_max:");
Serial.println(x_max);
Serial.print("y_min:");
Serial.println(y_min);
Serial.print("y_max:");
Serial.println(y_max);
Serial.print("z_min:");
Serial.println(z_min);
Serial.print("z_max:");
Serial.println(z_max);
Serial.print("x_midpoint_offset:");
Serial.println(x_midpoint_offset);
Serial.print("y_midpoint_offset:");
Serial.println(y_midpoint_offset);
Serial.print("z_midpoint_offset:");
Serial.println(z_midpoint_offset);
Serial.print("x_mean_offset:");
Serial.println(x_offset);
Serial.print("y_mean_offset:");
Serial.println(y_offset);
Serial.print("z_mean_offset:");
Serial.println(z_offset);
// Set the mean-based calibration values.
magnetometer.setOffset(x_offset, y_offset, z_offset);
Serial.println();
Serial.println("Calibration complete!");
Serial.println("Offsets applied using raw-sample means.");
Serial.println("Check if Magnetic Strength is ~25-65 uT");
Serial.println("If too low, repeat calibration with better rotation");
}
void setup()
{
Serial.begin(115200);
while (!Serial);
// LilyGo T-Beam-Supreme sensor requires a power source to function.
beginPower();
/**
* Supports QMC6310U and QMC6310N; simply pass the corresponding device address
* during initialization.
* - QMC6310U_SLAVE_ADDRESS
* - QMC6310N_SLAVE_ADDRESS
*/
uint8_t address = QMC6310U_SLAVE_ADDRESS;
// uint8_t address = QMC6310N_SLAVE_ADDRESS;
if (!magnetometer.begin(Wire, address, SENSOR_SDA, SENSOR_SCL)) {
while (1) {
Serial.println("Failed to find QMC6310 - check your wiring!");
delay(1000);
}
}
// The desired output data rate in Hz. Allowed values are 10.0, 50.0, 100.0 and 200.0HZ.
float data_rate_hz = 200.0f;
// op_mode: Allowed values are SUSPEND, NORMAL, SINGLE_MEASUREMENT, CONTINUOUS_MEASUREMENT
OperationMode op_mode = OperationMode::CONTINUOUS_MEASUREMENT;
// full_scale: Allowed values are FS_2G, FS_8G, FS_12G ,FS_30G
MagFullScaleRange full_scale = MagFullScaleRange::FS_8G;
// over_sample_ratio: Allowed values are OSR_1, OSR_2, OSR_4, OSR_8
MagOverSampleRatio over_sample_ratio = MagOverSampleRatio::OSR_1;
// down_sample_ratio: Allowed values are DSR_1, DSR_2, DSR_4, DSR_8
MagDownSampleRatio down_sample_ratio = MagDownSampleRatio::DSR_1;
/* Config Magnetometer */
if (magnetometer.configMagnetometer(
op_mode,
full_scale,
data_rate_hz,
over_sample_ratio,
down_sample_ratio)) {
Serial.println("Magnetometer configured successfully.");
} else {
Serial.println("Magnetometer configuration failed.");
while (1);
}
calibrate();
Serial.println("Calibration done.");
Serial.println();
// Print Sensor ID and basic configuration.
SensorInfo info = magnetometer.getSensorInfo();
Serial.print("Manufacturer: ");
Serial.println(info.manufacturer);
Serial.print("Model: ");
Serial.println(info.model);
Serial.print("I2C Address: 0x");
Serial.println(info.i2c_address, HEX);
Serial.print("Version: ");
Serial.println(info.version);
Serial.print("UID: 0x");
Serial.println(info.uid, HEX);
Serial.print("Type: ");
Serial.print("Type: "); Serial.println(SensorUtils::typeToString(info.type));
Serial.println();
/* Print Sensor settings. */
// TODO: Add getSensorSettings() method to retrieve current sensor settings and print them here.
// auto settings = magnetometer.getSensorSettings();
// Serial.print("DataRate: ");
// Serial.println(settings.outputDataRate);
// Serial.print("FullScaleRange: ");
// Serial.println(settings.fullScaleRange);
// Serial.print("Mode: ");
// Serial.println(settings.mode);
// Serial.println();
// User's location magnetic declination offset.
//float declination_offset_deg = -3.33f; // Lewis's
float declination_offset_deg = 14.28f; // Salem, Oregon
magnetometer.setDeclination(declination_offset_deg);
Serial.print(" Magnetic Declination: ");
Serial.print(declination_offset_deg);
Serial.println("°");
// Print current sensitivity.
// Full scale 2G: 0.000067 Gauss/LSB
// Full scale 8G: 0.000267 Gauss/LSB
// Full scale 12G: 0.000400 Gauss/LSB
// Full scale 30G: 0.001000 Gauss/LSB
Serial.print(" Sensitivity: ");
Serial.print(magnetometer.getSensitivity(), 6);
Serial.println(" Gauss/LSB");
}
void loop()
{
MagnetometerData data;
if (magnetometer.readData(data)) {
// Gauss to μT
float x = MagnetometerUtils::gaussToMicroTesla(data.magnetic_field.x);
float y = MagnetometerUtils::gaussToMicroTesla(data.magnetic_field.y);
float z = MagnetometerUtils::gaussToMicroTesla(data.magnetic_field.z);
Serial.print("Mag:");
Serial.print(" X:"); Serial.print(x);
Serial.print(" Y:"); Serial.print(y);
Serial.print(" Z:"); Serial.print(z);
Serial.print(" μT");
Serial.print(" Sensitivity: ");
Serial.print(magnetometer.getSensitivity(), 6);
Serial.print(" Gauss/LSB");
Serial.print(" Metadata:");
Serial.print(" X:");
Serial.print(data.raw.x);
Serial.print(" Y:");
Serial.print(data.raw.y);
Serial.print(" Z:");
Serial.print(data.raw.z);
//Serial.print(" Heading (rad): ");
//Serial.print(data.heading, 6);
//Serial.print(" rad");
//Serial.print(" Heading (deg): ");
//Serial.print(data.heading_degrees, 2);
//Serial.print("°");
// Use raw values (already offset-corrected by calibration)
float Xc = data.raw.x;
float Yc = data.raw.y;
// Compute corrected heading
//float heading_rad = atan2(Yc, Xc); // this was mirrored
float heading_rad = atan2(-Yc, Xc);
// Convert to degrees
float heading_deg = heading_rad * 180.0 / PI;
if (heading_deg < 0) heading_deg += 360;
// Print corrected values
Serial.print(" HeadingCorr (rad): ");
Serial.print(heading_rad, 6);
Serial.print(" rad");
Serial.print(" HeadingCorr (deg): ");
Serial.print(heading_deg, 2);
Serial.print("°");
float strength = MagnetometerUtils::calculateMagneticStrength(data);
strength = MagnetometerUtils::gaussToMicroTesla(strength);
Serial.print(" Magnetic Strength: ");
Serial.print(strength, 2);
Serial.println(" μT");
if (data.overflow) {
Serial.println("\tWarning: Data Overflow occurred!");
}
}
delay(10);
}

View file

@ -0,0 +1,424 @@
/**
*
* @license MIT License
*
* Copyright (c) 2026 lewis he
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @file QMC6310_CalibrateExample.ino
* @author Lewis He (lewishe@outlook.com)
* @date 2026-01-26
*
* Modified 2026-04-21 to compute mean-based offsets from CALRAW samples
* instead of midpoint-of-extrema offsets.
*
*/
#include <Wire.h>
#include <SPI.h>
#include <Arduino.h>
#include "SensorQMC6310.hpp"
#define ARDUINO_T_BEAM_S3_SUPREME
#ifdef ARDUINO_T_BEAM_S3_SUPREME
#include <XPowersAXP2101.tpp> //PMU Library https://github.com/lewisxhe/XPowersLib.git
#endif
#ifndef SENSOR_SDA
#define SENSOR_SDA 17
#endif
#ifndef SENSOR_SCL
#define SENSOR_SCL 18
#endif
SensorQMC6310 magnetometer;
void beginPower()
{
#if defined(ARDUINO_T_BEAM_S3_SUPREME)
XPowersAXP2101 power;
power.begin(Wire1, AXP2101_SLAVE_ADDRESS, 42, 41);
power.disableALDO1();
power.disableALDO2();
delay(250);
power.setALDO1Voltage(3300);
power.enableALDO1();
power.setALDO2Voltage(3300);
power.enableALDO2();
#endif
}
void calibrate()
{
if (!magnetometer.setOutputDataRate(200.0f)) {
Serial.println("Failed to set output data rate");
return ;
}
Serial.println("========================================");
Serial.println("Calibration Instructions:");
Serial.println("1. Rotate sensor in FIGURE-8 pattern");
Serial.println("2. Cover all axes (X, Y, Z directions)");
Serial.println("3. Rotate slowly and completely");
Serial.println("4. Wait for progress bar to complete");
Serial.println("5. Expected: Magnetic Strength ~25-65 uT");
Serial.println("========================================");
Serial.println();
Serial.println("Place the sensor on the plane and slowly rotate the sensor...");
Serial.println("Rotate in FIGURE-8 pattern to cover all directions!");
Serial.println();
int32_t x_min = 65535;
int32_t x_max = -65535;
int32_t y_min = 65535;
int32_t y_max = -65535;
int32_t z_min = 65535;
int32_t z_max = -65535;
int32_t range = 2000;
int32_t i = 0;
int32_t raw_x = 0, raw_y = 0, raw_z = 0;
int16_t x_offset = 0;
int16_t y_offset = 0;
int16_t z_offset = 0;
uint32_t sample_counter = 0;
// Mean-based accumulation of raw calibration samples.
int64_t x_sum = 0;
int64_t y_sum = 0;
int64_t z_sum = 0;
MagnetometerData data;
while (i < range) {
i += 1;
if (magnetometer.isDataReady()) {
magnetometer.readData(data);
sample_counter++;
raw_x = data.raw.x;
raw_y = data.raw.y;
raw_z = data.raw.z;
x_sum += raw_x;
y_sum += raw_y;
z_sum += raw_z;
Serial.print("CALRAW ");
Serial.print("X:");
Serial.print(raw_x);
Serial.print(" Y:");
Serial.print(raw_y);
Serial.print(" Z:");
Serial.println(raw_z);
// Track extrema on the direct raw sample, not on a running average.
if (raw_x < x_min) {
x_min = raw_x;
i = 0;
}
if (raw_x > x_max) {
x_max = raw_x;
i = 0;
}
if (raw_y < y_min) {
y_min = raw_y;
i = 0;
}
if (raw_y > y_max) {
y_max = raw_y;
i = 0;
}
if (raw_z < z_min) {
z_min = raw_z;
i = 0;
}
if (raw_z > z_max) {
z_max = raw_z;
i = 0;
}
if ((sample_counter % 100) == 0) {
Serial.print("CALSTAT i=");
Serial.print(i);
Serial.print(" samples=");
Serial.print(sample_counter);
Serial.print(" x_min=");
Serial.print(x_min);
Serial.print(" x_max=");
Serial.print(x_max);
Serial.print(" y_min=");
Serial.print(y_min);
Serial.print(" y_max=");
Serial.print(y_max);
Serial.print(" z_min=");
Serial.print(z_min);
Serial.print(" z_max=");
Serial.println(z_max);
}
}
delay(5);
}
if (sample_counter == 0) {
Serial.println("No calibration samples were collected.");
return;
}
// Mean/centroid-based offsets.
x_offset = (int16_t)lround((double)x_sum / (double)sample_counter);
y_offset = (int16_t)lround((double)y_sum / (double)sample_counter);
z_offset = (int16_t)lround((double)z_sum / (double)sample_counter);
// Also compute the old midpoint offsets for comparison/debugging.
int16_t x_midpoint_offset = (x_max + x_min) / 2;
int16_t y_midpoint_offset = (y_max + y_min) / 2;
int16_t z_midpoint_offset = (z_max + z_min) / 2;
Serial.print("samples:");
Serial.println(sample_counter);
Serial.print("x_min:");
Serial.println(x_min);
Serial.print("x_max:");
Serial.println(x_max);
Serial.print("y_min:");
Serial.println(y_min);
Serial.print("y_max:");
Serial.println(y_max);
Serial.print("z_min:");
Serial.println(z_min);
Serial.print("z_max:");
Serial.println(z_max);
Serial.print("x_midpoint_offset:");
Serial.println(x_midpoint_offset);
Serial.print("y_midpoint_offset:");
Serial.println(y_midpoint_offset);
Serial.print("z_midpoint_offset:");
Serial.println(z_midpoint_offset);
Serial.print("x_mean_offset:");
Serial.println(x_offset);
Serial.print("y_mean_offset:");
Serial.println(y_offset);
Serial.print("z_mean_offset:");
Serial.println(z_offset);
// Set the mean-based calibration values.
magnetometer.setOffset(x_offset, y_offset, z_offset);
Serial.println();
Serial.println("Calibration complete!");
Serial.println("Offsets applied using raw-sample means.");
Serial.println("Check if Magnetic Strength is ~25-65 uT");
Serial.println("If too low, repeat calibration with better rotation");
}
void setup()
{
Serial.begin(115200);
while (!Serial);
// LilyGo T-Beam-Supreme sensor requires a power source to function.
beginPower();
/**
* Supports QMC6310U and QMC6310N; simply pass the corresponding device address
* during initialization.
* - QMC6310U_SLAVE_ADDRESS
* - QMC6310N_SLAVE_ADDRESS
*/
uint8_t address = QMC6310U_SLAVE_ADDRESS;
// uint8_t address = QMC6310N_SLAVE_ADDRESS;
if (!magnetometer.begin(Wire, address, SENSOR_SDA, SENSOR_SCL)) {
while (1) {
Serial.println("Failed to find QMC6310 - check your wiring!");
delay(1000);
}
}
// The desired output data rate in Hz. Allowed values are 10.0, 50.0, 100.0 and 200.0HZ.
float data_rate_hz = 200.0f;
// op_mode: Allowed values are SUSPEND, NORMAL, SINGLE_MEASUREMENT, CONTINUOUS_MEASUREMENT
OperationMode op_mode = OperationMode::CONTINUOUS_MEASUREMENT;
// full_scale: Allowed values are FS_2G, FS_8G, FS_12G ,FS_30G
MagFullScaleRange full_scale = MagFullScaleRange::FS_8G;
// over_sample_ratio: Allowed values are OSR_1, OSR_2, OSR_4, OSR_8
MagOverSampleRatio over_sample_ratio = MagOverSampleRatio::OSR_1;
// down_sample_ratio: Allowed values are DSR_1, DSR_2, DSR_4, DSR_8
MagDownSampleRatio down_sample_ratio = MagDownSampleRatio::DSR_1;
/* Config Magnetometer */
if (magnetometer.configMagnetometer(
op_mode,
full_scale,
data_rate_hz,
over_sample_ratio,
down_sample_ratio)) {
Serial.println("Magnetometer configured successfully.");
} else {
Serial.println("Magnetometer configuration failed.");
while (1);
}
calibrate();
Serial.println("Calibration done.");
Serial.println();
// Print Sensor ID and basic configuration.
SensorInfo info = magnetometer.getSensorInfo();
Serial.print("Manufacturer: ");
Serial.println(info.manufacturer);
Serial.print("Model: ");
Serial.println(info.model);
Serial.print("I2C Address: 0x");
Serial.println(info.i2c_address, HEX);
Serial.print("Version: ");
Serial.println(info.version);
Serial.print("UID: 0x");
Serial.println(info.uid, HEX);
Serial.print("Type: ");
Serial.print("Type: "); Serial.println(SensorUtils::typeToString(info.type));
Serial.println();
/* Print Sensor settings. */
// TODO: Add getSensorSettings() method to retrieve current sensor settings and print them here.
// auto settings = magnetometer.getSensorSettings();
// Serial.print("DataRate: ");
// Serial.println(settings.outputDataRate);
// Serial.print("FullScaleRange: ");
// Serial.println(settings.fullScaleRange);
// Serial.print("Mode: ");
// Serial.println(settings.mode);
// Serial.println();
// User's location magnetic declination offset.
//float declination_offset_deg = -3.33f; // Lewis's
float declination_offset_deg = 14.28f; // Salem, Oregon
magnetometer.setDeclination(declination_offset_deg);
Serial.print(" Magnetic Declination: ");
Serial.print(declination_offset_deg);
Serial.println("°");
// Print current sensitivity.
// Full scale 2G: 0.000067 Gauss/LSB
// Full scale 8G: 0.000267 Gauss/LSB
// Full scale 12G: 0.000400 Gauss/LSB
// Full scale 30G: 0.001000 Gauss/LSB
Serial.print(" Sensitivity: ");
Serial.print(magnetometer.getSensitivity(), 6);
Serial.println(" Gauss/LSB");
}
void loop()
{
MagnetometerData data;
if (magnetometer.readData(data)) {
// Gauss to μT
float x = MagnetometerUtils::gaussToMicroTesla(data.magnetic_field.x);
float y = MagnetometerUtils::gaussToMicroTesla(data.magnetic_field.y);
float z = MagnetometerUtils::gaussToMicroTesla(data.magnetic_field.z);
Serial.print("Mag:");
Serial.print(" X:"); Serial.print(x);
Serial.print(" Y:"); Serial.print(y);
Serial.print(" Z:"); Serial.print(z);
Serial.print(" μT");
Serial.print(" Sensitivity: ");
Serial.print(magnetometer.getSensitivity(), 6);
Serial.print(" Gauss/LSB");
Serial.print(" Metadata:");
Serial.print(" X:");
Serial.print(data.raw.x);
Serial.print(" Y:");
Serial.print(data.raw.y);
Serial.print(" Z:");
Serial.print(data.raw.z);
//Serial.print(" Heading (rad): ");
//Serial.print(data.heading, 6);
//Serial.print(" rad");
//Serial.print(" Heading (deg): ");
//Serial.print(data.heading_degrees, 2);
//Serial.print("°");
// Use raw values (already offset-corrected by calibration)
float Xc = data.raw.x;
float Yc = data.raw.y;
// Compute corrected heading
//float heading_rad = atan2(Yc, Xc); // this was mirrored
float heading_rad = atan2(-Yc, Xc);
// Convert to degrees
float heading_deg = heading_rad * 180.0 / PI;
if (heading_deg < 0) heading_deg += 360;
// Print corrected values
Serial.print(" HeadingCorr (rad): ");
Serial.print(heading_rad, 6);
Serial.print(" rad");
Serial.print(" HeadingCorr (deg): ");
Serial.print(heading_deg, 2);
Serial.print("°");
float strength = MagnetometerUtils::calculateMagneticStrength(data);
strength = MagnetometerUtils::gaussToMicroTesla(strength);
Serial.print(" Magnetic Strength: ");
Serial.print(strength, 2);
Serial.println(" μT");
if (data.overflow) {
Serial.println("\tWarning: Data Overflow occurred!");
}
}
delay(10);
}

View file

@ -31,6 +31,9 @@
#include <SensorQMC6309.hpp>
#include <SensorWireHelper.h>
#include "LoRaBoards.h"
#include "QmcServices.h"
#define Serial QmcSerial
void calibrate();
MagnetometerBase *magnetometer;
@ -41,6 +44,7 @@ void setup()
while (!Serial);
setupBoards();
qmcServicesBegin();
// The desired output data rate in Hz. Allowed values are 1.0, 10.0, 50.0, 100.0 and 200.0HZ.
float data_rate_hz = 200.0f;
@ -184,6 +188,7 @@ void setup()
void loop()
{
qmcServicesUpdate();
MagnetometerData data;
@ -194,6 +199,20 @@ void loop()
float y = MagnetometerUtils::gaussToMicroTesla(data.magnetic_field.y);
float z = MagnetometerUtils::gaussToMicroTesla(data.magnetic_field.z);
float deg_xy = atan2(y, x) * 180.0f / PI;
float deg_xny = atan2(-y, x) * 180.0f / PI;
float deg_yx = atan2(x, y) * 180.0f / PI;
float deg_nxy = atan2(y, -x) * 180.0f / PI;
float deg_xz = atan2(z, x) * 180.0f / PI;
float deg_zx = atan2(x, z) * 180.0f / PI;
float deg_yz = atan2(z, y) * 180.0f / PI;
float deg_zy = atan2(y, z) * 180.0f / PI;
auto norm360 = [](float d) {
while (d < 0) d += 360.0f;
while (d >= 360.0f) d -= 360.0f;
return d;
};
Serial.print("Mag:");
Serial.print(" X:"); Serial.print(x);
Serial.print(" Y:"); Serial.print(y);
@ -222,10 +241,20 @@ void loop()
Serial.print(strength, 2);
Serial.println(" μT");
Serial.print(" XY:"); Serial.print(norm360(deg_xy), 2);
Serial.print(" XnY:"); Serial.print(norm360(deg_xny), 2);
Serial.print(" YX:"); Serial.print(norm360(deg_yx), 2);
Serial.print(" nXY:"); Serial.print(norm360(deg_nxy), 2);
Serial.print(" XZ:"); Serial.print(norm360(deg_xz), 2);
Serial.print(" ZX:"); Serial.print(norm360(deg_zx), 2);
Serial.print(" YZ:"); Serial.print(norm360(deg_yz), 2);
Serial.print(" ZY:"); Serial.println(norm360(deg_zy), 2);
if (data.overflow) {
Serial.println("\tWarning: Data Overflow occurred!");
}
}
qmcServicesUpdate();
delay(10);
}
@ -269,14 +298,25 @@ void calibrate()
int16_t y_offset = 0;
int16_t z_offset = 0;
uint32_t sample_counter = 0;
MagnetometerData data;
while (i < range) {
i += 1;
if (magnetometer->isDataReady()) {
sample_counter++;
magnetometer->readData(data);
Serial.print("CALRAW ");
Serial.print("X:");
Serial.print(data.raw.x);
Serial.print(" Y:");
Serial.print(data.raw.y);
Serial.print(" Z:");
Serial.println(data.raw.z);
x = (data.raw.x + x) / 2;
y = (data.raw.y + y) / 2;
z = (data.raw.z + z) / 2;
@ -304,13 +344,29 @@ void calibrate()
z_max = z;
i = 0;
}
int j = round(10 * i / range);
/* int j = round(10 * i / range);
Serial.print("[");
for (int k = 0; k < j; ++k) {
Serial.print("*");
}
Serial.println("]");
Serial.println("]"); */
if ((sample_counter % 100) == 0) {
Serial.print("CALSTAT i=");
Serial.print(i);
Serial.print(" x_min=");
Serial.print(x_min);
Serial.print(" x_max=");
Serial.print(x_max);
Serial.print(" y_min=");
Serial.print(y_min);
Serial.print(" y_max=");
Serial.print(y_max);
Serial.print(" z_min=");
Serial.print(z_min);
Serial.print(" z_max=");
Serial.println(z_max);
}
}
delay(5);
}

View file

@ -0,0 +1,174 @@
#include "QmcServices.h"
#include <TBeamClock.h>
#include <TBeamStorage.h>
#include <TBeamWeb.h>
#ifndef BOARD_ID
#define BOARD_ID "NODE"
#endif
#ifndef NODE_LABEL
#define NODE_LABEL BOARD_ID
#endif
#ifndef LOG_AP_IP_OCTET
#define LOG_AP_IP_OCTET 25
#endif
namespace {
tbeam::TBeamClock clockService(Wire1);
tbeam::TBeamStorage storageService(::Serial);
tbeam::TBeamWeb webService(::Serial);
bool servicesStarted = false;
uint32_t lastFlushMs = 0;
uint32_t lastClockUpdateMs = 0;
char logPath[128] = {};
void openRunLog()
{
if (!storageService.ready()) {
return;
}
char runId[64] = {};
tbeam::DateTime rtc;
int64_t epoch = 0;
if (clockService.readValidRtc(rtc, &epoch)) {
tbeam::TBeamClock::makeRunId(rtc, BOARD_ID, runId, sizeof(runId));
} else {
snprintf(runId, sizeof(runId), "%s_%lu", BOARD_ID, static_cast<unsigned long>(millis() / 1000));
}
snprintf(logPath, sizeof(logPath), "/logs/qmc63xx/%s.log", runId);
if (!storageService.openLog(logPath)) {
::Serial.printf("[qmc-services] log open failed: %s\n", storageService.lastError());
logPath[0] = '\0';
return;
}
storageService.println("# test: qmc63xx");
storageService.print("# board_id: ");
storageService.println(BOARD_ID);
storageService.print("# node_label: ");
storageService.println(NODE_LABEL);
storageService.print("# log_path: ");
storageService.println(logPath);
if (clockService.valid()) {
char iso[32] = {};
tbeam::TBeamClock::formatIsoUtc(clockService.lastRtc(), iso, sizeof(iso));
storageService.print("# rtc_utc: ");
storageService.println(iso);
}
storageService.println("# serial_tee: enabled");
storageService.flush();
}
} // namespace
QmcSerialTee QmcSerial;
void QmcSerialTee::begin(unsigned long baud)
{
::Serial.begin(baud);
}
size_t QmcSerialTee::write(uint8_t value)
{
const size_t serialWritten = ::Serial.write(value);
if (servicesStarted && storageService.isLogOpen()) {
storageService.write(&value, 1);
}
return serialWritten;
}
size_t QmcSerialTee::write(const uint8_t* buffer, size_t size)
{
const size_t serialWritten = ::Serial.write(buffer, size);
if (servicesStarted && storageService.isLogOpen()) {
storageService.write(buffer, size);
}
return serialWritten;
}
void QmcSerialTee::flush()
{
::Serial.flush();
if (servicesStarted && storageService.isLogOpen()) {
storageService.flush();
}
}
void qmcServicesBegin()
{
if (servicesStarted) {
return;
}
tbeam::ClockConfig clockConfig;
clockConfig.beginWire = false;
clockService.begin(clockConfig);
clockService.update();
tbeam::StorageConfig storageConfig;
storageConfig.logDir = "/logs/qmc63xx";
storageConfig.enablePinDumps = false;
storageService.begin(storageConfig);
openRunLog();
tbeam::WebConfig webConfig;
webConfig.ssidPrefix = "GPSQA";
webConfig.boardId = BOARD_ID;
webConfig.password = nullptr;
webConfig.ipOctet = LOG_AP_IP_OCTET;
webConfig.enableDelete = true;
webService.begin(storageService, webConfig);
servicesStarted = true;
::Serial.printf("[qmc-services] board=%s label=%s\n", BOARD_ID, NODE_LABEL);
if (clockService.valid()) {
char iso[32] = {};
tbeam::TBeamClock::formatIsoUtc(clockService.lastRtc(), iso, sizeof(iso));
::Serial.printf("[qmc-services] rtc=%s\n", iso);
} else {
::Serial.printf("[qmc-services] rtc invalid: %s\n", clockService.lastError());
}
::Serial.printf("[qmc-services] log=%s\n", logPath[0] ? logPath : "(not open)");
if (webService.ready()) {
::Serial.printf("[qmc-services] web=http://%s/ ssid=%s\n",
webService.ip().toString().c_str(),
webService.ssid());
} else {
::Serial.printf("[qmc-services] web unavailable: %s\n", webService.lastError());
}
}
void qmcServicesUpdate()
{
if (!servicesStarted) {
return;
}
const uint32_t now = millis();
storageService.update();
webService.update();
if (now - lastClockUpdateMs >= 1000) {
lastClockUpdateMs = now;
clockService.update();
}
if (now - lastFlushMs >= 2000) {
lastFlushMs = now;
storageService.flush();
}
}
const char* qmcServicesLogPath()
{
return logPath;
}

View file

@ -0,0 +1,19 @@
#pragma once
#include <Arduino.h>
class QmcSerialTee : public Print {
public:
void begin(unsigned long baud);
explicit operator bool() const { return true; }
size_t write(uint8_t value) override;
size_t write(const uint8_t* buffer, size_t size) override;
void flush();
};
extern QmcSerialTee QmcSerial;
void qmcServicesBegin();
void qmcServicesUpdate();
const char* qmcServicesLogPath();

View file

@ -0,0 +1,157 @@
#!/usr/bin/perl
#
# calc_heading_strength_stats.pl
#
# 20260420 ChatGPT
# $Id$
# $HeadURL$
#
# Example:
# chmod 755 calc_heading_strength_stats.pl
# ./calc_heading_strength_stats.pl magnetometer_readings.log
# ./calc_heading_strength_stats.pl ~/sessions/T-Beam_calibrate_20260420_Mon_014614.script
#
# This script scans a console/script log and extracts only lines like:
#
# 3005 00:36.756 Mag: X:289.20 Y:216.86 Z:608.78 μT Metadata: X:10845 Y:8132 Z:22829 Heading (rad): 0.585220 rad Heading (deg): 33.53° Magnetic Strength: 708.01 μT
#
# It ignores non-matching lines and computes statistics for:
# 1) Heading (deg)
# 2) Magnetic Strength
#
# Reported:
# - count
# - min / max / range
# - mean
# - population variance
# - sample variance
# - population std dev
# - sample std dev
# - line number / timestamp / text for min and max
#
use strict;
use warnings;
use utf8;
use open ':std', ':encoding(UTF-8)';
my $input_file = shift @ARGV
or die "Usage: $0 input_log_file\n";
open my $fh, '<', $input_file
or die "Cannot open '$input_file': $!\n";
my %stats = (
heading_deg => init_stat('Heading (deg)'),
mag_strength => init_stat('Magnetic Strength'),
);
my $matched_lines = 0;
while (my $line = <$fh>) {
chomp $line;
next unless $line =~ /^\s*(\d+)\s+(\d{2}:\d{2}\.\d{3})\s+Mag:/;
my $log_lineno = $1;
my $time_stamp = $2;
next unless $line =~ /Heading \(deg\):\s*([-+]?\d+(?:\.\d+)?)°/;
my $heading_deg = $1 + 0;
next unless $line =~ /Magnetic Strength:\s*([-+]?\d+(?:\.\d+)?)\s*μT/;
my $mag_strength = $1 + 0;
$matched_lines++;
update_stat($stats{heading_deg}, $heading_deg, $log_lineno, $time_stamp, $line);
update_stat($stats{mag_strength}, $mag_strength, $log_lineno, $time_stamp, $line);
}
close $fh;
if (!$matched_lines) {
die "No matching data lines were found in '$input_file'.\n";
}
print_report($stats{heading_deg});
print "\n";
print_report($stats{mag_strength});
exit 0;
sub init_stat {
my ($label) = @_;
return {
label => $label,
count => 0,
mean => 0,
m2 => 0, # Welford running sum of squared deviations
min => undef,
max => undef,
min_line => undef,
min_time => undef,
min_text => undef,
max_line => undef,
max_time => undef,
max_text => undef,
};
}
sub update_stat {
my ($s, $x, $log_lineno, $time_stamp, $text) = @_;
$s->{count}++;
if (!defined $s->{min} || $x < $s->{min}) {
$s->{min} = $x;
$s->{min_line} = $log_lineno;
$s->{min_time} = $time_stamp;
$s->{min_text} = $text;
}
if (!defined $s->{max} || $x > $s->{max}) {
$s->{max} = $x;
$s->{max_line} = $log_lineno;
$s->{max_time} = $time_stamp;
$s->{max_text} = $text;
}
my $delta = $x - $s->{mean};
$s->{mean} += $delta / $s->{count};
my $delta2 = $x - $s->{mean};
$s->{m2} += $delta * $delta2;
return;
}
sub print_report {
my ($s) = @_;
my $count = $s->{count};
my $range = $s->{max} - $s->{min};
my $pop_variance = ($count > 0) ? ($s->{m2} / $count) : 0;
my $samp_variance = ($count > 1) ? ($s->{m2} / ($count-1)) : 0;
my $pop_stddev = sqrt($pop_variance);
my $samp_stddev = sqrt($samp_variance);
print "$s->{label}\n";
print "=" x length($s->{label}), "\n";
printf "count : %d\n", $count;
printf "min : %.6f\n", $s->{min};
printf "max : %.6f\n", $s->{max};
printf "range : %.6f\n", $range;
printf "mean : %.6f\n", $s->{mean};
printf "population variance : %.6f\n", $pop_variance;
printf "sample variance : %.6f\n", $samp_variance;
printf "population std dev : %.6f\n", $pop_stddev;
printf "sample std dev : %.6f\n", $samp_stddev;
printf "min occurred at : line %s, time %s\n", $s->{min_line}, $s->{min_time};
printf "max occurred at : line %s, time %s\n", $s->{max_line}, $s->{max_time};
print "min line text : $s->{min_text}\n";
print "max line text : $s->{max_text}\n";
return;
}

44
examples/platformio.ini Normal file
View file

@ -0,0 +1,44 @@
[platformio]
default_envs = T_BEAM_S3_SUPREME_SX1262
; Change this line when you want to build a different copied LilyGO example.
;src_dir = QMC6310_CompassExample
;src_dir = Sensor/QMC63xx_GetDataExample
src_dir = Sensor/QMC6310_CalibrateExample
; Reuse LilyGO's custom board definitions without editing the LilyGO checkout.
boards_dir = /usr/local/src/LilyGo-LoRa-Series/boards
[env]
platform = espressif32@6.12.0
framework = arduino
upload_speed = 921600
monitor_speed = 115200
; Reuse LilyGO's bundled libraries:
; SensorLib, ESP8266_SSD1306, XPowersLib, RadioLib, etc.
lib_extra_dirs =
/usr/local/src/LilyGo-LoRa-Series/lib
/usr/local/src/microreticulum/microReticulumTbeam/lib
monitor_filters =
default
esp32_exception_decoder
[esp32s3_base]
build_flags =
-DARDUINO_USB_CDC_ON_BOOT=1
-DCORE_DEBUG_LEVEL=0
-I /usr/local/src/microreticulum/microReticulumTbeam/shared/boards
-I /usr/local/src/microreticulum/microReticulumTbeam/external/microReticulum_Firmware
-D BOARD_MODEL=BOARD_TBEAM_S_V1
-D BOARD_ID=\"CY\"
-D NODE_LABEL=\"Cy\"
-D LOG_AP_IP_OCTET=25
[env:T_BEAM_S3_SUPREME_SX1262]
board = t-beams3-supreme
build_flags = ${esp32s3_base.build_flags}
-DT_BEAM_S3_SUPREME_SX1262
-DBOARD_HAS_PSRAM
board_build.partitions = huge_app.csv

19
examples/run_calibration.sh Executable file
View file

@ -0,0 +1,19 @@
#!/usr/bin/bash
#
# for capturing magnet readings during calibration and then
# buildling 3D model
#
CURRENT_UNIT=${CURRENT_UNIT}
LOG=~/work/tbeam/${CURRENT_UNIT}_calibration_$(date +%Y%m%d_%H%M%S).log
echo "Logging to: $LOG"
pio run -e T_BEAM_S3_SUPREME_SX1262 -t upload --upload-port /dev/ttyt${CURRENT_UNIT} && \
pio device monitor -b 115200 -p /dev/ttyt${CURRENT_UNIT} | \
perl -MTime::HiRes=time -ne '
BEGIN { $t0 = time() }
$elapsed = time() - $t0;
$mm = int($elapsed / 60);
$ss = int($elapsed % 60);
$ms = int(($elapsed - int($elapsed)) * 1000);
printf "%6d %02d:%02d.%03d %s", $. , $mm, $ss, $ms, $_;
' | tee "$LOG"

View file

@ -0,0 +1,17 @@
#!/usr/bin/bash
#
# for capturing magnet readings during calibration and then
# buildling 3D model
#
LOG=~/work/tbeam/ED_TEST_after_calibration_$(date +%Y%m%d_%H%M%S).log
echo "Logging to: $LOG"
pio device monitor -b 115200 -p /dev/ttytED | \
perl -MTime::HiRes=time -ne '
BEGIN { $t0 = time() }
$elapsed = time() - $t0;
$mm = int($elapsed / 60);
$ss = int($elapsed % 60);
$ms = int(($elapsed - int($elapsed)) * 1000);
printf "%6d %02d:%02d.%03d %s", $. , $mm, $ss, $ms, $_;
' | tee "$LOG"