Signals you time, not read
The DHT sensor and the infrared remote share one thing, and it is the thing that matters: the information is in how long a pulse lasts.
There is no clock on either of them, no address and no bus.
Most of a kit talks over something with a name: I2C, SPI, a UART. These two do
not. They flash a line for a while and the length of the flash is the bit, which
means the driver is a stopwatch and the sketch's own timing is part of the
circuit. They are the parts that teach what micros() is for.
The DHT11 and the DHT22
One wire, three pins, forty bits. The blue brick is the DHT11 and the white one is the DHT22 (also sold as the AM2302); the wire is identical and only the five bytes differ.
The conversation
start the host holds DATA low: at least 18 ms for a DHT11,
at least 1 ms for a DHT22
release the host lets go and the pull-up takes the line high
response after 20-40 us the sensor pulls low for 80 us, then high for 80 us
40 bits each one is 50 us low, then high for 26-28 us (a nought)
or 70 us (a one), most significant bit first
end a last 50 us low, and the line is let go
A driver splits a nought from a one at about 50 us, which has room either side.
On both simulated boards here a pin read sees the level as of the start of a
10 us slice, so a nought measures 20-30 us and a one 70-80. That is three slices of
margin, and micros() itself is exact because it is the cycle counter.
The five bytes
humidity high, humidity low, temperature high, temperature low, checksum
and the checksum is the low eight bits of the sum of the other four. What the four mean is the only place the two sensors differ:
- DHT22. Humidity is a 16-bit number in tenths of a percent and temperature is a 16-bit number in tenths of a degree, and a negative temperature sets bit 15 and puts the magnitude in the rest. It is sign and magnitude, not two's complement, and getting it wrong is the commonest bug in a hand-written driver. It only shows up in winter.
- DHT11. Humidity high is whole percent and humidity low is zero; temperature high is whole degrees and temperature low is zero. A sketch that divides a DHT11 reading by ten is wrong on it.
Three things the model does on purpose
The first reading fails. Both datasheets say to leave the sensor alone for a
second after power comes up, and the part does exactly that: a start pulse
inside that second is not answered at all and the driver times out. That is the
real first-reading-fails, and a sketch that hides it behind a delay(2000) in
setup() is hiding something true.
It repeats itself. A DHT22 measures every two seconds (a DHT11 every one) and hands back the last reading in between. Drag the slider and the number does not move until the sensor has looked again, which is why a sketch that polls in a tight loop sees a value that seems stuck.
The range is the datasheet's. A DHT11 reads 20 to 90 %RH and 0 to 50 C; a DHT22 reads 0 to 100 %RH and -40 to 80 C. Ask for more and the part clamps, because a sensor cannot report what it cannot see.
DHT.h
The Adafruit tutorial compiles as it stands:
#include "DHT.h"
DHT dht(2, DHT22);
void setup() {
Serial.begin(9600);
dht.begin();
}
void loop() {
delay(2000);
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
Serial.print(t);
Serial.print(" C ");
Serial.print(h);
Serial.println(" %");
}
DHT.h is Mokxi's own, on mokxi_dht.h. Each float it hands back is built
from the driver's whole tenths bit by bit: the float is exactly the one a
division would give, it prints, and arithmetic of your own on it, such as
t * 1.8 + 32, works. readTemperature(true) gives Fahrenheit,
computeHeatIndex works, and isnan is there. A reading is taken at most
every two seconds, as upstream does, because the sensor cannot answer faster. On an ATtiny85 a
sketch that prints a reading fits, and the whole heat-index tutorial does not.
The pull-up
The three-pin modules carry one on DATA, usually 5.1 k or 10 k. The bare
four-pin sensor does not, and wants one adding. pullup is that resistor, on by
default; turn it off and the line floats between pulses and every reading fails,
which is what the bare part does on a real breadboard without a resistor.
What is not modeled
The accuracy: +/-2 %RH and +/-0.5 C on a DHT22, +/-5 and +/-2 on a DHT11. The number the slider says is the number that goes on the wire. Nor the seconds the capacitive element takes to follow a change in the air, nor self-heating.
The remote and the receiver
A 21-button handset and a VS1838B, the pair taped together in every kit.
The beam is a wire
In Mokxi two parts only ever meet on a net, so the light between the handset and
the receiver is drawn as a wire from one IR pin to the other. That is the one
thing in this circuit that is not what a bench looks like, and it is said here
rather than left to be discovered.
What it costs: there is no distance, no angle, no sunlight, no second remote in the room, and therefore no reason for the tuning that is most of what is inside a VS1838B. What it keeps is everything a sketch can see, which is the timing.
The 38 kHz carrier is implied for the same reason. A real remote does not switch
its LED on for nine milliseconds, it flashes it at 38 kHz for nine milliseconds,
and the receiver is a tuned amplifier that only notices light flickering at
about that rate, which is how it ignores daylight and a fluorescent tube.
Generating that here would be 340 edges a millisecond and nothing in the model
would do anything different with it, so the IR pin carries the envelope: high
while the LED is flashing, low while it is dark.
The receiver inverts
This is the whole of what a VS1838B does, and it is the first surprise it hands
anybody: OUT idles high and goes low while a burst is arriving. So the 9 ms
leader burst shows up as 9 ms of LOW, and every NEC decoder ever written is
written from that side. The dome on the part lights while the line is down, so
the two can be watched together.
Its output stage is a transistor with an internal 30 k pull-up, which is why it can be read by a bare pin and why it is a poor idea to hang anything heavy off it.
NEC, whole
One unit, T, is 562.5 us: 21 cycles of the carrier, which is why it is not a
round number.
leader 16 T on (9 ms), 8 T off (4.5 ms)
a nought 1 T on, 1 T off
a one 1 T on, 3 T off
stop 1 T on
repeat 16 T on, 4 T off (2.25 ms), 1 T on, every 110 ms
Thirty-two bits go between the leader and the stop bit, least significant bit first, as four bytes: address, address inverted, command, command inverted. The two inversions are the whole of the error checking, and a decoder that checks them throws away almost every frame a passing car's headlights corrupted.
Holding a button sends one whole frame and then a short repeat every 110 ms. A sketch that treats a repeat as a fresh press is why volume buttons run away.
0xFFA25D, and 0x45
Every table on the web calls the CH- button 0xFFA25D; this part calls it
address 0x00, command 0x45. They are the same thirty-two bits. The bits
arrive least significant first and the old Arduino decoder assembled them most
significant first, so each byte comes out bit-reversed: 0x45 reversed is
0xA2, 0xBA reversed is 0x5D, and the leading 0x00FF is the address and
its inverse. Neither reading is wrong. firmware/lib/mokxi_ir.h hands back
both, and the ir-lamps example prints both.
The handset's twenty-one commands are the set the VS1838B is sold with:
CH- 0x45 |
CH 0x46 |
CH+ 0x47 |
|<< 0x44 |
>>| 0x40 |
>|| 0x43 |
- 0x07 |
+ 0x15 |
EQ 0x09 |
0 0x16 |
100+ 0x19 |
200+ 0x0D |
1 0x0C |
2 0x18 |
3 0x5E |
4 0x08 |
5 0x1C |
6 0x5A |
7 0x42 |
8 0x52 |
9 0x4A |
What is not modeled
The carrier and the air, as above. The automatic gain control, so this receiver never blanks. A real one turns its gain down under a long continuous burst and stops seeing anything, which is why NEC's leader is 9 ms and not 90. The tuning, so a burst at any rate at all is a burst here and two remotes on different carriers would interfere. The few hundred microseconds of edge error a real receiver has, which is what makes a decoder's tolerances necessary; here the edges are exact. And no protocol but NEC: no RC-5, no Sony SIRC, no extended NEC with a 16-bit address.
Circuits to open
- Weather station: a DHT22 on pin 2 with the reading on an I2C display, and a first reading that fails on purpose.
- Remote-controlled lamps: a handset, a receiver and three lamps, with every frame printed both ways round.
Watching the wire
Both of these are worth putting a logic analyzer on. The bits are widths, and
an instrument that records edges with the time they happened is the instrument
for widths. Clip D0 to the DHT's data line or the receiver's OUT, run it,
and zoom the window down until one bit fills a division.