Practical case: ESP32 Wi-Fi Freezer Monitor

Practical case: ESP32 Wi-Fi Freezer Monitor — hero

case-device-block-diagram,
.prometeo-educational-note,
.prometeo-device-postcode-section,
.prometeo-device-section-card {
margin: 2.4rem 0;
padding: 1.45rem 1.55rem;
border: 1px solid rgba(148, 163, 184, 0.30);
border-radius: 14px;
background:
linear-gradient(135deg, rgba(30, 41, 59, 0.50), rgba(15, 23, 42, 0.18)),
rgba(17, 24, 39, 0.48);
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.20);
}
.case-objective > h2:first-of-type,
.case-device-block-diagram > h2:first-of-type,
.prometeo-device-postcode-section > h2:first-of-type,
.prometeo-device-section-card > h2:first-of-type {
margin-top: 0;
}
.prometeo-device-section-card > :last-child,
.case-objective > :last-child,
.case-device-block-diagram > :last-child,
.prometeo-device-postcode-section > :last-child {
margin-bottom: 0;
}
.prometeo-device-section-card.prometeo-device-section-card-code {
border-left: 4px solid rgba(56, 189, 248, 0.86);
background:
linear-gradient(135deg, rgba(8, 47, 73, 0.42), rgba(15, 23, 42, 0.18)),
rgba(15, 23, 42, 0.58);
}
.prometeo-device-section-card.prometeo-device-section-card-compact {
padding: 1.2rem 1.35rem;
}
.prometeo-device-section-card pre,
.case-objective pre,
.case-device-block-diagram pre,
.prometeo-device-postcode-section pre {
max-width: 100%;
}
.prometeo-device-postcode-section .prometeo-device-flow-item {
background:
linear-gradient(135deg, rgba(15, 23, 42, 0.50), rgba(30, 41, 59, 0.28)),
rgba(15, 23, 42, 0.28);
}
@media print {
.case-objective,
.case-device-block-diagram,
.prometeo-educational-note,
.prometeo-device-postcode-section,
.prometeo-device-section-card {
background: #fff;
color: #111827;
box-shadow: none;
break-inside: avoid;
page-break-inside: avoid;
}
}

Objective and use case

What you’ll build: A Wi-Fi-enabled freezer temperature monitoring system that reads an analog NTC thermistor, triggers a local audible and visual alarm when temperatures exceed a safe threshold (e.g., > -10°C), and serves real-time data to a local web dashboard with <1s latency.

Why it matters / Use cases

  • Food Safety and Storage: Prevents food spoilage by alerting users immediately if a freezer door is left ajar or if the compressor fails.
  • Laboratory Asset Monitoring: Provides an educational foundation for monitoring sensitive biological samples or chemicals that require strict cold-chain storage.
  • HVAC and Refrigeration Diagnostics: Acts as a prototype diagnostic tool for technicians to log cooling cycles and identify inefficient refrigeration units.
  • Remote Facility Monitoring: Delivers continuous remote visibility into off-site cold storage status without requiring physical inspection.

Expected outcome

  • A deployed microcontroller circuit reading NTC thermistor values with ±0.5°C estimated accuracy.
  • Instantaneous local buzzer and LED alarm activation (under 2 seconds latency) upon a temperature threshold breach.
  • A lightweight local web server hosting a dashboard that renders live temperature graphs updating at 1Hz.

Audience: IoT hobbyists, electronics students, and prototype engineers; Level: Intermediate

Architecture/flow: NTC Thermistor → Microcontroller ADC → Threshold Logic (GPIO Buzzer/LED) → Wi-Fi Access Point/Station → Local Web UI.

Conceptual block diagram

High-level view: what enters the system, what each block processes, and what comes out.

Functional architecture

Local button

ESP32 BLE

Advertising packet

Status LED

Phone scanner

Conceptual flow: local configuration, BLE advertising and phone-side reading.

Validation path

Source code

PlatformIO build

Flash

Serial monitor

Conceptual summary of the tools used to check the published ESP32 project.

Educational validation note

Before publication, this case passed the Prometeo automated validation gate with status PASS. For this ESP32 DevKitC profile, the project was checked as a PlatformIO project: the validator extracted platformio.ini and src/main.cpp, created a temporary project and ran pio run against platform = espressif32, board = esp32dev and framework = arduino. It also checked article structure, copy/paste-safe ASCII command options, and unsupported stacks such as direct ESP-IDF or non-scoped ESP32 boards.

Published validation evidence

  • Automatic result: PASS.
  • Parsed structure: 4 sections, 1 tables and 2 code blocks detected before publication.
  • Checked code: 1 PlatformIO config + 1 ESP32 source/pio run.
  • Supported catalog: the article text was checked against Prometeo’s validation-capable device profiles, and unsupported stacks block publication.
  • Report findings: no blocking findings.

