Library 25- AM2301 (DHT21) sensor for STM32F4

AM2301 or DHT21 is a digital sensor for measure temperature and humidity. It has temperature resolusion up to .1 degree and accuracy to .5 degree celcius. Humidity has .1% resolution and 3% accuracy. This is quite good.



  • Read temperature from sensor
  • Read humidity from sensor


    • STM32F4xx
    • STM32F4xx RCC
    • STM32F4xx GPIO
  • TM
    • TM DELAY
    • TM GPIO
    • defines.h

VCC 3V3-5V Positive supply
GND GND Ground
DATA PD1 Data line

You can select your own data pin for STM32F4xx, add lines below in your defines.h file and edit as you want:

On pin, internal pull-up resistor is enabled, but if you have a larger distance, add external resistor about 4k7 Ohm.

Read data

First you have to initialize sensor. Do this with

You are now able to read data. First make a new instance of data typedef.

Then check if data is valid with

Functions and enumerations


AM2301 Temperature and Humidity sensor

AM2301 Temperature and Humidity sensor

Project available on Github, download library below.

TM STM32F4 AM2301 Library


Owner of this site. Also electronic enthusiasts, web developer, 3D printer fan, handball player and more. Big fan of STM32F4 devices. In anticipation of the new Discovery board for STM32F7 lines.

You may also like...

Read before commenting!

Before you make a new comment, make sure you agree with things listed below:

  • - Read post to make sure if it is already posted what you are asking for,
  • - Make sure you have the latest version of libraries used in your project,
  • - Make a clean and grammatically correct written message,
  • - Report as many details as possible, including what have you done so far,
  • - Do NOT post any code here. Use Pastebin,
  • - Do NOT post any error codes here. Use Pastebin,
  • - Specify STM32Fxxx family and used Discovery/EVAL/Nucleo or custom made board,
  • - Make sure your clock is set correct for PLL,
  • - If you are using my HAL drivers, please check this post how to start.
