Calibration supercharged

This commit is contained in:
2026-04-11 21:15:01 -05:00
parent 6b78fd278a
commit 7c54fe38e3
10 changed files with 1202 additions and 173 deletions

View File

@@ -1,111 +1,111 @@
#include <Arduino.h>
#include <util/atomic.h>
// ── ADC Interrupt-driven single-channel read (A0) ────────────
volatile uint16_t adc_result = 0;
volatile bool adc_ready = false;
// ── ADC Interrupt-driven 3-channel read (A2, A3, A0) ─────────
// Channel index: 0 → A2 (sensor 0), 1 → A3 (sensor 1), 2 → A0 (raw ref)
static const uint8_t adc_mux[3] = {2, 3, 0};
volatile uint16_t adc_result[3] = {0, 0, 0};
volatile bool adc_ready[3] = {false, false, false};
volatile uint8_t adc_channel = 0;
void setupADC() {
// Reference = AVCC (5 V)
ADMUX = (1 << REFS0);
// Channel = A0 (MUX[3:0] = 0000)
ADMUX &= 0xF0;
// Prescaler = 16 → 16 MHz / 16 = 1 MHz ADC clock
// Each conversion ≈ 13 ADC clocks → ~76.9 kHz sample rate
ADCSRA = (1 << ADEN) | (1 << ADIE)
| (1 << ADPS2); // ADPS = 100 → /16
// Start first conversion
ADMUX = (1 << REFS0) | adc_mux[0]; // AVCC ref, start on A2
ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS2); // /16 prescaler
ADCSRA |= (1 << ADSC);
}
// ── OOR digital input ───────────────────────────────────────
#define OOR_PIN 2 // digital pin 2 — HIGH = out of range
// ── OOR digital inputs ───────────────────────────────────────
#define OOR_PIN_0 12 // HIGH = out of range, sensor 0 (A2)
#define OOR_PIN_1 13 // HIGH = out of range, sensor 1 (A3)
volatile bool OOR;
volatile bool OOR[2];
ISR(ADC_vect) {
uint16_t sample = ADC;
// Discard if OOR pin (PD2) is HIGH
OOR = (digitalRead(OOR_PIN));
if (!OOR) {
adc_result = sample;
adc_ready = true;
uint8_t ch = adc_channel;
uint8_t next = (ch + 1) % 3;
if (ch < 2) {
// Sensor channels: filter by OOR
OOR[ch] = digitalRead(ch == 0 ? OOR_PIN_0 : OOR_PIN_1);
if (!OOR[ch]) {
adc_result[ch] = sample;
adc_ready[ch] = true;
}
} else {
// A0: no OOR, always store
adc_result[2] = sample;
adc_ready[2] = true;
}
ADCSRA |= (1 << ADSC); // kick off next conversion immediately
ADMUX = (ADMUX & 0xF0) | adc_mux[next];
adc_channel = next;
ADCSRA |= (1 << ADSC);
}
// ── ADC → mm linear mapping ─────────────────────────────────
// ADC 185 → 16 mm, ADC 900 → 26 mm
// #define adcToMM(adc) (16.0f + (float)((adc) - 185) * (10.0f / 715.0f))
#define adcToMM(adc) (0.0f + (float)((adc) - 0) * (10.0f / 1024.0f))
// ── ADC → mm linear mappings (raw range: 1626 mm) ──────────
#define adcToMM0(adc) ((float)map(adc, 178, 895, 1600, 2600) / 100.0f)
#define adcToMM1(adc) ((float)map(adc, 176, 885, 1600, 2600) / 100.0f)
// ── Boundary tracking (in-range only) ────────────────────────
// Subtract mounting offsets so both sensors share the same position frame:
// Sensor 0 raw 1626 mm 16 → 010 mm
// Sensor 1 raw 1626 mm 6 → 1020 mm
#define OFFSET_MM0 15.6f
#define OFFSET_MM1 6.2f
// ── Boundary tracking (in-range only, per sensor) ────────────
#define TRACK_N 10
uint16_t lowestVals[TRACK_N];
uint16_t highestVals[TRACK_N];
uint8_t lowestCount = 0;
uint8_t highestCount = 0;
uint16_t lowestVals[2][TRACK_N];
uint16_t highestVals[2][TRACK_N];
uint8_t lowestCount[2] = {0, 0};
uint8_t highestCount[2] = {0, 0};
// Insert val into a sorted-ascending array of up to TRACK_N entries
// keeping only the N smallest values seen so far.
static void trackLowest(uint16_t val) {
if (lowestCount < TRACK_N) {
// Array not full — insert in sorted position
uint8_t i = lowestCount;
while (i > 0 && lowestVals[i - 1] > val) {
lowestVals[i] = lowestVals[i - 1];
i--;
}
lowestVals[i] = val;
lowestCount++;
} else if (val < lowestVals[TRACK_N - 1]) {
// Replace the current largest of the "lowest" set
static void trackLowest(uint8_t s, uint16_t val) {
uint16_t *lv = lowestVals[s];
uint8_t &lc = lowestCount[s];
if (lc < TRACK_N) {
uint8_t i = lc;
while (i > 0 && lv[i - 1] > val) { lv[i] = lv[i - 1]; i--; }
lv[i] = val;
lc++;
} else if (val < lv[TRACK_N - 1]) {
uint8_t i = TRACK_N - 1;
while (i > 0 && lowestVals[i - 1] > val) {
lowestVals[i] = lowestVals[i - 1];
i--;
}
lowestVals[i] = val;
while (i > 0 && lv[i - 1] > val) { lv[i] = lv[i - 1]; i--; }
lv[i] = val;
}
}
// Insert val into a sorted-descending array of up to TRACK_N entries
// keeping only the N largest values seen so far.
static void trackHighest(uint16_t val) {
if (highestCount < TRACK_N) {
uint8_t i = highestCount;
while (i > 0 && highestVals[i - 1] < val) {
highestVals[i] = highestVals[i - 1];
i--;
}
highestVals[i] = val;
highestCount++;
} else if (val > highestVals[TRACK_N - 1]) {
static void trackHighest(uint8_t s, uint16_t val) {
uint16_t *hv = highestVals[s];
uint8_t &hc = highestCount[s];
if (hc < TRACK_N) {
uint8_t i = hc;
while (i > 0 && hv[i - 1] < val) { hv[i] = hv[i - 1]; i--; }
hv[i] = val;
hc++;
} else if (val > hv[TRACK_N - 1]) {
uint8_t i = TRACK_N - 1;
while (i > 0 && highestVals[i - 1] < val) {
highestVals[i] = highestVals[i - 1];
i--;
}
highestVals[i] = val;
while (i > 0 && hv[i - 1] < val) { hv[i] = hv[i - 1]; i--; }
hv[i] = val;
}
}
static void resetTracking() {
lowestCount = 0;
highestCount = 0;
lowestCount[0] = highestCount[0] = 0;
lowestCount[1] = highestCount[1] = 0;
}
static void printBoundaries() {
Serial.println(F("--- 10 Lowest In-Range ADC Values ---"));
for (uint8_t i = 0; i < lowestCount; i++) {
Serial.println(lowestVals[i]);
}
Serial.println(F("--- 10 Highest In-Range ADC Values ---"));
for (uint8_t i = 0; i < highestCount; i++) {
Serial.println(highestVals[i]);
for (uint8_t s = 0; s < 2; s++) {
Serial.print(F("--- Sensor "));
Serial.print(s);
Serial.println(F(": 10 Lowest In-Range ADC Values ---"));
for (uint8_t i = 0; i < lowestCount[s]; i++) Serial.println(lowestVals[s][i]);
Serial.print(F("--- Sensor "));
Serial.print(s);
Serial.println(F(": 10 Highest In-Range ADC Values ---"));
for (uint8_t i = 0; i < highestCount[s]; i++) Serial.println(highestVals[s][i]);
}
}
@@ -115,8 +115,9 @@ bool rawMode = false;
// ═════════════════════════════════════════════════════════════
void setup() {
Serial.begin(2000000);
pinMode(OOR_PIN, INPUT);
Serial.begin(115200);
pinMode(OOR_PIN_0, INPUT);
pinMode(OOR_PIN_1, INPUT);
setupADC();
Serial.println(F("Send '1' to start sampling, '0' to stop and print bounds, '2' for raw ADC output."));
}
@@ -147,37 +148,49 @@ void loop() {
// ── Main sample path ────────────────────────────────────
if (!sampling) return;
// Grab the latest ADC value atomically
uint16_t val;
bool ready;
bool newOOR;
uint16_t val[3];
bool ready[3];
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
ready = adc_ready;
val = adc_result;
adc_ready = false;
newOOR = OOR;
for (uint8_t i = 0; i < 3; i++) {
ready[i] = adc_ready[i];
val[i] = adc_result[i];
adc_ready[i] = false;
}
}
if (!ready) return; // nothing new — come back next iteration
if (!ready[0] && !ready[1]) return;
if (rawMode) {
long mm_x100 = map(val, 178, 895, 1600, 2600);
Serial.print(mm_x100 / 100);
Serial.print('.');
long frac = mm_x100 % 100;
if (frac < 10) Serial.print('0');
Serial.println(frac);
if (ready[0]) {
Serial.print(adcToMM0(val[0]) - OFFSET_MM0);
Serial.print(F(", "));
Serial.println(val[2]);
}
if (ready[1]) {
Serial.print(adcToMM1(val[1]) - OFFSET_MM1);
Serial.print(F(", "));
Serial.println(val[2]);
}
return;
}
// All values here are in-range (OOR filtered in ISR)
trackLowest(val);
trackHighest(val);
float mm = adcToMM(val);
Serial.print(val);
Serial.print(", ");
Serial.print(mm, 2);
Serial.print(" mm, ");
Serial.println(newOOR ? "out of range" : "in range");
// Apply offset for whichever sensor(s) are in range
if (ready[0]) {
float mm = adcToMM0(val[0]) - OFFSET_MM0;
trackLowest(0, val[0]);
trackHighest(0, val[0]);
Serial.print(val[0]);
Serial.print(F(", "));
Serial.print(mm, 2);
Serial.println(F(" mm (s0)"));
}
if (ready[1]) {
float mm = adcToMM1(val[1]) - OFFSET_MM1;
trackLowest(1, val[1]);
trackHighest(1, val[1]);
Serial.print(val[1]);
Serial.print(F(", "));
Serial.print(mm, 2);
Serial.println(F(" mm (s1)"));
}
}