Siddhant Kumar
Project 068 · Industrial

Warehouse Climate Logger.

Records temperature and humidity across a warehouse to a tamper-evident, audit-ready log — proving storage stayed in spec and warning before it drifts out.

Intermediate 10–16 hours 32 min read SensorsLogsStorage
Jump to source Bill of materials
Warehouse Climate Logger — reference build illustration MCU VCC · GND · SIG · NC
Difficulty
Intermediate
Build time
10–16 hours
Indicative cost
₹3,000 – ₹4,800
Platform
ESP32 DevKit V1 (ESP-WROOM-32)
Category
Industrial
Last updated
28 July 2026
Contents — 26 sections

Project Overview

Records temperature and humidity across a warehouse to a tamper-evident, audit-ready log — proving storage stayed in spec and warning before it drifts out.

Many goods — pharmaceuticals, food, electronics, documents, museum pieces — must be stored within a temperature and humidity range, and the person storing them must be able to prove the conditions were maintained. A regulator, a customer, or an insurer will not take "it was fine" on trust; they want a continuous, trustworthy record. Yet warehouses are large and their climate varies from the loading dock to the back corner to the roof space, so a single wall thermometer proves nothing. This project logs temperature and humidity at multiple points across a warehouse to a tamper-evident, audit-ready record — proving storage stayed in spec, and alerting before it drifts out of it.

It places sensors where the warehouse is actually at risk — near doors, in the warmest and coldest corners, high and low (warm air rises), in the product zones that matter — because compliance is judged on the worst conditions goods experienced, not a flattering average. It timestamps every reading against a real-time clock, writes it to an append-only log, and mirrors it off-device, so the record is trustworthy and survives. Alongside temperature and humidity it derives the metrics compliance actually uses: excursions (any time-out-of-range, with duration and peak), mean kinetic temperature for temperature-sensitive goods, and dew point where condensation risk matters.

Beyond proving the past, it protects the present: it alarms the moment a zone drifts toward its limit — a door left open, HVAC failing, a humid spell — so someone can act before goods are damaged, and it runs on battery backup and local logging so a power cut (exactly when incidents happen) never leaves a gap. It exports the audit trail buyers and inspectors require. It is honest that a DIY logger is not automatically a certified/validated compliance instrument where regulations demand one (pharma GDP, for instance, has specific requirements), and that sensor placement and calibration decide everything. But as a multi-point, tamper-evident, alerting climate logger, it does exactly what regulated storage needs: it keeps an honest, defensible record that conditions stayed in spec, and warns in time when they might not.

Pallet racking in a distribution warehouse
Multi-point sensing across a warehouse represents the true envelope of storage conditions. Photograph sourced from Wikimedia Commons — Warehouse racking.jpg. Reused under the licence stated on that page; please check it before republishing.

What this project does

  • Logs temperature and humidity at multiple warehouse points
  • Places sensors at the worst/most-variable locations, not one average spot
  • Timestamps to a tamper-evident, append-only log, mirrored off-device
  • Derives excursions (duration/peak), mean kinetic temperature and dew point
  • Alarms before a zone drifts out of range
  • Rides power/network outages on battery + local logging
  • Exports an audit-ready trail for inspectors/customers

Real-World Applications

SettingHow it is used
Pharma / GDP storageTemperature/humidity compliance logging with excursions and MKT for medicine warehouses (supplementary to validated systems).
Food / FMCG warehousingProving ambient storage conditions and alerting before spoilage risk.
Electronics / documents / archivesHumidity/temperature control and audit for moisture- or heat-sensitive goods.
Museums / sensitive storageStable-climate monitoring with dew-point and excursion records.

Deployment contexts where a build of this kind earns its keep.

Features & Capabilities

  • Multi-point sensing representing the whole warehouse
  • Tamper-evident, timestamped compliance log (local + mirror)
  • MKT, excursion and dew-point metrics
  • Proactive alerts before goods are at risk
  • Outage ride-through (battery + local log)
  • Exportable audit trail
  • Honest scope vs certified/validated instruments

Difficulty, Time & Required Skills

AttributeValue
Difficulty levelIntermediate
Estimated completion time10–16 hours
Indicative build cost₹3,000 – ₹4,800
Primary disciplineIndustrial
Reference platformESP32 DevKit V1 (ESP-WROOM-32)

Skills you should have (or will pick up)

  • Multi-point temperature/humidity sensing and placement
  • Tamper-evident logging with a real-time clock and off-device mirror
  • Excursion, MKT and dew-point computation
  • Proactive alerting and outage ride-through
  • Audit-trail export and compliance framing

Bill of Materials

Every part below is commonly available from Indian and international hobby-electronics suppliers. Prices are indicative 2026 retail figures in Indian rupees and will drift — treat them as a budgeting guide, not a quotation.

