Compare commits
10 commits
master
...
jlpoole-de
| Author | SHA1 | Date | |
|---|---|---|---|
| 69c1ce596d | |||
| 9d43ce67ff | |||
| ffd936a99e | |||
| 22f0cea6fc | |||
| fd37ad6b50 | |||
| f4e2a211e2 | |||
| 49e3843292 | |||
| a2e34659a6 | |||
| 59e5887cec | |||
| 20781b3243 |
11 changed files with 1510 additions and 74 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -10,4 +10,7 @@ test
|
|||
script
|
||||
build
|
||||
LilyGoLoRaBoard
|
||||
record.*
|
||||
record.*
|
||||
*log
|
||||
.codex
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
174
examples/Sensor/QMC63xx_GetDataExample/QmcServices.cpp
Normal file
174
examples/Sensor/QMC63xx_GetDataExample/QmcServices.cpp
Normal 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;
|
||||
}
|
||||
19
examples/Sensor/QMC63xx_GetDataExample/QmcServices.h
Normal file
19
examples/Sensor/QMC63xx_GetDataExample/QmcServices.h
Normal 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();
|
||||
157
examples/Sensor/QMC63xx_GetDataExample/calc_heading_strength_stats.pl
Executable file
157
examples/Sensor/QMC63xx_GetDataExample/calc_heading_strength_stats.pl
Executable 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
44
examples/platformio.ini
Normal 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
19
examples/run_calibration.sh
Executable 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"
|
||||
17
examples/run_test_after_calibration.sh
Executable file
17
examples/run_test_after_calibration.sh
Executable 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"
|
||||
Loading…
Add table
Add a link
Reference in a new issue