DHT11 sensor, port manipulation

Hi everyone,

I am having some issues reading from DHT11 connected to the digital port (30) in an Arduino Mega 2560 rev3 (clone). I don't want to use any library so I am trying to do everything using port manipulation. I have coded the following:

#include <avr/io.h>
#include <avr/iom2560.h>

#include "UART.h"
#include "timers.h"
#include "ioports.h"

int hum_temp[5][8];




void send_request()
{
	//set_pin_mode();
	DDRC |= _BV(7); //Set pin as output
	
	PORTC &= 0x7F; //send low for 20 milliseconds;
	wait_ms(20);
	

	while (!sensor_comm);
	
	PORTC |= _BV(7);	//send high pulse for 40us
	sensor_comm = 0;
	wait_us(40);
	

	while (!sensor_comm);
	sensor_comm = 0;
	
	
	DDRC &= 0x7F; //Set pin as input to wait a reply from the sensor
}

void receive_reply()
{
	char * checkpoint3 = "checkpoint3\n";
	char * checkpoint4 = "checkpoint4\n";
	while ( !(PINC & _BV(7)) ) {send_UART(checkpoint3);} // ~54 us being low
	while ( PINC & _BV(7) ) {send_UART(checkpoint4);} //80us being high
}

void receive_data()
{
	int us_count = 0;

	for (int i=0; i<5; i++)
	{
		for (int j=0; j<8; j++)
		{
			sensor_comm = 0;
			wait_us(54);

			while (!sensor_comm);
			
			
			hum_temp[i][j]=0; //Clean bit
			
			
			timer_2_us_count_sensor();
			while (PINC & _BV(7))         //Check which bit is received 0 = ~24us as HIGH, 1 = ~70us as HIGH
			{
				if (sensor_count) {
					us_count++;
					sensor_count = 0;
				}

				while (!sensor_comm);
						
			}
			if (1 < us_count && us_count <= 4)
				hum_temp[i][j] = 0;
			else if (4 < us_count && us_count < 8)
				hum_temp[i][j] = 1;
			us_count = 0;
		}
	}
	char * data = "data_received\n\n\n";
	send_UART(data);
}


void wait_end_of_frame()
{
	while ( !(PINC & _BV(7)) );
}



void read_temp_hum()
{
	send_request();
	receive_reply();
	receive_data();
	wait_end_of_frame();
}

The program gets into an endless loop in receive_reply(), in the checkpoint4.

Questions:

  1. As I said, there are not libraries being used, so wait_ms and wait_us start timer3 and their interrupt sets sensor_comm=1. Is there any Nop() style commando to use in the empty Whiles. It gets stuck endlessly in them if its {} is empty. Therefore I use send_UART (deleted for cleaning purposes).

  2. Is the receive_data function correct. I guess I have to diss out some values first (as said in the documentation), but appart from that? Same methodology is used here, timer_2_us_count_sensor() starts a timer which interrupts every us and sets the value sensor_count==1.

Thank you in advance and have a nice day!
Lizarduino

If you don't to use the Library, you have to convert the timing diagrams of DHT11 (as given in the data sheets) into C/C++/Arduino Codes.

DHT11.pdf (466 KB)

Hi again, thanks for answering to both of you.

I seem to have done the trick and everything works quite well. I need to polish it.

Hardware and setup used:

  • Arduino Mega 2560 R3
  • I am using port 30 -> PORTD & _BV(7)
  • Custom delays, should be the same for standard delay functions.
  • Custom serial (send_UART) function.

Important notes, common errors:

  • There can't be any prints/serial_send in the code. This costs clock_cycles, the protocol is very strict with timing. Debuggin must be done in different fases. Check to a checkpoint, if it works, delete the chekpoint print and keep debugging.
  • While loops + NOP() functions must be avoided. The reason is the same as before, NOP = one cycle = 62.5us. Taking into account the timings of the protocol, this can be decisive and break everything.
  • 5 sec must pass between readings.
  • Reading can be quite variable, this is a DHT11 problem (can't confirm, but it's what I have read and tested).
  • Before starting any of port manipulation "projects", check if the hardware is working with .ino libraries.

DHT11.c

#define DHT11_PIN 7						// define Port 30

int read_dht11_byte()
{
	int result=0;
	int delay=50;
	for (int i=0; i<8; i++)
	{
		delay_us(delay);				//wait till 50us LOW is over.
		delay_us(40);					// Wait 40us since the actual bit is sent, and check its value
		if ( (PINC & _BV(DHT11_PIN)) )	//if the signal is HIGH, the bit is 1. Else bit value is 0
		{
			result |= 1<<(7-i);			
			delay_us(30);				//Wait until 70us in total has past. 
			delay = 50;					//Before every bit there is 50us signal as LOW, get the delay value set
		}
		else
			delay=38;					// The bit was 0, which consisted of 28us in total. We have waited 40 already, we are in the middle of the 50us LOW signal. 50+28 - 40 = 38
	}
	return result;
}

void hasi()
{
	delay_ms(2000);
	DDRC |= _BV(DHT11_PIN);   // Set port 30 as OUTPUT
	PORTC |= _BV(DHT11_PIN);  // Set port value HIGH
}

int segi()
{
	char * information;
	int values[5];
	char * error;
	
	int dht11_in;

	/* REQUEST DHT11 TO GET READY */
	PORTC &= ~_BV(DHT11_PIN);    // Set port 30 value LOW for 18ms
	delay_ms(18);
	
	PORTC |= _BV(DHT11_PIN);     // Set port 30 value HIGH for 40us
	delay_us(40);
	
	
	/* WAIT DHT RESPONSE */
	DDRC &= ~_BV(DHT11_PIN);     // Set port 30 as INPUT
	

	while (PINC & _BV(DHT11_PIN)) {__asm__("nop\n\t");}
	dht11_in = (PINC & _BV(DHT11_PIN));  // Check if the sensor has responded HIGH and delay 80us
	
	if(dht11_in)
	{
		error = "\n\n\nDHT seems NOT to have responded - ERROR\n\n\n";
		send_UART(error);
		return 0;
	}
	delay_us(80);
	
	dht11_in = (PINC & _BV(DHT11_PIN));  // Check if the sensor has switched port value to LOW -> if it did, the protocol is working
	if(!dht11_in)
	{
		error = "\n\n\nDHT didn't pull HIGH - ERROR\n\n\n";
		send_UART(error);
		return 0;
	}
	delay_us(80);
	
	
	/* Data transfer is about to start: 5 bytes are going to be read */
	for (int i=0; i<5; i++)
	values[i] = read_dht11_byte();
	
	
	/* print received values */
	for (int i=0; i<5; i++)
	{
		char value_char[3];
	
		
		if (i==2)
			values[i] = values[i]*40/255;   //Since the value of DHT11 range is 0-40 for temperature, adapt the byte value received.
			
		int_to_char(values[i], &value_char[0]);
		send_UART(&value_char[0]);
		
		char * space = "\n";
		send_UART(space);
	}
	
	information = "Data transfer has ended\n";
	send_UART(information);
	return 1;
}

main.c

int main(void)
{
   global_interrupts_disable();
   setup_UART();
   for (int j=0; j<10;j++)
   {
       delay_ms(3000);
       delay_ms(2000);
       init();
       int reading = ask_for_values();
   }

   //reading = 1 OK
   //reading = 0 ERROR
}

Hopefully this will be helpful for other people and will save them some time (a lot in my case).