Read temperature / humidity with Java from Raspberry Pi 3 & DHT11

Introduction

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
M-07003.jpg

Preparation

Use Pi4J to control the GPIO of the Raspberry Pi from Java. For the Pi4J environment, refer to "Installing Pi4J" in the following article.

Graph the sensor information of Raspberry Pi and prepare an environment that can be checked with a Web browser

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 DHT11.png ** Actual wiring diagram ** DHT11_ブレッドボード.png

program

The program is a rewrite to Java by referring to the Python program linked below (with some changes).

DHT11 Python library

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;
    }
}

Compile and run

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

Read temperature / humidity with Java from Raspberry Pi 3 & DHT11
Read barometric pressure and temperature with Java from Raspberry Pi 3 & BMP180
Find Raspberry Pi from Android with mDNS
Display characters on I2C 1602 LCD with Raspberry Pi 3 & Java
Code Java from Emacs with Eclim
Process the motion detected by the motion sensor HC-SR501 with Raspberry Pi 3 & Java
Display user-defined characters on the I2C 1602 LCD with Raspberry Pi 3 & Java
Easily monitor the indoor environment-⑩ Obtain temperature / humidity / atmospheric pressure from BME280 (substitute) with Java (I2C / Pi4J)-
Call Java library from C with JNI
API integration from Java with Jersey Client
Raspberry Pi Zero with Ubuntu-style Yocto-gcc with libusb
Getting Started with Java Starting from 0 Part 1
Read xlsx file in Java with Selenium
Launched Redmine with Docker on Raspberry Pi 3
Execute Java code from cpp with cocos2dx
Easily monitor the indoor environment ~ ③ Get temperature / humidity / illuminance etc. from TI SensorTag CC2650 with Java (Bluetooth LE / bluez-dbus) ~
Run Rust from Java with JNA (Java Native Access)
Read a string in a PDF file with Java
[Java] Set the time from the browser with jsoup
[Android] Read from QR code image with zxing
I couldn't install docker with raspberry pi2 b +.
Text extraction in Java from PDF with pdfbox-2.0.8
Memo: [Java] Get Raspberry Pi data by SFTP