Comment will be deleted on breaking these rules without notification!
  • Comrada

    Hello! I’m trying to use it for DHT22, but getting an error TM_AM2301_WAITHIGH_LOOP_ERROR
    It seems it has the same parameters. Have you tried to measure with this sensor?

    • Hi,

      I didnt. But if it has the same parameters, then make sure that your clock is set correct. Check PLL parameters.
      Also, use timer for delay functions if you use GCC. How to use it, look at my delay library.

      • Comrada

        I have this picture(1). First, there is a 250us high pulse, but in datasheet there is something like on the second picture.
        And in your library:
        /* Pin output */
        /* Set pin low for ~800-1000 us */
        /* Set pin high to ~30 us */
        /* Read mode */

        How did this impulse(250us)?

  • Lucas

    Hello Tilen,

    If I want read two sensors DHT22 in the same time, how I do?


    • Hi,

      this lib supports only one sensor right now.

      • Lucas

        Hi Tilen,

        Is working. I duplicated the library.


        • This is one way, another is to create a structure for each sensor where is located. I will add this into hal library for this.

  • yousra

    hello , its the same with dht11 ??

  • David Chau

    Hi Tilen,

    I am trying to use the HAL version of the AM2301 example you provided for the Nucleo F401RE (posting here as I could not find the HAL story).
    I have implemented the SPL version of your libraries and obtained the correct temp/hum readings.
    However, when using the HAL version, the data read is incorrect and does not match the SPL version.
    I am using the predefined clock/PLL configurations you provided. However, I suspect that it is a timing issue.
    I am not sure where to start debugging, any pointer or suggestions?


    • David Chau

      I seem to have fixed the issue, the bits read were reversed.
      This could have been due to how the HAL functions read the sensor.

  • Entropy

    Hello, I was trying to run this AM2301 program as a little piece (task) amongst other tasks I have in my RTOS, though the

    if (TM_AM2301_Read(&data) == TM_AM2301_OK)
    doesn’t seem to run, Having noticed some delays in the “AM2301.c” while I am sure they are “non blocking delays” I don’t understand why this task doesn;t go past syntax. I know everything works because running this piece of code on its own normal while true (like above) without RTOS, it displays everything correctly. (BTW each task in my case is running every 1 ms)

    Please assist whenever you can.


    • This is expected result as delays inside lib are done in software. What does non-blocking delay mean?

      Nevertheless, problem is your RTOS task switching. When you go to read function and if task is switched, then you don’t have baudrate correct to read/write to device and you have wrong readings.

      What you can do is to choose one of 2 options:
      1. Port lib to TIMER interrupt capability
      2. Disable systick timer (or timer which is used for generating periodic irqs for RTOS) when you enter to function and enable it again when you go out.

      Option 1 is of course better from point of performance.

      • Entropy

        Thanks for your prompt reply!

        Having thought of the delays being executed within the lib as you illustrated above too, I think I might then have confused a bit. This is because I am already using a custom timer 2 as you mentioned in the comment section of “tm_stm32f4_delay.h”

        #define TM_DELAY_TIM TIM2
        #define TM_DELAY_TIM_IRQ TIM2_IRQn
        #define TM_DELAY_TIM_IRQ_HANDLER TIM2_IRQHandler

        I didn’t have a look through the whole code to see how the delays are being generated exactly so when I saw the above syntax it lead me believing that the delays generated although within software might already be a timer-based through an iRQ?

        Btw I am using a Stm32f407 on a ARMGCC compiler.
        Cocoox IDE and a CoOS RTOS (max scheduler resolution of 1 ms)

        Thanks for assistance.

        • Entropy

          I think it makes sense now what you meant, i didn’t quite understood it but now I do. Well… Any approach to making the Lib a timer-triggered interrupt driven?

          • So the delay is accurate, but if CPU does not process the data because other thread is in use, it will fail to read/write in correct timebase. Approach would be to use big state machine inside TIM IRQ (TIM6 is good for example to create interrupts only) and then for each bit set correct timer period when IRQ should be received. This is very slow process of development but it should be most robust.

            Later, when you call function “Read”, you start the timer and you reset state machine. In “Read” function you want till state machine finishes inside timer IRQ and then you return result. “Read” function must still act as blocking but processing is done in TIM IRQ at precise timebase.

          • Entropy

            Finite State machine was one amongst the thing that came in my mind too! last time I did similar thing was on an AVR platform while I was using a low performance “cooperative multitasking” scheme, I had troubles calling “_delay_ms()” inside the task which then was blocking the scheduler from context switching. So I had to break the entire task into some chunks of pieces (state machines) or Sub-tasks, and then whenever I had to call “_delay_ms()” I would go in that state transition virtually doing nothing until that time is elapsed and move onto the next state this though somehow is very inefficient as there are some wasted CPU time isn’t?

            here was the initial declaration of how i approached it.

            typedef enum

            } TASK_1_PRM;

            typedef struct
            TASK_1_PRM Next_State_1;
            uint16_t State_Time_1;

            } TASK_1_SCD;

            static const PROGMEM TASK_1_SCD State_Table_1[]=
            {.Next_State_1 = SUB_TSK_1_B, .State_Time_1 = 10},
            {.Next_State_1 = SUB_TSK_1_C, .State_Time_1 = 30},
            {.Next_State_1 = SUB_TSK_1_D, .State_Time_1 = 30},
            {.Next_State_1 = SUB_TSK_1_E, .State_Time_1 = 30},
            {.Next_State_1 = SUB_TSK_1_A, .State_Time_1 = 10}


            variable “State_Time” depends on how long is the actual delaying. and the state machine was wrapped into eeprom as I had to wake up from the watchdog timer and wanted to keep the data non volatile otherwise.

            Do you thing similar approach would work in getting this Lib in similar manner?

            PS: Sorry to paste the code here I was aware of the rule but hope it was messy 🙂


          • I got the point.

            State machine is something you will have to implement. It will have a lot of cases I suppose as you will have to be very careful when you read data and that you set timer IRQ time at desired speed depends on what are you trying to write, either 1 or 0, etc.

            But this might be the only way doing it if you want such library as this one to work in RTOS (because it is software driven).