This validation confirms syntax and tool compatibility for the published code, but it does not replace physical testing on your exact ESP32 DevKitC board, wiring, power supply and local WiFi environment.

Educational safety note

This project is a low-voltage educational prototype, not a certified food-safety alarm. Verify the pinout of your exact ESP32 DevKitC board and temperature sensor, keep all GPIO signals within 3.3 V limits, and do not connect the circuit to mains wiring or to the freezer’s internal power system. Use a reliable USB supply, protect wiring from moisture, and disconnect power before changing the setup.

Prerequisites

Before beginning this tutorial, ensure you have the following ready:
* Software: Visual Studio Code with the PlatformIO IDE extension installed.
* Knowledge: Basic understanding of C++ programming, voltage dividers, and fundamental Wi-Fi networking concepts.
* Network: Access to a standard 2.4 GHz Wi-Fi network (ESP32 microcontrollers do not support 5 GHz networks).


Materials

To guarantee the code and wiring behave exactly as described, use the precise components listed below:

ComponentSpecification / Exact ModelQuantity
MicrocontrollerESP32 DevKitC (38-pin or 30-pin variant)1
Temperature Sensor10 kOhm NTC thermistor (B-value ~3950)1
Fixed Resistor10 kOhm (1/4 Watt, for the voltage divider)1
Current Limiting Resistor220 Ohm (1/4 Watt, for the LED)1
Visual IndicatorStatus LED (Standard 5mm, Red preferred)1
Audible IndicatorActive buzzer (3.3V or 5V compatible)1
PrototypingBreadboard and assorted jumper wires1 set
Power/DataMicro-USB data cable1

Setup/Connection

The hardware setup requires carefully wiring the NTC thermistor in a voltage divider configuration, alongside the output peripherals.

Important Engineering Note regarding ADC: We use GPIO34 for the thermistor. GPIO34 belongs to the ESP32’s ADC1 block. Do not use ADC2 pins (like GPIO4 or GPIO2) because ADC2 is utilized by the Wi-Fi driver and will fail to read analog voltages while the Wi-Fi radio is active.

1. NTC Thermistor Voltage Divider

  • Connect one leg of the 10 kOhm fixed resistor to the 3.3V pin on the ESP32 DevKitC.
  • Connect the other leg of the 10 kOhm fixed resistor to a blank row on the breadboard.
  • Connect one leg of the 10 kOhm NTC thermistor to that same breadboard row.
  • Connect the other leg of the NTC thermistor to GND.
  • Connect a jumper wire from the junction between the fixed resistor and the thermistor to GPIO34 on the ESP32.

2. Active Buzzer

  • Connect the positive pin (often marked with a + or the longer leg) of the active buzzer to GPIO26.
  • Connect the negative pin to GND.

3. Status LED

  • Connect the anode (longer leg) of the status LED to one leg of the 220 Ohm resistor.
  • Connect the other leg of the 220 Ohm resistor to GPIO27.
  • Connect the cathode (shorter leg) of the LED to GND.

4. USB Driver Setup

  • Depending on the exact manufacturer of your ESP32 DevKitC, it will feature either a CP210x or CH34x USB-to-UART bridge.
  • If your computer does not automatically recognize the board when plugged in, download and install the appropriate CP210x or CH34x drivers for your operating system.

Validated Code

The project relies on two files within the PlatformIO environment. Create a new PlatformIO project selecting the esp32dev board and the Arduino framework. Replace the contents of the generated files with the code below.

platformio.ini

This configuration file defines the hardware target, framework, and serial monitor baud rate.

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200

src/main.cpp

This is the primary source code. Ensure you update the WIFI_SSID and WIFI_PASSWORD variables to match your local network credentials before compiling.

Public preview of the validated file. The complete source is shown to members and in PDF/Print.

#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>

// ------------------------------------------------------------------
// Network Credentials
// ------------------------------------------------------------------
const char* WIFI_SSID = "LabNetwork";
const char* WIFI_PASSWORD = "LabPassword123";

// ------------------------------------------------------------------
// Pin Definitions
// ------------------------------------------------------------------
const int NTC_PIN = 34;      // ADC1_CH6 - Safe to use with Wi-Fi
const int BUZZER_PIN = 26;   // Output for active buzzer
const int LED_PIN = 27;      // Output for status LED

