Use the Raspberry Pi 3 to read temperature and humidity information from the inexpensive and available temperature and humidity sensor DHT11. The programming language uses Java.
| DHT11 | 
|---|
![]()  | 
Use Pi4J to control the GPIO of the Raspberry Pi from Java. For the Pi4J environment, refer to "Installing Pi4J" in the following article.
This time, Raspberry Pi and DHT11 are connected as follows. The data sheet listed a 5KΩ resistor between VDD and the Data path, but I didn't have it, so I replaced it with 10KΩ (I'm not very familiar with this, so I'm fine for now ...).
(Reference) DHT11 datasheet
circuit diagram
** Actual wiring diagram **

The program is a rewrite to Java by referring to the Python program linked below (with some changes).
The program consists of the following three files.
When instantiating the main class DHT11, specify the ** GPIO number ** of the pin of the Raspberry Pi to which the Data pin of DHT11 is connected to the constructor. For example, if you are connected to pin number 8 on your Raspberry Pi, specify the corresponding GPIO number 15.
(Reference) Pin assignment of Raspberry Pi 3
DHT11Demo.java
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class DHT11Demo {
    public static void main(String[] args) throws InterruptedException {
        DHT11 dht11 = new DHT11(15); // use GPIO pin 15
        while (true) {
            DHT11Result result = dht11.read();
            if (result.isValid()) {
                System.out.println("Last valid input: " + new Date());
                System.out.printf("Temperature: %.1f C\n" , result.getTemperature());
                System.out.printf("Humidity:    %.1f %%\n", result.getHumidity());
            }
            TimeUnit.SECONDS.sleep(2);
        }
    }
}
DHT11.java
import java.util.ArrayList;
import java.util.List;
import com.pi4j.wiringpi.Gpio;
import com.pi4j.wiringpi.GpioUtil;
public class DHT11 {
    // DHT11 sensor reader class for Raspberry Pi
    private int pin; // GPIO pin number
    private List<Integer> data    = new ArrayList<>(10000);
    private List<Integer> lengths = new ArrayList<>(40); // will contain the lengths of data pull up periods
    public DHT11(int pin) {
        // setup wiringPi
        if (Gpio.wiringPiSetup() != 0) {
            throw new RuntimeException("Initialization of the GPIO has failed.");
        }
        this.pin = pin;
        GpioUtil.export(pin, GpioUtil.DIRECTION_OUT);
    }
    public DHT11Result read() {
        data.clear();
        lengths.clear();
        // change to output, then send start signal
        Gpio.pinMode(pin, Gpio.OUTPUT);
        Gpio.digitalWrite(pin, Gpio.LOW);
        Gpio.delay(18); // ms
        Gpio.digitalWrite(pin, Gpio.HIGH);
        // change to input
        Gpio.pinMode(pin, Gpio.INPUT);
        // collect data into a list
        collectInput();
        // parse lengths of all data pull up periods
        parseDataPullUpLengths();
        // if bit count mismatch, return error (4 byte data + 1 byte checksum)
        if (lengths.size() != 40)
            return new DHT11Result(DHT11Result.ERR_MISSING_DATA, 0.0, 0.0, data.size(), lengths.size());
        // calculate bits from lengths of the pull up periods
        boolean[] bits = calculateBits();
        // we have the bits, calculate bytes
        byte[] bytes = bitsToBytes(bits);
        // calculate checksum and check
        if (bytes[4] != calculateChecksum(bytes))
            return new DHT11Result(DHT11Result.ERR_CRC, 0.0, 0.0, data.size(), lengths.size());
        // ok, we have valid data, return it
        return new DHT11Result(DHT11Result.ERR_NO_ERROR,
                Double.parseDouble(bytes[2] + "." + bytes[3]),
                Double.parseDouble(bytes[0] + "." + bytes[1]),
                data.size(), lengths.size());
    }
    // this is used to determine where is the end of the data
    private final int MAX_UNCHANGED_COUNT = 500;
    private void collectInput() {
        // collect the data while unchanged found
        int unchangedCount = 0;
        int last = -1;
        while (true) {
            int current = Gpio.digitalRead(pin);
            data.add(current);
            if (last != current) {
                unchangedCount = 0;
                last = current;
            } else {
                if (++unchangedCount > MAX_UNCHANGED_COUNT) break;
            }
        }
    }
    protected enum SignalTransition {
        STATE_INIT_PULL_DOWN      ,
        STATE_INIT_PULL_UP        ,
        STATE_DATA_FIRST_PULL_DOWN,
        STATE_DATA_PULL_UP        ,
        STATE_DATA_PULL_DOWN
    };
    private void parseDataPullUpLengths() {
        SignalTransition state = SignalTransition.STATE_INIT_PULL_DOWN;
        int currentLength = 0; // will contain the length of the previous period
        for (int current : data) {
            currentLength++;
            switch (state) {
            case STATE_INIT_PULL_DOWN:
                if (current == Gpio.LOW) {
                    // ok, we got the initial pull down
                    state = SignalTransition.STATE_INIT_PULL_UP;
                }
                break;
            case STATE_INIT_PULL_UP:
                if (current == Gpio.HIGH) {
                    // ok, we got the initial pull up
                    state = SignalTransition.STATE_DATA_FIRST_PULL_DOWN;
                }
                break;
            case STATE_DATA_FIRST_PULL_DOWN:
                if (current == Gpio.LOW) {
                    // we have the initial pull down, the next will be the data pull up
                    state = SignalTransition.STATE_DATA_PULL_UP;
                }
                break;
            case STATE_DATA_PULL_UP:
                if (current == Gpio.HIGH) {
                    // data pulled up, the length of this pull up will determine whether it is 0 or 1
                    currentLength = 0;
                    state = SignalTransition.STATE_DATA_PULL_DOWN;
                }
                break;
            case STATE_DATA_PULL_DOWN:
                if (current == Gpio.LOW) {
                    // pulled down, we store the length of the previous pull up period
                    lengths.add(currentLength);
                    state = SignalTransition.STATE_DATA_PULL_UP;
                }
                break;
            }
        }
    }
    private boolean[] calculateBits() {
        boolean[] bits = new boolean[40];
        int longestPullUp  = lengths.stream().mapToInt(Integer::intValue).max().getAsInt();
        int shortestPullUp = lengths.stream().mapToInt(Integer::intValue).min().getAsInt();
        // use the halfway to determine whether the period it is long or short
        int halfway = shortestPullUp + (longestPullUp - shortestPullUp) / 2;
        int i = 0;
        for (int length : lengths) bits[i++] = length > halfway;
        return bits;
    }
    private byte[] bitsToBytes(boolean[] bits) {
        byte[] bytes = new byte[5];
        byte   value = 0;
        for (int i = 0; i < bits.length; i ++) {
            value <<= 1;
            if (bits[i]) value |= 1;
            if (i % 8 == 7) {
                bytes[i / 8] = value;
                value = 0;
            }
        }
        return bytes;
    }
    private byte calculateChecksum(byte[] bytes) {
        return (byte)(bytes[0] + bytes[1] + bytes[2] + bytes[3]);
    }
}
DHT11Result.java
public class DHT11Result {
    // DHT11 sensor result returned by DHT11.read() method
    public static final int ERR_NO_ERROR     = 0;
    public static final int ERR_MISSING_DATA = 1;
    public static final int ERR_CRC          = 2;
    private int errorCode = ERR_NO_ERROR;
    private double temperature;
    private double humidity;
    private int listElements;                // for debug
    private int detectedBits;                // for debug
    DHT11Result(int errorCode, double temperature, double humidity, int listElements, int detectedBits) {
        this.errorCode   = errorCode;
        this.temperature = temperature;
        this.humidity    = humidity;
        this.listElements = listElements;
        this.detectedBits = detectedBits;
    }
    public boolean isValid() {
        return errorCode == ERR_NO_ERROR;
    }
    public int getErrorCode() {
        return errorCode;
    }
    public double getTemperature() {
        return temperature;
    }
    public double getHumidity() {
        return humidity;
    }
    double getListElements() {
        return listElements;
    }
    double getDetectedBits() {
        return detectedBits;
    }
}
Place the above three files in a suitable directory and compile.
pi@raspberrypi:~ $ pi4j -c DHT11Demo.java
--------------------------------------------
Pi4J - Compiling: DHT11Demo.java
--------------------------------------------
+ javac -classpath '.:classes:*:classes:/opt/pi4j/lib/*' -d . DHT11Demo.java
pi@raspberrypi:~ $
After compiling, execute it with root privileges. The temperature / humidity data is read and displayed every 2 seconds. Enter Ctrl + C to exit.
pi@raspberrypi:~ $ sudo pi4j DHT11Demo
+ java -classpath '.:classes:*:classes:/opt/pi4j/lib/*' DHT11Demo
Last valid input: Sun May 13 11:02:56 JST 2018
Temperature: 26.6 C
Humidity:    52.0 %
Last valid input: Sun May 13 11:02:58 JST 2018
Temperature: 26.7 C
Humidity:    52.0 %
Last valid input: Sun May 13 11:03:00 JST 2018
Temperature: 26.7 C
Humidity:    52.0 %
Last valid input: Sun May 13 11:03:02 JST 2018
Temperature: 26.7 C
Humidity:    52.0 %
Last valid input: Sun May 13 11:03:06 JST 2018
Temperature: 26.8 C
Humidity:    51.0 %
^Cpi@raspberrypi:~ $
Due to the time-critical communication of DHT11, reading from Java running on a multitasking OS often fails relatively often, especially at startup several times in a row. When actually using it, check the read result with the isValid () method of the DHT11Result class as in the sample code (DHT11Demo.java), and retry until it succeeds (if the method returns false, the read fails. I have).
Recommended Posts