5

I'm trying to read temperature data from a DHT11 temperature sensor, using pi4j. I followed the code written in c and python in this site: http://www.uugear.com/portfolio/dht11-h ... or-module/ But it's not working. when I test the instruction 'dht11Pin.getState()' it's always in HIGH state, never changing. Is there anything wrong in my code?

Below is my code:

import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.pi4j.component.ObserveableComponentBase;
import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalMultipurpose;
import com.pi4j.io.gpio.Pin;
import com.pi4j.io.gpio.PinMode;
import com.pi4j.io.gpio.PinPullResistance;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;

public class DHT11 extends ObserveableComponentBase {

private static final Pin DEFAULT_PIN = RaspiPin.GPIO_04;
private static final int MAXTIMINGS = 85;
private int[] dht11_dat = { 0, 0, 0, 0, 0 };
private GpioPinDigitalMultipurpose dht11Pin;
private static final Logger LOGGER = LogManager.getLogger(DHT11.class
        .getName());

public DHT11() {
    final GpioController gpio = GpioFactory.getInstance();
    dht11Pin = gpio.provisionDigitalMultipurposePin(DEFAULT_PIN,
            PinMode.DIGITAL_INPUT, PinPullResistance.PULL_UP);
}

public DHT11(int pin) {
    final GpioController gpio = GpioFactory.getInstance();
    dht11Pin = gpio.provisionDigitalMultipurposePin(LibPins.getPin(pin),
            PinMode.DIGITAL_INPUT, PinPullResistance.PULL_UP);
}

public double getTemperature() {
    PinState laststate = PinState.HIGH;
    int j = 0;
    dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_dat[4] = 0;
    StringBuilder value = new StringBuilder();
    try {

        dht11Pin.setMode(PinMode.DIGITAL_OUTPUT);
        dht11Pin.low();
        Thread.sleep(18);
        dht11Pin.high();
        TimeUnit.MICROSECONDS.sleep(40);
        dht11Pin.setMode(PinMode.DIGITAL_INPUT);

        for (int i = 0; i < MAXTIMINGS; i++) {
            int counter = 0;
            while (dht11Pin.getState() == laststate) {
                counter++;
                TimeUnit.MICROSECONDS.sleep(1);
                if (counter == 255) {
                    break;
                }
            }

            laststate = dht11Pin.getState();

            if (counter == 255) {
                break;
            }

            /* ignore first 3 transitions */
            if ((i >= 4) && (i % 2 == 0)) {
                /* shove each bit into the storage bytes */
                dht11_dat[j / 8] <<= 1;
                if (counter > 16) {
                    dht11_dat[j / 8] |= 1;
                }
                j++;
            }
        }
        // check we read 40 bits (8bit x 5 ) + verify checksum in the last
        // byte
        if ((j >= 40) && checkParity()) {
            value.append(dht11_dat[2]).append(".").append(dht11_dat[3]);
            LOGGER.info("temperature value readed: " + value.toString());
        }

    } catch (InterruptedException e) {

        LOGGER.error("InterruptedException: " + e.getMessage(), e);
    }
    if (value.toString().isEmpty()) {
        value.append(-1);
    }
    return Double.parseDouble(value.toString());
}

private boolean checkParity() {
    return (dht11_dat[4] == ((dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3]) & 0xFF));
}

}

yglodt
  • 13,807
  • 14
  • 91
  • 127

6 Answers6

8

I started with the original poster's java code, and replaced the com.pi4j.io.gpio package references with the com.pi4j.wiringpi package. I had recently installed the newest pi4j package and wiringpi version on my Raspberry Pi.

Using that package the Java code below works approximately the same as the c version of this program. I am getting about 80% - 85% accurate responses with a DHT-11. Which is about the same as I was getting using wiringPi in c.

package gpio;
import com.pi4j.wiringpi.Gpio;
import com.pi4j.wiringpi.GpioUtil;

public class DHT11 {
    private static final int    MAXTIMINGS  = 85;
    private final int[]         dht11_dat   = { 0, 0, 0, 0, 0 };

    public DHT11() {

        // setup wiringPi
        if (Gpio.wiringPiSetup() == -1) {
            System.out.println(" ==>> GPIO SETUP FAILED");
            return;
        }

        GpioUtil.export(3, GpioUtil.DIRECTION_OUT);
    }