// ------------------------------------------------------------------
// Thermistor & Steinhart-Hart Parameters
// ------------------------------------------------------------------
const float SERIES_RESISTOR = 10000.0;    // 10k Ohm fixed resistor
const float NOMINAL_RESISTANCE = 10000.0; // 10k Ohm NTC at 25 degrees C
const float NOMINAL_TEMPERATURE = 25.0;   // Nominal temperature in Celsius
const float B_COEFFICIENT = 3950.0;       // Beta value of the thermistor
const float ALARM_THRESHOLD = -10.0;      // Alarm triggers if temp rises above -10.0 C

// ------------------------------------------------------------------
// Global Variables
// ------------------------------------------------------------------
WebServer server(80);
float currentTemperature = 0.0;
bool isAlarmActive = false;

// Timing variables for non-blocking alarm
unsigned long previousMillis = 0;
const long blinkInterval = 500; // 500ms toggle rate for buzzer/LED
bool toggleState = false;

// ------------------------------------------------------------------
// Function Prototypes
// ------------------------------------------------------------------
void connectWiFi();
void handleRoot();
float readTemperature();
void handleAlarmLogic();

// ------------------------------------------------------------------
// Setup
// ------------------------------------------------------------------
void setup() {
    Serial.begin(115200);
    delay(1000); // Allow serial monitor to stabilize

    // Initialize output pins
    pinMode(BUZZER_PIN, OUTPUT);
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(BUZZER_PIN, LOW);
    digitalWrite(LED_PIN, LOW);

    // Initialize ADC
    analogReadResolution(12); // 12-bit ADC (0 - 4095)

    Serial.println("\n--- Freezer Temperature Alarm System ---");

    connectWiFi();

    // Setup Web Server Routes
    server.on("/", handleRoot);
    server.begin();
    Serial.println("Web server started.");
}

// ------------------------------------------------------------------
// Main Loop
// ------------------------------------------------------------------
void loop() {
    server.handleClient(); // Listen for incoming HTTP requests

    // Read temperature every cycle
    currentTemperature = readTemperature();

    // Evaluate and execute alarm logic
    handleAlarmLogic();

    // Small delay to stabilize ADC reads and prevent watchdog resets
    delay(50);
}
// ...

🔒 This content is premium. With the monthly subscription (7-day free trial) you can unlock the full didactic material and the print-ready PDF pack.🔓 Unlock it — 7-day free trial
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>

// ------------------------------------------------------------------
// Network Credentials
// ------------------------------------------------------------------
const char* WIFI_SSID = "LabNetwork";
const char* WIFI_PASSWORD = "LabPassword123";

// ------------------------------------------------------------------
// Pin Definitions
// ------------------------------------------------------------------
const int NTC_PIN = 34;      // ADC1_CH6 - Safe to use with Wi-Fi
const int BUZZER_PIN = 26;   // Output for active buzzer
const int LED_PIN = 27;      // Output for status LED

// ------------------------------------------------------------------
// Thermistor & Steinhart-Hart Parameters
// ------------------------------------------------------------------
const float SERIES_RESISTOR = 10000.0;    // 10k Ohm fixed resistor
const float NOMINAL_RESISTANCE = 10000.0; // 10k Ohm NTC at 25 degrees C
const float NOMINAL_TEMPERATURE = 25.0;   // Nominal temperature in Celsius
const float B_COEFFICIENT = 3950.0;       // Beta value of the thermistor
const float ALARM_THRESHOLD = -10.0;      // Alarm triggers if temp rises above -10.0 C

// ------------------------------------------------------------------
// Global Variables
// ------------------------------------------------------------------
WebServer server(80);
float currentTemperature = 0.0;
bool isAlarmActive = false;

// Timing variables for non-blocking alarm
unsigned long previousMillis = 0;
const long blinkInterval = 500; // 500ms toggle rate for buzzer/LED
bool toggleState = false;

// ------------------------------------------------------------------
// Function Prototypes
// ------------------------------------------------------------------
void connectWiFi();
void handleRoot();
float readTemperature();
void handleAlarmLogic();

// ------------------------------------------------------------------
// Setup
// ------------------------------------------------------------------
void setup() {
    Serial.begin(115200);
    delay(1000); // Allow serial monitor to stabilize

    // Initialize output pins
    pinMode(BUZZER_PIN, OUTPUT);
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(BUZZER_PIN, LOW);
    digitalWrite(LED_PIN, LOW);

    // Initialize ADC
    analogReadResolution(12); // 12-bit ADC (0 - 4095)

    Serial.println("\n--- Freezer Temperature Alarm System ---");

    connectWiFi();

    // Setup Web Server Routes
    server.on("/", handleRoot);
    server.begin();
    Serial.println("Web server started.");
}

