// Proyecto: Sensores de presión dual WIKA R-1
// Placa: Arduino Nano (ATmega328P)
// Pantalla: LCD I2C RS 257-6784 (PCF8574)
// Características: Auto-calibración VREF correcta, filtros anti-ruido, conversión a bar,
// modo diagnóstico y depuración por Serial.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// --- Configuración LCD I2C ---
LiquidCrystal_I2C lcd(0x27, 16, 2); // Cambiar a 0x3F si tu módulo tiene otra dirección
// --- Canales ADC ---
const uint8_t PIN_P1 = A0; // 0–5 V => 0–10 bar
const uint8_t PIN_P2 = A1; // 4–20 mA (shunt 250 Ω) => 1–5 V => 0–40 bar
// --- Constantes físicas ---
const float ADC_MAX = 1023.0f;
const float VREF_INT = 1.1f; // referencia interna típica del bandgap
const float V_FS_0_5V = 5.0f; // escala completa de entrada para 0–5 V
const float R_SHUNT_OHM = 250.0f; // resistencia shunt para el canal de 4–20 mA
// --- Filtrado IIR ---
const uint8_t SAMPLES = 15; // ventana para mediana
const float ALPHA = 0.20f; // coeficiente del filtro IIR (0–1)
float filtP1 = 0.0f, filtP2 = 0.0f;
bool filtInit = false;
// --- Modo diagnóstico ---
bool DIAG_MODE = false; // false: muestra bar; true: muestra V y ADC
// --- Prototipos ---
uint16_t readADCStable(uint8_t pin);
float measureVcc();
float medianOf(uint16_t *arr, uint8_t n);
void sortArray(uint16_t *arr, uint8_t n);
// --- Setup ---
void setup() {
// Referencia por defecto AVcc (recomendado 100 nF en AREF a GND)
analogReference(DEFAULT);
// Serial para depuración
Serial.begin(115200);
delay(50);
Serial.println(F("\n[Init] Proyecto Sensores de Presión WIKA R-1 (Nano + LCD I2C)"));
// I2C + LCD
Wire.begin();
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Sensores Presion");
lcd.setCursor(0, 1);
lcd.print("Init...");
delay(800);
}
// --- Loop principal ---
void loop() {
// 1) Medición correcta de Vcc usando la referencia interna (~1.1 V)
float vcc = measureVcc();
// 2) Lecturas robustas (mediana) por canal
uint16_t bufP1[SAMPLES], bufP2[SAMPLES];
for (uint8_t i = 0; i < SAMPLES; i++) {
bufP1[i] = readADCStable(PIN_P1);
bufP2[i] = readADCStable(PIN_P2);
delay(2);
}
float adcP1_med = medianOf(bufP1, SAMPLES);
float adcP2_med = medianOf(bufP2, SAMPLES);
// 3) Conversión a voltaje con Vcc corregida
float vP1 = (adcP1_med / ADC_MAX) * vcc;
float vP2 = (adcP2_med / ADC_MAX) * vcc;
// 4) Filtro IIR (inicializa con el primer valor real)
if (!filtInit) {
filtP1 = vP1;
filtP2 = vP2;
filtInit = true;
} else {
filtP1 = (ALPHA * vP1) + ((1.0f - ALPHA) * filtP1);
filtP2 = (ALPHA * vP2) + ((1.0f - ALPHA) * filtP2);
}
// 5) Conversión a presión
// P1: 0–5 V -> 0–10 bar
float p1_bar = (filtP1 / V_FS_0_5V) * 10.0f;
if (p1_bar < 0) p1_bar = 0;
// P2: 4–20 mA -> 0–40 bar, con shunt 250 Ω => ~1–5 V
float i_mA = (filtP2 / R_SHUNT_OHM) * 1000.0f; // V/R * 1000
float p2_bar = ((i_mA - 4.0f) / 16.0f) * 40.0f; // 16 mA de span
if (p2_bar < 0) p2_bar = 0;
if (p2_bar > 40) p2_bar = 40;
// 6) Visualización en LCD sin clear() continuo
// Línea 0: P1
lcd.setCursor(0, 0);
if (!DIAG_MODE) {
lcd.print("P1:");
lcd.print(p1_bar, 2);
lcd.print(" bar "); // espacios para limpiar restos
} else {
lcd.print("P1 ");
lcd.print(filtP1, 3);
lcd.print("V ");
lcd.print((int)adcP1_med);
lcd.print(" ");
}
// Línea 1: P2
lcd.setCursor(0, 1);
if (!DIAG_MODE) {
lcd.print("P2:");
lcd.print(p2_bar, 2);
lcd.print(" bar ");
} else {
lcd.print("P2 ");
lcd.print(filtP2, 3);
lcd.print("V ");
lcd.print((int)adcP2_med);
lcd.print(" ");
}
// 7) Depuración por Serial
static uint32_t tLast = 0;
uint32_t tNow = millis();
if (tNow - tLast > 300) { // cada ~300 ms
tLast = tNow;
Serial.print(F("Vcc=")); Serial.print(vcc, 3);
Serial.print(F(" V | P1:")); Serial.print(p1_bar, 2);
Serial.print(F(" bar (")); Serial.print(filtP1, 3); Serial.print(F(" V)"));
Serial.print(F(" | P2:")); Serial.print(p2_bar, 2);
Serial.print(F(" bar (")); Serial.print(filtP2, 3); Serial.print(F(" V, "));
Serial.print(i_mA, 2); Serial.print(F(" mA)\n"));
}
delay(120);
}
// --- Funciones auxiliares ---
uint16_t readADCStable(uint8_t pin) {
// Lectura estable: dummy tras cambiar de canal + pequeño retardo
static int8_t lastPin = -1;
if (pin != lastPin) {
analogRead(pin); // lectura de descarte tras conmutar
delayMicroseconds(150);
lastPin = pin;
}
uint16_t a = analogRead(pin);
delayMicroseconds(150);
uint16_t b = analogRead(pin);
return (a + b) >> 1;
}
float measureVcc() {
// Mide Vcc usando el bandgap interno de ~1.1 V con AVcc como referencia.
// Fórmula: lectura = 1023 * (Vbg / Vcc) => Vcc = 1.1 * 1023 / lectura
// Selección de AVcc como referencia y entrada = 1.1V bandgap (MUX=1110)
ADMUX = _BV(REFS0) | 0b1110; // REFS0=AVcc, MUX=14 (1.1V)
ADCSRB = 0; // (en ATmega328P, MUX5 no existe)
delay(2); // tiempo de asentamiento del mux/ref
// Conversión de descarte
ADCSRA |= _BV(ADSC);
while (ADCSRA & _BV(ADSC)) { }
// Conversión válida
ADCSRA |= _BV(ADSC);
while (ADCSRA & _BV(ADSC)) { }
uint16_t adc_ref = ADC; // lee ADCW (ADCL+ADCH)
if (adc_ref == 0) return 5.0; // evita división por cero
float vcc = (VREF_INT * 1023.0f) / (float)adc_ref;
// Límites razonables para filtrar lecturas espurias
if (vcc < 3.8f) vcc = 3.8f;
if (vcc > 5.5f) vcc = 5.5f;
return vcc;
}
float medianOf(uint16_t *arr, uint8_t n) {
sortArray(arr, n);
if (n & 1) {
return arr[n / 2];
} else {
return 0.5f * (arr[n / 2 - 1] + arr[n / 2]);
}
}
void sortArray(uint16_t *arr, uint8_t n) {
// Insertion sort (n pequeño)
for (uint8_t i = 1; i < n; i++) {
uint16_t key = arr[i];
int8_t j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
P2_ ALTA
P1_ BAJA