I've been trying to read DHT11 values from scratch.
I'm just posting to get some advice as to what could be important to know or change in terms of communication with the hardware, and any advice for possible next steps.
Of course, for some it may be all wrong, but some incremental advice wrt my current skills would be appreciated.
I'm also posting it so others can reuse, change and modify etc (MIT license.)
Here is the Arduino code (I don't know C++ so this uses the basic utilities / simplified C from Arduino):
/**
Hi !!
Code to read DHT11 values as in the ELEGOO UNO Kit.
This code reads the binary serial-output values from the sensors.
Note that the functions use as little extra stuff as possible to make
any delays as small as possible. I found problems otherwise.
*/
const uint8_t HT_DATA = 7; // Pin Number for the Humidity or Temp pin.
const uint8_t over_zero_ms = 30; // ensures we read just after 0_signal ends.
uint8_t bits[40] = {}; // store the 40 bits as they arrive (MSB order.)
float t = 0; // temp
float h = 0; // humidity
uint32_t execution_delay_ms = 1000; // loop frequency.
bool connect() {
/**
Start the connection between DHT11 and Arduino.
Datasheet: https://www.circuitbasics.com/wp-content/uploads/2015/11/DHT11-Datasheet.pdf
If it doesn't connect, returns `false` otherwise `true`.
*/
pinMode(HT_DATA, OUTPUT);
digitalWrite(HT_DATA, LOW);
delay(20);
pinMode(HT_DATA, INPUT_PULLUP);
delayMicroseconds(40);
delayMicroseconds(80); // low ~80us, next goes high.
if (digitalRead(HT_DATA) == LOW) { // shouldn't be low anymore
return false;
}
delayMicroseconds(80);
if (digitalRead(HT_DATA) == HIGH) { // shouldn't be high anymore
return false;
}
return true;
// if we got here, it starts sending the data.
}
void read_bits(uint8_t bits[40]) {
/**
Loop 40 times extracting the signal sent by DHT11
And write it in the input array.
*/
for (uint8_t i = 0; i < 40; i += 1) {
// digital read takes a few us.
while (digitalRead(HT_DATA) == LOW) {
// ~50 us.
// This fixes offsets that would otherwise accumulate.
}
delayMicroseconds(over_zero_ms);
if (digitalRead(HT_DATA) == HIGH) {
bits[i] = 1;
delayMicroseconds(40); // 1 uses +40 us in HIGH.
} else {
bits[i] = 0;
}
}
}
bool bits_to_values(uint8_t my_bits[40]) {
/**
Take the 40 bits, group them into 5 bytes, and calculate the values.
The received order is based on the site:
https://www.ocfreaks.com/basics-interfacing-dht11-dht22-humidity-temperature-sensor-mcu/
The order is MSB.
Returns whether the checksum matches our result.
*/
uint8_t accs[5] = { 0, 0, 0, 0, 0 }; // H_int, H_dec, T_int, T_dec, Checksum.
for (uint8_t acc = 0; acc < 5; acc += 1) {
uint8_t offset = 8 * acc; // no offset in first run.
for (uint8_t i = 0; i < 8; i += 1) {
uint8_t value = my_bits[i + offset];
accs[acc] += value << (7 - i); // `(7-i)` is because of MSB. LSB'd just use `i`.
}
}
h = val_as_float(accs[0], accs[1]);
t = val_as_float(accs[2], accs[3]);
uint8_t ours = (accs[0] + accs[1] + accs[2] + accs[3]) % 255;
uint8_t theirs = accs[4];
return ours == theirs;
}
void print_values(float h, float t, bool match) {
// simply print the values in a tidy way.
Serial.print("CHECKSUM MATCH ? ");
Serial.println(match);
Serial.print("HUMIDITY: ");
Serial.println(h);
Serial.print("TEMPERATURE: ");
Serial.println(t);
}
float val_as_float(uint8_t integer, uint8_t decimal) {
// take integer and decimal, and make a single float value.
float float_decimal = float(decimal);
float float_integer = float(integer);
while (float_decimal >= 1) {
float_decimal = float_decimal / 10;
}
return float_integer + float_decimal;
}
void run() {
if (!connect()) {
Serial.print("Communication failed.");
return;
}
read_bits(bits);
bool match = bits_to_values(bits);
print_values(h, t, match);
}
void setup() {
Serial.begin(9600);
delay(2000); // initial stabilise
run();
}
void loop() {
delay(execution_delay_ms);
run();
}
I'll try to address all tomorrow and reply, nice to get your response !
This is the output, seems very close to (your) DHTNew library which I used days ago. I could test to compare as well.
I do think that the 16 decimal bits for each case aren't useful, the hardware has error in the first integer (+-2, and +-5% for relative humidity.) but still are parsed..
This is interesting, and I appreciate your effort.
The whole point here though is going from scratch. Not being a "buy-this and copy-paste-code" monkey.
The aim is understand how to write code that interacts with hardware, from the datasheet.
So I was asking for advice in that sense, plus any additions or best practices. (See suggestions by @robtillaart for example.)
One of the questions for example, would be whether it's important to get this code into plain C++, or it's fine to keep with Arduino subset, or whether it needs better error handling, or whether some extra checks are needed, etc etc.
Another exercise you could do is to expand the code to an array of e.g 5 DHT11 sensors.
Then you could average the values to improve accuracy of the measurement. Also min and max of the day.
The next exercise would be to make the output of the array visible in the serial plotter in the IDE.
Another exercise would be control an output if the temperature is above and or under a threshold, e.g. control an RGB led.
Blue is cold, green is ok yellow is warm, orange is hot and red is dangerous hot.
One could then use Ps * RH/100 to get the actual partial pressure (at the specific temperature.)
Then to calculate the speed of sound accurately, assuming one can find or derive the effect of water pressure (which is small, but this is for fun only.)
I found some magic equation elsewhere but don't trust it.
Just writing this up in case you've tried this already, and solved it.
I think one needs something like the ratio to the total pressure (so the total pressure.) to recalculate all partial pressures of gases in air, and then use sqrt(kRT) but seems a bit hard for me.
I understand. The widgets were just for a drop-in, visual interpretation of the data you are extracting. Things that entertain me are adding layers for more simple use, and "un-abstract" the functions, as you are doing here with calling registers. I also like to see raw values changing in real-time in a dashboard display (vt220, text-only, or text-based graphics).
[edit] In the line of "Moba Tools" which is not just a stepper library, but an "all things timing" library. So many use the HC-SR04 to measure distance, but do not have the temperature and pressure (and probably humidity) data - or know how to use it. Adding "all things temp/humi/press" functionality... might be good, or might not. That's why we have the choice of sports cars and caravans.
Then to calculate the speed of sound accurately, assuming one can find or derive the effect of water pressure (which is small, but this is for fun only.)
i can't be sure whether it's correct, but the current SoS is a reasonable value., and shows how to get both the pressure of vapour alone, saturation pressure etc.