#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// Definición de pines
#define BUTTON_C 15
#define BUTTON_F 4
#define BUTTON_K 16
#define BUTTON_MODE 17
#define BUTTON_SAVE 18
#define LED_ALARM 32
#define LED_FULL 33
#define POT_PIN 34
#define ONE_WIRE_BUS 5
// Configuración del display OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Configuración del sensor
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// Estructura para rangos de temperatura
struct TempRange {
float min;
float max;
};
// Rangos predeterminados para cada unidad
TempRange tempRanges[] = {
{0, 100}, // Celsius
{32, 212}, // Fahrenheit
{273.15, 373.15} // Kelvin
};
// Variables globales
enum TempUnit { CELSIUS, FAHRENHEIT, KELVIN };
enum DisplayMode { MEDICION, HISTORIAL, REFERENCIA, RANGO_MIN, RANGO_MAX };
TempUnit currentUnit = CELSIUS;
DisplayMode currentMode = MEDICION;
float tempHistory[5] = {0.00, 0.00, 0.00, 0.00, 0.00};
int historyIndex = 0;
float referenceTemp = 50.0;
float currentTemp = 0.0;
float userRangeMin = 0.0;
float userRangeMax = 100.0;
// Variables para el control de botones
volatile bool saveRequested = false;
bool lastModeButtonState = HIGH;
// Función para convertir temperatura entre unidades
float convertTemperature(float temp, TempUnit fromUnit, TempUnit toUnit) {
float tempC;
switch(fromUnit) {
case CELSIUS: tempC = temp; break;
case FAHRENHEIT: tempC = (temp - 32) * 5.0/9.0; break;
case KELVIN: tempC = temp - 273.15; break;
}
switch(toUnit) {
case CELSIUS: return tempC;
case FAHRENHEIT: return (tempC * 9.0/5.0) + 32;
case KELVIN: return tempC + 273.15;
default: return tempC;
}
}
// Función para obtener el símbolo de la unidad
String getUnitSymbol(TempUnit unit) {
switch(unit) {
case CELSIUS: return "C";
case FAHRENHEIT: return "F";
case KELVIN: return "K";
default: return "C";
}
}
// Función para guardar temperatura en el historial
void saveTemperature() {
for(int i = 4; i > 0; i--) {
tempHistory[i] = tempHistory[i-1];
}
tempHistory[0] = currentTemp;
}
// Función para verificar si la temperatura está fuera de rango
bool isOutOfRange(float temp) {
float convertedTemp = convertTemperature(temp, CELSIUS, currentUnit);
float currentMin = tempRanges[currentUnit].min;
float currentMax = tempRanges[currentUnit].max;
return (convertedTemp < currentMin || convertedTemp > currentMax);
}
// Función para verificar si la temperatura supera el 80% del rango
bool isAbove80Percent() {
float currentRange = tempRanges[currentUnit].max - tempRanges[currentUnit].min;
float threshold = tempRanges[currentUnit].min + (currentRange * 0.8);
float convertedTemp = convertTemperature(currentTemp, CELSIUS, currentUnit);
return convertedTemp > threshold;
}
// Función para leer el potenciómetro con estabilización
float readStabilizedPot() {
const int numReadings = 10;
float total = 0;
for(int i = 0; i < numReadings; i++) {
total += analogRead(POT_PIN);
delay(1);
}
return total / numReadings;
}
// Funciones de display
void displayMedicion() {
display.setCursor(0, 0);
display.println("MEDICION");
display.setTextSize(2);
display.setCursor(4, 18);
float displayTemp = convertTemperature(currentTemp, CELSIUS, currentUnit);
display.print(displayTemp, 2);
display.print(" ");
display.print(getUnitSymbol(currentUnit));
if(isOutOfRange(currentTemp)) {
display.setTextSize(1);
display.setCursor(4, 45);
display.println("FUERA DE RANGO");
}
}
void displayHistorial() {
display.setCursor(0, 0);
display.setTextSize(1);
display.println("HISTORIAL");
for(int i = 0; i < 5; i++) {
display.setCursor(4, 12 + (i * 10));
float displayTemp = convertTemperature(tempHistory[i], CELSIUS, currentUnit);
display.print(displayTemp, 2);
display.print(" ");
display.print(getUnitSymbol(currentUnit));
}
}
void displayReferencia() {
display.setCursor(0, 0);
display.setTextSize(1);
display.println("REFERENCIA");
display.setTextSize(2);
display.setCursor(4, 18);
float displayRef = convertTemperature(referenceTemp, CELSIUS, currentUnit);
display.print(displayRef, 2);
display.print(" ");
display.print(getUnitSymbol(currentUnit));
}
void displayRangoMin() {
display.setCursor(0, 0);
display.setTextSize(1);
display.println("RANGO MINIMO");
display.setTextSize(2);
display.setCursor(4, 18);
float displayMin = convertTemperature(userRangeMin, CELSIUS, currentUnit);
display.print(displayMin, 2);
display.print(getUnitSymbol(currentUnit));
}
void displayRangoMax() {
display.setCursor(0, 0);
display.setTextSize(1);
display.println("RANGO MAXIMO");
display.setTextSize(2);
display.setCursor(4, 18);
float displayMax = convertTemperature(userRangeMax, CELSIUS, currentUnit);
display.print(displayMax, 2);
display.print(getUnitSymbol(currentUnit));
}
void updateDisplay() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
switch(currentMode) {
case MEDICION: displayMedicion(); break;
case HISTORIAL: displayHistorial(); break;
case REFERENCIA: displayReferencia(); break;
case RANGO_MIN: displayRangoMin(); break;
case RANGO_MAX: displayRangoMax(); break;
}
display.display();
}
// ISR para el botón de guardar
void IRAM_ATTR saveButtonISR() {
saveRequested = true;
}
void setup() {
// Inicialización de pines
pinMode(BUTTON_C, INPUT_PULLUP);
pinMode(BUTTON_F, INPUT_PULLUP);
pinMode(BUTTON_K, INPUT_PULLUP);
pinMode(BUTTON_MODE, INPUT_PULLUP);
pinMode(BUTTON_SAVE, INPUT_PULLUP);
pinMode(LED_ALARM, OUTPUT);
pinMode(LED_FULL, OUTPUT);
// Inicialización del display
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();
// Inicialización del sensor
sensors.begin();
// Configuración de la interrupción para el botón SAVE
attachInterrupt(digitalPinToInterrupt(BUTTON_SAVE), saveButtonISR, FALLING);
Serial.begin(115200);
}
void loop() {
// Lectura de temperatura
sensors.requestTemperatures();
currentTemp = sensors.getTempCByIndex(0);
// Manejo de botones de unidad
if (digitalRead(BUTTON_C) == LOW) {
currentUnit = CELSIUS;
delay(5); // Debounce
}
if (digitalRead(BUTTON_F) == LOW) {
currentUnit = FAHRENHEIT;
delay(5); // Debounce
}
if (digitalRead(BUTTON_K) == LOW) {
currentUnit = KELVIN;
delay(5); // Debounce
}
// Manejo del botón de modo con estado
bool currentModeButtonState = digitalRead(BUTTON_MODE);
if (currentModeButtonState == LOW && lastModeButtonState == HIGH) {
currentMode = static_cast<DisplayMode>((currentMode + 1) % 5);
delay(1); // Debounce
}
lastModeButtonState = currentModeButtonState;
// Procesamiento del botón de guardar
if (saveRequested) {
saveTemperature();
saveRequested = false;
}
// Lectura del potenciómetro según el modo
float potValue = readStabilizedPot();
if (currentMode == REFERENCIA) {
referenceTemp = map(potValue, 0, 4095,
tempRanges[currentUnit].min * 100,
tempRanges[currentUnit].max * 100) / 100.0;
} else if (currentMode == RANGO_MIN) {
float newMin = map(potValue, 0, 4095,
tempRanges[currentUnit].min * 100,
tempRanges[currentUnit].max * 100) / 100.0;
userRangeMin = convertTemperature(newMin, currentUnit, CELSIUS);
} else if (currentMode == RANGO_MAX) {
float newMax = map(potValue, 0, 4095,
tempRanges[currentUnit].min * 100,
tempRanges[currentUnit].max * 100) / 100.0;
if (newMax > convertTemperature(userRangeMin, CELSIUS, currentUnit)) {
userRangeMax = convertTemperature(newMax, currentUnit, CELSIUS);
}
}
// Control de LEDs
if (isAbove80Percent()) {
digitalWrite(LED_FULL, !digitalRead(LED_FULL));
} else {
digitalWrite(LED_FULL, LOW);
}
updateDisplay();
delay(100);
}