Overview
The DHT11 is the classic entry-level temperature and humidity sensor: temperature in °C, humidity in %RH (relative humidity percentage — "how much water vapor the air holds"). It communicates over just one data wire (this one-wire-carries-both-data-and-clock style is called a single-wire bus): range 0~50°C (accuracy ±2°C), humidity 20~90%RH (accuracy ±5%RH). This tutorial uses the Ai-WB2 to read the DHT11's temperature and humidity, printing them on the serial in real time, and walks through the full flow of wiring → coding → building → flashing (writing the compiled program into the board's chip) → running and verification.
In plain words: the single-wire bus is like "talking over one wire" — the controller and the data line agree: first pull low then high to tell the sensor "I want to ask", and the sensor answers with 40 bits of data (16 bits humidity + 16 bits temperature + checksum) encoded as high-level pulses of different lengths. The driver used in this tutorial parses those 40 bits and hands you two numbers: temperature and humidity.
This tutorial is based on the official Ai-Thinker SDK (Ai-Thinker-Open/Ai-Thinker-WB2, version
release_bl_iot_sdk_1.6.40) exampleapplications/iot-solution/demo_dht11; the code can be found directly in the local SDK.
Wire per the official example (see the SDK’s applications/iot-solution/demo_dht11/README.md), using dupont wires (jumper wires with pins at both ends):
| Ai-WB2 Pin | DHT11 Pin |
|---|---|
| IO11 | DATA |
| 3V3 | VCC |
| GND | GND |
💡 The DHT11 is a single-wire device — VCC to 3.3V (the DHT11 accepts 3.3V~5V supply; this tutorial uses 3.3V throughout). ⚠️ The official README says IO4, but this version’s official driver actually uses GPIO11 (see
.port = 11indemo_dht11/driver_dht11_interface_bl602.c) — wire to IO11 per the code; the wrong pin reads nothing. 💡 With long dupont wires (>20cm), add a 5.1kΩ pull-up resistor between DATA and 3V3 (a small resistor holding the line high by default) for more stable communication.
This tutorial directly uses the demo_dht11 example project shipped with the official SDK; open a terminal and enter it:
cd ~/Ai-Thinker-WB2/applications/iot-solution/demo_dht11
Note:
cdis the “change directory” command — entering the DHT11 example project directory; all subsequentmakebuild andmake flashflash commands must run in this directory first.
This is a multi-file project: main program + the official ported DHT11 driver library (from the open-source LibDriver, wrapping the single-wire timing):
| File | Purpose |
|---|---|
demo_dht11/main.c |
Main program source, the main file this tutorial looks at |
demo_dht11/driver_dht11_basic.c/h |
Basic read/write interface (init, read temperature/humidity), calling the low-level timing driver |
demo_dht11/driver_dht11.c/h |
Single-wire timing core (40-bit send/receive, checksum) |
demo_dht11/driver_dht11_interface_bl602.c |
Platform adaptation layer: hooks the driver to the WB2’s GPIO11 |
Makefile |
Build entry, usually no changes needed |
Open demo_dht11/main.c — the complete code for this step has been moved to the end of this page:
📜 Full Code — in the “Full Code” section below, collapsed by default — click to expand, identical to the official example (
applications/iot-solution/demo_dht11/demo_dht11/main.c).
Code highlights:
| Code | Purpose |
|---|---|
dht11_basic_init() |
Initializes the DHT11 driver (configures the single-wire pin for send/receive); without it the driver can’t work |
dht11_basic_read(&temperature, &humidity) |
Starts a single-wire communication to read temperature/humidity: temperature is float (°C), humidity is uint8_t (integer %); a failed read returns 1 |
blog_info("%.2f C degree\t%u%%", ...) |
Prints temperature and humidity on the serial; no print = no visible result |
vTaskDelay(pdMS_TO_TICKS(500)) |
Samples every 500 ms; too fast and the DHT11 hasn’t refreshed its internal data, so you read stale unchanged values |
Build in the project directory:
make -j8
Note:
makeis the “build” command, turning code into firmware (the program file) the board can run;-j8builds with 8 parallel CPU cores, faster.
On success a firmware build_out/demo_dht11.bin is generated.
⚠️ If it reports
riscv64-unknown-elf-gcc: command not found, the toolchain permissions aren’t configured — runcd toolchain/riscv/Linux && . chmod755.shfirst, then rebuild.
Keep the board connected via USB, confirm the serial device (usually /dev/ttyUSB0 on Linux), and flash:
make flash p=/dev/ttyUSB0 b=921600
Note:
make flashis the “flash” command, writing the compiled firmware into the board’s chip. Afterp=comes the serial device (change it to your computer’s actual one — check withls /dev/ttyUSB*),b=is the flash baud rate (transfer speed).
⏳ During flashing, press and hold the EN button on the board when prompted to enter download mode (some boards enter automatically); wait for the progress bar to complete — that means the flash succeeded. For flashing on Windows, see Windows Quick Start.
After flashing, the board automatically restarts and runs; open the serial assistant (baud rate 921600 — the baud rate is the serial transfer speed, both ends must be set the same) and temperature/humidity prints every 500 ms:
26.00 C degree 45%
26.00 C degree 46%
...
Breathe on the sensor — the humidity value should rise; pinch the sensor with your hand — the temperature value should slowly climb.
Seeing the serial print temperature/humidity every 500 ms means success; if nothing prints or the values are fixed (e.g. always 0), it hasn’t succeeded yet — check the FAQ at the end.
💡 Watch the IO11 waveform with a logic analyzer to see the single-wire start signal and the 40 data pulses (see the official example’s
img/logic_analyzer.jpg).
API Summary for This Tutorial
dht11_basic_init()
Initializes the DHT11 driver: registers the platform interface functions (GPIO read/write, microsecond delay, interrupt disable) and completes the sensor self-check.
Parameters:
- None
Return: 0 on success; 1 on failure (e.g. sensor init self-check failed). Driver implementation in demo_dht11/driver_dht11_basic.c
dht11_basic_read(temperature, humidity)
Performs one measurement per the DHT11 single-wire timing and reads the 40-bit data (8-bit humidity integer + 8-bit humidity decimal + 8-bit temperature integer + 8-bit temperature decimal + 8-bit checksum), parsing out temperature and humidity.
Parameters:
temperature:float *output pointer, receives the temperature (°C), requiredhumidity:uint8_t *output pointer, receives the humidity (%RH, integer), required
Return: 0 on success; 1 on failure (read failure or checksum error). Driver implementation in demo_dht11/driver_dht11_basic.c
blog_info(fmt, ...)
Prints an INFO-level log (UART0, subject to level filtering); this tutorial prints temperature/humidity with it.
Parameters:
fmt: format string, same usage asprintf, required...: variadic args matching thefmtplaceholders; can be omitted
Return: none
vTaskDelay(ms)
Suspends the current task for the given milliseconds, yielding the CPU to other tasks.
Parameters:
ms: delay in milliseconds, values: any non-negative integer (internally converted to system ticks viapdMS_TO_TICKS)
Return: none
Full Code
Below is the complete demo_dht11/main.c source, identical to the official example (applications/iot-solution/demo_dht11/demo_dht11/main.c). This is a multi-file project; the rest of the driver files (driver_dht11*.c/h, driver_dht11_interface_bl602.c) live in the same directory, no changes needed:
📜 Click to expand the full demo_dht11/main.c code
#include <stdio.h>
#include <FreeRTOS.h>
#include <task.h>
#include <blog.h>
#include "driver_dht11_basic.h"
int main(void)
{
dht11_basic_init();
float temperature;
uint8_t humidity;
for (;;) {
dht11_basic_read(&temperature, &humidity);
blog_info("%.2f C degree\t%u%%", temperature, humidity);
vTaskDelay(pdMS_TO_TICKS(500));
}
return 0;
}FAQ & Troubleshooting
⚠️ The serial never prints (can't read)
Cause: DATA isn't on IO11 (the official README's IO4 is misleading), poor dupont wire contact, or a damaged sensor
Fix: confirm DATA is on IO11 (per the driver code's .port = 11); re-seat the dupont wires; try another DHT11
⚠️ Values always 0 or garbled
Cause: insufficient VCC supply, no common ground between the board and module (GND not connected), or long-wire interference causing checksum failure
Fix: confirm VCC to 3V3 and GND to GND (common ground = the two devices' grounds must be joined, otherwise voltage has no reference point); with long wires add a 5.1kΩ pull-up between DATA and 3V3
⚠️ Temperature and humidity values fixed
Cause: read interval too short — the DHT11's internal measurement data hasn't refreshed yet (datasheet recommends ≥1 second between reads)
Fix: this example's 500 ms interval usually works; if the values stay unchanged for a long time, raise vTaskDelay to 1000 and retry
⚠️ Serial device not found or no permission
Cause: on Linux /dev/ttyUSB0 doesn't exist or permission denied; on Windows the USB-to-serial driver isn't installed
Fix: on Linux check with ls /dev/ttyUSB*; if permission denied run sudo usermod -aG dialout $USER and log back in; on Windows install the driver in Device Manager and confirm the COM port
⚠️ Flashing keeps waiting, progress bar doesn't move
Cause: download mode wasn't entered, or the cable only charges and can't transfer data
Fix: press and hold EN during flashing to enter download mode as prompted; try a Type-C data-capable cable
⚠️ make reports Makefile not found
Cause: the build command ran in the wrong directory (must be inside the example project directory)
Fix: run cd ~/Ai-Thinker-WB2/applications/iot-solution/demo_dht11 first, then make -j8
Self-Check
The serial prints temperature/humidity every 500 ms; breathing raises the humidity, pinching raises the temperature — the DHT11 measurement is verified.

