java 使用 pi4j 从 DHT11 读取温度

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28486159/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 13:40:59  来源:igfitidea点击:

read temperature from DHT11, using pi4j

javaraspberry-pisensorpi4j

提问by Júnior Mascarenhas

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?

我正在尝试使用 pi4j 从 DHT11 温度传感器读取温度数据。我按照本网站中用 c 和 python 编写的代码进行操作:http: //www.uugear.com/portfolio/dht11-h... or-module/ 但它不起作用。当我测试指令 'dht11Pin.getState()' 时,它总是处于 HIGH 状态,永远不会改变。我的代码有什么问题吗?

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

}

}

回答by Eric Smith

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.

我从原始海报的 java 代码开始,并用 com.pi4j.wiringpi 包替换了 com.pi4j.io.gpio 包引用。我最近在我的 Raspberry Pi 上安装了最新的 pi4j 包和 Wiringpi 版本。

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.

使用该包,下面的 Java 代码与该程序的 c 版本大致相同。我使用 DHT-11 得到大约 80% - 85% 的准确响应。这与我在 c 中使用 WiringPi 大致相同。

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!!");

    }
}

回答by Carlo

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

我有同样的问题,不幸的是,我读到 Java 无法以这种方式从 DHT11/22 读取数据以解决计时问题。

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.

我在 Raspberry 论坛中找到了一个帖子,您可以在其中找到一些使用 SPI 或 pigpio 的解决方案。另一个完整的 Java 可能的解决方案是那里

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

嗨,我已经解决了调用 python 脚本(使用Adafruit Driver)并读取它的输出的问题。python 脚本只是 Adafruit 库中发布的示例。我只更改了第 48 行的输出

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

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

使用新值更新值的 Java 方法是:

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.

要使其工作,您必须按照链接页面中的说明安装 Adafruit 库,并使用 scipt 的路径更改 DHTReader.py。我正在努力建立一个“图书馆”。如果你需要,当我完成后,我会在 GitHub 上发布它。

回答by Txugo

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).

如果您总是处于高状态,最好仔细检查接线是否正确(或者如果任何电线损坏,请使用 LED 进行测试)。

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

我在 C 和 python 中使用了 adafruit 的教程,它适用于我的 DHT22。

回答by marcandreuf

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

我得到了一个带有 Java Native Interface JNI 和 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.

我在树莓派中使用 java openjdk 7。这对于支持 JVM 的 JNI 功能很重要。我将 DHT11 传感器连接到 GPIO1 或引脚 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:

从位于 src/main/java 的包根目录中,您可以安装该库。我准备了一个脚本,您可以使用以下命令运行:

sudo sh jniDHT11SensorReaderBuilder.sh

须藤 sh jniDHT11SensorReaderBuilder.sh

Then to test if it works try to run the DHT11SensorReaderclass with the command

然后测试它是否有效尝试使用命令运行DHT11SensorReader

sudo java org.mandfer.dht11.DHT11SensorReader

须藤 java org.mandfer.dht11.DHT11SensorReader

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

如果一切正常并且您希望每 1.5 秒获得更多值,请尝试从项目根文件夹运行练习 20

sh runPi.sh org.mandfer.sunfunpi4j.Ex20_DHT11_Native

sh runPi.sh org.mandfer.sunfunpi4j.Ex20_DHT11_Native

If you have any issue, leave me a comment.

如果您有任何问题,请给我留言。

I hope it helps. Marc Andreu,

我希望它有帮助。马克·安德鲁

回答by iltelko

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.

根据dht11 的规范,当“Gpio.HIGH”的延迟大约为 70us 时,传输“1”位,如果延迟为 26-28us,则传输“0”位。很明显,Java 需要一些时间来执行,因此可以安全地假设,如果延迟超过 30us,则数据必须为“1”。但是,如果您的机器中 Java 程序的执行时间不同,这可能是不同的值(可能 pi 的处理器更快/更慢,有更多的后台程序等)。因此,30 不一定适用于每种情况。

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:

根据规范1,还可以注意到发送方(树莓派,规范中称为 MCU)也应该发送 Gpio.HIGH 至少 18 毫秒。之后,MCU 应发送 20-40 us "Gpio.HIGH"。我用 System.nanoTime() 测试了 Java 执行将 Gpio.Pinmode 设置为“输入”需要多长时间。它花了大约 20us,所以我补充说:

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!

...只是为了确保 Pin 处于高电平至少 20us,以便传感器可以注册该信号并开始发送其温度和湿度数据。在这些变化之后,读取温度数据几乎总是正确的,成功率大约是 90%。我不确定这些修改是否适用于其他系统,但希望这些修改可以使其他实验更加成功!

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

(ps 我还做了一个永恒的循环,以便在调用该方法时每次都会一遍又一遍地创建该类。)

回答by Bobby Bridgeman

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).

我发现使用 Raspian 加载的 RPi3b 太慢,无法使用此处显示的代码示例。可能与 java>pi4j>wiringpi 传播延迟有关。我的方法如下;发送激活传感器的命令后,我读取所需引脚上的时间电平变化并将值保存到数组中。然后解析稍后完成。我使用此代码获得了 95% 的成功率。我让它在一个带有循环的 Runnable 类中运行,所以它有自己的线程。如果您发现时间不太正确,请尝试调整计数器偏移。还启用标记为调试的 println,它有助于指示哪些位未收到(由 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:

ReadTempRH 构造函数:

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!

抱歉我的代码有点乱,我没有时间整理!但你应该明白。我通常是 ac# 人,而 Netbeans 在整理方面不像 VS 那样工作!