    public void getTemperature(final int pin) {
        int laststate = Gpio.HIGH;
        int j = 0;
        dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_dat[4] = 0;

        Gpio.pinMode(pin, Gpio.OUTPUT);
        Gpio.digitalWrite(pin, Gpio.LOW);
        Gpio.delay(18);

        Gpio.digitalWrite(pin, Gpio.HIGH);
        Gpio.pinMode(pin, Gpio.INPUT);

        for (int i = 0; i < MAXTIMINGS; i++) {
            int counter = 0;
            while (Gpio.digitalRead(pin) == laststate) {
                counter++;
                Gpio.delayMicroseconds(1);
                if (counter == 255) {
                    break;
                }
            }

            laststate = Gpio.digitalRead(pin);

            if (counter == 255) {
                break;
            }

            /* ignore first 3 transitions */
            if (i >= 4 && i % 2 == 0) {
                /* shove each bit into the storage bytes */
                dht11_dat[j / 8] <<= 1;
                if (counter > 16) {
                    dht11_dat[j / 8] |= 1;
                }
                j++;
            }
        }
        // check we read 40 bits (8bit x 5 ) + verify checksum in the last
        // byte
        if (j >= 40 && checkParity()) {
            float h = (float) ((dht11_dat[0] << 8) + dht11_dat[1]) / 10;
            if (h > 100) {
                h = dht11_dat[0]; // for DHT11
            }
            float c = (float) (((dht11_dat[2] & 0x7F) << 8) + dht11_dat[3]) / 10;
            if (c > 125) {
                c = dht11_dat[2]; // for DHT11
            }
            if ((dht11_dat[2] & 0x80) != 0) {
                c = -c;
            }
            final float f = c * 1.8f + 32;
            System.out.println("Humidity = " + h + " Temperature = " + c + "(" + f + "f)");
        } else {
            System.out.println("Data not good, skip");
        }

    }

    private boolean checkParity() {
        return dht11_dat[4] == (dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3] & 0xFF);
    }

    public static void main(final String ars[]) throws Exception {

        final DHT11 dht = new DHT11();

        for (int i = 0; i < 10; i++) {
            Thread.sleep(2000);
            dht.getTemperature(21);
        }

        System.out.println("Done!!");

    }
}
yglodt
  • 13,807
  • 14
  • 91
  • 127
Eric Smith
  • 168
  • 1
  • 10
  • This works for me, thanks a lot. I need ~25 reads (without delay) to get a valid value, but executed in a Loop this is no problem. – Simulant Mar 18 '17 at 23:36
  • Just out of curiosity: On which hardware type of Pi did you run this code? – yglodt Mar 26 '17 at 20:18
  • And another question: Do you think your code would also work for a DHT22 or AM2302? – yglodt Mar 26 '17 at 20:41
  • I used a Pi 2. I am not familiar with the DHT22 or AM2302, I have only tried it with the DHT11. – Eric Smith Mar 28 '17 at 02:12
  • I used a Pi 3 with a DHT22. works. Read the values in a loop until you get a valid result. – Simulant Apr 03 '17 at 12:04
  • I tried your code with a DHT22 on a Pi3. Half of the readings produce "data not good" but the other half is fine. Thanks for sharing ! – yglodt Apr 08 '17 at 18:16
  • I just noticed that the readings are more reliable when I use jdk1.8.0_121 from Oracle instead of 1.8.0_65-b17 from Raspbian. – yglodt Apr 08 '17 at 20:20
  • I seeing frequent reading errors with this code, but usually a second or third read will yield success. So it's usable actually on a Pi3. Nevertheless I will investigate https://github.com/adafruit/DHT-sensor-library/issues/48 to see if this fix will improve the reading quality in Java. – yglodt Nov 10 '17 at 08:50
  • Somehow I don't get what this line is good for: `GpioUtil.export(3, GpioUtil.DIRECTION_OUT);` To me it looks like this line does not provide any functionality to the code since it's just exporting a random pin, which is completely irrelevant for the rest of the code. – Brainsucker92 Jan 06 '20 at 12:10
1

I found that the RPi3b loaded with Raspian was too slow to use the code examples shown here already. Probably something to do with a java>pi4j>wiringpi propagation delay. My approach was as follows; after sending the command to activate the sensor, I read and time level changes on the required pin and save the values into an array. Then the parsing is done later. I get a 95% success rate with this code. I have it running in a Runnable class with a loop, so it has its own thread. If you find your timings are not quite right, try adjusting the counter offset. Also enable the println marked for debugging, it helps indicate which bits were not received (indicated by a 0).