ComponentKey specificationQtyApprox. cost
ESP32 DevKit V1 (ESP-WROOM-32)
Wi-Fi transmit bursts peak near 500 mA — size the regulator accordingly.
Dual-core Xtensa LX6 @ 240 MHz, 520 KB SRAM, 4 MB flash, Wi-Fi 802.11 b/g/n + BLE 4.2, 34 GPIO, 18× 12-bit ADC, 2× 8-bit DAC1₹450
SHT31-D temperature + humidity sensor
The built-in heater burns off condensation — essential for outdoor or greenhouse use.
−40 to +125 °C ±0.2 °C, 0–100 %RH ±2 %, 1 Hz–10 Hz, on-chip heater4₹2,480
DHT22 / AM2302 temperature + humidity sensor
Needs a 4.7 kΩ pull-up on the data line and 2 s between reads.
−40 to +80 °C ±0.5 °C, 0–100 %RH ±2 %, 0.5 Hz sample rate, single-wire digital1₹250
Reed switch + magnet pair (door contact)
Wire it normally-closed so a cut cable reads the same as an open door — fail-safe by construction.
NO contact, 10–15 mm operating gap, 100 V / 0.5 A, > 10⁷ operations1₹55
DS3231 precision RTC + CR2032 backup
Vastly better than DS1307; some boards trickle-charge a non-rechargeable cell — cut that resistor.
±2 ppm (±1 min/year), temperature-compensated crystal, alarms, 32 kHz out1₹180
microSD card 32 GB A1 class
For 24/7 loggers buy a high-endurance card — normal cards die in months.
A1 rated, 10 MB/s random write, UHS-I, endurance-grade recommended1₹450
Active piezo buzzer 5 V
Active buzzers make tone on DC; passive ones need a PWM carrier.
85 dB at 10 cm, 2.3 kHz resonance, 12 mm diameter1₹25
18650 Li-ion cell 3400 mAh + holder
Never charge below 0 °C; always use a protected cell or a BMS.
3.7 V nominal, 4.2 V full, 3400 mAh, ~12.6 Wh, 2 C discharge1₹450
Multi-point T/RH sensors
Placement at worst points is critical
SHT31 (accurate) at door/corner/high/low/product zones5₹4,500
Door sensorsReed contacts logging door openings (a common excursion cause)2₹400
Backup battery + RTC18650 backup and DS3231 RTC for outage ride-through and accurate time1₹300
Mirror/host
Use existing infrastructure
Server/cloud for the off-device audit trail1

Estimated total: ₹9,540, excluding tools, shipping and consumables.

Tools and consumables

  • Soldering iron (temperature controlled, 350 °C) with 0.8 mm 60/40 or lead-free solder
  • Digital multimeter — continuity, DC volts and current ranges
  • Wire strippers, flush cutters and a small set of precision screwdrivers
  • Heat-shrink tubing and a heat gun (or a lighter, carefully)
  • A laptop with a USB port and the toolchain listed above

Hardware Specifications

PartSpecificationSupplyInterfaceReference
ESP32 DevKit V1 (ESP-WROOM-32)Dual-core Xtensa LX6 @ 240 MHz, 520 KB SRAM, 4 MB flash, Wi-Fi 802.11 b/g/n + BLE 4.2, 34 GPIO, 18× 12-bit ADC, 2× 8-bit DAC3.3 V logic / 5 V USBUART, SPI, I²C, I²S, CAN, PWMDatasheet
SHT31-D temperature + humidity sensor−40 to +125 °C ±0.2 °C, 0–100 %RH ±2 %, 1 Hz–10 Hz, on-chip heater2.4–5.5 VI²C (0x44/0x45)Datasheet
DHT22 / AM2302 temperature + humidity sensor−40 to +80 °C ±0.5 °C, 0–100 %RH ±2 %, 0.5 Hz sample rate, single-wire digital3.3–6 V1-wire proprietaryDatasheet
Reed switch + magnet pair (door contact)NO contact, 10–15 mm operating gap, 100 V / 0.5 A, > 10⁷ operationspassiveDigital with pull-upDatasheet
DS3231 precision RTC + CR2032 backup±2 ppm (±1 min/year), temperature-compensated crystal, alarms, 32 kHz out2.3–5.5 VI²C (0x68)Datasheet
microSD card 32 GB A1 classA1 rated, 10 MB/s random write, UHS-I, endurance-grade recommended3.3 VSDIO / SPIDatasheet
Active piezo buzzer 5 V85 dB at 10 cm, 2.3 kHz resonance, 12 mm diameter3–5 VDigital / PWMDatasheet
18650 Li-ion cell 3400 mAh + holder3.7 V nominal, 4.2 V full, 3400 mAh, ~12.6 Wh, 2 C discharge3.0–4.2 VHolder / spot-welded tabsDatasheet

Consolidated electrical and interface specifications for every active part in the build.

Power Budget & Supply Sizing

Add up the typical active current of every part, then size the supply with at least 50 % headroom so transmit bursts and motor inrush never brown out the controller.

LoadSupply railTypical current (mA)Notes
ESP32 DevKit V1 (ESP-WROOM-32)3.3 V logic / 5 V USB160Wi-Fi transmit bursts peak near 500 mA — size the regulator accordingly.
SHT31-D temperature + humidity sensor2.4–5.5 V6The built-in heater burns off condensation — essential for outdoor or greenhouse use.
DHT22 / AM2302 temperature + humidity sensor3.3–6 V1.5Needs a 4.7 kΩ pull-up on the data line and 2 s between reads.
DS3231 precision RTC + CR2032 backup2.3–5.5 V0.2Vastly better than DS1307; some boards trickle-charge a non-rechargeable cell — cut that resistor.
microSD card 32 GB A1 class3.3 V100For 24/7 loggers buy a high-endurance card — normal cards die in months.
Active piezo buzzer 5 V3–5 V30Active buzzers make tone on DC; passive ones need a PWM carrier.

