The DHT11 is one of the cheapest temperature + humidity sensors you can buy, and it does something a little unusual: it talks to the microcontroller over a single bidirectional data wire using a custom timing-encoded protocol. Not I²C, not SPI, not UART — its own thing. Decoding it is a beautiful introduction to bit-banged communication.
The handshake
The master (your Uno) initiates a reading by pulling the data line LOW for at least 18 milliseconds. That's the "wake up" signal. Then the master releases the line (sets the pin to INPUT) and waits.
The DHT11 responds by pulling the line LOW for ~80 microseconds, then HIGH for ~80 microseconds. That's its acknowledgement — "I heard you; I'm about to send data." The master sees two falling-then-rising edges.
The data: 40 bits, value-in-pulse-width
The DHT11 then transmits 40 bits of data:
- 8 bits humidity integer part
- 8 bits humidity decimal (always 0 on DHT11; the DHT22 uses this)
- 8 bits temperature integer
- 8 bits temperature decimal (same — 0 on DHT11)
- 8 bits checksum (sum of the first four bytes, mod 256)
Each bit is encoded by the LENGTH of a HIGH pulse, preceded by a 50 µs LOW transition. A 26 µs HIGH means 0. A 70 µs HIGH means 1. The Arduino's job is to measure those pulse widths.
The math is straightforward: when you detect a falling edge, start a microsecond counter. When you detect the next rising edge, the LOW duration is the inter-bit gap (always ~50 µs). When the next falling edge arrives, the HIGH duration tells you the bit value. Bit ≤ 50 µs HIGH? It's a 0. Bit > 50 µs HIGH? It's a 1.
Why bit-banged protocols still exist
I²C and SPI require dedicated controller hardware on both sides. Bit-banged custom protocols like DHT11's need none — just a GPIO that can switch between input and output. That's why DHT11 chips can be made for cents: no I²C peripheral, no UART, just one pin and a small state machine.
The cost is on the master side. The Arduino has to time micro-second-accurate pulse widths, which means either a tight polling loop (and your sketch can do nothing else during the ~4 ms read) or careful use of pin-change interrupts. The libraries (DHT.h, Adafruit_DHT) handle this so most users never see the timing dance — but if you've ever wondered why the sensor returns "NaN" sometimes, it's because the timing got disturbed by another interrupt.