A cheap sensor for humidity and temperature
The DHT11 is a basic, low-cost digital temperature and humidity sensor. It uses a capacitive humidity sensor and a thermistor to measure the surrounding air, and spits out a digital signal on the data pin (no analog input pins needed).
The communication on data pin occurs according to a non-standard protocol known as One-Wire protocol.
Getting started with DHT11
In this article we want to provide a demonstration for DHT11 compatible with ChibiOS/HAL 3.0, but also explain concepts inside that demo. Following that purpose we need for the DHT11 datasheet.
Features and connections
Typical circuit requires a pull-up resistor on data pin. In our case Data out is connected directly to Port A, pad 8 of a ST Nucleo F4 using a simple short wire, and it works perfectly. Anyway in a definitive circuit is suggested to connect a pull-up resistor of 4,7 kΩ as shown at page 4 of datasheet.
- the VDD power supply 3.5~5.5V DC
- DATA serial data, a single bus
- NC, empty pin
- GND ground, the negative power
When the DHT11 is queried , it replies using a 40bit length data packet. Data is organized as:
- 8bit humidity integer data + 8bit humidity decimal data
- 8bit temperature integer data + 8bit temperature decimal data
- 8bit parity bit
Humidity rate data is provided as percentage with resolution 1%. Temperature is provided as Celsius with resolution of 1°C. That means on DHT11 data packet, decimal data is always zero. This is quite different for DHT22 that provides same data packet but with higher resolution and in this case decimal data is non-zero.
According to data sheet DHT11 must be queried from host(our MCU) pulling down data pin for at least 18ms, than sensor lowers data pin for 80us as the response signal, followed by an high logical state output of 80 micro-seconds as notification of starting to send the data packet: so sensor replies with data. DHT11 actually sends out square waves and bit could be recognized measuring the time during which signal stays high: if that time is 26-28us it is a 0-bit, if it is 70 us is a 1-bit.
Summering getting data from DHT11 requires:
- Set a pin as output and pull down that output to start communication.
- Set the same pin as input to capture the square wave measuring high and low logical state period.
- Eventually control if communication succeeds using check sum.
Using DHT11 with ChibiOS/HAL 3.0
Rereading above, the question is: what we need for to use DHT11 with ChibiOS? Basically one-wire protocol requires a pin that could be used as output push-pull as well as capture input. HAL has a driver that can capture an input (typically a square wave) and measure time period as well as time during which signal is in logical high state or logical low state. This driver is Input Capture Unit (ICU) ad is associated to a TIM like PWM.
Basically every pin could be used as digital output, but only some pin are connected to a TIM. We have to choose a pin internally connected to a timer channel. Reading Alternate Function table on data-sheet we could see that PA8 could be used as TIM1 Channel 1 thought AF 1 (If you are not familiar with PAL driver checkout the tutorial Hello ChibiOS). Reading ST Nucleo user manual we could learn that PA8 is connected to D7 pin of Arduino Connector.
Let outlining our program flow:
- Set PA8 mode as output push-pull and request data to DHT11 lowering Data out for at least 18ms.
- Set PA8 as AF1 and start ICU with a clock of at least 1MHz (This way we have a resolution of 1us).
- Start capturing data with ICU. Using ICU width callback acquire 41 widths.
- For each acquisition we need to write some variable according to widths.
- Eventually stop the ICU and restore PA8 mode to default configuration (input pull-up)
Note that first acquisition that should be 80 ticks (80us since our resolution is 1us) and others should be 26-28 ticks for a 0-bit or 70 ticks for 1-bit.
Creating this application from scratch
First step as always is duplicate a working project (If you don’t know how to do that checkout the tutorial Quickview of Chibistudio paying particular attention to video. Once we have created a new project, we have to enable ICU in halconf.h.
Since we want to use PA8 and so TIM1 CH1 we need to enable ICUD1 in mcuconf.h
If we want to use chprintf() to print data on a serial we have to edit makefile adding memstreams.c and chprintf.c to CSRC and including related folder modifying INCDIR
Now we are ready to write main.c. First of all we have to include chprintf.h declare a base sequential stream to use with chprintf(). Furthermore we could define some constants.
Now we can write an ICU configuration according to ChibiOS documentation. Since we need to measure time during high logical states we need to set ICU input as Active High. That means ICU width will measure time between signal rising edge and signal falling edge. Measure could have an uncertainly of ± 1 tick (In this case 1us). Other configuration are width, period and overflow callbacks, channels selected and value of DIE Register of TIM. We choose only width callback and enable channel 1.
ICU width callback must be declared before ICU configuration. According to our configuration (ICU_INPUT_ACTIVE_HIGH), width callback is called on falling edge. Here we can get a width measure from driver ICU, and make some elaboration. Lets take a look to the code.
This code gets the latest width, checking if it is related to a start bit (80us) or to one of the forty bits sent from DHT11. If current bit is the start one the counter bit_counter is reset. During the forty callback occurencies related to data bits, width is stored into a buffer (just for debug purposes).
bit_counter counts data related callback occurencies and data is “built” bit by bit and stored into the static variables (HR, TEMP, CHECK_SUM). To do this bit masks are used (Take a look at Registers and bit-masks). The tmp variable is used as temporary variable to build 8-bit lenght words during the sequence of callbacks. So when bit_counter reaches the value 7, tmp contains the integer part of the humidity rate, on 15 the decimal part of the humidity rate, on 23 the integer part of the temperature and so on…
Main initialize serial driver (We will use it with chprintf), set pin properly to make request, reset pin for ICU launching this driver, waits time needed by communication and prints out data.
How to port this application on other MCUs
Most likely we have just to change ICU configuration and pin used by data. Note that every driver configuration in ChibiOS has a part common to every MCU. For ICU the timer frequency, capture mode, and callback are independent from platform. We should expect to modify last part of configuration like DIER. A good idea to understand how is structured configuration, is copying it from a platform demo in chibios3/testhal (same MCU same configuration, so, as example F401RE and F407 have same configurations).
The attached demo has been tested under ChibiOS 17.6.x. Note that you can find more recent version of this project int the Download Page. Note also that starting from the version 20 all the demos from PLAY Embedded will be distributed together with ChibiStudio.