public void scopeSensor(int pin){

    int x = 0;
    int lastState = 1;
    int valueRead = 1;
    int counter = 0; 
    int limit = 84;
    int timeout = 0;
    int[] results = new int[limit];    
    int[] pinState = new int[limit]; 


    //set pin low for 18ms to request data        
    Gpio.pinMode(pin, Gpio.OUTPUT);
    Gpio.digitalWrite(pin, Gpio.LOW);
    Gpio.delay(18);        

    //get ready to recieve data back from dht11
    Gpio.pinMode(pin, Gpio.INPUT);
    Gpio.pullUpDnControl(pin, Gpio.PUD_UP); //activate internal pullup



    while (x < limit) //84 sample changes to cover DHT11
    {           
        timeout = 0;
        counter = 2; //offset for time taken to perform read by pi
        while (valueRead == lastState && timeout < 300){
             Gpio.delayMicroseconds(1); 
            valueRead = Gpio.digitalRead(pin);

                    counter++;  
                    timeout++;
        }         

        if (timeout < 300)
        {
        results[x] = counter;
        pinState[x] = lastState;
        lastState = valueRead;
        }

        x++;
    }

    //reset our bytes
    dht11_dat[0] = dht11_dat[1] =dht11_dat[2]=dht11_dat[3]=dht11_dat[4]=0;
    int pointer = 0;
    for (int i = 4; i<x; i=i+2){
        //shift left so we are ready for next result

            pointer = ((i-4) / 2) / 8;

            dht11_dat[pointer] = dht11_dat[pointer] <<= 1;

            //if more than 30, mark bit as 1
        if (results[i] > 30){
            dht11_dat[pointer] = dht11_dat[pointer] |= 1;
        }        

    //for debugging only
       // System.out.println(Integer.toString(pinState[i]) + "," + Integer.toString(results[i]));           

    }

    int checksumByte = ((dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3]) & 0xff);
    if (dht11_dat[4]  != checksumByte){
        System.out.println("Warning: Bad checksum value!");
    }


        System.out.println("                                                                    Temp: " +  Integer.toString((dht11_dat[2])) + "  RH: " +  Integer.toString((dht11_dat[0])));

         WriteToFile.writeTextToFile("RH-T.csv", Integer.toString((dht11_dat[0])) + "," + Integer.toString((dht11_dat[2])));
}

the run method:

@Override
public void run() {
    ReadTempRH dht = new ReadTempRH();

    while (NbSerialApp.runThreads){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
            Logger.getLogger(ReadTempRH.class.getName()).log(Level.SEVERE, null, ex);
        }
        //getTempRH(7);
        scopeSensor(7);
    }
}

ReadTempRH constructor:

private final int[] dht11_dat = {0,0,0,0,0};

public ReadTempRH() {

    //setup wiringPi
    if (Gpio.wiringPiSetup() == -1){
        System.out.println("GPIO setup failed!");
        return;            
    }

    GpioUtil.export(3, GpioUtil.DIRECTION_OUT);
    System.out.println("GPIO setup complete!");

}

Sorry my code is a little messy, I haven't had time to tidy things up! But you should get the idea. I am normally a c# guy and Netbeans doesn't work like VS in the tidying up front!

Bobby Bridgeman
  • 121
  • 1
  • 2
0

I've the same issue and, unfortunately, I've read that Java cannot read data from DHT11/22 in this way for timing problems.

I've found in the Raspberry Forum a thread where you can find some solutions using SPI or pigpio. Another full Java possible solution is there.

I've received my sensor yesterday and I have not already tried this solutions. When I'll try, I'll let you know.

[EDIT]

Hi, I've solved the problem calling a python script (which uses the Adafruit Driver) and reading it's output. The python script is simply the example published in the Adafruit's library. I've only changed the output at line 48 in

print '{0:0.1f}   {1:0.1f}'.format(temperature, humidity)

The Java method that updates the values with new values is:

