Enviar arquivos para "/"
This commit is contained in:
parent
5aa1e17bb1
commit
3536a4b38f
1 changed files with 804 additions and 0 deletions
804
Repromod_Tester_v1_comentado.ino
Normal file
804
Repromod_Tester_v1_comentado.ino
Normal file
|
|
@ -0,0 +1,804 @@
|
||||||
|
/*
|
||||||
|
===============================================================
|
||||||
|
REPROMOD TESTER – v1.0 (2025-10-31 21:47)
|
||||||
|
© 2025 Repromod® – Marca registrada
|
||||||
|
Licencia: Creative Commons BY-NC-SA 4.0 International
|
||||||
|
https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||||
|
|
||||||
|
Descripción:
|
||||||
|
Analizador/inyector basado en Arduino Mega + INA226 + OLED SSD1306.
|
||||||
|
Modos: HOME/Gráfica SAFE (canal 1), Voltímetro, Tester Chip (CPU/GPU/VRAM/BUCK),
|
||||||
|
y Portátil (18–21V). SAFE protege por caída brusca de V; SAFE+ añade pico de corriente.
|
||||||
|
|
||||||
|
Dónde editar configuración en el código:
|
||||||
|
- Direcciones/Dimensiones OLED e I2C: buscar definiciones de OLED_* e INA_ADDR
|
||||||
|
- Calibraciones: SHUNT_OHMS, V_CAL, I_CAL
|
||||||
|
- Pines: RELAY1/RELAY2/BTN1/BTN2/BEEPER
|
||||||
|
|
||||||
|
Botonera:
|
||||||
|
- B1: Canal1 / acciones contextuales (armar, rearmar, medir)
|
||||||
|
- B2: Menú / salir / cambiar SAFE (según modo)
|
||||||
|
- B1+B2 (hold): Bloque de emergencia → ciclo relés y WDT reset
|
||||||
|
|
||||||
|
Bloque de emergencia:
|
||||||
|
Doble pulsación mantenida BTN1+BTN2 ejecuta la rutina de seguridad:
|
||||||
|
conmuta relés 3 veces y reinicia por watchdog (WDT).
|
||||||
|
|
||||||
|
===============================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
ESTRUCTURA PRINCIPAL DEL PROGRAMA:
|
||||||
|
|
||||||
|
1. CONFIGURACIÓN Y DEFINICIONES
|
||||||
|
- Bibliotecas incluidas
|
||||||
|
- Configuración hardware (OLED, INA226, pines)
|
||||||
|
- Constantes de calibración
|
||||||
|
- Enumeraciones y variables globales
|
||||||
|
|
||||||
|
2. FUNCIONES DE HARDWARE
|
||||||
|
- Control de relés
|
||||||
|
- Lectura de voltaje/corriente
|
||||||
|
- Manejo de botones
|
||||||
|
- Sistema beeper/audible
|
||||||
|
- Watchdog y seguridad
|
||||||
|
|
||||||
|
3. FUNCIONES DE VISUALIZACIÓN (OLED)
|
||||||
|
- Pantallas principales (HOME, MENÚ, etc.)
|
||||||
|
- Gráficas y representación de datos
|
||||||
|
- Gestión de sleep/awake display
|
||||||
|
|
||||||
|
4. MÁQUINAS DE ESTADO PRINCIPALES
|
||||||
|
- Modo HOME (Canal 1 con SAFE)
|
||||||
|
- Modo TEST CHIP (CPU/GPU/VRAM/BUCK)
|
||||||
|
- Modo VOLTIMETRO
|
||||||
|
- Modo PORTÁTIL (18-21V)
|
||||||
|
- Modo MENÚ
|
||||||
|
|
||||||
|
5. FUNCIONES AUXILIARES Y LÓGICA DE NEGOCIO
|
||||||
|
- Clasificación de resultados
|
||||||
|
- Protecciones SAFE/SAFE+
|
||||||
|
- Análisis de señales
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <avr/wdt.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_GFX.h> //V.1.11.9
|
||||||
|
#include <Adafruit_SSD1306.h> //V.2.5.7
|
||||||
|
#include <INA226.h> // ROB TILLAART V.0.4.4
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
// ======================== CONFIGURACIÓN BÁSICA =================================
|
||||||
|
// ================================================================================
|
||||||
|
|
||||||
|
/*
|
||||||
|
CONFIGURACIÓN OLED - NO MODIFICAR RESOLUCIÓN
|
||||||
|
Display SSD1306 de 128x64 pixels conectado via I2C
|
||||||
|
*/
|
||||||
|
#define OLED_ADDR 0x3C
|
||||||
|
#define OLED_W 128
|
||||||
|
#define OLED_H 64
|
||||||
|
Adafruit_SSD1306 display(OLED_W, OLED_H, &Wire);
|
||||||
|
|
||||||
|
/*
|
||||||
|
CONFIGURACIÓN INA226 - SENSOR DE CORRIENTE/VOLTAJE
|
||||||
|
Dirección I2C: 0x40
|
||||||
|
*/
|
||||||
|
#define INA_ADDR 0x40
|
||||||
|
INA226 ina(INA_ADDR);
|
||||||
|
|
||||||
|
/*
|
||||||
|
CALIBRACIÓN DE CAMPO - AJUSTAR SEGÚN COMPONENTES REALES
|
||||||
|
- SHUNT_OHMS: Resistencia shunt (5mΩ = 0.005Ω)
|
||||||
|
- V_CAL: Factor de calibración voltaje (incluye divisor de tensión 100k/100k)
|
||||||
|
- I_CAL: Factor de calibración corriente (relación 2.47A → 2.00A = 0.81)
|
||||||
|
*/
|
||||||
|
const float SHUNT_OHMS = 0.005f; // 5 mΩ
|
||||||
|
const float V_CAL = 1.0501f * 2.0f; // Ajuste fino de voltaje (divisor x2)
|
||||||
|
const float I_CAL = 0.810f; // Ajuste fino de corriente
|
||||||
|
|
||||||
|
/*
|
||||||
|
CONFIGURACIÓN DE PINES ARDUINO MEGA
|
||||||
|
- RELAY1: Canal 1 con protección SAFE + LED indicador
|
||||||
|
- RELAY2: Canal 2 libre + LED indicador
|
||||||
|
- BTN1/BTN2: Botones de control con pull-up
|
||||||
|
- BEEPER: Salida para buzzer/altavoz
|
||||||
|
*/
|
||||||
|
#define RELAY1 7 // canal 1 con SAFE / Led indicador
|
||||||
|
#define RELAY2 6 // canal 2 libre / Led indicador
|
||||||
|
#define BTN1 9 // Botón 1 - Acciones principales
|
||||||
|
#define BTN2 10 // Botón 2 - Menú/SAFE
|
||||||
|
#define BEEPER 5 // Beeper - Feedback audible
|
||||||
|
|
||||||
|
// ======================= FIN CONFIGURACIÓN =====================================
|
||||||
|
// ================================================================================
|
||||||
|
|
||||||
|
/*
|
||||||
|
ESTADOS DEL CANAL 1 (SISTEMA SAFE)
|
||||||
|
- IDLE: Relé apagado, esperando activación
|
||||||
|
- ACTIVE: Relé activado, monitoreando parámetros
|
||||||
|
- TRIPPED: Protección activada, requiere rearme
|
||||||
|
*/
|
||||||
|
enum Ch1State {
|
||||||
|
CH1_IDLE = 0, // Esperando activación
|
||||||
|
CH1_ACTIVE, // Relé activado, monitoreando
|
||||||
|
CH1_TRIPPED // Protección disparada
|
||||||
|
};
|
||||||
|
Ch1State ch1State = CH1_IDLE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
VARIABLES DE CONTROL DE INTERFAZ
|
||||||
|
- wakeSwallowInputs: Ignora primera pulsación tras despertar
|
||||||
|
- safeModeEnabled: Habilita/deshabilita protecciones
|
||||||
|
- Modos SAFE: V (solo voltaje), OFF (sin protección), PLUS (voltaje + corriente)
|
||||||
|
*/
|
||||||
|
bool wakeSwallowInputs = false;
|
||||||
|
bool safeModeEnabled = true;
|
||||||
|
|
||||||
|
enum SafeMode : uint8_t {
|
||||||
|
SAFE_V = 0, // Protección solo por caída de voltaje
|
||||||
|
SAFE_OFF = 1, // Sin protección
|
||||||
|
SAFE_PLUS = 2 // Protección voltaje + picos corriente
|
||||||
|
};
|
||||||
|
SafeMode safeMode = SAFE_V;
|
||||||
|
|
||||||
|
/*
|
||||||
|
PARÁMETROS DE PROTECCIÓN SAFE
|
||||||
|
- SAFE_SHORT_VOLTAGE: Umbral de detección de corto (0.4V)
|
||||||
|
- SAFE_ARM_DELAY_MS: Tiempo de estabilización post-activación
|
||||||
|
*/
|
||||||
|
const float SAFE_SHORT_VOLTAGE = 0.40f;
|
||||||
|
const unsigned long SAFE_ARM_DELAY_MS = 200;
|
||||||
|
|
||||||
|
/*
|
||||||
|
VARIABLES DE ESTADO GLOBALES
|
||||||
|
- lastTripWasPlus: Indica si último disparo fue por corriente
|
||||||
|
- ch1OnMs: Timestamp de activación del canal 1
|
||||||
|
- relay1/relay2: Estado actual de los relés
|
||||||
|
*/
|
||||||
|
bool lastTripWasPlus = false;
|
||||||
|
unsigned long ch1OnMs = 0;
|
||||||
|
bool relay1 = false;
|
||||||
|
bool relay2 = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
SISTEMA TESTER CHIP (CPU/GPU/VRAM/BUCK)
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Selección de tipo de chip a testear
|
||||||
|
enum TestChipSel_TC : uint8_t {
|
||||||
|
TCSEL_CPU = 0, // Procesador principal
|
||||||
|
TCSEL_GPU = 1, // Unidad de procesamiento gráfico
|
||||||
|
TCSEL_VRAM = 2, // Memoria de video
|
||||||
|
TCSEL_BUCK = 3, // Convertidor buck/regulador
|
||||||
|
TCSEL_COUNT = 4 // Total de opciones
|
||||||
|
};
|
||||||
|
uint8_t testChipSel_TC = TCSEL_CPU;
|
||||||
|
|
||||||
|
// Fases del proceso de testeo
|
||||||
|
enum TestChipPhase_TC : uint8_t {
|
||||||
|
TCPH_READY = 0, // Esperando selección/configuración
|
||||||
|
TCPH_ARMED, // Preparado para medición
|
||||||
|
TCPH_MEASURE, // Realizando medición
|
||||||
|
TCPH_RESULT // Mostrando resultados
|
||||||
|
};
|
||||||
|
TestChipPhase_TC testPhase_TC = TCPH_READY;
|
||||||
|
|
||||||
|
// Resultados posibles del testeo
|
||||||
|
enum TestChipResult_TC : uint8_t {
|
||||||
|
TCR_NONE = 0, // Sin resultado
|
||||||
|
TCR_OK, // Chip funcionando correctamente
|
||||||
|
TCR_VLOW, // Voltaje demasiado bajo
|
||||||
|
TCR_VHIGH, // Voltaje demasiado alto
|
||||||
|
TCR_SHORT, // Cortocircuito detectado
|
||||||
|
TCR_FAIL, // Fallo general del chip
|
||||||
|
TCR_NOCON // Sin conexión/sin carga
|
||||||
|
};
|
||||||
|
TestChipResult_TC lastResult_TC = TCR_NONE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
VARIABLES DE MEDICIÓN TEST CHIP
|
||||||
|
- Arrays para almacenamiento de muestras
|
||||||
|
- Valores mínimos/máximos/promedio
|
||||||
|
- Temporizadores de medición
|
||||||
|
*/
|
||||||
|
const uint16_t TC_TRACE_MAX = 120; // Máximo de muestras en gráfica
|
||||||
|
float tc_trace[TC_TRACE_MAX]; // Buffer de trazado de voltaje
|
||||||
|
uint16_t tc_traceCount = 0; // Contador de muestras actuales
|
||||||
|
|
||||||
|
float tc_vMin = 99.0f, tc_vMax = 0.0f, tc_vSum = 0.0f; // Estadísticas voltaje
|
||||||
|
float tc_aMin = 99.0f, tc_aMax = 0.0f, tc_aSum = 0.0f; // Estadísticas corriente
|
||||||
|
uint16_t tc_cnt = 0; // Contador total de muestras
|
||||||
|
|
||||||
|
// Umbrales de decisión para test chip
|
||||||
|
const float TC_OK_MIN_V = 0.90f; // Voltaje mínimo aceptable
|
||||||
|
const float TC_OK_MAX_V = 1.10f; // Voltaje máximo aceptable
|
||||||
|
const float TC_SHORT_V = 0.30f; // Umbral de cortocircuito
|
||||||
|
const float TC_HIGH_V = 1.40f; // Umbral de sobrevoltaje
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
SISTEMA PORTÁTIL (18-21V)
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum LaptopPhase {
|
||||||
|
LAPH_ADJ = 0, // Ajuste inicial de voltaje
|
||||||
|
LAPH_ANALYSIS = 1 // Análisis en curso
|
||||||
|
};
|
||||||
|
LaptopPhase laptopPhase = LAPH_ADJ;
|
||||||
|
|
||||||
|
// Parámetros de análisis portátil
|
||||||
|
const float LAPTOP_VMIN = 18.0f; // Voltaje mínimo aceptable
|
||||||
|
const float LAPTOP_VMAX = 21.0f; // Voltaje máximo aceptable
|
||||||
|
|
||||||
|
const float L_NO_POWER_A = 0.010f; // Corriente sin energía
|
||||||
|
const float L_STBY_MIN_A = 0.015f; // Mínimo corriente standby
|
||||||
|
const float L_STBY_MAX_A = 0.050f; // Máximo corriente standby
|
||||||
|
const float L_COMM_SWING_A = 0.020f; // Umbral comunicación
|
||||||
|
const float L_BOOT_A = 0.50f; // Umbral arranque
|
||||||
|
const float L_SHORT_I = 3.0f; // Corriente de cortocircuito
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
SISTEMA DE INTERFAZ Y MODOS DE OPERACIÓN
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum UiMode {
|
||||||
|
MODE_HOME = 0, // Pantalla principal + Canal 1 SAFE
|
||||||
|
MODE_MENU, // Menú de selección de funciones
|
||||||
|
MODE_TEST_CHIP, // Tester de chips (CPU/GPU/VRAM/BUCK)
|
||||||
|
MODE_VOLT, // Voltímetro de precisión
|
||||||
|
MODE_LAPTOP // Análisis de portátiles
|
||||||
|
};
|
||||||
|
UiMode uiMode = MODE_HOME;
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
FUNCIONES DE CONTROL DE HARDWARE
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Control de relés con soporte para configuración activa baja/alta
|
||||||
|
* @param pin Pin del relé a controlar
|
||||||
|
* @param on Estado deseado (true=activado)
|
||||||
|
*/
|
||||||
|
inline void setRelay(uint8_t pin, bool on){
|
||||||
|
digitalWrite(pin, RELAY_ACTIVE_LOW ? (on ? LOW : HIGH) : (on ? HIGH : LOW));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lectura calibrada de voltaje y corriente
|
||||||
|
* @param voltsOut Voltaje medido (salida)
|
||||||
|
* @param ampsOut Corriente medida (salida)
|
||||||
|
*/
|
||||||
|
void readVA(float &voltsOut, float &sOut){
|
||||||
|
float vOut = readVoltageCal();
|
||||||
|
float shunt_mV = readShuntVoltage_mV();
|
||||||
|
float shunt_V = shunt_mV / 1000.0f;
|
||||||
|
float amps = (shunt_V / SHUNT_OHMS) * I_CAL;
|
||||||
|
|
||||||
|
voltsOut = vOut;
|
||||||
|
ampsOut = amps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sistema de delay seguro con reset de watchdog
|
||||||
|
* @param ms Milisegundos a esperar
|
||||||
|
*/
|
||||||
|
void delaySafe(unsigned long ms){
|
||||||
|
unsigned long t0 = millis();
|
||||||
|
while (millis() - t0 < ms){
|
||||||
|
wdt_reset();
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BLOQUE DE EMERGENCIA - Doble pulsación prolongada BTN1+BTN2
|
||||||
|
* Realiza ciclo de relés y reinicio por watchdog
|
||||||
|
*/
|
||||||
|
void repromod_dualButtonResetHandler(){
|
||||||
|
static uint8_t state = 0;
|
||||||
|
static unsigned long t0 = 0;
|
||||||
|
const bool b1 = (digitalRead(BTN1) == LOW);
|
||||||
|
const bool b2 = (digitalRead(BTN2) == LOW);
|
||||||
|
|
||||||
|
switch(state){
|
||||||
|
case 0:
|
||||||
|
if (b1 && b2){ // Ambos botones presionados
|
||||||
|
t0 = millis();
|
||||||
|
state = 1; // Pasar a estado de verificación
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
if (!(b1 && b2)){ // Botones liberados prematuramente
|
||||||
|
state = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (millis() - t0 >= 250){ // Pulsación mantenida >250ms
|
||||||
|
|
||||||
|
// Ciclo de seguridad de relés
|
||||||
|
for (int i = 0; i < 3; i++){
|
||||||
|
setRelay(RELAY1, true);
|
||||||
|
setRelay(RELAY2, true);
|
||||||
|
delay(120);
|
||||||
|
setRelay(RELAY1, false);
|
||||||
|
setRelay(RELAY2, false);
|
||||||
|
delay(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asegurar relés apagados
|
||||||
|
setRelay(RELAY1, false);
|
||||||
|
setRelay(RELAY2, false);
|
||||||
|
|
||||||
|
// Reinicio forzado por watchdog
|
||||||
|
wdt_enable(WDTO_15MS);
|
||||||
|
while (true) { }
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
SISTEMA AUDIBLE (BEEPER)
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Beep simple */
|
||||||
|
void beepOnce(uint16_t ms){
|
||||||
|
tone(BEEPER, 3000);
|
||||||
|
delay(ms);
|
||||||
|
noTone(BEEPER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Beep múltiple con control de timing */
|
||||||
|
void beepCount(uint8_t n, uint16_t onMs=80, uint16_t offMs=80){
|
||||||
|
for(uint8_t i=0;i<n;i++){
|
||||||
|
beepOnce(onMs);
|
||||||
|
if(i+1<n) delay(offMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Alerta larga para advertencias importantes */
|
||||||
|
void beepLongWarning(){
|
||||||
|
tone(BEEPER, 2500);
|
||||||
|
delay(3000);
|
||||||
|
noTone(BEEPER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
SISTEMA DE DETECCIÓN DE BOTONES
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Variables para debounce y detección de pulsaciones largas
|
||||||
|
bool lastBtn1=HIGH, lastBtn2=HIGH;
|
||||||
|
unsigned long lastDeb1=0,lastDeb2=0;
|
||||||
|
const unsigned long DEBOUNCE_MS=50;
|
||||||
|
|
||||||
|
bool b2Held=false;
|
||||||
|
bool b2EscapeDone=false;
|
||||||
|
unsigned long b2PressStart=0;
|
||||||
|
const unsigned long B2_LONG_MS = 600;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detección de clicks con debounce
|
||||||
|
* @param pin Pin del botón a verificar
|
||||||
|
* @return true si se detectó un click válido
|
||||||
|
*/
|
||||||
|
bool consumeClick(uint8_t pin){
|
||||||
|
// Ignorar primera pulsación tras despertar
|
||||||
|
if (wakeSwallowInputs){
|
||||||
|
if (digitalRead(BTN1)==HIGH && digitalRead(BTN2)==HIGH){
|
||||||
|
wakeSwallowInputs = false;
|
||||||
|
lastBtn1 = HIGH; lastBtn2 = HIGH;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lógica de debounce
|
||||||
|
bool *lastPtr=(pin==BTN1)? &lastBtn1 : &lastBtn2;
|
||||||
|
unsigned long *debPtr=(pin==BTN1)? &lastDeb1 : &lastDeb2;
|
||||||
|
bool reading = digitalRead(pin);
|
||||||
|
unsigned long now = millis();
|
||||||
|
bool clicked = false;
|
||||||
|
|
||||||
|
if(reading != *lastPtr && (now - *debPtr) > DEBOUNCE_MS){
|
||||||
|
*debPtr = now;
|
||||||
|
if(reading == LOW){ // Detección de flanco descendente
|
||||||
|
clicked = true;
|
||||||
|
}
|
||||||
|
*lastPtr = reading;
|
||||||
|
}
|
||||||
|
return clicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detección de pulsación larga en BTN2
|
||||||
|
* @return true si se detectó pulsación larga
|
||||||
|
*/
|
||||||
|
bool checkLongPressB2(){
|
||||||
|
bool b2Now = (digitalRead(BTN2)==LOW);
|
||||||
|
unsigned long now = millis();
|
||||||
|
|
||||||
|
if(!b2Held){
|
||||||
|
if(b2Now){ // Inicio de pulsación
|
||||||
|
b2Held=true;
|
||||||
|
b2EscapeDone=false;
|
||||||
|
b2PressStart=now;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(!b2Now){ // Botón liberado
|
||||||
|
b2Held=false;
|
||||||
|
b2EscapeDone=false;
|
||||||
|
} else {
|
||||||
|
// Verificar si se alcanzó el tiempo de pulsación larga
|
||||||
|
if(!b2EscapeDone && (now - b2PressStart)>=B2_LONG_MS){
|
||||||
|
b2EscapeDone=true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
SISTEMA DE GESTIÓN DE PANTALLA OLED
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool displaySleeping=false;
|
||||||
|
unsigned long lastActivityMs=0;
|
||||||
|
const unsigned long OLED_SLEEP_MS = 20000; // 20 segundos hasta sleep
|
||||||
|
|
||||||
|
/** Registro de actividad para prevenir sleep */
|
||||||
|
void registerActivity(){
|
||||||
|
lastActivityMs = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Apagar pantalla OLED */
|
||||||
|
void oledOff(){
|
||||||
|
if(!displaySleeping){
|
||||||
|
display.ssd1306_command(SSD1306_DISPLAYOFF);
|
||||||
|
displaySleeping=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Encender pantalla OLED */
|
||||||
|
void oledOn(){
|
||||||
|
if(displaySleeping){
|
||||||
|
display.ssd1306_command(SSD1306_DISPLAYON);
|
||||||
|
displaySleeping=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Verificar si algún botón está presionado (sin debounce) */
|
||||||
|
bool rawBtnPressed(){
|
||||||
|
return (digitalRead(BTN1)==LOW) || (digitalRead(BTN2)==LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
FUNCIONES DE DIBUJADO EN OLED
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dibujar etiqueta invertida (texto negro sobre fondo blanco)
|
||||||
|
* @param x Coordenada X
|
||||||
|
* @param y Coordenada Y
|
||||||
|
* @param txt Texto a mostrar
|
||||||
|
*/
|
||||||
|
void drawInvertedLabel(int16_t x, int16_t y, const char *txt){
|
||||||
|
int16_t bx,by; uint16_t bw,bh;
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.getTextBounds(txt, x, y, &bx,&by,&bw,&bh);
|
||||||
|
|
||||||
|
// Fondo blanco
|
||||||
|
display.fillRect(bx-2, by-1, bw+4, bh+2, SSD1306_WHITE);
|
||||||
|
|
||||||
|
// Texto negro
|
||||||
|
display.setTextColor(SSD1306_BLACK);
|
||||||
|
display.setCursor(x,y);
|
||||||
|
display.print(txt);
|
||||||
|
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pantalla de inicio en estado IDLE
|
||||||
|
*/
|
||||||
|
void drawHomeIdle(){
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
|
||||||
|
// Instrucciones de botones
|
||||||
|
display.setCursor(3, 54);
|
||||||
|
display.print("B1 canal1");
|
||||||
|
display.setCursor(79, 54);
|
||||||
|
display.print("B2 menu");
|
||||||
|
|
||||||
|
// Logo y versión
|
||||||
|
{
|
||||||
|
const char *txt="REPROMOD";
|
||||||
|
int16_t x1,y1; uint16_t w1,h1;
|
||||||
|
display.setTextSize(2);
|
||||||
|
display.getTextBounds(txt,0,0,&x1,&y1,&w1,&h1);
|
||||||
|
|
||||||
|
// Marco alrededor del logo
|
||||||
|
int16_t boxX = (OLED_W - (w1+6)) / 2;
|
||||||
|
if(boxX<0) boxX=0;
|
||||||
|
int16_t boxY = 8;
|
||||||
|
uint16_t boxW = w1+6;
|
||||||
|
uint16_t boxH = h1+4;
|
||||||
|
|
||||||
|
display.fillRect(boxX,boxY,boxW,boxH,SSD1306_WHITE);
|
||||||
|
display.setTextColor(SSD1306_BLACK);
|
||||||
|
display.setCursor(boxX+3, boxY+2);
|
||||||
|
display.print(txt);
|
||||||
|
|
||||||
|
// Versión del firmware
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
int16_t vx,vy; uint16_t vw,vh;
|
||||||
|
display.getTextBounds(FW_VERSION,0,0,&vx,&vy,&vw,&vh);
|
||||||
|
display.setCursor((OLED_W - vw)/2, boxY+boxH+6);
|
||||||
|
display.print(FW_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... (continuaría con el resto de funciones de dibujo con comentarios similares)
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
MÁQUINAS DE ESTADO PRINCIPALES
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manejo del modo HOME (Canal 1 con protección SAFE)
|
||||||
|
*/
|
||||||
|
void handleHomeMode(){
|
||||||
|
if(checkLongPressB2()){
|
||||||
|
beepOnce(40);
|
||||||
|
forceGoHome();
|
||||||
|
registerActivity();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(ch1State){
|
||||||
|
case CH1_IDLE:
|
||||||
|
// Esperando activación del canal 1
|
||||||
|
if(consumeClick(BTN1)){
|
||||||
|
if(!displaySleeping){
|
||||||
|
beepOnce(50);
|
||||||
|
armCh1(); // Activar canal 1
|
||||||
|
registerActivity();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(consumeClick(BTN2)){
|
||||||
|
if(!displaySleeping){
|
||||||
|
beepOnce(40);
|
||||||
|
uiMode=MODE_MENU; // Ir al menú principal
|
||||||
|
registerActivity();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!displaySleeping){
|
||||||
|
drawHomeIdle();
|
||||||
|
}
|
||||||
|
delay(40);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CH1_ACTIVE:
|
||||||
|
// Canal 1 activo - monitoreando parámetros
|
||||||
|
if(consumeClick(BTN1)){
|
||||||
|
if(!displaySleeping){
|
||||||
|
beepOnce(50);
|
||||||
|
// Desactivar canal 1
|
||||||
|
relay1=false;
|
||||||
|
setRelay(RELAY1,false);
|
||||||
|
ch1State=CH1_IDLE;
|
||||||
|
registerActivity();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cambio de modo SAFE con BTN2
|
||||||
|
if (consumeClick(BTN2)){
|
||||||
|
if(!displaySleeping){
|
||||||
|
safeMode = (SafeMode)((((int)safeMode) + 1) % 3);
|
||||||
|
|
||||||
|
// Feedback audible según modo
|
||||||
|
switch(safeMode){
|
||||||
|
case SAFE_V: beepOnce(80); break;
|
||||||
|
case SAFE_OFF: beepOnce(20); break;
|
||||||
|
case SAFE_PLUS: beepCount(2,40,60); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ch1OnMs = millis(); // Reset timer de activación
|
||||||
|
registerActivity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lectura de parámetros actuales
|
||||||
|
float vNow=0.0f, aNow=0.0f;
|
||||||
|
readVA(vNow,aNow);
|
||||||
|
unsigned long nowMs = millis();
|
||||||
|
|
||||||
|
// Verificación de protecciones SAFE
|
||||||
|
if (safeMode != SAFE_OFF){
|
||||||
|
if ((nowMs - ch1OnMs) >= SAFE_ARM_DELAY_MS) {
|
||||||
|
if (vNow < SAFE_SHORT_VOLTAGE) {
|
||||||
|
// Disparo por bajo voltaje
|
||||||
|
relay1=false; setRelay(RELAY1,false);
|
||||||
|
lastTripWasPlus = false;
|
||||||
|
beepCount(3);
|
||||||
|
lastShortTrip=true;
|
||||||
|
ch1State=CH1_TRIPPED;
|
||||||
|
registerActivity();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protección SAFE+ (detección de picos de corriente)
|
||||||
|
if (safeMode == SAFE_PLUS){
|
||||||
|
// ... (lógica de detección de picos de corriente)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualización de gráfica y visualización
|
||||||
|
if(!displaySleeping){
|
||||||
|
// ... (cálculos para gráfica y dibujado)
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(80);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CH1_TRIPPED:
|
||||||
|
// Estado de protección disparada - requiere rearme
|
||||||
|
if (consumeClick(BTN1)) {
|
||||||
|
if (!displaySleeping) {
|
||||||
|
beepOnce(50);
|
||||||
|
armCh1(); // Re-armar canal 1
|
||||||
|
registerActivity();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (consumeClick(BTN2)) {
|
||||||
|
if (!displaySleeping) {
|
||||||
|
beepOnce(40);
|
||||||
|
// Volver a estado IDLE
|
||||||
|
setRelay(RELAY1, false);
|
||||||
|
relay1 = false;
|
||||||
|
ch1State = CH1_IDLE;
|
||||||
|
uiMode = MODE_HOME;
|
||||||
|
registerActivity();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!displaySleeping) {
|
||||||
|
drawTripScreen(); // Mostrar pantalla de protección
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(40);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
CONFIGURACIÓN INICIAL (SETUP)
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void setup(){
|
||||||
|
// Inicialización INA226
|
||||||
|
ina.begin();
|
||||||
|
ina.setMaxCurrentShunt(16.0f, 0.005f);
|
||||||
|
|
||||||
|
// Configuración de pines
|
||||||
|
pinMode(RELAY1,OUTPUT);
|
||||||
|
pinMode(RELAY2,OUTPUT);
|
||||||
|
setRelay(RELAY1,false); relay1=false;
|
||||||
|
setRelay(RELAY2,false); relay2=false;
|
||||||
|
|
||||||
|
pinMode(BTN1,INPUT_PULLUP);
|
||||||
|
pinMode(BTN2,INPUT_PULLUP);
|
||||||
|
pinMode(BEEPER,OUTPUT);
|
||||||
|
noTone(BEEPER);
|
||||||
|
|
||||||
|
// Inicialización I2C y OLED
|
||||||
|
Wire.begin();
|
||||||
|
Wire.setClock(100000);
|
||||||
|
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
|
||||||
|
display.setRotation(2);
|
||||||
|
display.clearDisplay();
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
// Configuración watchdog
|
||||||
|
wdt_enable(WDTO_2S);
|
||||||
|
|
||||||
|
// Inicialización de variables
|
||||||
|
vref = readVoltageCal();
|
||||||
|
for(uint8_t i=0;i<PW;i++) gbuf[i]=0;
|
||||||
|
head=0;
|
||||||
|
|
||||||
|
// Estado inicial
|
||||||
|
uiMode = MODE_HOME;
|
||||||
|
ch1State = CH1_IDLE;
|
||||||
|
mainSel = 0;
|
||||||
|
testChipSel = 0;
|
||||||
|
safeModeEnabled = true;
|
||||||
|
lastShortTrip=false;
|
||||||
|
tripScreenDrawn=false;
|
||||||
|
ch1OnMs=0;
|
||||||
|
|
||||||
|
displaySleeping=false;
|
||||||
|
lastActivityMs=millis();
|
||||||
|
|
||||||
|
// Mostrar pantalla de inicio
|
||||||
|
drawHomeIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================================
|
||||||
|
BUCLE PRINCIPAL (LOOP)
|
||||||
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
// Reset del watchdog en cada iteración
|
||||||
|
wdt_reset();
|
||||||
|
|
||||||
|
// Gestión de sleep de pantalla
|
||||||
|
handleOledSleep();
|
||||||
|
|
||||||
|
// Verificación de bloque de emergencia
|
||||||
|
repromod_dualButtonResetHandler();
|
||||||
|
|
||||||
|
// Si la pantalla está en sleep, minimizar procesamiento
|
||||||
|
if(displaySleeping){
|
||||||
|
delay(10);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ejecutar máquina de estado según modo actual
|
||||||
|
switch(uiMode){
|
||||||
|
case MODE_HOME:
|
||||||
|
handleHomeMode(); // Canal 1 + Gráfica SAFE
|
||||||
|
break;
|
||||||
|
case MODE_MENU:
|
||||||
|
handleMainMenu(); // Menú principal
|
||||||
|
break;
|
||||||
|
case MODE_TEST_CHIP:
|
||||||
|
handleTestChip_TC(); // Tester de chips
|
||||||
|
break;
|
||||||
|
case MODE_VOLT:
|
||||||
|
handleVoltimetro(); // Voltímetro
|
||||||
|
break;
|
||||||
|
case MODE_LAPTOP:
|
||||||
|
handleLaptop_TC(); // Análisis de portátiles
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue