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