Считывать температуру с DHT11, используя pi4j

Я пытаюсь прочитать данные о температуре от датчика температуры DHT11, используя pi4j. Я следовал коду, написанному на c и python на этом сайте: http://www.uugear.com/portfolio/dht11-h... or-module / Но он не работает. когда я тестирую инструкцию dht11Pin.getState(), она всегда находится в состоянии HIGH и никогда не меняется. Что-то не так в моем коде?

Ниже мой код:

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

}

6 ответов

Решение

Я начал с исходного java-кода автора и заменил ссылки на пакеты com.pi4j.io.gpio на пакет com.pi4j.wiringpi. Я недавно установил новейший пакет pi4j и версию wiringpi на мой Raspberry Pi.

При использовании этого пакета приведенный ниже код Java работает примерно так же, как и версия c этой программы. Я получаю около 80% - 85% точных ответов с DHT-11. Что примерно так же, как я использовал wiringPi в c.

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

    }
}

Я обнаружил, что RPi3b, загруженный Raspian, был слишком медленным, чтобы использовать примеры кода, показанные здесь уже. Вероятно, что-то делать с задержкой распространения 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])));
}

метод запуска:

@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:

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

}

Извините, мой код немного грязный, у меня не было времени привести в порядок вещи! Но вы должны понять. Я обычно aC# парень, и Netbeans не работает как VS в уборке сразу!

У меня та же проблема, и, к сожалению, я прочитал, что Java не может читать данные из DHT11/22 таким образом из-за проблем с синхронизацией.

На Raspberry Forum я нашел ветку, где можно найти некоторые решения с использованием SPI или pigpio. Есть еще одно полное возможное решение Java.

Я получил свой датчик вчера, и я еще не пробовал это решение. Когда я попробую, я дам вам знать.

[РЕДАКТИРОВАТЬ]

Привет, я решил проблему вызова скрипта Python (который использует драйвер Adafruit) и чтения его вывода. Скрипт python - это просто пример, опубликованный в библиотеке Adafruit. Я только изменил вывод в строке 48 в

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

Метод 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);
    }
}

Чтобы это работало, вы должны установить библиотеку Adafruit, как описано на связанной странице, и изменить DHTReader.py с помощью пути к сценарию. Я работаю над созданием "библиотеки". Если вам нужно, когда я закончу, я опубликую его на GitHub.

Если вы всегда получаете высокое состояние, возможно, было бы хорошо дважды проверить правильность проводки (или, если какой-либо из проводов оборван, проверить его с помощью светодиода).

Я использовал учебник Adafruit по Си и Python, и он работал на моем DHT22.

Я получил решение с помощью Java Native Interface JNI и WiringPi.

Я использую Java OpenJDK 7 в Raspberry Pi. Это важно для поддержки возможностей JNI JVM. Я подключил датчик DHT11 к GPIO1 или контакту 1.

Из корня пакета в src/main/java вы можете установить библиотеку. Я подготовил скрипт, который вы можете запустить с помощью команды:

sudo sh jniDHT11SensorReaderBuilder.sh

Затем, чтобы проверить, работает ли он, попробуйте запустить класс DHT11SensorReader с помощью команды

sudo java org.mandfer.dht11.DHT11SensorReader

Если все в порядке и вам нужно больше значений каждые 1,5 секунды, попробуйте запустить упражнение 20 из корневой папки проекта.

sh runPi.sh org.mandfer.sunfunpi4j.Ex20_DHT11_Native

Если у вас есть какие-либо проблемы, оставьте мне комментарий.

Я надеюсь, что это помогает. Марк Андреу,

Отличный код Эрика Смита прекрасно работает для меня с двумя небольшими модами. Сначала я отредактировал эту строку:

if (counter > 16)

Для того, чтобы:

if (counter > 30)

Согласно спецификациям dht11, бит "1" передается, когда задержка "Gpio.HIGH" составляет около 70 мкс, а бит "0" передается, если задержка составляет 26–28 мкс. Понятно, что для выполнения Java требуется некоторое время, поэтому можно с уверенностью предположить, что если задержка превышает 30us, данные должны быть равны "1". Но это может иметь другое значение, если время выполнения Java-программы на вашем компьютере отличается (возможно, процессор pi быстрее / медленнее, есть больше фоновых программ и т. Д.). Таким образом, 30 не обязательно является правильным значением для каждой ситуации.

Согласно спецификации 1, можно также отметить, что отправитель (raspberry pi, в спецификации называемый MCU) также должен отправлять Gpio.HIGH не менее 18 мс. После этого MCU должен отправить нам 20-40 "Gpio.HIGH". Я протестировал с System.nanoTime(), сколько времени требуется Java для выполнения установки Gpio.Pinmode на "Input". Потребовалось что-то вроде 20us, поэтому я добавил:

Gpio.delayMicroseconds(7);

... просто чтобы быть уверенным, что ПИН ВЫСОКИЙ как минимум на 20 мкс, чтобы датчик мог зарегистрировать этот сигнал и начать отправку данных о температуре и влажности. После этих изменений данные о температуре читаются почти всегда правильно, вероятность успеха составляет около 90%. Я не уверен, что модификации могут работать с другой системой, но, надеюсь, такие модификации могут сделать другие эксперименты более успешными!

(PS Я также сделал вечный цикл, чтобы класс создавался каждый раз снова и снова при вызове метода.)

Другие вопросы по тегам