Summed typical draw is 297.7 mA. With a 1.5× design margin the supply should deliver at least 500 mA continuously at the stated rail voltage.

Software Requirements & Development Environment

Reference toolchain: Arduino IDE 2.3.x with the ESP32 board package 3.x (or PlatformIO on VS Code). Anything newer normally works; anything older may lack the board definitions used here.

  • Install the Arduino IDE 2.3.x (or PlatformIO if you prefer a real editor and dependency locking).
  • Add https://espressif.github.io/arduino-esp32/package_esp32_index.json under File → Preferences → Additional Board Manager URLs, then install esp32 from the Boards Manager.
  • Set the correct port under Tools → Port. On Linux add yourself to the dialout group: sudo usermod -aG dialout $USER and log out and back in.
  • Open the Serial Monitor at 115200 baud — every sketch here logs its state there.
  • Keep File → Preferences → Show verbose output during: compilation switched on while you are debugging build errors.

Required libraries

LibraryWhy it is neededInstall
WiFi (ESP32 core) bundledStation/AP connection management for the ESP32.Bundled with the ESP32 Arduino core
PubSubClient 2.8Lightweight MQTT 3.1.1 client for constrained devices.Library Manager → "PubSubClient" by Nick O'Leary
Adafruit BME280 2.2.xCompensation maths for the Bosch pressure/humidity/temperature sensor.Library Manager → "Adafruit BME280 Library"
Adafruit Unified Sensor 1.1.xCommon sensor event abstraction; a dependency of most Adafruit drivers.Library Manager → "Adafruit Unified Sensor"
NTPClient / configTime bundledWall-clock time from an NTP server for timestamping.Bundled (`configTime()` on ESP32)
SQLite 3.45+Zero-configuration embedded database for local logs.Bundled with Python (`import sqlite3`)
Preferences (NVS) bundledWear-levelled key/value storage in ESP32 flash for settings.Bundled with the ESP32 core

Block Diagram

The block diagram shows the functional decomposition of the system — what senses, what decides, what acts, and where the data ends up.

Warehouse Climate Logger — system block diagramFunctional block diagram of the Warehouse Climate Logger system. Sense zonesT/RH ×Nworst pointsDoorsopeningsRecordESP32 + RTCtimestampAppend-onlylocal + mirrorMKT/dew/excursioncomputedProtectAlarmbefore out-of-rangeRide-throughbattery+logAuditExportaudit trailrightrightnone
Warehouse Climate Logger — system block diagram

Circuit Diagram & Wiring

Every signal line in the build is shown below, followed by a pin-by-pin connection table you can work through with a multimeter in hand.

Warehouse Climate Logger — wiring schematicConnection schematic showing which controller pin drives each peripheral. Sensors / InputsControllerActuators / OutputsESP32 DevKit V1(ESP-WROOM-32)3.3 V logic / 5 V USBSHT31 ×NGPIO 21/22 (+mux)Zone temp/humidity(I²C)Door reedsGPIO 34/35Door open/closeDS3231 RTCGPIO 21/22TimestampsmicroSDGPIO 18/19/23/5Append-only locallogBuzzerGPIO 13Local alarmWi-FiOff-device mirrorBackup batteryADCPower supervision
Warehouse Climate Logger — wiring schematic
PeripheralPeripheral pinController pinSignal
SHT31 ×NSDA/SCLGPIO 21/22 (+mux)Zone temp/humidity (I²C)
Door reedsNCGPIO 34/35Door open/close
DS3231 RTCSDA/SCLGPIO 21/22Timestamps
microSDSPIGPIO 18/19/23/5Append-only local log
BuzzerINGPIO 13Local alarm
Wi-Fion-chipOff-device mirror
Backup batterysenseADCPower supervision

Wire one row at a time and tick it off — most "it does not work" reports trace back to a single swapped pair.

Wiring explanation

  • Place accurate T/RH sensors at the warehouse's worst/most-variable points (near doors, warm/cold corners, high and low, product zones), not a single convenient wall.
  • Use an I²C mux or addressable sensors for multiple SHT31s; map each to its location.
  • Give the RTC a coin-cell backup for accurate timestamps, and log door openings (a common excursion cause).
  • Power from mains with battery backup and sense the supply so outages are ridden through and logged.
  • Mirror the append-only log off-device so the record survives tampering or loss.
An ESP32 development board with the ESP-WROOM-32 module and USB connector
ESP32 module timestamping every zone to a tamper-evident, mirrored compliance log. Photograph sourced from Wikimedia Commons — ESP32 Espressif ESP-WROOM-32 Dev Board.jpg. Reused under the licence stated on that page; please check it before republishing.

System Architecture

Read the stack from the bottom up: physical hardware, the firmware that drives it, the transport that moves data off the device, and the software a human actually looks at.

Warehouse Climate Logger — architecture stackLayered architecture from hardware to user interface. Hardware layerESP32 DevKit V1 (ESP-WROOM-32) · SHT31-D temperature + humidity sensor ·DHT22 / AM2302 temperature + humidity sensorDriver layerwifi · pubsub · bme · unifiedApplication logicsampling loop · filtering · thresholds · state machineTransport layerWi-Fi + MQTT; local SD is the source of truth · TLS · retry and backoffPresentation layerdashboard · mobile notifications · historical charts
Warehouse Climate Logger — architecture stack

Working Principle

A compliance climate logger exists to answer one question convincingly: did storage stay within spec, and can you prove it? "Prove" is the operative word, because in regulated or contractual storage, an unprovable claim is treated as a failure — a customer or inspector who cannot see a trustworthy record must assume the worst. So the logger's design centres on producing a record that is representative (of the whole warehouse, not one flattering spot), trustworthy (tamper-evident and timestamped), complete (no gaps, even through outages), and audit-ready (exportable in a form an inspector accepts).

Being representative means multi-point sensing at the worst locations. A warehouse is not one temperature: it is warm near the roof and the loading dock, cool in a shaded back corner, humid near a leaky door, and the goods in each zone experience their local conditions. Compliance is judged on the worst conditions any product experienced, so a single sensor — especially one placed somewhere convenient and benign — proves nothing and can hide a real excursion. The logger therefore places sensors deliberately at the door, the warmest and coldest corners, high and low, and in the product zones, so the record reflects the true envelope of conditions, and it logs door openings because an open door is the commonest cause of a local excursion.

Being trustworthy and complete mirrors the discipline of any compliance record. Every reading is timestamped against a backed-up real-time clock and written to append-only storage (you can add, not silently rewrite), with a sequence/hash so a deleted or altered entry is detectable, and the log is mirrored off-device so it survives tampering or loss of the unit. And because power and network fail precisely during the incidents you most need to document, the logger runs on battery backup and keeps logging locally through outages, syncing the backlog on recovery — a compliance logger that goes blind during a power cut is blind during the very event it exists to record.

On top of the raw log, the logger computes the metrics compliance actually uses, so the record is not just data but evidence. An excursion is any continuous period out of range, characterised by its start, duration and peak — because how far and how long matter, not merely that it happened. Mean kinetic temperature summarises a fluctuating temperature history into the single effective temperature the goods experienced, weighting warm periods more heavily (the standard the regulated cold/ambient chains use precisely because degradation accelerates with temperature). Dew point flags condensation risk where humidity control matters. And beyond documenting the past, the logger protects the present with proactive alarms the moment a zone drifts toward its limit, so a person can act — close the door, fix the HVAC — before goods are damaged, not after. The design is candid that a homebrew logger is not automatically a certified/validated instrument where regulations (e.g. pharmaceutical GDP) demand specific, qualified equipment, and that sensor calibration and placement bound its accuracy. But within that honest frame, it delivers exactly what regulated storage needs: an honest, representative, tamper-evident, complete and audit-ready record that conditions stayed in spec — and a timely warning when they might not.

The maths behind it

Mean kinetic temperature

plainMean kinetic temperature
MKT = (ΔH/R) / −ln[ (Σ e^(−ΔH/(R·T_i))) / n ]

  T_i absolute temps (K) of each interval,
  ΔH ≈ 83 kJ/mol, R = 8.314 J/mol·K

Warm periods dominate the average (degradation is
exponential in T) — the standard for temp-sensitive goods.

Excursion + dew point

plainExcursion + dew point
Excursion (per representative zone vs limits):
  in_excursion while T/RH out of range
  record start, end, duration, peak

Dew point from T, RH (Magnus):
  γ = ln(RH/100) + a·T/(b+T)
  Td = b·γ/(a−γ)
  condensation risk if surfaces near/below Td.

Representative (worst-case) evaluation

plainRepresentative (worst-case) evaluation
Judge compliance on the worst zone:
  T_rep = max over zones (for over-temp)
  RH_rep = max over zones (for humidity)

A single benign sensor hides excursions the goods actually
saw — use the worst point, not an average.

Program Flowchart

The firmware is a single cooperative loop. Nothing blocks for long, so networking, sensing and the user interface all stay responsive.

Warehouse Climate Logger — firmware flowchartControl flow through the main program loop. Sample zones + doorsTimestamp; append to log;mirrorAny zone near/out ofrange?Alarm; start/continue excursionUpdate MKT/dewAlarm; start/continueexcursionUpdate MKT/dewWait interval
Warehouse Climate Logger — firmware flowchart

Assembly Instructions

Build on a breadboard first and only commit to solder once the whole system has run for an hour without a fault.

  1. Place sensors at the worst points and set up time

    Mount accurate T/RH sensors at the door, warm/cold corners, high/low and product zones, mapped to locations; add door sensors. Set up the RTC with coin-cell backup for accurate timestamps.

  2. Set up tamper-evident logging

    Define an append-only, sequence-numbered log to local storage, mirrored off-device, and run on mains with battery backup so outages do not create gaps.

  3. Compute metrics and alerts

    Compute excursions, MKT and dew point, and configure proactive alarms before a zone drifts out of range, plus audit export.

Step-by-Step Implementation Guide

Work through these in order. Each step ends in something you can observe, so a failure is always localised to the step you just finished.

  1. Log representatively and compute metrics

    Each interval, read all zones (and doors), append timestamped records, evaluate the worst zone against limits for excursions, and update MKT and dew point.

    cppclimate-compliance.ino
    struct MKT { double sumExp=0; uint32_t n=0; } mkt;
    const double DH_R = 83000.0/8.314;
    
    void mktAdd(float tC){ mkt.sumExp += exp(-DH_R/(tC+273.15)); mkt.n++; }
    float mktValue(){ return mkt.n? (float)(DH_R/-log(mkt.sumExp/mkt.n)-273.15):NAN; }
    
    float dewPoint(float t, float rh){
      const float a=17.27f, b=237.7f;
      float g = logf(rh/100.0f) + a*t/(b+t);
      return b*g/(a-g);
    }
    
    struct Excursion { bool active=false; uint32_t start=0; float peak=-99; } exc;
    
    void evaluate(float *zoneT, int n, float tLimit, uint32_t now){
      float tRep = -99; for(int i=0;i<n;i++) if(zoneT[i]>tRep) tRep=zoneT[i]; // worst
      mktAdd(tRep);
      if (tRep > tLimit){
        if (!exc.active){ exc.active=true; exc.start=now; exc.peak=tRep; }
        if (tRep>exc.peak) exc.peak=tRep;
        if (now-exc.start > GRACE) raiseAlarm("temp excursion", tRep, exc.peak);
      } else if (exc.active){
        logExcursion(exc.start, now, exc.peak);   // start/end/duration/peak
        exc.active=false; exc.peak=-99;
      }
    }
    float tRep = -99; for(int i=0;i<n;i++) if(zoneT[i]>tRep) tRep=zoneT[i]The worst (warmest) zone represents the warehouse, because compliance is judged on the worst conditions goods experienced, not an average that hides them.
    void mktAdd(float tC)Each interval folds into the running mean kinetic temperature, weighting warm periods exponentially as the standard requires.
    float dewPoint(float t, float rh)Dew point is derived so condensation risk can be flagged where humidity control matters.
    if (now-exc.start > GRACE) raiseAlarmAn out-of-range condition sustained past a grace period alarms, and the full excursion (start/end/duration/peak) is logged when it recovers — the evidence compliance needs.
  2. Log tamper-evidently, alert and export

    Append each record with a sequence number/timestamp to local storage and mirror off-device, alarm proactively before limits, ride outages on battery, and provide audit export.

Complete Source Code

The listing below is complete and compiles as written — there are no elided sections. Read the annotations under each block before you upload it.

cppwarehouse-climate-logger.ino
/* ═══════════════════════════════════════════════════════════════
   Warehouse Climate Logger — ESP32, multi-point, tamper-evident

   Logs T/RH at multiple worst-case points to an append-only,
   timestamped, mirrored record; computes excursions, MKT and dew
   point; alarms before drift-out; rides outages on battery + SD.
   Supplementary to certified/validated compliance systems.
   ══════════════════════════════════════════════════════════════════ */

#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_SHT31.h>
#include <RTClib.h>
#include <SD.h>
#include <SPI.h>
#include <math.h>

#define NZONES 5
#define SD_CS  5
#define PIN_BUZZER 13
#define T_LIMIT 25.0f
#define RH_LIMIT 60.0f
#define GRACE_MS 600000UL   // 10 min sustained before excursion alarm
#define LOG_MS 60000UL

const char *zoneName[NZONES] =
  {"Door","Warm corner","Cold corner","High","Product"};
Adafruit_SHT31 sht[NZONES];    // via mux/addresses in practice
RTC_DS3231 rtc;
WiFiClient net; PubSubClient mqtt(net);

struct MKT { double sumExp=0; uint32_t n=0; } mkt;
const double DH_R = 83000.0/8.314;
struct Exc { bool active=false; uint32_t start=0; float peak=-99; } exc;
uint32_t lastLog=0, seq=0;

void mktAdd(float t){ mkt.sumExp += exp(-DH_R/(t+273.15)); mkt.n++; }
float mktValue(){ return mkt.n? (float)(DH_R/-log(mkt.sumExp/mkt.n)-273.15):NAN; }
float dewPoint(float t,float rh){ const float a=17.27f,b=237.7f;
  float g=logf(rh/100.0f)+a*t/(b+t); return b*g/(a-g); }

void logLine(DateTime t, float *zt, float *zh, float tRep){
  File f = SD.open("/climate.csv", FILE_APPEND);        // append-only
  if(!f) return;
  f.printf("%lu,%04d-%02d-%02d %02d:%02d:%02d",(unsigned long)seq++,
    t.year(),t.month(),t.day(),t.hour(),t.minute(),t.second());
  for(int i=0;i<NZONES;i++) f.printf(",%.2f,%.1f", zt[i], zh[i]);
  f.printf(",%.2f,%.2f\n", mktValue(), tRep);
  f.close();
}

void setup(){
  Serial.begin(115200);
  pinMode(PIN_BUZZER, OUTPUT);
  Wire.begin(21,22);
  for(int i=0;i<NZONES;i++) sht[i].begin(0x44);          // + mux select
  rtc.begin(); SD.begin(SD_CS);
  WiFi.begin(WIFI_SSID,WIFI_PASS); mqtt.setServer(MQTT_HOST,1883);
}

void loop(){
  if(!mqtt.connected()&&WiFi.status()==WL_CONNECTED) mqtt.connect("climate-1");
  mqtt.loop();
  uint32_t now = millis();
  if (now-lastLog < LOG_MS) return;
  lastLog = now;

  float zt[NZONES], zh[NZONES], tRep=-99, rhRep=0;
  for(int i=0;i<NZONES;i++){
    selectMux(i);
    zt[i]=sht[i].readTemperature(); zh[i]=sht[i].readHumidity();
    if(zt[i]>tRep) tRep=zt[i];
    if(zh[i]>rhRep) rhRep=zh[i];
  }
  DateTime t = rtc.now();
  mktAdd(tRep);
  logLine(t, zt, zh, tRep);                               // always, even offline

  // proactive near-limit warning + excursion
  if (tRep > T_LIMIT-1.0f) mqtt.publish("wh/1/warn","approaching temp limit");
  if (rhRep > RH_LIMIT-3.0f) mqtt.publish("wh/1/warn","approaching RH limit");
  if (tRep > T_LIMIT){
    if(!exc.active){ exc.active=true; exc.start=now; exc.peak=tRep; }
    if(tRep>exc.peak) exc.peak=tRep;
    if(now-exc.start>GRACE_MS){ digitalWrite(PIN_BUZZER,HIGH);
      mqtt.publish("wh/1/alarm","temp excursion"); }
  } else if(exc.active){
    char m[120]; snprintf(m,sizeof m,"{\"exc_end\":1,\"peak\":%.2f}",exc.peak);
    mqtt.publish("wh/1/excursion", m);
    exc.active=false; digitalWrite(PIN_BUZZER,LOW);
  }

  char m[220];
  snprintf(m,sizeof m,"{\"tRep\":%.2f,\"rhRep\":%.1f,\"mkt\":%.2f,"
    "\"dew\":%.2f}", tRep, rhRep, mktValue(), dewPoint(tRep,rhRep));
  mqtt.publish("wh/1/live", m);                            // mirror off-device
}
if(zt[i]>tRep) tRep=zt[i]The warmest zone represents the warehouse for compliance, so an excursion at the worst point is never hidden by cooler zones.
File f = SD.open("/climate.csv", FILE_APPEND); // append-onlyRecords are appended with a sequence number and RTC timestamp, never overwritten, making the compliance log tamper-evident.
logLine(t, zt, zh, tRep); // always, even offlineLogging happens regardless of network, so a power cut or outage — when incidents happen — never leaves a gap in the record.
if (tRep > T_LIMIT-1.0f) mqtt.publish("wh/1/warn"A near-limit warning fires before the range is breached, so staff can act to prevent damage rather than just document it.
mqtt.publish("wh/1/live", m); // mirror off-deviceThe record and metrics are mirrored off-device so the audit trail survives tampering or loss of the unit.

Configuration & Calibration

Configuration steps

  • Place and map sensors to worst-case zones; set the temperature/humidity limits and grace period.
  • Configure the append-only log, off-device mirror, RTC/NTP, and battery-backup behaviour.
  • Set MKT/dew-point and near-limit warning thresholds.
  • Configure audit export and access controls.

Calibration procedure

An uncalibrated sensor produces confident, precise, wrong numbers. Do this once per physical unit and record the constants.

  1. Sensor accuracy

    Calibrate/verify each T/RH sensor against a reference; record offsets. For regulated use, use appropriately qualified/calibrated sensors.

  2. Placement

    Confirm sensors are at the genuinely worst/most-variable points; validate with a survey across a day.

  3. Clock/logging

    Verify accurate timestamps (RTC backup) and that the append-only log and off-device mirror work and survive a power-off.

Network Architecture & Connectivity

Warehouse Climate Logger — network topologyPath taken by telemetry from field node to end user. Edge nodesGatewayCloudClientsClimate loggerESP32 + SDOther areasmore nodesWi-Fi 2.4 GHzRouterto serverMQTT/TLSCompliance storemirror + auditDashboardlive + auditQA/Phonealerts
Warehouse Climate Logger — network topology

Communication protocol

Readings, MKT/dew and excursions publish on a slow cadence and immediately on excursion/warning; the local append-only log is authoritative and mirrored off-device.

Topic / endpointDirectionPayload
wh/1/livenode → storeper-zone T/RH, worst-case, MKT, dew
wh/1/excursionnode → storeexcursion start/end, duration, peak
wh/1/warnnode → QAapproaching-limit warning

Message contract between the device and the broker.

Cloud platform configuration

A compliance store mirrors the record, keeps the audit trail, and supports export/reporting for inspectors and customers; near-limit warnings drive preventive action.

Dashboard setup

Per-zone and worst-case trends, MKT/dew, an excursion/door timeline, and an audit-export button.

Mobile app integration

Near-limit warnings and excursion alerts, and power-loss/battery notifications.

Security considerations

  • TLS and append-only, sequence-numbered logs mirrored off-device.
  • Restrict audit access; keep the record tamper-evident.
  • Alert on power/connectivity loss and on a node going silent.

Testing Procedure & Expected Output

Test from the bottom up. Confirm power, then each sensor in isolation, then the integrated loop — the first failing step tells you exactly where to look.

TestWhat you should see
Warm a zone above limit briefly then recoverExcursion logged with duration/peak; near-limit warning first
Compare zonesWorst zone drives compliance evaluation
Cut mains powerBattery keeps logging locally with correct timestamps
Drop the networkLocal log continues; mirrors/backfills on reconnect
Attempt to edit a past recordSequence break makes tampering detectable
Export the audit trailComplete, timestamped record suitable for inspection

Bench-test checklist. If a row fails, stop and fix it before moving on.

Expected output

The dashboard shows each zone's T/RH, the worst-case values, MKT and dew point, and an excursion/door-event timeline; an export produces the audit trail.

plainclimate.csv
seq,timestamp,door_t,door_rh,corner_t,corner_rh,...,mkt,tRep
20431,2026-07-27 14:00:00,24.1,54,23.6,52,...,23.8,24.6
20432,2026-07-27 14:01:00,26.3,58,23.7,52,...,23.9,26.3
# near-limit warning 14:01; excursion if sustained > grace

The door zone spiking to 26.3 °C (worst-case) drives a near-limit warning and, if sustained, an excursion record — the representative, tamper-evident evidence that proves conditions and flags risk.

A Grafana time-series dashboard
A dashboard proves conditions with MKT/excursion records and warns before a zone drifts out of range. Photograph sourced from Wikimedia Commons — Grafana dashboard.png. Reused under the licence stated on that page; please check it before republishing.

Troubleshooting: Common Errors & Fixes

Log looks fine but goods damaged

Likely cause. Sensors at benign spots, missing the worst zones

Fix. Relocate to door/corners/high/product; judge on the worst zone

Timestamps wrong after power cut

Likely cause. RTC backup dead

Fix. Fit a fresh coin cell; re-sync NTP; verify time survives power-off

Gaps in the record

Likely cause. Stopped logging during an outage

Fix. Battery backup + local logging + backfill; log before/independent of network

Record can be edited

Likely cause. Overwritable log

Fix. Append-only with sequence numbers; mirror off-device

Treated as certified compliance

Likely cause. Scope misunderstanding

Fix. For regulated use, use validated/qualified instruments; this is supplementary unless validated

The sketch will not upload — "Failed to connect" or "avrdude: stk500_recv()"

Likely cause. The bootloader is not being reached: wrong port, wrong board, a serial monitor holding the port open, or a USB cable that only carries power.

Fix. Close every serial monitor, confirm Tools → Board and Port, and swap to a known data-capable USB cable. On an ESP32 hold BOOT while the IDE prints "Connecting…", then release. If a peripheral is wired to the UART pins (GPIO 1/3 on ESP32, D0/D1 on Uno) unplug it — it fights the programmer.

The board resets in a loop, or the serial monitor prints "Brownout detector was triggered"

Likely cause. The supply cannot deliver peak current. Wi-Fi transmit bursts, relay coils and servos all pull far more than their average draw.

Fix. Power peripherals from a separate regulated supply with a common ground rather than from the board 5 V pin. Add a 470–1000 µF electrolytic capacitor across the supply near the load, and use a real power adapter rather than a laptop USB port.

Serial monitor shows garbage characters

Likely cause. Baud rate mismatch between Serial.begin() and the monitor, or a floating/shared UART line.

Fix. Set the monitor to 115200 to match the sketch. If it still garbles, the crystal or the USB bridge is being confused by noise — shorten the cable and keep motor wiring away from the USB lead.

An I²C device is not detected

Likely cause. Wrong address, missing pull-ups, swapped SDA/SCL, or a bus too long for the pull-up value.

Fix. Run an I²C scanner sketch first — it should print the device address. Most breakout boards include 4.7 kΩ pull-ups, but if you have chained four of them the parallel resistance is too low; remove the pull-ups from all but one board. Keep the bus under 30 cm at 100 kHz.

Wi-Fi connects but MQTT never does (state -2)

Likely cause. Wrong broker address or port, a firewall in the way, or the broker requiring credentials the sketch is not sending.

Fix. Test from a laptop on the same network first: mosquitto_sub -h <broker> -t "#" -v. If that works, the problem is on the device — check the IP literal, port 1883 (or 8883 for TLS), and that client.setServer() runs before connect(). PubSubClient state codes are documented in its header.

Readings arrive for a while and then stop

Likely cause. The Wi-Fi or MQTT session dropped and the sketch never reconnects, or the broker dropped the client on keep-alive timeout.

Fix. Never assume the link stays up. Check WiFi.status() and client.connected() at the top of every loop and reconnect with exponential backoff. Add a watchdog so a wedged network stack reboots the device instead of going silent.

Performance Optimisation

  • A one-minute cadence suits warehouse climate; publish warnings/excursions immediately.
  • Compute MKT incrementally; keep the log append-only and rotate by day/month.
  • Ride outages on battery + local log; backfill the mirror on reconnect.
  • Use accurate sensors and worst-case placement for a trustworthy record.
  • Replace every delay() with a millis() comparison — blocking delays are the single most common cause of dropped readings.
  • Sample sensors on a fixed cadence and publish on a slower one; you almost never need to transmit at the sampling rate.
  • Move networking into its own FreeRTOS task so a slow DNS lookup cannot stall the control loop.
  • Use uint8_t / uint16_t where the range allows; on an 8-bit AVR a 32-bit add costs four times as much.
  • Batch several samples into one MQTT publish. Radio time, not CPU time, dominates the energy budget.
  • Set the MQTT keep-alive to a value that matches your reporting interval so the broker does not churn reconnections.
  • For battery builds use deep sleep between samples: an ESP32 drops from ~160 mA awake to about 10 µA asleep, which is the difference between days and months of runtime.

Safety Precautions

  • For regulated storage (e.g. pharma GDP), use validated/qualified/calibrated instruments — a DIY logger is supplementary unless validated to the applicable standard.
  • Placement and calibration decide accuracy; sensors must be at the worst points and verified.
  • Keep the record tamper-evident and mirrored so it is defensible.
  • Alert before limits to prevent damage, and maintain outage ride-through.
  • Lithium cells vent and burn when abused. Only use protected cells or a proper BMS, never charge below 0 °C, and never leave a charging pack unattended on a wooden desk.
  • Wear eye protection when soldering or cutting, and solder in a ventilated space — rosin flux fumes are a respiratory irritant.
  • Power the circuit through a bench supply with a current limit while you are testing. A 300 mA limit turns a wiring mistake into a beep instead of a dead board.
  • Disconnect power before changing any wiring. Hot-plugging a sensor onto a live bus is the fastest way to lose a controller.

Maintenance

  • Re-verify/calibrate sensors on a schedule; replace the RTC coin cell.
  • Confirm placement still represents the worst conditions as storage/use changes.
  • Check the off-device mirror, battery backup and backfill.
  • Export/archive the audit trail per retention policy.
  • Re-check every screw terminal and header after the first week — thermal cycling loosens connections that felt tight on day one.
  • Rotate the microSD card annually and keep an image of the working system. Cards used as loggers wear out silently.
  • Log pack voltage. When resting voltage after a full charge drops below about 4.0 V, the cell is near end of life — replace it.
  • Keep the broker and dashboard containers patched, and rotate device credentials at least once a year.
  • Recalibrate at the interval given in the calibration section, and keep the constants in a text file next to the firmware — not only in flash.
  • Keep a short logbook of firmware versions and what changed. Six months later you will not remember why that constant is 1.083.

Future Improvements & Upgrades

A working v1 is a platform, not a finish line. These are the upgrades that add the most capability for the least rework.

  • Add CO₂/particulate for specific storage needs.
  • Add wireless zone nodes for easy coverage.
  • Cryptographically sign records for a stronger audit trail.
  • Integrate with a validated compliance/QMS system.
  • Design a proper PCB. Once the breadboard version has run for a month, moving to a two-layer board removes the intermittent-contact failures that dominate prototype faults.
  • Add over-the-air firmware updates so you never have to physically reach a deployed node again.
  • Add persistent local storage (microSD or the on-chip flash) so a network outage does not create a hole in your data.
  • Move configuration out of the source: a captive-portal setup page or a JSON config file makes the build reusable without a recompile.
  • Add a battery and solar option so the unit survives a power cut and can be sited away from a socket.
  • Write a small test harness that feeds synthetic sensor values through the decision logic, so you can validate thresholds without physically triggering the event.

Frequently Asked Questions

Why multiple sensors instead of one?

A warehouse varies from door to corner to roof, and goods experience their local conditions. Compliance is judged on the worst conditions, so a single benign sensor proves nothing and can hide a real excursion.

What makes the log "tamper-evident"?

It is append-only with sequence numbers (optionally hashes) and mirrored off-device — you can add readings but not silently rewrite them, and a deleted or altered entry breaks the sequence.

What is mean kinetic temperature?

A way to summarise a fluctuating temperature history into the single effective temperature the goods experienced, weighting warm periods more heavily because degradation accelerates with temperature. It is the standard for temperature-sensitive goods.

Does it keep logging in a power cut?

Yes — battery backup and local logging keep the record complete through outages (when incidents happen), and it backfills the off-device mirror on recovery.

Is this good enough for pharma compliance?

For regulated storage you must use validated, qualified, calibrated instruments to the applicable standard. This is a faithful, supplementary logger and teaching tool unless it is validated for that use.

References & Learning Resources

These are the primary sources worth reading in full. Manufacturer datasheets always outrank forum posts when the two disagree.

  1. Good Distribution Practice (GDP) — temperature controlWHO
  2. Mean kinetic temperatureReference
  3. Dew point and condensationReference
  4. SHT31 temperature/humidity sensor (datasheet)Sensirion
  5. Data integrity and audit trails (ALCOA)Reference