2937 lines
74 KiB
Arduino
2937 lines
74 KiB
Arduino
|
|
/*
|
|||
|
|
===============================================================
|
|||
|
|
REPROMOD TESTER – v1.4 (04-11-2025 18: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).
|
|||
|
|
|
|||
|
|
===============================================================
|
|||
|
|
*/
|
|||
|
|
/* Notas para operador (resumen práctico)
|
|||
|
|
- SAFE / SAFE+: en la gráfica de Canal 1, pulsa B2 para rotar: SAFE → NO SAFE → SAFE+.
|
|||
|
|
SAFE corta si V < 0.30 V tras el arm (protege por corto). SAFE+ añade corte por pico de I.
|
|||
|
|
- Voltímetro: B2 para salir (aviso pinzas). Estado: ESTABLE / FLUCTUANTE / PULSANTE.
|
|||
|
|
- Tester Chip: B2 rota la categoría; B1 inicia la medición. Si el ajuste no es 1.0 V (1.2 V VRAM), avisa.
|
|||
|
|
- Portátil: B1 valida 18–21 V y entra en análisis. B1 conmuta relé; B2 vuelve atrás.
|
|||
|
|
- OLED Sleep: solo en HOME+IDLE. Cualquier tecla despierta (se ignora el primer click tras despertar).
|
|||
|
|
*/
|
|||
|
|
/*───────────────────────────────────────────────────────────────
|
|||
|
|
🧾 CHANGELOG – REPROMOD TESTER FIRMWARE
|
|||
|
|
───────────────────────────────────────────────────────────────
|
|||
|
|
Base oficial: v1.0 (31-10-2025 21:47)
|
|||
|
|
Hardware: Arduino MEGA 2560 Pro, INA226, OLED SSD1306 128×64
|
|||
|
|
───────────────────────────────────────────────────────────────
|
|||
|
|
v1.4
|
|||
|
|
• Añadido mensaje “RETIRE LA BATERÍA” en modo Portátil (fase de ajuste).
|
|||
|
|
• Corrección visual: flecha “<B2” (antes “B2<”) en todas las pantallas.
|
|||
|
|
• Beep corto cada 5 s cuando el análisis Portátil está en ON.
|
|||
|
|
• Parpadeo visual del texto “ANÁLISIS” (blanco/negro) según estado ON/OFF.
|
|||
|
|
• Mantenimiento de relé activo en análisis, visualización estable.
|
|||
|
|
• Implementado modo CALIBRACIÓN:
|
|||
|
|
- Nueva opción en menú principal.
|
|||
|
|
- Paso 1: comprobación 10 V con resistencia R100 5 W.
|
|||
|
|
- Paso 2: ajuste de corriente (0.100 A ±0.005 A).
|
|||
|
|
- Paso 3: ajuste de voltaje (10.00 V ±0.05 V).
|
|||
|
|
- Guardado automático en EEPROM (factores calI_k / calV_k).
|
|||
|
|
- Relé activo durante calibración para lectura continua.
|
|||
|
|
- Mensajes de error y confirmación “CALIBRACIÓN COMPLETADA”.
|
|||
|
|
• Refinado muestreo de lectura INA226 durante ajustes.
|
|||
|
|
• Ajustes menores de sincronización y presentación OLED.
|
|||
|
|
|
|||
|
|
───────────────────────────────────────────────────────────────*/
|
|||
|
|
|
|||
|
|
#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// =====================================================
|
|||
|
|
// PANTALLA OLED *No altere la resolución
|
|||
|
|
#define OLED_ADDR 0x3C
|
|||
|
|
#define OLED_W 128
|
|||
|
|
#define OLED_H 64
|
|||
|
|
Adafruit_SSD1306 display(OLED_W, OLED_H, &Wire);
|
|||
|
|
|
|||
|
|
// INA226 *Cálculo de medición en referencia al valor de la fuente
|
|||
|
|
#define INA_ADDR 0x40
|
|||
|
|
INA226 ina(INA_ADDR);
|
|||
|
|
// Calibración campo
|
|||
|
|
const float SHUNT_OHMS = 0.005f; // 5 mΩ, si tienes otra resistencia cámbiala en tu placa a esta
|
|||
|
|
// Si metiste divisor de tensión de 100k/100k a VBUS multiplicamos x2:
|
|||
|
|
const float V_CAL = 1.0501f * 2.0f; // Valor de fuente ejem. 10.5v / Valor de Ina226 ejem. 5v = V_CAL (2.10f)
|
|||
|
|
// Ajuste el fino de amperajes
|
|||
|
|
const float I_CAL = 0.810f; // Este ajuste es para (2.47A → 2.00A) relación 1:!
|
|||
|
|
|
|||
|
|
// CONFIGURACIÓN DE PINES
|
|||
|
|
#define RELAY1 7 // canal 1 con SAFE / Led indicador
|
|||
|
|
#define RELAY2 6 // canal 2 libre / Led indicador
|
|||
|
|
#define BTN1 9 // Botón 1
|
|||
|
|
#define BTN2 10 // Botón 2
|
|||
|
|
#define BEEPER 5 // Beeper
|
|||
|
|
|
|||
|
|
// =============================FIN DE CONFIGURACIÓN// ================================================
|
|||
|
|
|
|||
|
|
enum Ch1State {
|
|||
|
|
CH1_IDLE = 0,
|
|||
|
|
CH1_ACTIVE,
|
|||
|
|
CH1_TRIPPED
|
|||
|
|
};
|
|||
|
|
Ch1State ch1State = CH1_IDLE;
|
|||
|
|
|
|||
|
|
bool wakeSwallowInputs = false;
|
|||
|
|
bool safeModeEnabled = true;
|
|||
|
|
float tc_lastAdjV = NAN;
|
|||
|
|
float tc_lastAdjA = NAN;
|
|||
|
|
const float SAFE_SHORT_VOLTAGE = 0.40f;
|
|||
|
|
const unsigned long SAFE_ARM_DELAY_MS = 200;
|
|||
|
|
|
|||
|
|
enum SafeMode : uint8_t { SAFE_V = 0, SAFE_OFF = 1, SAFE_PLUS = 2 };
|
|||
|
|
SafeMode safeMode = SAFE_V;
|
|||
|
|
|
|||
|
|
bool lastTripWasPlus = false;
|
|||
|
|
unsigned long ch1OnMs = 0;
|
|||
|
|
|
|||
|
|
float tc_aPrev = NAN;
|
|||
|
|
float tc_aP2P = 0.0f;
|
|||
|
|
float tc_aStepMax = 0.0f;
|
|||
|
|
uint16_t tc_runMs = 4000;
|
|||
|
|
uint16_t tc_settleMs = 200;
|
|||
|
|
bool lastShortTrip = false;
|
|||
|
|
bool tripScreenDrawn = false;
|
|||
|
|
|
|||
|
|
enum TestChipSel_TC : uint8_t {
|
|||
|
|
TCSEL_CPU = 0,
|
|||
|
|
TCSEL_GPU = 1,
|
|||
|
|
TCSEL_VRAM = 2,
|
|||
|
|
TCSEL_BUCK = 3,
|
|||
|
|
TCSEL_COUNT = 4
|
|||
|
|
};
|
|||
|
|
uint8_t testChipSel_TC = TCSEL_CPU;
|
|||
|
|
|
|||
|
|
enum TestChipPhase_TC : uint8_t {
|
|||
|
|
TCPH_READY = 0,
|
|||
|
|
TCPH_ARMED,
|
|||
|
|
TCPH_MEASURE,
|
|||
|
|
TCPH_RESULT
|
|||
|
|
};
|
|||
|
|
TestChipPhase_TC testPhase_TC = TCPH_READY;
|
|||
|
|
|
|||
|
|
enum LaptopPhase { LAPH_ADJ = 0, LAPH_ANALYSIS = 1 };
|
|||
|
|
LaptopPhase laptopPhase = LAPH_ADJ;
|
|||
|
|
|
|||
|
|
enum TestChipResult_TC : uint8_t {
|
|||
|
|
TCR_NONE = 0,
|
|||
|
|
TCR_OK,
|
|||
|
|
TCR_VLOW,
|
|||
|
|
TCR_VHIGH,
|
|||
|
|
TCR_SHORT,
|
|||
|
|
TCR_FAIL,
|
|||
|
|
TCR_NOCON
|
|||
|
|
};
|
|||
|
|
TestChipResult_TC lastResult_TC = TCR_NONE;
|
|||
|
|
|
|||
|
|
bool RELAY_ACTIVE_LOW = false;
|
|||
|
|
inline void setRelay(uint8_t pin, bool on){
|
|||
|
|
digitalWrite(pin,
|
|||
|
|
RELAY_ACTIVE_LOW
|
|||
|
|
? (on ? LOW : HIGH)
|
|||
|
|
: (on ? HIGH : LOW)
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool relay1=false;
|
|||
|
|
bool relay2=false;
|
|||
|
|
|
|||
|
|
const char *FW_VERSION = "Tester v1.4";
|
|||
|
|
|
|||
|
|
const uint16_t TC_TRACE_MAX = 120;
|
|||
|
|
float tc_trace[TC_TRACE_MAX];
|
|||
|
|
uint16_t tc_traceCount = 0;
|
|||
|
|
|
|||
|
|
uint8_t tc_finalCode = 4;
|
|||
|
|
|
|||
|
|
float tc_vMin = 99.0f;
|
|||
|
|
float tc_vMax = 0.0f;
|
|||
|
|
float tc_vSum = 0.0f;
|
|||
|
|
uint16_t tc_cnt = 0;
|
|||
|
|
float tc_lastCheckVolt = 0.0f;
|
|||
|
|
|
|||
|
|
float tc_aMin = 99.0f;
|
|||
|
|
float tc_aMax = 0.0f;
|
|||
|
|
float tc_aSum = 0.0f;
|
|||
|
|
|
|||
|
|
float tc_vMinStable = 99.0f;
|
|||
|
|
float tc_vMaxStable = 0.0f;
|
|||
|
|
float tc_aSumStable = 0.0f;
|
|||
|
|
uint16_t tc_cntStable = 0;
|
|||
|
|
float tc_aMinStable = 99.0f;
|
|||
|
|
float tc_aMaxStable = 0.0f;
|
|||
|
|
|
|||
|
|
unsigned long tc_stableStartMs = 0;
|
|||
|
|
float tc_aSumEarly = 0.0f; uint16_t tc_aCntEarly = 0;
|
|||
|
|
float tc_aSumLate = 0.0f; uint16_t tc_aCntLate = 0;
|
|||
|
|
|
|||
|
|
unsigned long tcMeasureStartMs = 0;
|
|||
|
|
unsigned long TC_MEASURE_WINDOW_MS = 2000;
|
|||
|
|
|
|||
|
|
const float TC_OK_MIN_V = 0.90f;
|
|||
|
|
const float TC_OK_MAX_V = 1.10f;
|
|||
|
|
const float TC_SHORT_V = 0.30f;
|
|||
|
|
const float TC_HIGH_V = 1.40f;
|
|||
|
|
|
|||
|
|
bool laptopNeedsAdj = true;
|
|||
|
|
|
|||
|
|
const float LAPTOP_VMIN = 18.0f;
|
|||
|
|
const float LAPTOP_VMAX = 21.0f;
|
|||
|
|
|
|||
|
|
const float L_NO_POWER_A = 0.010f;
|
|||
|
|
const float L_STBY_MIN_A = 0.015f;
|
|||
|
|
const float L_STBY_MAX_A = 0.050f;
|
|||
|
|
const float L_COMM_SWING_A = 0.020f;
|
|||
|
|
const float L_BOOT_A = 0.50f;
|
|||
|
|
const float L_SHORT_I = 3.0f;
|
|||
|
|
const float L_SHORT_DROP_P = 0.10f;
|
|||
|
|
|
|||
|
|
static float lap_aFast = 0.0f, lap_aSlow = 0.0f, lap_aSwingPeak = 0.0f;
|
|||
|
|
static float lap_vBase = 0.0f;
|
|||
|
|
static bool lap_lastRelayOn = false;
|
|||
|
|
static bool lap_wasAboveStandby = false;
|
|||
|
|
static unsigned long lap_lastAboveTs = 0;
|
|||
|
|
static unsigned long lap_lastSampleMs = 0;
|
|||
|
|
static float lap_aMax = 0.0f, lap_aMin = 99.0f;
|
|||
|
|
#include <avr/wdt.h>
|
|||
|
|
void delaySafe(unsigned long ms){
|
|||
|
|
unsigned long t0 = millis();
|
|||
|
|
while (millis() - t0 < ms){
|
|||
|
|
wdt_reset();
|
|||
|
|
delay(10);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
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){
|
|||
|
|
t0 = millis();
|
|||
|
|
state = 1;
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case 1:
|
|||
|
|
if (!(b1 && b2)){
|
|||
|
|
state = 0;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
if (millis() - t0 >= 250){
|
|||
|
|
|
|||
|
|
for (int i = 0; i < 3; i++){
|
|||
|
|
setRelay(RELAY1, true);
|
|||
|
|
setRelay(RELAY2, true);
|
|||
|
|
delay(120);
|
|||
|
|
|
|||
|
|
setRelay(RELAY1, false);
|
|||
|
|
setRelay(RELAY2, false);
|
|||
|
|
delay(200);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
setRelay(RELAY1, false);
|
|||
|
|
setRelay(RELAY2, false);
|
|||
|
|
|
|||
|
|
wdt_enable(WDTO_15MS);
|
|||
|
|
while (true) { }
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
void beepOnce(uint16_t ms){
|
|||
|
|
tone(BEEPER, 3000);
|
|||
|
|
delay(ms);
|
|||
|
|
noTone(BEEPER);
|
|||
|
|
}
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void beepLongWarning(){
|
|||
|
|
tone(BEEPER, 2500);
|
|||
|
|
delay(3000);
|
|||
|
|
noTone(BEEPER);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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;
|
|||
|
|
bool consumeClick(uint8_t pin){
|
|||
|
|
|
|||
|
|
if (wakeSwallowInputs){
|
|||
|
|
|
|||
|
|
if (digitalRead(BTN1)==HIGH && digitalRead(BTN2)==HIGH){
|
|||
|
|
wakeSwallowInputs = false;
|
|||
|
|
|
|||
|
|
lastBtn1 = HIGH; lastBtn2 = HIGH;
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
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){
|
|||
|
|
clicked = true;
|
|||
|
|
}
|
|||
|
|
*lastPtr = reading;
|
|||
|
|
}
|
|||
|
|
return clicked;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool checkLongPressB2(){
|
|||
|
|
bool b2Now = (digitalRead(BTN2)==LOW);
|
|||
|
|
unsigned long now = millis();
|
|||
|
|
|
|||
|
|
if(!b2Held){
|
|||
|
|
if(b2Now){
|
|||
|
|
b2Held=true;
|
|||
|
|
b2EscapeDone=false;
|
|||
|
|
b2PressStart=now;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
if(!b2Now){
|
|||
|
|
b2Held=false;
|
|||
|
|
b2EscapeDone=false;
|
|||
|
|
} else {
|
|||
|
|
if(!b2EscapeDone && (now - b2PressStart)>=B2_LONG_MS){
|
|||
|
|
b2EscapeDone=true;
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool voltCheckExitHold(){
|
|||
|
|
static bool vHeld=false;
|
|||
|
|
static unsigned long vStart=0;
|
|||
|
|
static bool fired=false;
|
|||
|
|
|
|||
|
|
bool nowPressed = (digitalRead(BTN2)==LOW);
|
|||
|
|
unsigned long nowMs = millis();
|
|||
|
|
|
|||
|
|
if(!vHeld){
|
|||
|
|
if(nowPressed){
|
|||
|
|
vHeld=true;
|
|||
|
|
vStart=nowMs;
|
|||
|
|
fired=false;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
if(!nowPressed){
|
|||
|
|
vHeld=false;
|
|||
|
|
} else {
|
|||
|
|
if(!fired && (nowMs - vStart)>=B2_LONG_MS){
|
|||
|
|
fired=true;
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#ifndef REPROMOD_COMPAT_FWD_INCLUDED
|
|||
|
|
#define REPROMOD_COMPAT_FWD_INCLUDED
|
|||
|
|
|
|||
|
|
#include <stdint.h>
|
|||
|
|
|
|||
|
|
enum TestChipResult_TC : uint8_t;
|
|||
|
|
|
|||
|
|
void beepOnce(uint16_t ms);
|
|||
|
|
void beepCount(uint8_t n, uint16_t onMs = 80, uint16_t offMs = 80);
|
|||
|
|
|
|||
|
|
extern const unsigned long SAFE_ARM_DELAY_MS;
|
|||
|
|
|
|||
|
|
class Adafruit_SSD1306;
|
|||
|
|
extern Adafruit_SSD1306 display;
|
|||
|
|
|
|||
|
|
void setRelay(uint8_t pin, bool on);
|
|||
|
|
void readVA(float &voltsOut, float &sOut);
|
|||
|
|
void registerActivity(void);
|
|||
|
|
|
|||
|
|
extern bool relay1;
|
|||
|
|
extern unsigned long ch1OnMs;
|
|||
|
|
extern bool lastShortTrip;
|
|||
|
|
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
bool displaySleeping=false;
|
|||
|
|
unsigned long lastActivityMs=0;
|
|||
|
|
const unsigned long OLED_SLEEP_MS = 20000;
|
|||
|
|
|
|||
|
|
void registerActivity(){
|
|||
|
|
lastActivityMs = millis();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void oledOff(){
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
display.ssd1306_command(SSD1306_DISPLAYOFF);
|
|||
|
|
displaySleeping=true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
void oledOn(){
|
|||
|
|
if(displaySleeping){
|
|||
|
|
display.ssd1306_command(SSD1306_DISPLAYON);
|
|||
|
|
displaySleeping=false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool rawBtnPressed(){
|
|||
|
|
return (digitalRead(BTN1)==LOW) || (digitalRead(BTN2)==LOW);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
enum UiMode {
|
|||
|
|
MODE_HOME = 0,
|
|||
|
|
MODE_MENU,
|
|||
|
|
MODE_TEST_CHIP,
|
|||
|
|
MODE_VOLT,
|
|||
|
|
MODE_LAPTOP,
|
|||
|
|
MODE_CALIB
|
|||
|
|
};
|
|||
|
|
UiMode uiMode = MODE_HOME;
|
|||
|
|
|
|||
|
|
enum MainMenuItem {
|
|||
|
|
MM_CANAL2 = 0,
|
|||
|
|
MM_TESTCHIP,
|
|||
|
|
MM_MEDICION,
|
|||
|
|
MM_LAPTOP,
|
|||
|
|
MM_CALIBRACION,
|
|||
|
|
MM_COUNT
|
|||
|
|
};
|
|||
|
|
uint8_t mainSel = 0;
|
|||
|
|
|
|||
|
|
enum TestChipItem {
|
|||
|
|
TC_CPU = 0,
|
|||
|
|
TC_GPU,
|
|||
|
|
TC_VRAM,
|
|||
|
|
TC_BUCK,
|
|||
|
|
TC_COUNT
|
|||
|
|
};
|
|||
|
|
uint8_t testChipSel = 0;
|
|||
|
|
|
|||
|
|
const uint8_t PW=118;
|
|||
|
|
const uint8_t PH=24;
|
|||
|
|
const uint8_t GX=5;
|
|||
|
|
const uint8_t GY=20;
|
|||
|
|
uint8_t gbuf[PW];
|
|||
|
|
uint8_t head=0;
|
|||
|
|
|
|||
|
|
float aHist[4] = {0,0,0,0};
|
|||
|
|
unsigned long tHist[4] = {0,0,0,0};
|
|||
|
|
uint8_t aHidx = 0;
|
|||
|
|
bool aHistPrimed = false;
|
|||
|
|
const float GRAPH_MAX_DROP = 1.0f;
|
|||
|
|
|
|||
|
|
float vref = 0.0f;
|
|||
|
|
const float VREF_HYST = 0.40f;
|
|||
|
|
const unsigned long VREF_REARM_HOLD_MS=1000;
|
|||
|
|
|
|||
|
|
#define INA_SHUNT_REG 0x01
|
|||
|
|
|
|||
|
|
float readVoltageCal(){
|
|||
|
|
return ina.getBusVoltage() * V_CAL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int16_t inaReadRegister16(uint8_t reg){
|
|||
|
|
Wire.beginTransmission(INA_ADDR);
|
|||
|
|
Wire.write(reg);
|
|||
|
|
Wire.endTransmission(false);
|
|||
|
|
Wire.requestFrom(INA_ADDR,(uint8_t)2);
|
|||
|
|
if(Wire.available()<2) return 0;
|
|||
|
|
uint16_t msb=Wire.read();
|
|||
|
|
uint16_t lsb=Wire.read();
|
|||
|
|
uint16_t raw=(msb<<8)|lsb;
|
|||
|
|
return (int16_t)raw;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float readShuntVoltage_mV(){
|
|||
|
|
int16_t raw=inaReadRegister16(INA_SHUNT_REG);
|
|||
|
|
return (float)raw * 0.0025f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float aSlow = 0.0f;
|
|||
|
|
float aPeakSwing = 0.0f;
|
|||
|
|
unsigned long lastVoltSampleMs=0;
|
|||
|
|
|
|||
|
|
const float FLUCT_THRESH = 0.01f;
|
|||
|
|
const float PULSE_THRESH = 0.05f;
|
|||
|
|
|
|||
|
|
void drawLaptopAnalysisSimple(const char* msg, float ampsNow, bool relayOn) {
|
|||
|
|
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
static bool blink = false;
|
|||
|
|
static unsigned long blinkTs = 0;
|
|||
|
|
if (millis() - blinkTs >= 500) {
|
|||
|
|
blink = !blink;
|
|||
|
|
blinkTs = millis();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
if (relayOn && blink) {
|
|||
|
|
display.fillRect(0, 0, 128, 20, SSD1306_WHITE);
|
|||
|
|
display.setTextColor(SSD1306_BLACK);
|
|||
|
|
display.setCursor(18, 2);
|
|||
|
|
display.print("ANALISIS");
|
|||
|
|
} else {
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(18, 0);
|
|||
|
|
display.print("ANALISIS");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.fillRect(0, 16, 160, 14, SSD1306_WHITE);
|
|||
|
|
display.setTextColor(SSD1306_BLACK);
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(2, 20);
|
|||
|
|
display.print(msg);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setCursor(10, 34);
|
|||
|
|
display.print(fabs(ampsNow), 3);
|
|||
|
|
display.print("A");
|
|||
|
|
|
|||
|
|
float vNow = 0.0f, aNow = 0.0f;
|
|||
|
|
readVA(vNow, aNow);
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(48, 52);
|
|||
|
|
int vDisplay = (int)vNow;
|
|||
|
|
int vDec = (int)((vNow - vDisplay) * 10);
|
|||
|
|
if (vDisplay < 10) display.print('0');
|
|||
|
|
display.print(vDisplay);
|
|||
|
|
display.print('.');
|
|||
|
|
display.print(vDec);
|
|||
|
|
display.print('V');
|
|||
|
|
extern float lap_aMax, lap_aMin;
|
|||
|
|
display.setCursor(88, 35);
|
|||
|
|
display.print("A Max");
|
|||
|
|
display.setCursor(88, 43);
|
|||
|
|
display.print(lap_aMax, 2);
|
|||
|
|
display.fillRect(2, 52, 40, 14, SSD1306_WHITE);
|
|||
|
|
display.setTextColor(SSD1306_BLACK);
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(5, 54);
|
|||
|
|
display.print("B1 ");
|
|||
|
|
display.print(relayOn ? "ON" : "OFF");
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
display.setCursor(98, 54);
|
|||
|
|
display.print("< B2");
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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);
|
|||
|
|
|
|||
|
|
display.fillRect(bx-2, by-1, bw+4, bh+2, SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
display.setTextColor(SSD1306_BLACK);
|
|||
|
|
display.setCursor(x,y);
|
|||
|
|
display.print(txt);
|
|||
|
|
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawHomeIdle(){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
display.setCursor(3, 54);
|
|||
|
|
display.print("B1 canal1");
|
|||
|
|
|
|||
|
|
display.setCursor(79, 54);
|
|||
|
|
display.print("B2 menu");
|
|||
|
|
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
const char *txt="REPROMOD";
|
|||
|
|
int16_t x1,y1; uint16_t w1,h1;
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.getTextBounds(txt,0,0,&x1,&y1,&w1,&h1);
|
|||
|
|
|
|||
|
|
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);
|
|||
|
|
|
|||
|
|
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();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawTripScreen() {
|
|||
|
|
display.clearDisplay();
|
|||
|
|
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(2, 2);
|
|||
|
|
display.println(F("PROTECCION"));
|
|||
|
|
|
|||
|
|
display.fillRect(0, 28, 128, 14, SSD1306_WHITE);
|
|||
|
|
display.setTextColor(SSD1306_BLACK);
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(4, 32);
|
|||
|
|
if (lastTripWasPlus) {
|
|||
|
|
display.println(F(" Pico de corriente"));
|
|||
|
|
} else {
|
|||
|
|
display.println(F(" Caida de voltaje"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(2, 54);
|
|||
|
|
display.println(F("B1> Rearmar"));
|
|||
|
|
display.setCursor(94, 54);
|
|||
|
|
display.println(F("< B2"));
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawChannel1Frame(float v, float a, float dropV, float pct, bool safeOn){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
|
|||
|
|
display.fillRect(0,0,128,12,SSD1306_BLACK);
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(0,0);
|
|||
|
|
display.print(v,2); display.print("v ");
|
|||
|
|
display.print(a,2); display.print("A");
|
|||
|
|
|
|||
|
|
const int16_t STATUS_X = 76;
|
|||
|
|
const int16_t STATUS_Y = 0;
|
|||
|
|
const int16_t STATUS_W = 52;
|
|||
|
|
const int16_t STATUS_H = 12;
|
|||
|
|
|
|||
|
|
display.fillRect(STATUS_X, STATUS_Y, STATUS_W, STATUS_H, SSD1306_BLACK);
|
|||
|
|
|
|||
|
|
switch (safeMode) {
|
|||
|
|
case SAFE_V: {
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(STATUS_X + 12, STATUS_Y);
|
|||
|
|
display.print("SAFE");
|
|||
|
|
} break;
|
|||
|
|
|
|||
|
|
case SAFE_PLUS: {
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(STATUS_X + 6, STATUS_Y);
|
|||
|
|
display.print("SAFE+");
|
|||
|
|
} break;
|
|||
|
|
|
|||
|
|
case SAFE_OFF: {
|
|||
|
|
|
|||
|
|
static bool blink=false;
|
|||
|
|
static unsigned long t0=0;
|
|||
|
|
unsigned long now=millis();
|
|||
|
|
if (now - t0 > 500) { t0 = now; blink = !blink; }
|
|||
|
|
|
|||
|
|
if (blink) {
|
|||
|
|
display.fillRect(STATUS_X, STATUS_Y, STATUS_W, STATUS_H, SSD1306_WHITE);
|
|||
|
|
display.drawRect(STATUS_X, STATUS_Y, STATUS_W, STATUS_H, SSD1306_BLACK);
|
|||
|
|
display.setTextColor(SSD1306_BLACK);
|
|||
|
|
display.setCursor(STATUS_X + 6, STATUS_Y + 1);
|
|||
|
|
display.print("NO SAFE");
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
} else {
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(STATUS_X + 6, STATUS_Y);
|
|||
|
|
display.print("NO SAFE");
|
|||
|
|
}
|
|||
|
|
} break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const uint8_t PLOT_TOP = 14;
|
|||
|
|
const uint8_t PLOT_BOTTOM = OLED_H - 18;
|
|||
|
|
const uint8_t plotH = (PLOT_BOTTOM > PLOT_TOP) ? (PLOT_BOTTOM - PLOT_TOP) : 1;
|
|||
|
|
|
|||
|
|
for(uint8_t band=1; band<=4; band++){
|
|||
|
|
uint8_t yGuide = PLOT_TOP + (uint16_t)plotH * band / 5;
|
|||
|
|
for(uint8_t x=GX; x < (uint8_t)(GX+PW); x++){
|
|||
|
|
if((x % 4) < 2){
|
|||
|
|
if(yGuide < OLED_H) display.drawPixel(x, yGuide, SSD1306_WHITE);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
uint8_t x = GX;
|
|||
|
|
uint8_t idx = head;
|
|||
|
|
for(uint8_t i=0; i<PW; i++){
|
|||
|
|
|
|||
|
|
uint8_t oldY = gbuf[idx];
|
|||
|
|
uint8_t yScaled = (uint8_t)((uint32_t)oldY * (uint32_t)(plotH-1) / (uint32_t)((PH>1)?(PH-1):1));
|
|||
|
|
uint8_t baseY = PLOT_TOP + yScaled;
|
|||
|
|
|
|||
|
|
for(int8_t dy=-1; dy<=1; dy++){
|
|||
|
|
int16_t yy = (int16_t)baseY + dy;
|
|||
|
|
if(yy>=0 && yy<OLED_H) display.drawPixel(x, yy, SSD1306_WHITE);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
x++;
|
|||
|
|
idx = (idx+1) % PW;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int mvInt = (int)(dropV*1000.0f+0.5f); if(mvInt>999) mvInt=999;
|
|||
|
|
int pctInt=(int)(pct+0.5f);
|
|||
|
|
if(pctInt<0) pctInt=0; if(pctInt>99) pctInt=99;
|
|||
|
|
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(2, OLED_H-18);
|
|||
|
|
|
|||
|
|
if(mvInt<100) display.print('0');
|
|||
|
|
if(mvInt<10) display.print('0');
|
|||
|
|
display.print(mvInt);
|
|||
|
|
display.print("mV ");
|
|||
|
|
|
|||
|
|
if(pctInt<10) display.print('0');
|
|||
|
|
display.print(pctInt);
|
|||
|
|
display.print("%");
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawTestChipMenu_TC(uint8_t sel, TestChipResult_TC pendingRes){
|
|||
|
|
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
display.setCursor(30,0);
|
|||
|
|
display.print("TESTER CHIP");
|
|||
|
|
|
|||
|
|
if(sel==TCSEL_CPU){
|
|||
|
|
drawInvertedLabel(10,14,"CPU");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(10,14); display.print("CPU");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(sel==TCSEL_GPU){
|
|||
|
|
drawInvertedLabel(90,14,"GPU");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(90,14); display.print("GPU");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(sel==TCSEL_VRAM){
|
|||
|
|
drawInvertedLabel(10,30,"VRAM");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(10,30); display.print("VRAM");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(sel==TCSEL_BUCK){
|
|||
|
|
drawInvertedLabel(90,30,"BUCK");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(90,30); display.print("BUCK");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
display.setCursor(4,42);
|
|||
|
|
display.print("Ajuste 1v a 4A B1>");
|
|||
|
|
drawInvertedLabel(6, 54, " DESCONECTE PINZAS!");
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
void drawTestArmed_TC(uint8_t sel){
|
|||
|
|
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
switch(sel){
|
|||
|
|
case TCSEL_CPU: display.setCursor(0,0); display.print("CPU"); break;
|
|||
|
|
case TCSEL_GPU: display.setCursor(0,0); display.print("GPU"); break;
|
|||
|
|
case TCSEL_VRAM: display.setCursor(0,0); display.print("VRAM"); break;
|
|||
|
|
case TCSEL_BUCK: display.setCursor(0,0); display.print("BUCK"); break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(0,24);
|
|||
|
|
display.print("Pinza + en placa");
|
|||
|
|
|
|||
|
|
display.setCursor(0,34);
|
|||
|
|
display.print("Pinza - en GND");
|
|||
|
|
|
|||
|
|
drawInvertedLabel(0,50,"B1 TEST");
|
|||
|
|
display.setCursor(60,50);
|
|||
|
|
display.print("< B2");
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawTestMeasuring_TC(uint8_t sel){
|
|||
|
|
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
switch(sel){
|
|||
|
|
case TCSEL_CPU: display.setCursor(0,0); display.print("CPU"); break;
|
|||
|
|
case TCSEL_GPU: display.setCursor(0,0); display.print("GPU"); break;
|
|||
|
|
case TCSEL_VRAM: display.setCursor(0,0); display.print("VRAM"); break;
|
|||
|
|
case TCSEL_BUCK: display.setCursor(0,0); display.print("BUCK"); break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(0,28);
|
|||
|
|
display.print("Comprobando V...");
|
|||
|
|
|
|||
|
|
display.setCursor(0,40);
|
|||
|
|
display.print("Mantenga 1v estable");
|
|||
|
|
|
|||
|
|
unsigned long elapsed = millis() - tcMeasureStartMs;
|
|||
|
|
uint8_t barW = (elapsed >= TC_MEASURE_WINDOW_MS)
|
|||
|
|
? 100
|
|||
|
|
: (uint8_t)( (elapsed*100UL)/TC_MEASURE_WINDOW_MS );
|
|||
|
|
display.drawRect(0,56,104,6,SSD1306_WHITE);
|
|||
|
|
display.fillRect(1,57,barW,4,SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawTestResult_TC(uint8_t sel,
|
|||
|
|
TestChipResult_TC res,
|
|||
|
|
float vAvg,float vMin,float vMax)
|
|||
|
|
{
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
switch(sel){
|
|||
|
|
case TCSEL_CPU: display.setCursor(0,0); display.print("CPU"); break;
|
|||
|
|
case TCSEL_GPU: display.setCursor(0,0); display.print("GPU"); break;
|
|||
|
|
case TCSEL_VRAM: display.setCursor(0,0); display.print("VRAM"); break;
|
|||
|
|
case TCSEL_BUCK: display.setCursor(0,0); display.print("BUCK"); break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
|
|||
|
|
const char *msgMain = "";
|
|||
|
|
bool invertMsg=false;
|
|||
|
|
|
|||
|
|
switch(res){
|
|||
|
|
case TCR_OK: msgMain="CHIP OK!"; invertMsg=false; break;
|
|||
|
|
case TCR_VLOW: msgMain="V BAJO"; invertMsg=true; break;
|
|||
|
|
case TCR_VHIGH: msgMain="V ALTO"; invertMsg=true; break;
|
|||
|
|
case TCR_SHORT: msgMain="CORTO!"; invertMsg=true; break;
|
|||
|
|
case TCR_NOCON: msgMain="SIN CARGA";invertMsg=true; break;
|
|||
|
|
case TCR_FAIL:
|
|||
|
|
default: msgMain="CHIP FALLA"; invertMsg=true; break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(invertMsg){
|
|||
|
|
drawInvertedLabel(0,24,msgMain);
|
|||
|
|
}else{
|
|||
|
|
display.setCursor(0,24);
|
|||
|
|
display.print(msgMain);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float useMin = (tc_cntStable>0) ? tc_vMinStable : vMin;
|
|||
|
|
float useMax = (tc_cntStable>0) ? tc_vMaxStable : vMax;
|
|||
|
|
|
|||
|
|
float dropPct = 0.0f;
|
|||
|
|
if(useMax > 0.001f){
|
|||
|
|
dropPct = (useMax - useMin)/useMax * 100.0f;
|
|||
|
|
if(dropPct < 0) dropPct = 0;
|
|||
|
|
if(dropPct > 99.0f) dropPct = 99.0f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
display.setCursor(0,36);
|
|||
|
|
display.print("Media ");
|
|||
|
|
display.print(vAvg,2);
|
|||
|
|
display.print("v");
|
|||
|
|
|
|||
|
|
display.setCursor(0,46);
|
|||
|
|
display.print("MIN ");
|
|||
|
|
display.print(vMin,2);
|
|||
|
|
display.print("v");
|
|||
|
|
|
|||
|
|
display.setCursor(64,46);
|
|||
|
|
display.print("MAX ");
|
|||
|
|
display.print(vMax,2);
|
|||
|
|
display.print("v");
|
|||
|
|
|
|||
|
|
display.setCursor(0,56);
|
|||
|
|
display.print("Caida ");
|
|||
|
|
display.print(dropPct,0);
|
|||
|
|
display.print("%");
|
|||
|
|
|
|||
|
|
drawInvertedLabel(95,56,"B1>");
|
|||
|
|
|
|||
|
|
display.setCursor(70,56);
|
|||
|
|
display.print("< B2");
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
void drawTestSummaryScreen_WithGraph_Compact(
|
|||
|
|
uint8_t sel,
|
|||
|
|
uint8_t finalCode,
|
|||
|
|
float vAvg,
|
|||
|
|
float vMin,
|
|||
|
|
float vMax,
|
|||
|
|
uint8_t dropPct
|
|||
|
|
){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
const char *chipTxt = "CPU";
|
|||
|
|
switch(sel){
|
|||
|
|
case TCSEL_CPU: chipTxt="CPU"; break;
|
|||
|
|
case TCSEL_GPU: chipTxt="GPU"; break;
|
|||
|
|
case TCSEL_VRAM: chipTxt="VRAM"; break;
|
|||
|
|
case TCSEL_BUCK: chipTxt="BUCK"; break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const char *resTxt = "OK";
|
|||
|
|
bool badRes = false;
|
|||
|
|
switch(finalCode){
|
|||
|
|
case 0: resTxt="OK"; badRes=false; break;
|
|||
|
|
case 1: resTxt="V BAJO"; badRes=true; break;
|
|||
|
|
case 2: resTxt="V ALTO"; badRes=true; break;
|
|||
|
|
case 3: resTxt="CORTO"; badRes=true; break;
|
|||
|
|
case 5: resTxt="SIN CARGA"; badRes=true; break;
|
|||
|
|
default:
|
|||
|
|
case 4: resTxt="INESTABLE"; badRes=true; break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
char headerBuf[20];
|
|||
|
|
snprintf(headerBuf,sizeof(headerBuf),"%s %s", chipTxt, resTxt);
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
int16_t hbX, hbY; uint16_t hbW, hbH;
|
|||
|
|
display.getTextBounds(headerBuf, 0,0, &hbX,&hbY,&hbW,&hbH);
|
|||
|
|
|
|||
|
|
int16_t boxX = (OLED_W - (int16_t)hbW - 6) / 2;
|
|||
|
|
if(boxX < 0) boxX = 0;
|
|||
|
|
int16_t boxY = 0;
|
|||
|
|
uint16_t boxW = hbW + 6;
|
|||
|
|
uint16_t boxH = hbH + 4;
|
|||
|
|
|
|||
|
|
display.fillRect(boxX, boxY, boxW, boxH, SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
display.setTextColor(SSD1306_BLACK);
|
|||
|
|
display.setCursor(boxX+3, boxY+2);
|
|||
|
|
display.print(headerBuf);
|
|||
|
|
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
const int GXg = 0;
|
|||
|
|
const int GW = 128;
|
|||
|
|
const int yTop = 16;
|
|||
|
|
const int y1V = 24;
|
|||
|
|
const int y05V = 36;
|
|||
|
|
const int y0V = 52;
|
|||
|
|
const int yText0 = 10;
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(0, yText0);
|
|||
|
|
display.print("1v");
|
|||
|
|
|
|||
|
|
display.setCursor(0, y05V-3);
|
|||
|
|
display.print("0.5");
|
|||
|
|
|
|||
|
|
display.setCursor(0, y0V-3);
|
|||
|
|
display.print("0v");
|
|||
|
|
|
|||
|
|
for (int x = 12; x < GW; x++) {
|
|||
|
|
if ((x % 4) < 2) {
|
|||
|
|
display.drawPixel(GXg + x, y1V, SSD1306_WHITE);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (int x = 12; x < GW; x++) {
|
|||
|
|
if ((x % 4) < 2) {
|
|||
|
|
display.drawPixel(GXg + x, y05V, SSD1306_WHITE);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (int x = 12; x < GW; x++) {
|
|||
|
|
display.drawPixel(GXg + x, y0V, SSD1306_WHITE);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (tc_traceCount > 1) {
|
|||
|
|
|
|||
|
|
uint16_t startIdx = (uint16_t)(tc_traceCount * 20UL / 100UL);
|
|||
|
|
if (startIdx >= tc_traceCount - 1) startIdx = 0;
|
|||
|
|
|
|||
|
|
uint16_t usedCount = tc_traceCount - startIdx;
|
|||
|
|
if (usedCount < 2) usedCount = 2;
|
|||
|
|
|
|||
|
|
const float V_TOP = 1.00f;
|
|||
|
|
const float V_MID = 0.50f;
|
|||
|
|
|
|||
|
|
const int yTopTrace = y1V;
|
|||
|
|
const int yBottomTrace = y05V;
|
|||
|
|
|
|||
|
|
int prevX = -1;
|
|||
|
|
int prevY = -1;
|
|||
|
|
|
|||
|
|
for (int x = 12; x < GW; x++) {
|
|||
|
|
|
|||
|
|
uint16_t idx = startIdx + (uint16_t)(
|
|||
|
|
((uint32_t)(x-12) * (uint32_t)usedCount) / (uint32_t)(GW-12)
|
|||
|
|
);
|
|||
|
|
if (idx >= tc_traceCount) idx = tc_traceCount - 1;
|
|||
|
|
|
|||
|
|
float vNow = tc_trace[idx];
|
|||
|
|
|
|||
|
|
if (vNow > V_TOP) vNow = V_TOP;
|
|||
|
|
if (vNow < V_MID) vNow = V_MID;
|
|||
|
|
|
|||
|
|
float norm = (vNow - V_MID) / (V_TOP - V_MID);
|
|||
|
|
|
|||
|
|
int yPix = yBottomTrace
|
|||
|
|
- (int)( norm * (float)(yBottomTrace - yTopTrace) + 0.5f );
|
|||
|
|
|
|||
|
|
if (yPix < yTopTrace) yPix = yTopTrace;
|
|||
|
|
if (yPix > yBottomTrace) yPix = yBottomTrace;
|
|||
|
|
|
|||
|
|
int Xpix = GXg + x;
|
|||
|
|
display.drawPixel(Xpix, yPix, SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
if (prevX >= 0) {
|
|||
|
|
if (abs(yPix - prevY) > 1) {
|
|||
|
|
int yA = (yPix < prevY) ? yPix : prevY;
|
|||
|
|
int yB = (yPix < prevY) ? prevY : yPix;
|
|||
|
|
for (int yy = yA; yy <= yB; yy++) {
|
|||
|
|
display.drawPixel(Xpix, yy, SSD1306_WHITE);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
prevX = Xpix;
|
|||
|
|
prevY = yPix;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} else {
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(32, (y1V + y05V)/2 - 3);
|
|||
|
|
display.print("SIN DATOS");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
char footerBuf[48];
|
|||
|
|
|
|||
|
|
int vMax_i = (int)floor(fabs(vMax));
|
|||
|
|
int vMax_d = (int)round((fabs(vMax) - (float)vMax_i) * 100.0f);
|
|||
|
|
if (vMax_d > 99) vMax_d = 99;
|
|||
|
|
|
|||
|
|
int vMin_i = (int)floor(fabs(vMin));
|
|||
|
|
int vMin_d = (int)round((fabs(vMin) - (float)vMin_i) * 100.0f);
|
|||
|
|
if (vMin_d > 99) vMin_d = 99;
|
|||
|
|
|
|||
|
|
int vAvg_i = (int)floor(fabs(vAvg));
|
|||
|
|
int vAvg_d = (int)round((fabs(vAvg) - (float)vAvg_i) * 100.0f);
|
|||
|
|
if (vAvg_d > 99) vAvg_d = 99;
|
|||
|
|
|
|||
|
|
snprintf(
|
|||
|
|
footerBuf,
|
|||
|
|
sizeof(footerBuf),
|
|||
|
|
"%02u%% +%d.%02d -%d.%02d =%d.%02d",
|
|||
|
|
(unsigned int)dropPct,
|
|||
|
|
vMax_i, vMax_d,
|
|||
|
|
vMin_i, vMin_d,
|
|||
|
|
vAvg_i, vAvg_d
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
int16_t fx, fy;
|
|||
|
|
uint16_t fW, fH;
|
|||
|
|
display.getTextBounds(footerBuf, 0, 0, &fx, &fy, &fW, &fH);
|
|||
|
|
|
|||
|
|
int16_t footerY = OLED_H - fH;
|
|||
|
|
int16_t footerX = (OLED_W - fW) / 2;
|
|||
|
|
if (footerX < 0) footerX = 0;
|
|||
|
|
|
|||
|
|
display.setCursor(footerX, footerY);
|
|||
|
|
display.print(footerBuf);
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TestChipResult_TC classifyResult_TC(){
|
|||
|
|
|
|||
|
|
uint16_t useCnt = (tc_cntStable > 0) ? tc_cntStable : tc_cnt;
|
|||
|
|
float useVmin = (tc_cntStable > 0) ? tc_vMinStable : tc_vMin;
|
|||
|
|
float useVmax = (tc_cntStable > 0) ? tc_vMaxStable : tc_vMax;
|
|||
|
|
|
|||
|
|
if(useCnt == 0){
|
|||
|
|
return TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float vAvg = tc_vSum / (float)tc_cnt;
|
|||
|
|
float aAvg = tc_aSum / (float)tc_cnt;
|
|||
|
|
|
|||
|
|
if (testChipSel_TC == TCSEL_GPU) {
|
|||
|
|
if (aAvg < 0.02f) return TCR_NOCON;
|
|||
|
|
} else {
|
|||
|
|
if (aAvg < 0.05f) return TCR_NOCON;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((useVmin < 0.60f && aAvg > 0.5f) || (useVmin < 0.35f)) {
|
|||
|
|
return TCR_SHORT;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(useVmax > 1.40f){
|
|||
|
|
return TCR_VHIGH;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool vOk = (vAvg >= 0.80f && vAvg <= 1.20f);
|
|||
|
|
bool aOk = (aAvg >= 0.20f && aAvg <= 3.50f);
|
|||
|
|
if(vOk && aOk){
|
|||
|
|
return TCR_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(vAvg < 0.80f){
|
|||
|
|
return TCR_VLOW;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(vAvg > 1.20f){
|
|||
|
|
return TCR_VHIGH;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const float CPU_SHORT_V = 0.30f;
|
|||
|
|
const float CPU_MIN_AVG_OK_V = 0.75f;
|
|||
|
|
const float CPU_MAX_EXPECTED_V = 1.20f;
|
|||
|
|
const float CPU_DROP_BAD_PCT = 30.0f;
|
|||
|
|
|
|||
|
|
TestChipResult_TC classifyCPU_TC(){
|
|||
|
|
if(tc_cnt == 0){
|
|||
|
|
|
|||
|
|
return TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float vAvg = tc_vSum / (float)tc_cnt;
|
|||
|
|
|
|||
|
|
float dropPct = 0.0f;
|
|||
|
|
if (tc_vMax > 0.001f) {
|
|||
|
|
dropPct = (tc_vMax - tc_vMin) / tc_vMax * 100.0f;
|
|||
|
|
if(dropPct < 0.0f) dropPct = 0.0f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (tc_vMin < CPU_SHORT_V){
|
|||
|
|
return TCR_SHORT;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (tc_vMax > CPU_MAX_EXPECTED_V){
|
|||
|
|
return TCR_VHIGH;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (vAvg < CPU_MIN_AVG_OK_V){
|
|||
|
|
return TCR_VLOW;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (dropPct > CPU_DROP_BAD_PCT){
|
|||
|
|
return TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return TCR_OK;
|
|||
|
|
}
|
|||
|
|
void drawMainMenu(uint8_t sel){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
if(sel==MM_CANAL2){
|
|||
|
|
drawInvertedLabel(4,6,"CANAL 2");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(4,6);
|
|||
|
|
display.print("CANAL 2");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(sel==MM_TESTCHIP){
|
|||
|
|
drawInvertedLabel(74,6,"TEST CHIP");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(74,6);
|
|||
|
|
display.print("TEST CHIP");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(sel==MM_MEDICION){
|
|||
|
|
drawInvertedLabel(4,21,"VOLTIMETRO");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(4,21);
|
|||
|
|
display.print("VOLTIMETRO");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(sel==MM_LAPTOP){
|
|||
|
|
drawInvertedLabel(74,21,"PORTATIL");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(74,21);
|
|||
|
|
display.print("PORTATIL");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(sel==MM_CALIBRACION){
|
|||
|
|
drawInvertedLabel(4,36,"CALIBRAR");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(4,36);
|
|||
|
|
display.print("CALIBRAR");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
drawInvertedLabel(4,52,"B1>");
|
|||
|
|
display.setCursor(95,52);
|
|||
|
|
display.print("< B2");
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawTestChipMenu(uint8_t sel){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
if(sel==TC_CPU){
|
|||
|
|
drawInvertedLabel(4,8,"CPU");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(4,8); display.print("CPU");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(sel==TC_GPU){
|
|||
|
|
drawInvertedLabel(74,8,"GPU");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(74,8); display.print("GPU");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(sel==TC_VRAM){
|
|||
|
|
drawInvertedLabel(4,24,"VRAM");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(4,24); display.print("VRAM");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(sel==TC_BUCK){
|
|||
|
|
drawInvertedLabel(74,24,"BUCK");
|
|||
|
|
} else {
|
|||
|
|
display.setCursor(74,24); display.print("BUCK");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
display.setCursor(4,44);
|
|||
|
|
display.print("Ajuste 1v Pulse B1");
|
|||
|
|
|
|||
|
|
display.setCursor(4,56);
|
|||
|
|
display.print("Volver B2 largo");
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawVoltimetro(float vShow, const char *estadoTxt){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setCursor(2,0);
|
|||
|
|
display.print("VOLTIMETRO");
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
char buf[16];
|
|||
|
|
dtostrf(vShow,0,3,buf);
|
|||
|
|
int16_t x1,y1; uint16_t w1,h1;
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.getTextBounds(buf,0,0,&x1,&y1,&w1,&h1);
|
|||
|
|
int16_t xx=(OLED_W - (w1+12))/2;
|
|||
|
|
if(xx<0)xx=0;
|
|||
|
|
int16_t yy=24;
|
|||
|
|
display.setCursor(xx,yy);
|
|||
|
|
display.print(buf);
|
|||
|
|
display.print("V");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(10,50);
|
|||
|
|
display.print("Senal ");
|
|||
|
|
display.print(estadoTxt);
|
|||
|
|
|
|||
|
|
drawInvertedLabel(90,50,"< B2");
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawVoltimetroExitWarning(){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.fillRect(0,0,OLED_W,OLED_H,SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
display.setTextColor(SSD1306_BLACK);
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setCursor(16,12);
|
|||
|
|
display.print("RECUERDE");
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(28,36);
|
|||
|
|
display.print("¡DESCONECTE");
|
|||
|
|
display.setCursor(28,48);
|
|||
|
|
display.print("LAS PINZAS!");
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawLaptopScreen(bool needAdj19v){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setCursor(20,0);
|
|||
|
|
display.print("PORTATIL");
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
|
|||
|
|
if (needAdj19v) {
|
|||
|
|
|
|||
|
|
display.setCursor(15, 20);
|
|||
|
|
display.print("AJUSTE EL VOLTAJE");
|
|||
|
|
drawInvertedLabel(22, 30, "RANGO 18 A 21V");
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(22,42);
|
|||
|
|
display.print("RETIRE BATERIA");
|
|||
|
|
display.setCursor(4, 54);
|
|||
|
|
drawInvertedLabel(4,54, "B1>");
|
|||
|
|
display.setCursor(100, 54);
|
|||
|
|
display.print("< B2");
|
|||
|
|
} else {
|
|||
|
|
|
|||
|
|
display.setCursor(4, 16);
|
|||
|
|
display.print("ANALISIS");
|
|||
|
|
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setCursor(0, 30);
|
|||
|
|
display.print("0.000A");
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(4, 54);
|
|||
|
|
display.print("B1 on/off");
|
|||
|
|
display.setCursor(90, 54);
|
|||
|
|
display.print("< B2");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawLaptopAdjustWarning(float vAvg){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(0, 0);
|
|||
|
|
display.print("AVISO");
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(0, 22);
|
|||
|
|
display.print("Medido: ");
|
|||
|
|
display.print(vAvg, 2);
|
|||
|
|
display.print("V");
|
|||
|
|
|
|||
|
|
display.fillRect(0, 36, 128, 14, SSD1306_WHITE);
|
|||
|
|
display.setTextColor(SSD1306_BLACK);
|
|||
|
|
display.setCursor(8, 38);
|
|||
|
|
display.print("Ajuste 18 a 21V primero");
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(88, 54);
|
|||
|
|
display.print("< B2");
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawCalibracionScreen(){
|
|||
|
|
float vNow=0.0f, aNow=0.0f;
|
|||
|
|
readVA(vNow, aNow);
|
|||
|
|
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setCursor(17,0);
|
|||
|
|
display.print("CALIBRAR");
|
|||
|
|
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(4,20);
|
|||
|
|
display.print("COLOQUE UNA R100 5W");
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(22,30);
|
|||
|
|
display.print("EN LAS PINZAS");
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setCursor(10,40);
|
|||
|
|
display.print("AJUSTE EN 10.000v");
|
|||
|
|
|
|||
|
|
drawInvertedLabel(4,52,"B1 Siguiente");
|
|||
|
|
display.setCursor(94,54);
|
|||
|
|
display.print("< B2");
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void armCh1(){
|
|||
|
|
relay1=true;
|
|||
|
|
setRelay(RELAY1,true);
|
|||
|
|
|
|||
|
|
safeModeEnabled=true;
|
|||
|
|
ch1OnMs=millis();
|
|||
|
|
vref = readVoltageCal();
|
|||
|
|
|
|||
|
|
lastShortTrip=false;
|
|||
|
|
tripScreenDrawn=false;
|
|||
|
|
|
|||
|
|
ch1State=CH1_ACTIVE;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void forceGoHome(){
|
|||
|
|
relay1=false;
|
|||
|
|
setRelay(RELAY1,false);
|
|||
|
|
ch1State = CH1_IDLE;
|
|||
|
|
lastShortTrip=false;
|
|||
|
|
tripScreenDrawn=false;
|
|||
|
|
uiMode = MODE_HOME;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void redrawCurrentScreenAfterWake() {
|
|||
|
|
uiMode = MODE_HOME;
|
|||
|
|
ch1State = CH1_IDLE;
|
|||
|
|
relay1 = false;
|
|||
|
|
setRelay(RELAY1,false);
|
|||
|
|
drawHomeIdle();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool tcCheckOneVolt_OK() {
|
|||
|
|
|
|||
|
|
relay1 = true;
|
|||
|
|
setRelay(RELAY1, true);
|
|||
|
|
safeModeEnabled = true;
|
|||
|
|
ch1OnMs = millis();
|
|||
|
|
|
|||
|
|
delay(50);
|
|||
|
|
|
|||
|
|
float vNow = 0.0f, aNow = 0.0f;
|
|||
|
|
readVA(vNow, aNow);
|
|||
|
|
|
|||
|
|
if (aNow >= 4.0f) {
|
|||
|
|
relay1 = false;
|
|||
|
|
setRelay(RELAY1, false);
|
|||
|
|
beepCount(3);
|
|||
|
|
lastResult_TC = TCR_SHORT;
|
|||
|
|
testPhase_TC = TCPH_RESULT;
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
tc_lastCheckVolt = vNow;
|
|||
|
|
|
|||
|
|
relay1 = false;
|
|||
|
|
setRelay(RELAY1, false);
|
|||
|
|
|
|||
|
|
if (vNow >= TC_OK_MIN_V && vNow <= TC_OK_MAX_V) {
|
|||
|
|
return true;
|
|||
|
|
} else {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
struct TCProfile {
|
|||
|
|
float minV;
|
|||
|
|
float maxV;
|
|||
|
|
float shortV;
|
|||
|
|
float highV;
|
|||
|
|
float dropBadPct;
|
|||
|
|
unsigned long windowMs;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
TCProfile tcProfile;
|
|||
|
|
|
|||
|
|
void configureProfileForCurrentSelection(uint8_t sel) {
|
|||
|
|
switch (sel) {
|
|||
|
|
case TCSEL_CPU:
|
|||
|
|
tcProfile = {0.80f, 1.20f, 0.30f, 1.40f, 30.0f, 5500};
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case TCSEL_GPU:
|
|||
|
|
tcProfile = {0.80f, 1.20f, 0.30f, 1.50f, 25.0f, 5500};
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case TCSEL_VRAM:
|
|||
|
|
tcProfile = {1.00f, 1.40f, 0.50f, 1.60f, 20.0f, 3000};
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case TCSEL_BUCK:
|
|||
|
|
tcProfile = {0.60f, 2.00f, 0.20f, 2.50f, 50.0f, 500};
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
tcProfile = {0.80f, 1.20f, 0.30f, 1.40f, 30.0f, 5500};
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TC_MEASURE_WINDOW_MS = tcProfile.windowMs;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void handleTestChip_TC(){
|
|||
|
|
|
|||
|
|
if(checkLongPressB2()){
|
|||
|
|
beepOnce(40);
|
|||
|
|
|
|||
|
|
setRelay(RELAY1,false);
|
|||
|
|
relay1=false;
|
|||
|
|
|
|||
|
|
ch1State = CH1_IDLE;
|
|||
|
|
uiMode = MODE_HOME;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch(testPhase_TC){
|
|||
|
|
|
|||
|
|
case TCPH_READY: {
|
|||
|
|
|
|||
|
|
drawTestChipMenu_TC(testChipSel_TC, lastResult_TC);
|
|||
|
|
display.display();
|
|||
|
|
|
|||
|
|
if (consumeClick(BTN2)) {
|
|||
|
|
beepOnce(40);
|
|||
|
|
testChipSel_TC = (testChipSel_TC + 1) % TCSEL_COUNT;
|
|||
|
|
drawTestChipMenu_TC(testChipSel_TC, lastResult_TC);
|
|||
|
|
display.display();
|
|||
|
|
registerActivity();
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (consumeClick(BTN1)) {
|
|||
|
|
|
|||
|
|
relay1 = true;
|
|||
|
|
setRelay(RELAY1, true);
|
|||
|
|
delay(100);
|
|||
|
|
|
|||
|
|
float vNow = 0.0f, aNow = 0.0f;
|
|||
|
|
readVA(vNow, aNow);
|
|||
|
|
tc_lastAdjV = vNow;
|
|||
|
|
tc_lastAdjA = aNow;
|
|||
|
|
delay(200);
|
|||
|
|
relay1 = false;
|
|||
|
|
setRelay(RELAY1, false);
|
|||
|
|
|
|||
|
|
float vTarget = 1.0f;
|
|||
|
|
if (testChipSel_TC == TCSEL_VRAM) {
|
|||
|
|
vTarget = 1.20f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float vMinOk = vTarget * 0.90f;
|
|||
|
|
float vMaxOk = vTarget * 1.10f;
|
|||
|
|
|
|||
|
|
if (vNow >= vMinOk && vNow <= vMaxOk) {
|
|||
|
|
|
|||
|
|
switch (testChipSel_TC) {
|
|||
|
|
case TCSEL_GPU: tc_runMs = 6000; tc_settleMs = 300; break;
|
|||
|
|
case TCSEL_VRAM: tc_runMs = 4000; tc_settleMs = 200; break;
|
|||
|
|
case TCSEL_BUCK: tc_runMs = 300; tc_settleMs = 100; break;
|
|||
|
|
default: tc_runMs = 4000; tc_settleMs = 200; break;
|
|||
|
|
}
|
|||
|
|
beepOnce(80);
|
|||
|
|
testPhase_TC = TCPH_ARMED;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
beepCount(2, 60, 80);
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(30,0);
|
|||
|
|
display.print("TESTER CHIP");
|
|||
|
|
|
|||
|
|
if (testChipSel_TC == TCSEL_VRAM) {
|
|||
|
|
drawInvertedLabel(4,48,"AJUSTE 1.2V PRIMERO");
|
|||
|
|
display.setCursor(4,28);
|
|||
|
|
display.print("Medido: ");
|
|||
|
|
display.print(tc_lastAdjV, 3);
|
|||
|
|
display.print("V");
|
|||
|
|
} else {
|
|||
|
|
drawInvertedLabel(4,48,"AJUSTE 1V PRIMERO");
|
|||
|
|
display.setCursor(4,28);
|
|||
|
|
display.print("Medido: ");
|
|||
|
|
display.print(tc_lastAdjV, 3);
|
|||
|
|
display.print("V");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
display.display();
|
|||
|
|
display.display();
|
|||
|
|
delaySafe(6000);
|
|||
|
|
break;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} break;
|
|||
|
|
|
|||
|
|
case TCPH_ARMED: {
|
|||
|
|
|
|||
|
|
drawTestArmed_TC(testChipSel_TC);
|
|||
|
|
|
|||
|
|
if (consumeClick(BTN2)) {
|
|||
|
|
beepOnce(40);
|
|||
|
|
testPhase_TC = TCPH_READY;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(consumeClick(BTN1)){
|
|||
|
|
beepOnce(80);
|
|||
|
|
|
|||
|
|
tc_vMin = 99.0f;
|
|||
|
|
tc_vMax = 0.0f;
|
|||
|
|
tc_vSum = 0.0f;
|
|||
|
|
tc_cnt = 0;
|
|||
|
|
tc_aMinStable = 99.0f;
|
|||
|
|
tc_aMaxStable = 0.0f;
|
|||
|
|
|
|||
|
|
tc_vMinStable = 99.0f;
|
|||
|
|
tc_vMaxStable = 0.0f;
|
|||
|
|
tc_aSumStable = 0.0f;
|
|||
|
|
tc_cntStable = 0;
|
|||
|
|
tc_aPrev = NAN;
|
|||
|
|
tc_aP2P = 0.0f;
|
|||
|
|
tc_aStepMax = 0.0f;
|
|||
|
|
|
|||
|
|
for (uint16_t i = 0; i < TC_TRACE_MAX; i++) {
|
|||
|
|
tc_trace[i] = 0.0f;
|
|||
|
|
}
|
|||
|
|
tc_traceCount = 0;
|
|||
|
|
|
|||
|
|
relay1=true;
|
|||
|
|
setRelay(RELAY1,true);
|
|||
|
|
safeModeEnabled=true;
|
|||
|
|
ch1OnMs=millis();
|
|||
|
|
|
|||
|
|
tcMeasureStartMs=millis();
|
|||
|
|
|
|||
|
|
testPhase_TC = TCPH_MEASURE;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
configureProfileForCurrentSelection(testChipSel_TC);
|
|||
|
|
|
|||
|
|
} break;
|
|||
|
|
case TCPH_MEASURE: {
|
|||
|
|
|
|||
|
|
const unsigned long WARMUP_IGNORE_MS = 50;
|
|||
|
|
const unsigned long STABLE_START_MS = 200;
|
|||
|
|
|
|||
|
|
unsigned long now = millis();
|
|||
|
|
unsigned long elapsed = now - tcMeasureStartMs;
|
|||
|
|
|
|||
|
|
float vNow = 0.0f;
|
|||
|
|
float aNow = 0.0f;
|
|||
|
|
readVA(vNow, aNow);
|
|||
|
|
|
|||
|
|
if (elapsed < (tcMeasureStartMs + (TC_MEASURE_WINDOW_MS / 2))) {
|
|||
|
|
tc_aSumEarly += aNow;
|
|||
|
|
tc_aCntEarly++;
|
|||
|
|
} else {
|
|||
|
|
tc_aSumLate += aNow;
|
|||
|
|
tc_aCntLate++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (aNow > tc_aMax) tc_aMax = aNow;
|
|||
|
|
if (aNow < tc_aMin) tc_aMin = aNow;
|
|||
|
|
|
|||
|
|
if (!isnan(tc_aPrev)) {
|
|||
|
|
float step = fabs(aNow - tc_aPrev);
|
|||
|
|
if (step > tc_aStepMax) tc_aStepMax = step;
|
|||
|
|
}
|
|||
|
|
tc_aPrev = aNow;
|
|||
|
|
|
|||
|
|
if (tc_traceCount < TC_TRACE_MAX) {
|
|||
|
|
tc_trace[tc_traceCount] = vNow;
|
|||
|
|
tc_traceCount++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (elapsed >= WARMUP_IGNORE_MS){
|
|||
|
|
|
|||
|
|
if (vNow < tc_vMin) tc_vMin = vNow;
|
|||
|
|
if (vNow > tc_vMax) tc_vMax = vNow;
|
|||
|
|
tc_vSum += vNow;
|
|||
|
|
|
|||
|
|
if (aNow < tc_aMin) tc_aMin = aNow;
|
|||
|
|
if (aNow > tc_aMax) tc_aMax = aNow;
|
|||
|
|
tc_aSum += aNow;
|
|||
|
|
if (aNow < tc_aMinStable) tc_aMinStable = aNow;
|
|||
|
|
if (aNow > tc_aMaxStable) tc_aMaxStable = aNow;
|
|||
|
|
tc_cnt++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (elapsed >= STABLE_START_MS){
|
|||
|
|
if (vNow < tc_vMinStable) tc_vMinStable = vNow;
|
|||
|
|
if (vNow > tc_vMaxStable) tc_vMaxStable = vNow;
|
|||
|
|
tc_aSumStable += aNow;
|
|||
|
|
tc_cntStable++;
|
|||
|
|
|
|||
|
|
if (tc_stableStartMs == 0) tc_stableStartMs = millis();
|
|||
|
|
|
|||
|
|
unsigned long stableMs = millis() - tc_stableStartMs;
|
|||
|
|
const unsigned long STABLE_SPLIT_MS = 400;
|
|||
|
|
|
|||
|
|
if (stableMs < STABLE_SPLIT_MS) {
|
|||
|
|
tc_aSumEarly += aNow; tc_aCntEarly++;
|
|||
|
|
} else {
|
|||
|
|
tc_aSumLate += aNow; tc_aCntLate++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (safeModeEnabled){
|
|||
|
|
if ((now - ch1OnMs) >= SAFE_ARM_DELAY_MS){
|
|||
|
|
if (vNow < TC_SHORT_V){
|
|||
|
|
relay1 = false;
|
|||
|
|
setRelay(RELAY1,false);
|
|||
|
|
beepCount(3);
|
|||
|
|
|
|||
|
|
lastResult_TC = TCR_SHORT;
|
|||
|
|
tc_finalCode = 3;
|
|||
|
|
|
|||
|
|
testPhase_TC = TCPH_RESULT;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
drawTestMeasuring_TC(testChipSel_TC);
|
|||
|
|
|
|||
|
|
if (elapsed >= TC_MEASURE_WINDOW_MS){
|
|||
|
|
|
|||
|
|
relay1 = false;
|
|||
|
|
setRelay(RELAY1,false);
|
|||
|
|
|
|||
|
|
if (testChipSel_TC == TCSEL_CPU || testChipSel_TC == TCSEL_GPU) {
|
|||
|
|
|
|||
|
|
uint16_t useCnt = (tc_cntStable > 0) ? tc_cntStable : tc_cnt;
|
|||
|
|
float useVmin = (tc_cntStable > 0) ? tc_vMinStable : tc_vMin;
|
|||
|
|
float useVmax = (tc_cntStable > 0) ? tc_vMaxStable : tc_vMax;
|
|||
|
|
|
|||
|
|
float vAvg_all = (tc_cnt>0) ? (tc_vSum / (float)tc_cnt) : 0.0f;
|
|||
|
|
float aAvg_all = (tc_cnt>0) ? (tc_aSum / (float)tc_cnt) : 0.0f;
|
|||
|
|
|
|||
|
|
float aEarly = (tc_aCntEarly>0) ? (tc_aSumEarly/(float)tc_aCntEarly) : 0.0f;
|
|||
|
|
float aLate = (tc_aCntLate >0) ? (tc_aSumLate /(float)tc_aCntLate ) : aEarly;
|
|||
|
|
|
|||
|
|
if (aEarly >= 0.30f && aLate <= 0.05f && useVmin >= tcProfile.minV*0.95f) {
|
|||
|
|
lastResult_TC = TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
else if (tc_aStepMax >= 0.50f) {
|
|||
|
|
lastResult_TC = TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float dropPct = 0.0f;
|
|||
|
|
if (useVmax > 0.001f) {
|
|||
|
|
dropPct = (useVmax - useVmin) / useVmax * 100.0f;
|
|||
|
|
if (dropPct < 0.0f) dropPct = 0.0f;
|
|||
|
|
if (dropPct > 99.0f) dropPct = 99.0f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float aEarlyCPU = (tc_aCntEarly > 0) ? (tc_aSumEarly / (float)tc_aCntEarly) : 0.0f;
|
|||
|
|
float aLateCPU = (tc_aCntLate > 0) ? (tc_aSumLate / (float)tc_aCntLate ) : 0.0f;
|
|||
|
|
float aDropCPU = aEarlyCPU - aLateCPU;
|
|||
|
|
float aP2P_CPU = tc_aMax - tc_aMin;
|
|||
|
|
|
|||
|
|
const float EARLY_MIN_A = 0.20f;
|
|||
|
|
const float LATE_NEAR_ZERO = 0.05f;
|
|||
|
|
const float DROP_MIN_A = 0.15f;
|
|||
|
|
const float STEP_BAD_A = 0.35f;
|
|||
|
|
const float P2P_BAD_A = 0.45f;
|
|||
|
|
const float DROP_V_HELP = 10.0f;
|
|||
|
|
|
|||
|
|
if ( (aEarlyCPU >= EARLY_MIN_A && aLateCPU <= LATE_NEAR_ZERO) ||
|
|||
|
|
(aDropCPU >= DROP_MIN_A) ||
|
|||
|
|
(tc_aStepMax >= STEP_BAD_A) ||
|
|||
|
|
(aP2P_CPU >= P2P_BAD_A && dropPct >= DROP_V_HELP) ) {
|
|||
|
|
lastResult_TC = TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const float NO_LOAD_A = 0.02f;
|
|||
|
|
const float MIN_A_CPU = 0.30f;
|
|||
|
|
const float MIN_A_GPU = 0.50f;
|
|||
|
|
const float MIN_A_OK = (testChipSel_TC==TCSEL_GPU) ? MIN_A_GPU : MIN_A_CPU;
|
|||
|
|
|
|||
|
|
if (aAvg_all <= 0.005f) {
|
|||
|
|
lastResult_TC = TCR_NOCON;
|
|||
|
|
}
|
|||
|
|
else if ((useVmin < 0.60f && aAvg_all > 0.5f) || (useVmin < 0.35f)) {
|
|||
|
|
lastResult_TC = TCR_SHORT;
|
|||
|
|
}
|
|||
|
|
else if (useVmax > tcProfile.highV) {
|
|||
|
|
lastResult_TC = TCR_VHIGH;
|
|||
|
|
}
|
|||
|
|
else if (vAvg_all < tcProfile.minV) {
|
|||
|
|
lastResult_TC = TCR_VLOW;
|
|||
|
|
}
|
|||
|
|
else if (vAvg_all > tcProfile.maxV) {
|
|||
|
|
lastResult_TC = TCR_VHIGH;
|
|||
|
|
}
|
|||
|
|
else if (aAvg_all < MIN_A_OK) {
|
|||
|
|
lastResult_TC = TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
else if (dropPct > tcProfile.dropBadPct) {
|
|||
|
|
lastResult_TC = TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
lastResult_TC = TCR_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
float aP2P = tc_aMax - tc_aMin;
|
|||
|
|
float rel = (aAvg_all > 0.0f) ? (aP2P / aAvg_all) : 0.0f;
|
|||
|
|
if (rel > 0.55f && dropPct > 6.0f) {
|
|||
|
|
lastResult_TC = TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else if (testChipSel_TC == TCSEL_VRAM) {
|
|||
|
|
|
|||
|
|
float vAvg_all = (tc_cnt>0) ? (tc_vSum/(float)tc_cnt) : 0.0f;
|
|||
|
|
float aAvg_all = (tc_cnt>0) ? (tc_aSum/(float)tc_cnt) : 0.0f;
|
|||
|
|
|
|||
|
|
if (aAvg_all < 0.02f) lastResult_TC = TCR_NOCON;
|
|||
|
|
else if (tc_vMin < 0.50f) lastResult_TC = TCR_SHORT;
|
|||
|
|
else if (tc_vMax > 1.60f) lastResult_TC = TCR_VHIGH;
|
|||
|
|
else if (vAvg_all < 1.10f) lastResult_TC = TCR_VLOW;
|
|||
|
|
else if (vAvg_all > 1.30f) lastResult_TC = TCR_VHIGH;
|
|||
|
|
else lastResult_TC = TCR_OK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
else {
|
|||
|
|
uint16_t useCnt = (tc_cntStable > 0) ? tc_cntStable : tc_cnt;
|
|||
|
|
float useVmin = (tc_cntStable > 0) ? tc_vMinStable : tc_vMin;
|
|||
|
|
float useVmax = (tc_cntStable > 0) ? tc_vMaxStable : tc_vMax;
|
|||
|
|
|
|||
|
|
float vAvg_all = (tc_cnt > 0) ? (tc_vSum / (float)tc_cnt) : 0.0f;
|
|||
|
|
float aAvg_all = (tc_cnt > 0) ? (tc_aSum / (float)tc_cnt) : 0.0f;
|
|||
|
|
float aP2P = tc_aMax - tc_aMin;
|
|||
|
|
|
|||
|
|
float dropPct = 0.0f;
|
|||
|
|
if (useVmax > 0.001f) {
|
|||
|
|
dropPct = (useVmax - useVmin) / useVmax * 100.0f;
|
|||
|
|
if (dropPct < 0.0f) dropPct = 0.0f;
|
|||
|
|
if (dropPct > 99.0f) dropPct = 99.0f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const float NOLOAD_A = 0.02f;
|
|||
|
|
const float OK_V_MIN = 0.85f;
|
|||
|
|
const float OK_V_MAX = 1.20f;
|
|||
|
|
const float OK_DROP_MAX = 20.0f;
|
|||
|
|
const float FLUCT_A_STRONG = 0.40f;
|
|||
|
|
const float FLUCT_A_MED = 0.25f;
|
|||
|
|
const float UNSTABLE_DROP = 25.0f;
|
|||
|
|
const float SHORT_A_SPIKE = 3.0f;
|
|||
|
|
const float SHORT_V_ABS = 0.60f;
|
|||
|
|
|
|||
|
|
if (aAvg_all < NOLOAD_A && dropPct < 3.0f) {
|
|||
|
|
lastResult_TC = TCR_NOCON;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
else if ( (tc_aMax >= SHORT_A_SPIKE && useVmin < SHORT_V_ABS) ||
|
|||
|
|
(dropPct >= 55.0f && aAvg_all > 0.5f) ) {
|
|||
|
|
lastResult_TC = TCR_SHORT;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
else if (aP2P >= FLUCT_A_STRONG) {
|
|||
|
|
lastResult_TC = TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
else if (aP2P >= FLUCT_A_MED && dropPct >= UNSTABLE_DROP) {
|
|||
|
|
lastResult_TC = TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
else if (vAvg_all >= OK_V_MIN && vAvg_all <= OK_V_MAX && dropPct <= OK_DROP_MAX) {
|
|||
|
|
|
|||
|
|
if (aAvg_all >= 0.04f && aAvg_all <= 3.0f) {
|
|||
|
|
lastResult_TC = TCR_OK;
|
|||
|
|
} else {
|
|||
|
|
|
|||
|
|
lastResult_TC = TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
else if (vAvg_all < OK_V_MIN) {
|
|||
|
|
lastResult_TC = TCR_VLOW;
|
|||
|
|
}
|
|||
|
|
else if (vAvg_all > OK_V_MAX) {
|
|||
|
|
lastResult_TC = TCR_VHIGH;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
else {
|
|||
|
|
lastResult_TC = TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (tc_cntStable >= 12 && lastResult_TC != TCR_SHORT && lastResult_TC != TCR_NOCON) {
|
|||
|
|
float aP2P_stable = tc_aMaxStable - tc_aMinStable;
|
|||
|
|
|
|||
|
|
const float TH_CPU_GPU = 0.45f;
|
|||
|
|
const float TH_VRAM_BUCK = 0.30f;
|
|||
|
|
|
|||
|
|
bool isCpuGpu = (testChipSel_TC == TCSEL_CPU || testChipSel_TC == TCSEL_GPU);
|
|||
|
|
float th = isCpuGpu ? TH_CPU_GPU : TH_VRAM_BUCK;
|
|||
|
|
|
|||
|
|
float vAvg_all = (tc_cnt>0) ? (tc_vSum / (float)tc_cnt) : 0.0f;
|
|||
|
|
if (vAvg_all >= 0.75f && vAvg_all <= 1.25f) {
|
|||
|
|
if (aP2P_stable >= th) {
|
|||
|
|
lastResult_TC = TCR_FAIL;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch(lastResult_TC){
|
|||
|
|
case TCR_OK: tc_finalCode = 0; break;
|
|||
|
|
case TCR_VLOW: tc_finalCode = 1; break;
|
|||
|
|
case TCR_VHIGH: tc_finalCode = 2; break;
|
|||
|
|
case TCR_SHORT: tc_finalCode = 3; break;
|
|||
|
|
case TCR_NOCON: tc_finalCode = 5; break;
|
|||
|
|
default: tc_finalCode = 4; break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
testPhase_TC = TCPH_RESULT;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} break;
|
|||
|
|
|
|||
|
|
case TCPH_RESULT: {
|
|||
|
|
|
|||
|
|
float vAvg = (tc_cnt>0) ? (tc_vSum/(float)tc_cnt) : 0.0f;
|
|||
|
|
|
|||
|
|
float dropPctF = 0.0f;
|
|||
|
|
if(tc_vMax>0.001f){
|
|||
|
|
dropPctF = (tc_vMax - tc_vMin)/tc_vMax * 100.0f;
|
|||
|
|
if(dropPctF<0) dropPctF=0;
|
|||
|
|
}
|
|||
|
|
uint8_t dropPct = (dropPctF>99.0f)?99:(uint8_t)(dropPctF+0.5f);
|
|||
|
|
|
|||
|
|
drawTestSummaryScreen_WithGraph_Compact(
|
|||
|
|
testChipSel_TC,
|
|||
|
|
tc_finalCode,
|
|||
|
|
vAvg,
|
|||
|
|
tc_vMin,
|
|||
|
|
tc_vMax,
|
|||
|
|
dropPct
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (consumeClick(BTN1)) {
|
|||
|
|
beepOnce(80);
|
|||
|
|
|
|||
|
|
tc_vMin = 99.0f;
|
|||
|
|
tc_vMax = 0.0f;
|
|||
|
|
tc_vSum = 0.0f;
|
|||
|
|
tc_cnt = 0;
|
|||
|
|
|
|||
|
|
tc_aMin = 99.0f;
|
|||
|
|
tc_aMax = 0.0f;
|
|||
|
|
tc_aSum = 0.0f;
|
|||
|
|
tc_aMinStable = 99.0f;
|
|||
|
|
tc_aMaxStable = 0.0f;
|
|||
|
|
tc_vMinStable = 99.0f;
|
|||
|
|
tc_vMaxStable = 0.0f;
|
|||
|
|
tc_aSumStable = 0.0f;
|
|||
|
|
tc_cntStable = 0;
|
|||
|
|
tc_aPrev = NAN;
|
|||
|
|
tc_aP2P = 0.0f;
|
|||
|
|
tc_aStepMax = 0.0f;
|
|||
|
|
for (uint16_t i = 0; i < TC_TRACE_MAX; i++) {
|
|||
|
|
tc_trace[i] = 0.0f;
|
|||
|
|
}
|
|||
|
|
tc_traceCount = 0;
|
|||
|
|
|
|||
|
|
lastResult_TC = TCR_NONE;
|
|||
|
|
tc_finalCode = 4;
|
|||
|
|
|
|||
|
|
relay1 = true;
|
|||
|
|
setRelay(RELAY1, true);
|
|||
|
|
safeModeEnabled = true;
|
|||
|
|
ch1OnMs = millis();
|
|||
|
|
|
|||
|
|
tcMeasureStartMs = millis();
|
|||
|
|
|
|||
|
|
testPhase_TC = TCPH_MEASURE;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (consumeClick(BTN2)) {
|
|||
|
|
beepOnce(40);
|
|||
|
|
|
|||
|
|
relay1 = false;
|
|||
|
|
setRelay(RELAY1,false);
|
|||
|
|
|
|||
|
|
tc_vMin = 99.0f;
|
|||
|
|
tc_vMax = 0.0f;
|
|||
|
|
tc_vSum = 0.0f;
|
|||
|
|
tc_cnt = 0;
|
|||
|
|
tc_aMinStable = 99.0f;
|
|||
|
|
tc_aMaxStable = 0.0f;
|
|||
|
|
tc_aMin = 99.0f;
|
|||
|
|
tc_aMax = 0.0f;
|
|||
|
|
tc_aSum = 0.0f;
|
|||
|
|
|
|||
|
|
tc_vMinStable = 99.0f;
|
|||
|
|
tc_vMaxStable = 0.0f;
|
|||
|
|
tc_aSumStable = 0.0f;
|
|||
|
|
tc_cntStable = 0;
|
|||
|
|
tc_aPrev = NAN;
|
|||
|
|
tc_aP2P = 0.0f;
|
|||
|
|
tc_aStepMax = 0.0f;
|
|||
|
|
for (uint16_t i = 0; i < TC_TRACE_MAX; i++) {
|
|||
|
|
tc_trace[i] = 0.0f;
|
|||
|
|
}
|
|||
|
|
tc_traceCount = 0;
|
|||
|
|
|
|||
|
|
lastResult_TC = TCR_NONE;
|
|||
|
|
tc_finalCode = 4;
|
|||
|
|
|
|||
|
|
testPhase_TC = TCPH_READY;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} break;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void handleLaptop_TC() {
|
|||
|
|
if (laptopPhase == LAPH_ADJ) {
|
|||
|
|
if (consumeClick(BTN2)) {
|
|||
|
|
beepOnce(40);
|
|||
|
|
relay1 = false; setRelay(RELAY1, false);
|
|||
|
|
ch1State = CH1_IDLE;
|
|||
|
|
uiMode = MODE_MENU;
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (consumeClick(BTN1)) {
|
|||
|
|
beepOnce(80);
|
|||
|
|
relay1 = true; setRelay(RELAY1, true);
|
|||
|
|
safeModeEnabled = true;
|
|||
|
|
ch1OnMs = millis();
|
|||
|
|
unsigned long t0 = millis();
|
|||
|
|
float vSum = 0.0f;
|
|||
|
|
uint16_t vCnt = 0;
|
|||
|
|
float vNow = 0.0f, aNow = 0.0f;
|
|||
|
|
while (millis() - t0 < 1000) {
|
|||
|
|
readVA(vNow, aNow);
|
|||
|
|
vSum += vNow;
|
|||
|
|
vCnt++;
|
|||
|
|
delay(10);
|
|||
|
|
}
|
|||
|
|
relay1 = false; setRelay(RELAY1, false);
|
|||
|
|
float vAvg = (vCnt ? (vSum / (float)vCnt) : 0.0f);
|
|||
|
|
if (vAvg < LAPTOP_VMIN || vAvg > LAPTOP_VMAX) {
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setTextSize(2); display.setCursor(15, 0); display.print("ATENCION");
|
|||
|
|
display.setTextSize(1); display.setCursor(18, 24);
|
|||
|
|
display.print("Medido: "); display.print(vAvg, 2); display.print("V");
|
|||
|
|
drawInvertedLabel(17, 40, "AJUSTE 18 a 21V");
|
|||
|
|
display.display();
|
|||
|
|
delaySafe(3500);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
laptopPhase = LAPH_ANALYSIS;
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
drawLaptopScreen(true);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
if (consumeClick(BTN2)) {
|
|||
|
|
beepOnce(40);
|
|||
|
|
relay1 = false; setRelay(RELAY1, false);
|
|||
|
|
ch1State = CH1_IDLE;
|
|||
|
|
uiMode = MODE_MENU;
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (consumeClick(BTN1)) {
|
|||
|
|
beepOnce(60);
|
|||
|
|
relay1 = !relay1;
|
|||
|
|
setRelay(RELAY1, relay1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float vNow = 0.0f, aNow = 0.0f;
|
|||
|
|
readVA(vNow, aNow);
|
|||
|
|
|
|||
|
|
if (relay1 && !lap_lastRelayOn) {
|
|||
|
|
lap_vBase = vNow;
|
|||
|
|
lap_aFast = aNow;
|
|||
|
|
lap_aSlow = aNow;
|
|||
|
|
lap_aSwingPeak = 0.0f;
|
|||
|
|
lap_aMax = 0.0f;
|
|||
|
|
lap_aMin = 99.0f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (relay1) {
|
|||
|
|
if (aNow > lap_aMax) lap_aMax = aNow;
|
|||
|
|
if (aNow < lap_aMin) lap_aMin = aNow;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
lap_lastRelayOn = relay1;
|
|||
|
|
|
|||
|
|
static unsigned long lap_beepTs = 0;
|
|||
|
|
if (relay1) {
|
|||
|
|
if (lap_beepTs == 0) lap_beepTs = millis();
|
|||
|
|
if (millis() - lap_beepTs >= 5000UL) {
|
|||
|
|
beepOnce(40);
|
|||
|
|
lap_beepTs = millis();
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
lap_beepTs = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
unsigned long nowMs = millis();
|
|||
|
|
if (nowMs - lap_lastSampleMs >= 30) {
|
|||
|
|
lap_lastSampleMs = nowMs;
|
|||
|
|
lap_aFast += (aNow - lap_aFast) * 0.40f;
|
|||
|
|
lap_aSlow += (aNow - lap_aSlow) * 0.05f;
|
|||
|
|
float swing = fabs(lap_aFast - lap_aSlow);
|
|||
|
|
if (swing > lap_aSwingPeak) lap_aSwingPeak = swing;
|
|||
|
|
else lap_aSwingPeak *= 0.92f;
|
|||
|
|
if (aNow >= L_STBY_MIN_A) {
|
|||
|
|
lap_wasAboveStandby = true;
|
|||
|
|
lap_lastAboveTs = nowMs;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const char* diagMsg = "Analisis activo";
|
|||
|
|
if (!relay1) {
|
|||
|
|
diagMsg = "Analisis pausado";
|
|||
|
|
drawLaptopAnalysisSimple(diagMsg, aNow, relay1);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (vNow > LAPTOP_VMAX || vNow < LAPTOP_VMIN) {
|
|||
|
|
diagMsg = "V FUERA RANGO";
|
|||
|
|
drawLaptopAnalysisSimple(diagMsg, aNow, relay1);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((aNow >= L_SHORT_I) ||
|
|||
|
|
(lap_vBase > 0.1f && vNow < (lap_vBase * (1.0f - L_SHORT_DROP_P)) && aNow > 0.10f)) {
|
|||
|
|
diagMsg = "CORTO!";
|
|||
|
|
drawLaptopAnalysisSimple(diagMsg, aNow, relay1);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (aNow < L_NO_POWER_A) {
|
|||
|
|
if (lap_wasAboveStandby && (nowMs - lap_lastAboveTs) < 1500) {
|
|||
|
|
diagMsg = "SE APAGA";
|
|||
|
|
} else {
|
|||
|
|
diagMsg = "SIN ALIMENTACION";
|
|||
|
|
}
|
|||
|
|
drawLaptopAnalysisSimple(diagMsg, aNow, relay1);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (aNow >= L_STBY_MIN_A && aNow <= L_STBY_MAX_A && lap_aSwingPeak < L_COMM_SWING_A) {
|
|||
|
|
diagMsg = "STANDBY OK";
|
|||
|
|
drawLaptopAnalysisSimple(diagMsg, aNow, relay1);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (lap_aSwingPeak >= L_COMM_SWING_A && aNow < L_BOOT_A) {
|
|||
|
|
diagMsg = "COMUNICACION OK";
|
|||
|
|
drawLaptopAnalysisSimple(diagMsg, aNow, relay1);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (aNow >= L_BOOT_A) {
|
|||
|
|
diagMsg = "INTENTO ARRANQUE";
|
|||
|
|
drawLaptopAnalysisSimple(diagMsg, aNow, relay1);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
diagMsg = "ALIMENTANDO...";
|
|||
|
|
drawLaptopAnalysisSimple(diagMsg, aNow, relay1);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void handleHomeMode(){
|
|||
|
|
if(checkLongPressB2()){
|
|||
|
|
beepOnce(40);
|
|||
|
|
forceGoHome();
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch(ch1State){
|
|||
|
|
|
|||
|
|
case CH1_IDLE:{
|
|||
|
|
if(consumeClick(BTN1)){
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
beepOnce(50);
|
|||
|
|
armCh1();
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (consumeClick(BTN2)) {
|
|||
|
|
beepOnce(40);
|
|||
|
|
setRelay(RELAY1, false);
|
|||
|
|
relay1 = false;
|
|||
|
|
ch1State = CH1_IDLE;
|
|||
|
|
uiMode = MODE_HOME;
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if(consumeClick(BTN2)){
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
beepOnce(40);
|
|||
|
|
uiMode=MODE_MENU;
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
drawHomeIdle();
|
|||
|
|
}
|
|||
|
|
delay(40);
|
|||
|
|
} break;
|
|||
|
|
|
|||
|
|
case CH1_ACTIVE:{
|
|||
|
|
if(consumeClick(BTN1)){
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
beepOnce(50);
|
|||
|
|
relay1=false;
|
|||
|
|
setRelay(RELAY1,false);
|
|||
|
|
ch1State=CH1_IDLE;
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (consumeClick(BTN2)){
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
|
|||
|
|
safeMode = (SafeMode)((((int)safeMode) + 1) % 3);
|
|||
|
|
|
|||
|
|
switch(safeMode){
|
|||
|
|
case SAFE_V: beepOnce(80); break;
|
|||
|
|
case SAFE_OFF: beepOnce(20); break;
|
|||
|
|
case SAFE_PLUS: beepCount(2,40,60); break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ch1OnMs = millis();
|
|||
|
|
registerActivity();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float vNow=0.0f, aNow=0.0f;
|
|||
|
|
readVA(vNow,aNow);
|
|||
|
|
|
|||
|
|
unsigned long nowMs = millis();
|
|||
|
|
|
|||
|
|
if (safeMode != SAFE_OFF){
|
|||
|
|
if ((nowMs - ch1OnMs) >= SAFE_ARM_DELAY_MS) {
|
|||
|
|
if (vNow < SAFE_SHORT_VOLTAGE) {
|
|||
|
|
relay1=false; setRelay(RELAY1,false);
|
|||
|
|
lastTripWasPlus = false;
|
|||
|
|
beepCount(3);
|
|||
|
|
lastShortTrip=true;
|
|||
|
|
ch1State=CH1_TRIPPED;
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const float I_BASE = 0.06f;
|
|||
|
|
const float I_SPIKE = 1.10f;
|
|||
|
|
const float I_ABS_HARD = 1.50f;
|
|||
|
|
const unsigned long I_ABS_CONFIRM_MS = 4;
|
|||
|
|
const unsigned long I_IGN_START_MS = 6;
|
|||
|
|
const float DI_DT = 0.80f;
|
|||
|
|
|
|||
|
|
if (safeMode == SAFE_PLUS){
|
|||
|
|
if ((nowMs - ch1OnMs) >= (SAFE_ARM_DELAY_MS + I_IGN_START_MS)) {
|
|||
|
|
aHist[aHidx] = aNow;
|
|||
|
|
tHist[aHidx] = nowMs;
|
|||
|
|
aHidx = (aHidx + 1) & 0x03;
|
|||
|
|
if (!aHistPrimed && aHidx==0) aHistPrimed = true;
|
|||
|
|
if (aHistPrimed){
|
|||
|
|
int i0 = (aHidx + 3) & 0x03;
|
|||
|
|
int i1 = (aHidx + 2) & 0x03;
|
|||
|
|
float a_now = aHist[i0];
|
|||
|
|
float a_prev = aHist[i1];
|
|||
|
|
unsigned long dt = tHist[i0] - tHist[i1];
|
|||
|
|
float di = a_now - a_prev;
|
|||
|
|
bool jumpFast = ((a_now >= I_SPIKE) && (dt <= 40)) || ((di >= DI_DT) && (dt <= 40));
|
|||
|
|
bool absHigh = (a_now >= I_ABS_HARD);
|
|||
|
|
static unsigned long absStartMs = 0;
|
|||
|
|
if (absHigh){
|
|||
|
|
if (absStartMs == 0) absStartMs = nowMs;
|
|||
|
|
} else {
|
|||
|
|
absStartMs = 0;
|
|||
|
|
}
|
|||
|
|
bool absConfirmed = (absStartMs && (nowMs - absStartMs) >= I_ABS_CONFIRM_MS);
|
|||
|
|
if (jumpFast || absConfirmed){
|
|||
|
|
relay1=false; setRelay(RELAY1,false);
|
|||
|
|
lastTripWasPlus = true;
|
|||
|
|
beepCount(3);
|
|||
|
|
lastShortTrip=true;
|
|||
|
|
ch1State=CH1_TRIPPED;
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
static unsigned long diffStart=0;
|
|||
|
|
unsigned long now=millis();
|
|||
|
|
float dvfabs=fabs(vNow - vref);
|
|||
|
|
if(dvfabs > VREF_HYST){
|
|||
|
|
if(diffStart==0){
|
|||
|
|
diffStart=now;
|
|||
|
|
} else if(now-diffStart>=VREF_REARM_HOLD_MS){
|
|||
|
|
vref=vNow;
|
|||
|
|
diffStart=0;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
diffStart=0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float dropV = vref - vNow;
|
|||
|
|
if(dropV<0) dropV=0;
|
|||
|
|
float pctDrop=(vref>0.001f)?(dropV/vref*100.0f):0.0f;
|
|||
|
|
|
|||
|
|
if(pctDrop>=99.0f){
|
|||
|
|
beepOnce(60);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float zoomFactor;
|
|||
|
|
if (dropV < 0.10f) {
|
|||
|
|
zoomFactor = 4.0f;
|
|||
|
|
} else if (dropV < 0.20f) {
|
|||
|
|
zoomFactor = 2.5f;
|
|||
|
|
} else if (dropV < 0.40f) {
|
|||
|
|
zoomFactor = 1.5f;
|
|||
|
|
} else {
|
|||
|
|
zoomFactor = 1.0f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float scaledDrop = dropV * zoomFactor;
|
|||
|
|
|
|||
|
|
if(scaledDrop > GRAPH_MAX_DROP) scaledDrop = GRAPH_MAX_DROP;
|
|||
|
|
if(scaledDrop < 0) scaledDrop = 0;
|
|||
|
|
|
|||
|
|
float norm = scaledDrop / GRAPH_MAX_DROP;
|
|||
|
|
uint8_t yVal = (uint8_t)(norm * (PH-1) + 0.5f);
|
|||
|
|
|
|||
|
|
gbuf[head] = yVal;
|
|||
|
|
head = (head+1) % PW;
|
|||
|
|
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
drawChannel1Frame(vNow,aNow,dropV,pctDrop,safeModeEnabled);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
delay(80);
|
|||
|
|
} break;
|
|||
|
|
|
|||
|
|
case CH1_TRIPPED: {
|
|||
|
|
|
|||
|
|
if (consumeClick(BTN1)) {
|
|||
|
|
if (!displaySleeping) {
|
|||
|
|
beepOnce(50);
|
|||
|
|
armCh1();
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (consumeClick(BTN2)) {
|
|||
|
|
if (!displaySleeping) {
|
|||
|
|
beepOnce(40);
|
|||
|
|
setRelay(RELAY1, false);
|
|||
|
|
relay1 = false;
|
|||
|
|
ch1State = CH1_IDLE;
|
|||
|
|
uiMode = MODE_HOME;
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!displaySleeping) {
|
|||
|
|
drawTripScreen();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
delay(40);
|
|||
|
|
} break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void handleMainMenu(){
|
|||
|
|
if(checkLongPressB2()){
|
|||
|
|
beepOnce(40);
|
|||
|
|
forceGoHome();
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(consumeClick(BTN2)){
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
beepOnce(40);
|
|||
|
|
mainSel = (mainSel+1)%MM_COUNT;
|
|||
|
|
registerActivity();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(consumeClick(BTN1)){
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
beepOnce(80);
|
|||
|
|
registerActivity();
|
|||
|
|
|
|||
|
|
if(mainSel==MM_CANAL2){
|
|||
|
|
relay2 = !relay2;
|
|||
|
|
setRelay(RELAY2, relay2);
|
|||
|
|
forceGoHome();
|
|||
|
|
return;
|
|||
|
|
} else if (mainSel == MM_TESTCHIP) {
|
|||
|
|
|
|||
|
|
relay1 = false;
|
|||
|
|
setRelay(RELAY1, false);
|
|||
|
|
ch1State = CH1_IDLE;
|
|||
|
|
|
|||
|
|
testChipSel = 0;
|
|||
|
|
testChipSel_TC = TCSEL_CPU;
|
|||
|
|
lastResult_TC = TCR_NONE;
|
|||
|
|
testPhase_TC = TCPH_READY;
|
|||
|
|
tc_vMin = 99.0f;
|
|||
|
|
tc_vMax = 0.0f;
|
|||
|
|
tc_vSum = 0.0f;
|
|||
|
|
tc_cnt = 0;
|
|||
|
|
tcMeasureStartMs = 0;
|
|||
|
|
|
|||
|
|
safeModeEnabled = true;
|
|||
|
|
ch1OnMs = millis();
|
|||
|
|
|
|||
|
|
uiMode = MODE_TEST_CHIP;
|
|||
|
|
|
|||
|
|
registerActivity();
|
|||
|
|
drawTestChipMenu_TC(testChipSel_TC, lastResult_TC);
|
|||
|
|
display.display();
|
|||
|
|
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
else if(mainSel==MM_LAPTOP){
|
|||
|
|
relay1=false; setRelay(RELAY1,false);
|
|||
|
|
ch1State=CH1_IDLE;
|
|||
|
|
laptopPhase = LAPH_ADJ;
|
|||
|
|
uiMode=MODE_LAPTOP;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
else if(mainSel==MM_MEDICION){
|
|||
|
|
relay1=false; setRelay(RELAY1,false);
|
|||
|
|
ch1State=CH1_IDLE;
|
|||
|
|
uiMode=MODE_VOLT;
|
|||
|
|
return;
|
|||
|
|
} else if(mainSel==MM_LAPTOP){
|
|||
|
|
relay1=false; setRelay(RELAY1,false);
|
|||
|
|
ch1State=CH1_IDLE;
|
|||
|
|
uiMode=MODE_LAPTOP;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
else if(mainSel==MM_CALIBRACION){
|
|||
|
|
relay1=false; setRelay(RELAY1,false);
|
|||
|
|
ch1State=CH1_IDLE;
|
|||
|
|
uiMode=MODE_CALIB;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
drawMainMenu(mainSel);
|
|||
|
|
}
|
|||
|
|
delay(60);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void handleTestChipMenu(){
|
|||
|
|
if(checkLongPressB2()){
|
|||
|
|
beepOnce(40);
|
|||
|
|
forceGoHome();
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(consumeClick(BTN2)){
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
beepOnce(40);
|
|||
|
|
testChipSel = (testChipSel+1)%TC_COUNT;
|
|||
|
|
registerActivity();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(consumeClick(BTN1)){
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
|
|||
|
|
beepOnce(80);
|
|||
|
|
registerActivity();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
drawTestChipMenu(testChipSel);
|
|||
|
|
}
|
|||
|
|
delay(60);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void handleVoltimetro(){
|
|||
|
|
if (consumeClick(BTN2)){
|
|||
|
|
beepOnce(60);
|
|||
|
|
drawVoltimetroExitWarning();
|
|||
|
|
beepLongWarning();
|
|||
|
|
registerActivity();
|
|||
|
|
forceGoHome();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
static float vSlow=0.0f;
|
|||
|
|
static float vFast=0.0f;
|
|||
|
|
static float vPeakSwing=0.0f;
|
|||
|
|
static unsigned long winStart=0;
|
|||
|
|
static uint8_t phase=0;
|
|||
|
|
static uint8_t lastState=255;
|
|||
|
|
static float vShow=0.0f;
|
|||
|
|
static uint8_t pendingState=255;
|
|||
|
|
static unsigned long stateStart=0;
|
|||
|
|
const float FLUCT_THRESH_V=0.020f;
|
|||
|
|
const float PULSE_THRESH_V=0.250f;
|
|||
|
|
const float DV_STEP=0.120f;
|
|||
|
|
const float PULSE_RATE=0.25f;
|
|||
|
|
const unsigned long STABLE_TIME_MS=1000UL;
|
|||
|
|
if (phase==0){
|
|||
|
|
if (winStart==0){
|
|||
|
|
winStart=micros();
|
|||
|
|
vPeakSwing=0.0f;
|
|||
|
|
vFast=0.0f;
|
|||
|
|
vSlow=0.0f;
|
|||
|
|
}
|
|||
|
|
float vPrev=0.0f;
|
|||
|
|
bool havePrev=false;
|
|||
|
|
unsigned long n=0, spikes=0;
|
|||
|
|
float vMin=999.0f, vMax=0.0f, vSum=0.0f;
|
|||
|
|
while ((unsigned long)(micros()-winStart) < 30000UL){
|
|||
|
|
float vNow=0.0f, aDummy=0.0f;
|
|||
|
|
readVA(vNow,aDummy);
|
|||
|
|
if (vFast==0.0f && vSlow==0.0f){ vFast=vNow; vSlow=vNow; }
|
|||
|
|
vFast += (vNow - vFast)*0.5f;
|
|||
|
|
vSlow += (vNow - vSlow)*0.05f;
|
|||
|
|
float swing=fabs(vFast - vSlow);
|
|||
|
|
if (swing>vPeakSwing) vPeakSwing=swing;
|
|||
|
|
if (!havePrev){ vPrev=vNow; havePrev=true; }
|
|||
|
|
else { if (fabs(vNow - vPrev)>DV_STEP) spikes++; vPrev=vNow; }
|
|||
|
|
if (vNow<vMin) vMin=vNow;
|
|||
|
|
if (vNow>vMax) vMax=vNow;
|
|||
|
|
vSum+=vNow;
|
|||
|
|
n++;
|
|||
|
|
}
|
|||
|
|
float p2p=(vMax - vMin);
|
|||
|
|
float rate=(n>0)?(float)spikes/(float)n:0.0f;
|
|||
|
|
uint8_t state;
|
|||
|
|
if ((p2p>PULSE_THRESH_V) && (rate>PULSE_RATE)) state=2;
|
|||
|
|
else if (p2p>FLUCT_THRESH_V || rate>(PULSE_RATE*0.5f)) state=1;
|
|||
|
|
else state=0;
|
|||
|
|
unsigned long now=millis();
|
|||
|
|
if (state!=pendingState){
|
|||
|
|
pendingState=state;
|
|||
|
|
stateStart=now;
|
|||
|
|
}
|
|||
|
|
if ((now - stateStart)>=STABLE_TIME_MS && state!=lastState){
|
|||
|
|
if (state==2) beepCount(3);
|
|||
|
|
else if (state==1) beepCount(2);
|
|||
|
|
else beepOnce(60);
|
|||
|
|
lastState=state;
|
|||
|
|
}
|
|||
|
|
vShow=(n>0)?(vSum/(float)n):vSlow;
|
|||
|
|
phase=1;
|
|||
|
|
} else {
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
const char *estadoTxt;
|
|||
|
|
if (lastState==2) estadoTxt="PULSANTE";
|
|||
|
|
else if (lastState==1) estadoTxt="FLUCTUANTE";
|
|||
|
|
else estadoTxt="ESTABLE";
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setCursor(8,0);
|
|||
|
|
display.print("VOLTIMETRO");
|
|||
|
|
char buf[16];
|
|||
|
|
dtostrf(vShow,0,3,buf);
|
|||
|
|
int16_t x1,y1; uint16_t w1,h1;
|
|||
|
|
display.setTextSize(2);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.getTextBounds(buf,0,0,&x1,&y1,&w1,&h1);
|
|||
|
|
int16_t xx=(OLED_W - (w1+12))/2; if(xx<0)xx=0;
|
|||
|
|
int16_t yy=24;
|
|||
|
|
display.setCursor(xx,yy);
|
|||
|
|
display.print(buf);
|
|||
|
|
display.print("V");
|
|||
|
|
display.setTextSize(1);
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setCursor(10,50);
|
|||
|
|
display.print("V ");
|
|||
|
|
display.print(estadoTxt);
|
|||
|
|
drawInvertedLabel(88,50,"< B2");
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
phase=0;
|
|||
|
|
winStart=0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
void handleLaptop(){
|
|||
|
|
if(checkLongPressB2()){
|
|||
|
|
beepOnce(40);
|
|||
|
|
forceGoHome();
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool needAdj19v = true;
|
|||
|
|
|
|||
|
|
if(consumeClick(BTN1)){
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
beepOnce(80);
|
|||
|
|
registerActivity();
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(!displaySleeping){
|
|||
|
|
drawLaptopScreen(needAdj19v);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
delay(60);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void handleOledSleep(){
|
|||
|
|
unsigned long now = millis();
|
|||
|
|
|
|||
|
|
if(displaySleeping){
|
|||
|
|
if(rawBtnPressed()){
|
|||
|
|
oledOn();
|
|||
|
|
beepOnce(30);
|
|||
|
|
redrawCurrentScreenAfterWake();
|
|||
|
|
wakeSwallowInputs = true;
|
|||
|
|
registerActivity();
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(uiMode == MODE_HOME && ch1State == CH1_IDLE){
|
|||
|
|
if((now - lastActivityMs) >= OLED_SLEEP_MS){
|
|||
|
|
oledOff();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void setup(){
|
|||
|
|
ina.begin();
|
|||
|
|
ina.setMaxCurrentShunt(16.0f, 0.005f);
|
|||
|
|
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);
|
|||
|
|
|
|||
|
|
Wire.begin();
|
|||
|
|
Wire.setClock(100000);
|
|||
|
|
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
|
|||
|
|
display.setRotation(2);
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.display();
|
|||
|
|
|
|||
|
|
ina.begin();
|
|||
|
|
wdt_enable(WDTO_2S);
|
|||
|
|
vref = readVoltageCal();
|
|||
|
|
for(uint8_t i=0;i<PW;i++) gbuf[i]=0;
|
|||
|
|
head=0;
|
|||
|
|
|
|||
|
|
uiMode = MODE_HOME;
|
|||
|
|
ch1State = CH1_IDLE;
|
|||
|
|
mainSel = 0;
|
|||
|
|
testChipSel = 0;
|
|||
|
|
safeModeEnabled = true;
|
|||
|
|
lastShortTrip=false;
|
|||
|
|
tripScreenDrawn=false;
|
|||
|
|
ch1OnMs=0;
|
|||
|
|
aSlow=0.0f;
|
|||
|
|
aPeakSwing=0.0f;
|
|||
|
|
lastVoltSampleMs=millis();
|
|||
|
|
|
|||
|
|
displaySleeping=false;
|
|||
|
|
lastActivityMs=millis();
|
|||
|
|
|
|||
|
|
drawHomeIdle();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static uint8_t calPhase = 0;
|
|||
|
|
|
|||
|
|
void drawCalibStep1(){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setTextSize(2); display.setCursor(17,0); display.print("CALIBRAR");
|
|||
|
|
display.setTextSize(1); display.setCursor(6,18); display.print("COLOQUE UNA R100 5W");
|
|||
|
|
display.setCursor(22,28); display.print("EN LAS PINZAS");
|
|||
|
|
display.setCursor(17,38); display.print("AJUSTE A 10.00v");
|
|||
|
|
drawInvertedLabel(6,48,"B1> Siguiente");
|
|||
|
|
display.setCursor(98,52); display.print("< B2");
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#include <EEPROM.h>
|
|||
|
|
|
|||
|
|
float calI_k = 1.000f;
|
|||
|
|
float calV_k = 1.000f;
|
|||
|
|
const uint16_t EE_CAL_I_ADDR = 96;
|
|||
|
|
const uint16_t EE_CAL_V_ADDR = 100;
|
|||
|
|
|
|||
|
|
void saveCalNV(){ EEPROM.put(EE_CAL_I_ADDR, calI_k); EEPROM.put(EE_CAL_V_ADDR, calV_k); }
|
|||
|
|
void loadCalNV(){ EEPROM.get(EE_CAL_I_ADDR, calI_k); EEPROM.get(EE_CAL_V_ADDR, calV_k); if(!(calI_k>0.5f && calI_k<1.5f)) calI_k=1.0f;
|
|||
|
|
if(!(calV_k>0.5f && calV_k<1.5f)) calV_k=1.0f; }
|
|||
|
|
|
|||
|
|
void drawCalibStep2(float iDisp, float kI){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setTextSize(2); display.setCursor(17,0); display.print("CALIBRAR");
|
|||
|
|
display.setTextSize(1); display.setCursor(10,18); display.print("AJUSTE A 0.100 AMP");
|
|||
|
|
display.setTextSize(2);display.setCursor(28,28); display.print(iDisp,3); display.print("A");
|
|||
|
|
drawInvertedLabel(35,48,"B1+ B2-");
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawCalibStep3(float vDisp, float kV){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setTextSize(2); display.setCursor(17,0); display.print("CALIBRAR");
|
|||
|
|
display.setTextSize(1); display.setCursor(10,18); display.print("AJUSTE A 10.00 VOL");
|
|||
|
|
display.setTextSize(2);display.setCursor(28,28); display.print(vDisp,2); display.print("V");
|
|||
|
|
drawInvertedLabel(35,48,"B1+ B2-");
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void drawCalibDone(){
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setTextSize(2); display.setCursor(6,0); display.print("COMPLETADO");
|
|||
|
|
display.setTextSize(1); display.setCursor(12,24); display.print("CAL_I "); display.print(calI_k,3);
|
|||
|
|
display.setCursor(12,36); display.print("CAL_V "); display.print(calV_k,3);
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void handleCalibracion(){
|
|||
|
|
static uint8_t calPhase = 0;
|
|||
|
|
static unsigned long calI_okStart = 0, calV_okStart = 0;
|
|||
|
|
static unsigned long calI_outTs = 0, calV_outTs = 0;
|
|||
|
|
static float iFilt = 0.0f, vFilt = 0.0f;
|
|||
|
|
if (calPhase==0 && consumeClick(BTN2)) {
|
|||
|
|
beepOnce(40);
|
|||
|
|
relay1 = false; setRelay(RELAY1, false);
|
|||
|
|
uiMode = MODE_MENU;
|
|||
|
|
registerActivity();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if(calPhase==0){
|
|||
|
|
if(consumeClick(BTN1)){
|
|||
|
|
beepOnce(80);
|
|||
|
|
relay1=true; setRelay(RELAY1,true);
|
|||
|
|
unsigned long t0 = millis();
|
|||
|
|
float vSum=0.0f; uint16_t vCnt=0; float vNow=0.0f, aNow=0.0f;
|
|||
|
|
while(millis()-t0<1000){ readVA(vNow,aNow); vSum+=vNow; vCnt++; delay(10); }
|
|||
|
|
relay1=false; setRelay(RELAY1,false);
|
|||
|
|
float vAvg = vCnt ? (vSum/(float)vCnt) : 0.0f;
|
|||
|
|
if(vAvg>=9.5f && vAvg<=10.5f){ calPhase=1; registerActivity(); }
|
|||
|
|
else{
|
|||
|
|
display.clearDisplay();
|
|||
|
|
display.setTextColor(SSD1306_WHITE);
|
|||
|
|
display.setTextSize(2); display.setCursor(18,0); display.print("ATENCION");
|
|||
|
|
display.setTextSize(1); display.setCursor(8,24); display.print("Medido: "); display.print(vAvg,2); display.print("V");
|
|||
|
|
drawInvertedLabel(6,40,"DESAJUSTE EXCESIVO");
|
|||
|
|
drawInvertedLabel(6,50,"AJUSTE V_CAL ANTES");
|
|||
|
|
display.display();
|
|||
|
|
delaySafe(10000);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
drawCalibStep1();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(calPhase==1){
|
|||
|
|
if(!relay1){ relay1=true; setRelay(RELAY1,true); }
|
|||
|
|
float vNow=0.0f, aNow=0.0f; readVA(vNow,aNow);
|
|||
|
|
iFilt += (aNow - iFilt) * 0.30f;
|
|||
|
|
if(consumeClick(BTN1)){ float v=calI_k+0.01f; if(v>1.20f) v=1.20f; calI_k=v; beepOnce(40); registerActivity(); }
|
|||
|
|
if(consumeClick(BTN2)){ float v=calI_k-0.01f; if(v<0.80f) v=0.80f; calI_k=v; beepOnce(40); registerActivity(); }
|
|||
|
|
float iDisp = iFilt*calI_k;
|
|||
|
|
bool inRange = (iDisp>=0.095f && iDisp<=0.105f);
|
|||
|
|
unsigned long now = millis();
|
|||
|
|
if(inRange){
|
|||
|
|
if(calI_okStart==0) calI_okStart = now;
|
|||
|
|
calI_outTs = 0;
|
|||
|
|
}else{
|
|||
|
|
if(calI_outTs==0) calI_outTs = now;
|
|||
|
|
if(now - calI_outTs >= 150UL){ calI_okStart = 0; }
|
|||
|
|
}
|
|||
|
|
drawCalibStep2(iDisp, calI_k);
|
|||
|
|
if(calI_okStart){
|
|||
|
|
unsigned long dt = now - calI_okStart; if(dt>5000UL) dt=5000UL;
|
|||
|
|
uint16_t w = (uint16_t)(dt*128UL/5000UL);
|
|||
|
|
display.fillRect(0,62,w,2,SSD1306_WHITE);
|
|||
|
|
display.display();
|
|||
|
|
if(dt>=5000UL){ calPhase=2; registerActivity(); }
|
|||
|
|
}else{
|
|||
|
|
display.drawRect(0,62,128,2,SSD1306_WHITE);
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(calPhase==2){
|
|||
|
|
float vNow=0.0f, aNow=0.0f; readVA(vNow,aNow);
|
|||
|
|
if(consumeClick(BTN1)){ float v=calV_k+0.01f; if(v>1.20f) v=1.20f; calV_k=v; beepOnce(40); registerActivity(); }
|
|||
|
|
if(consumeClick(BTN2)){ float v=calV_k-0.01f; if(v<0.80f) v=0.80f; calV_k=v; beepOnce(40); registerActivity(); }
|
|||
|
|
float vDisp = vNow*calV_k;
|
|||
|
|
if(vDisp>=9.90f && vDisp<=10.10f){ if(calV_okStart==0) calV_okStart=millis(); }
|
|||
|
|
else { calV_okStart=0; }
|
|||
|
|
drawCalibStep3(vDisp, calV_k);
|
|||
|
|
if(calV_okStart){
|
|||
|
|
unsigned long dt = millis()-calV_okStart; if(dt>5000UL) dt=5000UL;
|
|||
|
|
uint16_t w = (uint16_t)(dt*128UL/5000UL);
|
|||
|
|
display.fillRect(0,62,w,2,SSD1306_WHITE);
|
|||
|
|
display.display();
|
|||
|
|
if(dt>=5000UL){ saveCalNV(); calPhase=3; calV_okStart=0; registerActivity(); }
|
|||
|
|
} else {
|
|||
|
|
display.drawRect(0,62,128,2,SSD1306_WHITE);
|
|||
|
|
display.display();
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(calPhase>=3){
|
|||
|
|
relay1=false; setRelay(RELAY1,false);
|
|||
|
|
drawCalibDone();
|
|||
|
|
delaySafe(1200);
|
|||
|
|
uiMode = MODE_MENU;
|
|||
|
|
calPhase = 0;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
void loop(){
|
|||
|
|
wdt_reset();
|
|||
|
|
handleOledSleep();
|
|||
|
|
repromod_dualButtonResetHandler();
|
|||
|
|
|
|||
|
|
if(displaySleeping){
|
|||
|
|
delay(10);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch(uiMode){
|
|||
|
|
case MODE_HOME:
|
|||
|
|
handleHomeMode();
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case MODE_MENU:
|
|||
|
|
handleMainMenu();
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case MODE_TEST_CHIP:
|
|||
|
|
handleTestChip_TC();
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case MODE_VOLT:
|
|||
|
|
handleVoltimetro();
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case MODE_LAPTOP:
|
|||
|
|
handleLaptop_TC();
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case MODE_CALIB:
|
|||
|
|
handleCalibracion();
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|