简体   繁体   English

使用pi4j从DHT11读取温度

[英]read temperature from DHT11, using pi4j

I'm trying to read temperature data from a DHT11 temperature sensor, using pi4j. 我正在尝试使用pi4j从DHT11温度传感器读取温度数据。 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. 我按照c和python编写的代码访问了这个站点: http//www.uugear.com/portfolio/dht11-h ... or-module /但是它没有用。 when I test the instruction 'dht11Pin.getState()' it's always in HIGH state, never changing. 当我测试指令'dht11Pin.getState()'时,它总是处于HIGH状态,永远不会改变。 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));
}

} }

I started with the original poster's java code, and replaced the com.pi4j.io.gpio package references with the com.pi4j.wiringpi package. 我从原始海报的java代码开始,用com.pi4j.wiringpi包替换了com.pi4j.io.gpio包引用。 I had recently installed the newest pi4j package and wiringpi version on my Raspberry Pi. 我最近在我的Raspberry Pi上安装了最新的pi4j包和wirespi版本。

Using that package the Java code below works approximately the same as the c version of this program. 使用该包,下面的Java代码与该程序的c版本大致相同。 I am getting about 80% - 85% accurate responses with a DHT-11. 使用DHT-11,我得到了大约80% - 85%的准确答案。 Which is about the same as I was getting using wiringPi in c. 这与我在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!!");

    }
}

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. 我在Raspberry论坛上发现了一个线程,你可以在其中找到一些使用SPI或pigpio的解决方案。 Another full Java possible solution is there . 另一个完整的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. 嗨,我已经解决了调用python脚本(使用Adafruit驱动程序 )并读取它的输出的问题。 The python script is simply the example published in the Adafruit's library. python脚本只是Adafruit库中发布的示例。 I've only changed the output at line 48 in 我只更改了第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. 要使其工作,您必须按照链接页面中的描述安装Adafruit库,并使用scipt的路径更改DHTReader.py。 I'm working to build a "library". 我正在努力建立一个“图书馆”。 If you need, when I've finished, I'll publish it on GitHub. 如果你需要,当我完成后,我会在GitHub上发布它。

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. 我在C和python中使用过adafruit的教程 ,它在我的DHT22上工作。

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. 我在raspberry pi中使用java openjdk 7。 This is important to for support JNI capabilites of the JVM. 这对于支持JVM的JVM功能非常重要。 I connected the DHT11 sensor to GPIO1 or pin 1. 我将DHT11传感器连接到GPIO1或引脚1。

From the package root at src/main/java you can install the library. 从src / main / java的包根目录,您可以安装库。 I prepared a script in that you can run with the command: 我准备了一个脚本,您可以使用以下命令运行:

sudo sh jniDHT11SensorReaderBuilder.sh sudo sh jniDHT11SensorReaderBuilder.sh

Then to test if it works try to run the DHT11SensorReader class with the command 然后测试它是否有效尝试使用该命令运行DHT11SensorReader

sudo java org.mandfer.dht11.DHT11SensorReader 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. 如果一切正常并且您希望每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, Marc Andreu,

Eric Smith's excellent code works fine for me with two small mods, First I edited this line: Eric Smith的优秀代码适合我使用两个小mod,首先我编辑了这一行:

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. 根据dht11的规格,当“Gpio.HIGH”的延迟大约为70us时发送“1”位,如果延迟为26-28us则发送“0”位。 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". 很明显,Java需要一些时间来执行,因此可以安全地假设如果延迟超过30us,则数据必须为“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). 但是如果你的机器中Java程序的执行时间不同(这可能是pi的处理器更快/更慢,有更多的后台程序等),这可能是不同的值。 Therefore, the 30 is not necessarily the right value for every situation. 因此,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. 根据规范1 ,还可以注意到发送者(raspberry pi,在规范中称为MCU),也应该发送Gpio.HIGH至少18ms。 After that, the MCU should send 20-40 us "Gpio.HIGH". 之后,MCU应发送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". 我使用System.nanoTime()测试了Java执行将Gpio.Pinmode设置为“Input”所需的时间。 It took something like 20us, so I added: 它需要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. ...只是为了确保引脚为高电平至少20us,以便传感器可以注册该信号并开始发送其温度和湿度数据。 After these changes, the temperature data is read almost always right, the success rate is something like 90%. 在这些变化之后,温度数据几乎总是正确读取,成功率大约为90%。 Im not sure can the modifications work with another system, but hopefully these kind of modifications can make other experiments more successful! 我不确定修改是否可以与其他系统一起使用,但希望这些修改可以使其他实验更成功!

(ps I also made the eternal loop so that the class is created everytime over and over again when the method is called.) (ps我也创建了永久循环,以便在调用方法时反复创建类。)

I found that the RPi3b loaded with Raspian was too slow to use the code examples shown here already. 我发现装有Raspian的RPi3b太慢了,无法使用此处显示的代码示例。 Probably something to do with a java>pi4j>wiringpi propagation delay. 可能与java> pi4j> wirespi传播延迟有关。 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. 我使用此代码获得95%的成功率。 I have it running in a Runnable class with a loop, so it has its own thread. 我让它在带有循环的Runnable类中运行,因此它有自己的线程。 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). 同时启用标记为调试的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 ac# guy and Netbeans doesn't work like VS in the tidying up front! 我通常是ac#guy,而Netbeans在整理前端不像VS那样工作!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM