// Read 4 analog sensors on XIAO RP2040 and print values // Works in Serial Monitor and also in Serial Plotter if desired const int P[4] = {A0, A1, A2, A3}; void setup() { Serial.begin(115200); analogReadResolution(12); // 0..4095 on RP2040 delay(800); Serial.println("Reading A0 A1 A2 A3..."); } void loop() { int r0 = analogRead(P[0]); int r1 = analogRead(P[1]); int r2 = analogRead(P[2]); int r3 = analogRead(P[3]); // Print in a single line for readability Serial.print("A0: "); Serial.print(r0); Serial.print(" A1: "); Serial.print(r1); Serial.print(" A2: "); Serial.print(r2); Serial.print(" A3: "); Serial.println(r3); delay(200); // adjust speed as needed } // XIAO RP2040 — 4 analog sensors with per-channel live calibration. // Press 'd' in Serial Monitor to capture DARK baselines. // Press 'b' to capture BRIGHT baselines. // Prints raw readings and normalized 0..1 per sensor + a fused score. const int P[4] = {A0, A1, A2, A3}; const int ADC_BITS = 12; // 0..4095 on RP2040 const int PRINT_EVERY_MS = 120; // print rate const float EMA_ALPHA = 0.18f; // smoothing for noisy sensors (0..1) float ema[4] = {0,0,0,0}; int darkV[4] = { 3835,1880,3925,200 }; // placeholders; will be overwritten int brightV[4] = { 4065,4048,4080,4000 }; // placeholders; will be overwritten bool haveDark = false, haveBright = false; // clamp helper static inline float clamp01(float x){ return (x < 0) ? 0 : (x > 1 ? 1 : x); } // Map one sensor reading to 0..1 using its own dark/bright, auto-direction. float normalizeOne(int ch, float v) { int d = darkV[ch]; int b = brightV[ch]; // If the sensor gets larger with light, normal map; else invert if (b > d) { float n = (v - d) / float(b - d); return clamp01(n); } else if (b < d) { float n = (d - v) / float(d - b); return clamp01(n); } else { // Avoid divide-by-zero if not calibrated yet return 0.0f; } } void captureDark() { for (int i=0;i<4;i++) darkV[i] = (int)ema[i]; haveDark = true; Serial.println(F("[CAL] DARK captured.")); printCalibration(); } void captureBright() { for (int i=0;i<4;i++) brightV[i] = (int)ema[i]; haveBright = true; Serial.println(F("[CAL] BRIGHT captured.")); printCalibration(); } void printCalibration() { Serial.print(F("DARK: ")); for (int i=0;i<4;i++){ Serial.print(darkV[i]); Serial.print(i<3? '\t':'\n'); } Serial.print(F("BRITE: ")); for (int i=0;i<4;i++){ Serial.print(brightV[i]); Serial.print(i<3? '\t':'\n'); } } void setup() { Serial.begin(115200); analogReadResolution(ADC_BITS); // Seed EMA with first readings for (int i=0;i<4;i++) ema[i] = analogRead(P[i]); Serial.println(F("4-sensor calibrator")); Serial.println(F("Press 'd' to capture DARK (finger over sensors)")); Serial.println(F("Press 'b' to capture BRIGHT (flashlight)")); Serial.println(F("Press 's' to show current calibration")); Serial.println(); } void loop() { // Read + smooth for (int i=0;i<4;i++) { int r = analogRead(P[i]); ema[i] = (1.0f - EMA_ALPHA) * ema[i] + EMA_ALPHA * r; } // Handle serial commands while (Serial.available()) { char c = Serial.read(); if (c == 'd' || c == 'D') captureDark(); if (c == 'b' || c == 'B') captureBright(); if (c == 's' || c == 'S') printCalibration(); } static uint32_t t0 = 0; if (millis() - t0 >= PRINT_EVERY_MS) { t0 = millis(); // Normalize each channel using its own calibration float n[4]; for (int i=0;i<4;i++) n[i] = normalizeOne(i, ema[i]); // Fuse — choose mean or max depending on your use case float score = 0.25f*(n[0]+n[1]+n[2]+n[3]); // mean // float score = max( max(n[0],n[1]), max(n[2],n[3]) ); // or max // Print a clean line (good for Serial Monitor) Serial.print(F("A0: ")); Serial.print((int)ema[0]); Serial.print(F(" A1: ")); Serial.print((int)ema[1]); Serial.print(F(" A2: ")); Serial.print((int)ema[2]); Serial.print(F(" A3: ")); Serial.print((int)ema[3]); Serial.print(F(" | n0: ")); Serial.print(n[0], 3); Serial.print(F(" n1: ")); Serial.print(n[1], 3); Serial.print(F(" n2: ")); Serial.print(n[2], 3); Serial.print(F(" n3: ")); Serial.print(n[3], 3); Serial.print(F(" score: ")); Serial.println(score, 3); } delay(5); } ________________________ Now using just the a3 to dim::: // XIAO RP2040 — LED on D7 driven linearly by A3 (dark=200, bright=4000) const int SENSOR = A3; const int LED_PIN = D7; // ---- your calibration ---- const int RAW_DARK = 200; // reading at darkest const int RAW_BRIGHT = 4000; // reading at brightest // ---- LED output (12-bit PWM) ---- const int PWM_RANGE = 4095; // do not change int PWM_CAP = 100; // max brightness (0..4095) — raise if too dim int PWM_FLOOR = 0; // small floor (e.g., 2–6) if you want a faint glow at "dark" bool LED_ACTIVE_HIGH = true; // set false if wired active-low // smoothing (optional) const float EMA_ALPHA = 0.15f; // 0..1 (higher = snappier) float ema = 0; static inline float clamp01(float x){ return x < 0 ? 0 : (x > 1 ? 1 : x); } void setup() { Serial.begin(115200); analogReadResolution(12); // high-res PWM analogWriteResolution(12); analogWriteRange(PWM_RANGE); analogWriteFreq(1500); pinMode(LED_PIN, OUTPUT); // seed smoothing ema = analogRead(SENSOR); Serial.println("Linear A3 -> LED (dark=200, bright=4000)"); } void loop() { int raw = analogRead(SENSOR); // smooth a bit so it doesn't jitter ema = (1.0f - EMA_ALPHA)*ema + EMA_ALPHA*raw; // linear normalize: RAW_DARK..RAW_BRIGHT -> 0..1 float n = (ema - RAW_DARK) / float(RAW_BRIGHT - RAW_DARK); n = clamp01(n); // scale to (very) dim PWM range int pwm = int(n * PWM_CAP + 0.5f); if (pwm < PWM_FLOOR) pwm = PWM_FLOOR; if (pwm > PWM_CAP) pwm = PWM_CAP; if (!LED_ACTIVE_HIGH) pwm = PWM_RANGE - pwm; analogWrite(LED_PIN, pwm); // debug (optional) static uint32_t t0=0; if (millis() - t0 > 250) { t0 = millis(); Serial.print("A3 "); Serial.print(int(ema)); Serial.print(" n "); Serial.print(n,3); Serial.print(" pwm "); Serial.println(pwm); } delay(8); }