public void update() {
    String cmd = "sudo python DHTReader.py 11 4";
    try {
        String ret = "";
        try {
            String line;
            Process p = Runtime.getRuntime().exec(cmd.split(" "));
            p.waitFor();
            BufferedReader input = new BufferedReader
                    (new InputStreamReader(p.getInputStream()));
            while ((line = input.readLine()) != null) {
                output += (line + '\n');
            }
            input.close();
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
        ret.trim();
        if (ret.length() == 0) // Library is not present
            throw new RuntimeException(LIB_NOT_PRESENT_MESSAGE);
        else{
            // Error reading the the sensor, maybe is not connected. 
            if(ret.contains(ERROR_READING)){
                String msg = String.format(ERROR_READING_MSG,toString());
                throw new Exception(msg);
            }
            else{
                // Read completed. Parse and update the values
                String[] vals = ret.split("   ");
                float t = Float.parseFloat(vals[0].trim());
                float h = Float.parseFloat(vals[1].trim());
                if( (t != lastTemp) || (h != lastHum) ){
                    lastUpdate = new Date();
                    lastTemp = t;
                    lastHum = h;
                }
            }
        }
    } catch (Exception e) {
        System.out.println(e.getMessage());
        if( e instanceof RuntimeException)
            System.exit(-1);
    }
}

To make it work you have to install the Adafruit library as described in the linked page and change DHTReader.py with the path of the scipt. I'm working to build a "library". If you need, when I've finished, I'll publish it on GitHub.

Carlo
  • 9
  • 2
0

If you're always getting a High State it might be good to double check if the wiring is correct (or if any of the wires are broken, test it with a led).

I've used adafruit's tutorial in C and python and it worked on my DHT22.

Txugo
  • 5,008
  • 5
  • 33
  • 40
0

I got a solution with Java Native Interface JNI and WiringPi.

I am using java openjdk 7 in the raspberry pi. This is important to for support JNI capabilites of the JVM. I connected the DHT11 sensor to GPIO1 or pin 1.

From the package root at src/main/java you can install the library. I prepared a script in that you can run with the command:

sudo sh jniDHT11SensorReaderBuilder.sh

Then to test if it works try to run the DHT11SensorReader class with the command

sudo java org.mandfer.dht11.DHT11SensorReader

If it is all fine and you want more values every 1.5 seconds try to run the exercise 20 from the project root folder.

sh runPi.sh org.mandfer.sunfunpi4j.Ex20_DHT11_Native

If you have any issue, leave me a comment.

I hope it helps. Marc Andreu,

marcandreuf
  • 301
  • 3
  • 8
  • I am not able to compile this lib, Multiple errors first wiringpi.h not found, then no jni file. Can you give me workable script to get .so file for android – Manoj Jun 15 '17 at 11:03
  • Hello Manoj, did you try with the script [jniDHT11SensorReaderBuilder.sh](https://github.com/marcandreuf/sunfounder-sensors-raspi-4j/blob/master/src/main/java/jniDHT11SensorReaderBuilder.sh) ? Make sure you are actually running JAVA OpenJDK 1.7 and run it from folder "src/main/java/" in the raspberry pi. Also follow make sure that you installed Pi4j and the wiringpi libs for your [version of raspberry pi](http://pi4j.com/) – marcandreuf Jun 16 '17 at 15:32
0

Eric Smith's excellent code works fine for me with two small mods, First I edited this line:

if (counter > 16)

To:

if (counter > 30)

According to specs of dht11, the "1" bit is transmitted when the delay of the "Gpio.HIGH" is about 70us, and "0" bit is transmitted if the delay is 26-28us. It is clear that Java takes some time to execute, so it is safe to assume that if the delay is more than 30us the data must be "1". But this might be different value if the execution time of Java program is different in your machine (perhaps the processor of pi is faster/slower, there is more background programs etc). Therefore, the 30 is not necessarily the right value for every situation.

According to specs 1, it can be also noted that sender (raspberry pi, called MCU in the specs), should also send Gpio.HIGH for at least 18ms. After that, the MCU should send 20-40 us "Gpio.HIGH". I tested with System.nanoTime() how much time it takes for the Java to execute setting the Gpio.Pinmode to the "Input". It took something like 20us, so I added:

Gpio.delayMicroseconds(7);

...just to be sure that the Pin is HIGH for at least 20us so that the Sensor can register that signal and start sending its temperature and humidity data. After these changes, the temperature data is read almost always right, the success rate is something like 90%. Im not sure can the modifications work with another system, but hopefully these kind of modifications can make other experiments more successful!

(p.s. I also made the eternal loop so that the class is created everytime over and over again when the method is called.)

iltelko
  • 57
  • 10
  • so in your code, you´ll have something like this? Gpio.pinMode(pin, Gpio.OUTPUT); Gpio.digitalWrite(pin, Gpio.LOW); Gpio.delay(18); Gpio.digitalWrite(pin, Gpio.HIGH); Gpio.delayMicroseconds(7); Gpio.pinMode(pin, Gpio.INPUT); – goblingift Feb 25 '19 at 17:19