A camera-free desk clock that indicates sleep quality
Notes for an ESP32 bedside clock that uses CO2, temperature, light, and bed presence to explain why a night felt off, without putting a camera in the bedroom.
Share

As you build it, I would treat every score as a prompt for reflection rather than a medical conclusion.
Some mornings I wake up tired and cannot tell why: the room was stuffy, the air conditioner ran a little warm, light leaked through the curtains, or I simply went to bed too late. I want a bedside clock that records a few of those clues so the next morning I can see what to fix first.
The boundary for me is a camera in the bedroom. This project does not try to watch anyone sleep. It combines simple room and bed signals, then shows a reference indicator in the morning. It does not measure REM, claim to identify deep sleep, or diagnose sleep apnea.
What can the clock honestly know?
| Signal | Reasonable observation |
|---|---|
| CO2, temperature, humidity | Whether the room became stuffy, warm, or humid relative to its normal baseline |
| Light | Whether the room was dark at bedtime and when morning light arrived |
| Sound level | How many periods rose noticeably above the room's normal noise floor |
| Under-bed FSR | Bedtime, bed exits, and total time in bed |
| Seven-day history | Whether bedtime and wake time stayed consistent |
An FSR cannot tell whether I am awake or asleep. A noise spike cannot prove that it woke me. The display should therefore say “Sleep conditions” or “Last night indicator,” not claim “3 hours 20 minutes of deep sleep.”

Version one should measure a few clear signals: CO2, temperature, humidity, light, sound level, and bed occupancy.
Hardware for version one
- ESP32-S3, preferably with PSRAM for a larger interface.
- SH1107 128 x 128 OLED or a 2.9-inch e-paper display. OLED is easier for a continuous clock; e-paper is gentler but not suited to per-second updates.
- SCD40/SCD41 for CO2, temperature, and humidity.
- BH1750 for room illuminance.
- I2S MEMS microphone or analog sound-level board, used only for local noise calculation.
- Pushable rotary encoder for score details and alarm settings.
- A separate ESP32 node with one or two FSR strips under a bed slat.
- A printed enclosure with sensor vents, keeping ESP32 heat away from the SCD41 airflow.
For the smallest build, leave out the microphone and bed sensor. The clock can still review bedroom conditions and lights-out consistency. That score contains less information, but it is easier to trust than an overambitious algorithm.
A score that can explain itself
I would start with a deliberately simple formula:
40 points: CO2, temperature, humidity, darkness, and noise
35 points: total time with the bed occupied
15 points: extended bed-exit events
10 points: seven-day bedtime consistency
The thresholds should not be universal. Record roughly one week, mark the nights that felt reasonably good, and use those as a personal baseline. An air-conditioned room, an open-window room, and a bedroom shared by two people will produce very different CO2 and temperature curves.
The score must also include a reason. 72 - room became stuffy after 02:10 is more useful than an unexplained orange ring.
ESPHome room-sensor outline
The basic sensor configuration stays short:
i2c:
sda: GPIO8
scl: GPIO9
sensor:
- platform: scd4x
measurement_mode: low_power_periodic
co2:
name: "Bedroom CO2"
temperature:
name: "Bedroom Temperature"
humidity:
name: "Bedroom Humidity"
- platform: bh1750
name: "Bedroom Illuminance"
update_interval: 60s
Home Assistant can calculate the final value because it needs overnight history. The clock only receives sleep_condition_score and one short reason. If Home Assistant is offline, time and alarms should continue locally; the morning screen can simply say that there was not enough data.

At night, keep only a very dim clock or turn the display off; the indicator score can wait until morning.
Privacy is more than removing a camera
For the microphone, firmware should calculate RMS/dB over a short window and discard the audio samples. It should never save a file, stream sound, or expose a playback API. A physical switch that cuts microphone power makes this behavior visible and verifiable.
Data can stay on the local network through ESPHome and Home Assistant. The clock face should show only the summary score, especially when it sits somewhere other people can easily see it.
My first MVP
- Use SOAS for the clock, retaining its encoder and night display behavior.
- Add an SCD41 and BH1750, then record seven nights before deciding thresholds.
- Build a one-side FSR node with
delayed_onanddelayed_offso turning over does not create false events. - Start with only time in bed, average CO2, and minutes with excessive light.
- Each morning, press a simple “fine/not fine” button to tune the personal baseline.
The thing I want to test is not a perfect sleep score. I only need it to say something honest: CO2 stayed high after 02:10, morning light arrived earlier than usual, or time in bed was shorter than last week. If that helps me change one small habit tonight, the clock has earned its spot.
References
Share
Keep exploring
Read next
Related articles
A smart thermostat that learns how your room warms up
Notes on building an ESP32 smart thermostat that learns a room's heating rate, starts at the right time, and keeps safety control local.
A fridge and freezer temperature alert device for power outages
Notes for a small ESP32 box with two DS18B20 probes and Home Assistant alerts, meant to catch a warming fridge or freezer after a power outage.
A Vietnamese voice remote for local Home Assistant control
Notes for building an ESP32-S3 voice remote with ESPHome and a local Home Assistant Assist pipeline so Vietnamese voice commands can stay inside the home network.