// ------------------------------------------------------------------
// Main Loop
// ------------------------------------------------------------------
void loop() {
    server.handleClient(); // Listen for incoming HTTP requests

    // Read temperature every cycle
    currentTemperature = readTemperature();

    // Evaluate and execute alarm logic
    handleAlarmLogic();

    // Small delay to stabilize ADC reads and prevent watchdog resets
    delay(50);
}

// ------------------------------------------------------------------
// Functions
// ------------------------------------------------------------------

void connectWiFi() {
    Serial.print("Connecting to Wi-Fi: ");
    Serial.println(WIFI_SSID);

    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.println("\nWi-Fi Connected!");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());
}

float readTemperature() {
    // Read the analog value (0-4095)
    int adcValue = analogRead(NTC_PIN);

    // Prevent division by zero if pin is shorted to Ground or 3.3V
    if (adcValue == 0) return -99.0;
    if (adcValue >= 4095) return 99.0;

    // Calculate NTC Resistance
    // Based on Voltage Divider: Vout = Vcc * (R_NTC / (R_NTC + R_SERIES))
    // Derives to: R_NTC = R_SERIES * (ADC / (4095 - ADC))
    float ntcResistance = SERIES_RESISTOR * ((float)adcValue / (4095.0 - (float)adcValue));

    // Apply Steinhart-Hart equation
    float steinhart;
    steinhart = ntcResistance / NOMINAL_RESISTANCE;       // (R/Ro)
    steinhart = log(steinhart);                           // ln(R/Ro)
    steinhart /= B_COEFFICIENT;                           // 1/B * ln(R/Ro)
    steinhart += 1.0 / (NOMINAL_TEMPERATURE + 273.15);    // + (1/To)
    steinhart = 1.0 / steinhart;                          // Invert
    steinhart -= 273.15;                                  // Convert Kelvin to Celsius

    return steinhart;
}

void handleAlarmLogic() {
    if (currentTemperature > ALARM_THRESHOLD) {
        isAlarmActive = true;

        // Non-blocking toggle for LED and Buzzer
        unsigned long currentMillis = millis();
        if (currentMillis - previousMillis >= blinkInterval) {
            previousMillis = currentMillis;
            toggleState = !toggleState;
            digitalWrite(LED_PIN, toggleState ? HIGH : LOW);
            digitalWrite(BUZZER_PIN, toggleState ? HIGH : LOW);
        }
    } else {
        isAlarmActive = false;
        // Ensure outputs are turned off when temperature is safe
        digitalWrite(LED_PIN, LOW);
        digitalWrite(BUZZER_PIN, LOW);
        toggleState = false;
    }
}

void handleRoot() {
    // Construct a simple, auto-refreshing HTML dashboard
    String html = "<!DOCTYPE html><html><head>";
    html += "<meta name='viewport' content='width=device-width, initial-scale=1.0'>";
    html += "<meta http-equiv='refresh' content='5'>"; // Auto-refresh every 5 seconds
    html += "<style>";
    html += "body { font-family: Arial, sans-serif; text-align: center; margin-top: 50px; }";
    html += ".temp { font-size: 3em; font-weight: bold; }";
    html += ".status-ok { color: green; font-size: 2em; }";
    html += ".status-alarm { color: red; font-size: 2em; font-weight: bold; animation: blinker 1s linear infinite; }";
    html += "@keyframes blinker { 50% { opacity: 0; } }";
    html += "</style></head><body>";

    html += "<h1>Freezer Monitor Dashboard</h1>";
    html += "<div class='temp'>" + String(currentTemperature, 1) + " &deg;C</div>";

    if (isAlarmActive) {
        html += "<div class='status-alarm'>WARNING: TEMPERATURE HIGH</div>";
    } else {
        html += "<div class='status-ok'>STATUS: NORMAL</div>";
    }

    html += "</body></html>";

    // Send the response to the client
    server.send(200, "text/html", html);
}

Find this product and/or books on this topic on Amazon

Go to Amazon

As an Amazon Associate, I earn from qualifying purchases. If you buy through this link, you help keep this project running.

Quick Quiz

Question 1: What is the primary function of the system being built?




Question 2: Which type of temperature sensor is used in this project?




Question 3: What is the example safe temperature threshold mentioned for triggering the alarm?




Question 4: What is the expected latency for the real-time data served to the local web dashboard?




Question 5: How does the system alert users locally when a temperature threshold is breached?




Question 6: What is the estimated accuracy of the NTC thermistor readings in the expected outcome?




Question 7: How does the system help prevent food spoilage?




Question 8: What is one of the laboratory use cases for this monitoring system?




Question 9: How can HVAC technicians use this system as a diagnostic tool?




Question 10: What benefit does the system provide for remote facility monitoring?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Telecommunications Electronics Engineer and Computer Engineer (official degrees in Spain).

Follow me:
Scroll to Top