Adafruit SHT31-D 和 Raspberry Pi2 -- 无法从传感器读取数据

Adafruit SHT31-D and Raspberry Pi2 -- Unable to read data from sensor

希望你们中的一位能帮助我!

我正在尝试使用 Adafruit SHT31-D (an i2c device) board with my Pi2. I am going off of this datasheet 来指导我的编码工作。我正在使用 Wiring Pi (wiringpi.com) 来简化事情。

我能够成功打开与设备的连接,发送命令似乎也能正常工作,但我无法读回数据!这是我放在一起的小型迷你图书馆。我希望你们中的某个人可能对这类事情有一些经验,并且能够帮助我找出我哪里出错了。

为了排除传感器硬件的任何可能问题,我用我的 Arduino UNO 对其进行了测试,它可以正常工作。

这是我的 C++ 代码:

SHT3x.h

#pragma once

/* Sensor Commands */
#define DEFAULT_SHT_ADDR 0x44
#define MEAS_HREP_STRETCH 0x2C06
#define MEAS_MREP_STRETCH 0x2C0D
#define MEAS_LREP_STRETCH 0x2C10
#define MEAS_HREP 0x2400
#define MEAS_MREP 0x240B
#define MEAS_LREP 0x2416

#include <cstdint>


class SHT3x {

  public:
    SHT3x(const uint8_t& i2cAddr);
    float readHumidity(const uint16_t& command) const;
    float readTempC(const uint16_t& command) const;
    float readTempF(const uint16_t& command) const;

  private:
    int8_t _fd;
    uint8_t _header;
    uint32_t getMeasurement(const uint16_t& command) const;
    void sendCommand(const uint16_t& command) const;
    uint32_t receiveData(void) const;
};

SHT3x.cpp

#include <stdexcept>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include "SHT3x.h"


SHT3x::SHT3x(const uint8_t& i2cAddr) {
    _fd = wiringPiI2CSetup(i2cAddr);
    _header = i2cAddr << 1;
    if (_fd < 0) {
        throw std::runtime_error("Unable to connect");
    }
}

float SHT3x::readHumidity(const uint16_t& command) const {
    uint32_t raw_data = getMeasurement(command);
    if (!raw_data) {
        throw std::runtime_error("Bad Reading.");
    }
    uint16_t raw_humidity = raw_data & 0xFFFF;
    float humidity = 100.0 * ((float) raw_humidity / (float) 0xFFFF);
    return humidity;
}

float SHT3x::readTempC(const uint16_t& command) const {
    uint32_t raw_data = getMeasurement(command);
    if (!raw_data) {
        throw std::runtime_error("Bad Reading.");
    }
    uint16_t raw_temp = raw_data >> 16;
    float tempC = -45.0 + (175.0 * ((float) raw_temp / (float) 0xFFFF));
    return tempC;
}

float SHT3x::readTempF(const uint16_t& command) const {
    uint32_t raw_data = getMeasurement(command);
    if (!raw_data) {
        throw std::runtime_error("Bad Reading.");
    }
    uint16_t raw_temp = raw_data >> 16;
    float tempF = -49.0 + (315.0 * ((float) raw_temp / (float) 0xFFFF));
    return tempF;
}

uint32_t SHT3x::getMeasurement(const uint16_t& command) const {
    try {
        sendCommand(command);
    } catch (std::runtime_error& e) {
        throw;
    }
    return receiveData();
}

void SHT3x::sendCommand(const uint16_t& command) const {
    // break command into bytes
    uint8_t MSB = command >> 8;
    uint8_t LSB = command & 0xFF;

    // send header
    int8_t ack = wiringPiI2CWrite(_fd, _header);

    // send command
    ack &= wiringPiI2CWrite(_fd, MSB);
    ack &= wiringPiI2CWrite(_fd, LSB);

    // handle errors
    if (ack) {
        throw std::runtime_error("Sending command failed.");
    }
}

uint32_t SHT3x::receiveData(void) const {
    uint32_t data;

    // send header
    uint8_t read_header = _header | 0x01;
    int8_t ack = wiringPiI2CWrite(_fd, read_header);

    // handle errors
    if (ack) throw std::runtime_error("Unable to read data.");

    // read data
    data = wiringPiI2CRead(_fd);
    for (size_t i = 0; i < 4; i++) {
        printf("Data: %d\n", data);
        data <<= 8;
        if (i != 1) {
            data |= wiringPiI2CRead(_fd);
        } else {
            wiringPiI2CRead(_fd);   // skip checksum
        }
    }
    wiringPiI2CRead(_fd);   // second checksum
    return data;
}

SHT31使用的是16bit的读写,与其用2个8bit的写,不如用wiringpi的16bit的写。 wiringPiI2CWriteReg16().同样的事情也适用于读取。

下面是我在 PI 上阅读 sht31-d 所做的早期副本。除了 i2c-dev,它没有依赖项。加热器 enable/disable 不工作,但 softreset、clearstatus、getserial 和 get temp/humid 都很好。

/*
 * Referances
 * https://www.kernel.org/doc/Documentation/i2c/dev-interface
 * https://github.com/adafruit/Adafruit_SHT31
 * https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/Humidity_and_Temperature_Sensors/Sensirion_Humidity_and_Temperature_Sensors_SHT3x_Datasheet_digital.pdf
 *
 * This depends on i2c dev lib
 * sudo apt-get install libi2c-dev
 *
 * Below is also a good one to have, but be careful i2cdump from the below cause the sht31 interface to become unstable for me
 * and requires a hard-reset to recover correctly.
 * sudo apt-get install i2c-tools
 *
 * on PI make sure below 2 commands are in /boot/config.txt
 * dtparam=i2c_arm=on
 * dtparam=i2c1_baudrate=10000
 * I know we are slowing down the baurate from optimal, but it seems to be the most stable setting in my testing.
 * add another 0 to the above baudrate for max setting, ie dtparam=i2c1_baudrate=100000
 */

#include <linux/i2c-dev.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <elf.h>
#include <unistd.h>

#define SHT31_INTERFACE_ADDR       1
#define SHT31_DEFAULT_ADDR         0x44
#define SHT31_READ_SERIALNO        0x3780
#define SHT31_MEAS_HIGHREP_STRETCH 0x2C06 // Doesn't work on PI
#define SHT31_MEAS_MEDREP_STRETCH  0x2C0D // Seems to work on PI but shouldn't
#define SHT31_MEAS_LOWREP_STRETCH  0x2C10 // Seems to work on PI but shouldn't
#define SHT31_MEAS_HIGHREP         0x2400 // Doesn't work on PI
#define SHT31_MEAS_MEDREP          0x240B
#define SHT31_MEAS_LOWREP          0x2416
#define SHT31_READSTATUS           0xF32D
#define SHT31_CLEARSTATUS          0x3041
#define SHT31_SOFTRESET            0x30A2
#define SHT31_HEATER_ENABLE        0x306D
#define SHT31_HEATER_DISABLE       0x3066

#define CHECK_BIT(var,pos) (((var)>>(pos)) & 1)

/*
 * delay:
 *  Wait for some number of milliseconds
 *********************************************************************************
 */

void delay (unsigned int howLong)
{
  struct timespec sleeper, dummy ;

  sleeper.tv_sec  = (time_t)(howLong / 1000) ;
  sleeper.tv_nsec = (long)(howLong % 1000) * 1000000 ;

  nanosleep (&sleeper, &dummy) ;
}

/*
*
* CRC-8 formula from page 14 of SHT spec pdf
*
* Test data 0xBE, 0xEF should yield 0x92
*
* Initialization data 0xFF
* Polynomial 0x31 (x8 + x5 +x4 +1)
* Final XOR 0x00
*/
uint8_t crc8(const uint8_t *data, int len)
{
  const uint8_t POLYNOMIAL = 0x31;
  uint8_t crc = 0xFF;
  int j;
  int i;

  for (j = len; j; --j ) {
    crc ^= *data++;

    for ( i = 8; i; --i ) {
      crc = ( crc & 0x80 )
            ? (crc << 1) ^ POLYNOMIAL
            : (crc << 1);
    }
  }
  return crc;
}


/*
 * 
 * buffer should return with data read, size defined by readsize
 *********************************************************************************
*/

int writeandread(int fd, uint16_t sndword, uint8_t *buffer, int readsize)
{
  int rtn;
  uint8_t snd[3];
  // Split the 16bit word into two 8 bits that are flipped.
  snd[0]=(sndword >> 8) & 0xff;
  snd[1]=sndword & 0xff;

  rtn = write(fd, snd, 2);
  if ( rtn != 2 ) {
    return 1;
  } 

  if (readsize > 0) {
    delay(10);
    rtn = read(fd, buffer, readsize);
    if ( rtn < readsize) {
      return 2;
    }
  }

  return 0;
}

void printserialnum(int file)
{
  uint8_t buf[10];
  int rtn;

  rtn = writeandread(file, SHT31_READ_SERIALNO, buf, 6);
  if (rtn != 0)
    printf("ERROR:- Get serial i2c %s failed\n",(rtn==1?"write":"read"));
  else {
    if (buf[2] != crc8(buf, 2) || buf[5] != crc8(buf+3, 2))
      printf("WARNING:- Get serial CRC check failed, don't trust result\n");

    uint32_t serialNo = ((uint32_t)buf[0] << 24)
    | ((uint32_t)buf[1] << 16)
    | ((uint32_t)buf[3] << 8)
    | (uint32_t)buf[4];
    printf("Serial# = %d\n",serialNo);
  }
}

void printtempandhumidity(int file)
{
  uint8_t buf[10];
  int rtn;

    rtn = writeandread(file, SHT31_MEAS_MEDREP_STRETCH, buf, 6);

  if (rtn != 0)
    printf("ERROR:- Get temp/humidity i2c %s failed\n",(rtn==1?"write":"read"));
  else {
    if ( buf[2] != crc8(buf, 2) || buf[5] != crc8(buf+3, 2))
      printf("WARNING:- Get temp/humidity CRC check failed, don't trust results\n");

    uint16_t ST, SRH;
    ST = buf[0];
    ST <<= 8;
    ST |= buf[1];

    SRH = buf[3];
    SRH <<= 8;
    SRH |= buf[4];

    double stemp = ST;
    stemp *= 175;
    stemp /= 0xffff;
    stemp = -45 + stemp;

    double stempf = ST;
    stempf *= 315;
    stempf /= 0xffff;
    stempf = -49 + stempf;

    printf("Temperature %.2fc - %.2ff\n",stemp,stempf);

    double shum = SRH;
    shum *= 100;
    shum /= 0xFFFF;

    printf("Humidity %.2f%%\n",shum);
  }
}

void printBitStatus(uint16_t stat)
{
  printf("Status\n");
  printf("    Checksum status %d\n", CHECK_BIT(stat,0));
  printf("    Last command status %d\n", CHECK_BIT(stat,1));
  printf("    Reset detected status %d\n", CHECK_BIT(stat,4));
  printf("    'T' tracking alert %d\n", CHECK_BIT(stat,10));
  printf("    'RH' tracking alert %d\n", CHECK_BIT(stat,11));
  printf("    Heater status %d\n", CHECK_BIT(stat,13));
  printf("    Alert pending status %d\n", CHECK_BIT(stat,15));
}

void printstatus(int file)
{
  uint8_t buf[10];
  int rtn;

  rtn = writeandread(file, SHT31_READSTATUS, buf, 3);
  if (rtn != 0)
    printf("ERROR:- readstatus %s failed\n",(rtn==1?"write":"read"));
  else {
    if ( buf[2] != crc8(buf, 2))
      printf("WARNING:- Get status CRC check failed, don't trust results\n");

    uint16_t stat = buf[0];
    stat <<= 8;
    stat |= buf[1];  
    printBitStatus(stat);
  }
}



void clearstatus(int file)
{
  if( writeandread(file, SHT31_CLEARSTATUS, NULL, 0) != 0)
    printf("ERROR:- sht31 clear status failed\n");
  else 
    printf("Clearing status - ok\n");
}

void softreset(int file)
{
  if( writeandread(file, SHT31_SOFTRESET, NULL, 0) != 0)
    printf("ERROR:- sht31 soft reset failed\n");
  else  
    printf("Soft reset - ok\n");
}

void enableheater(int file)
{
  if( writeandread(file, SHT31_HEATER_ENABLE, NULL, 0) != 0)
    printf("ERROR:- sht31 heater enable failed\n");
  else 
    printf("Enabiling heater - ok\n");
}

void disableheater(int file)
{
  if( writeandread(file, SHT31_HEATER_DISABLE, NULL, 0) != 0)
    printf("ERROR:- sht31 heater enable failed\n");
  else  
    printf("Disableing heater - ok\n");
}

int main()
{

  int file;
  char filename[20];

  snprintf(filename, 19, "/dev/i2c-%d", SHT31_INTERFACE_ADDR);
  file = open(filename, O_RDWR);
  if (file < 0) {
    printf("ERROR:- Can't open %s\n",filename);
    exit(1);
  }

  if (ioctl(file, I2C_SLAVE, SHT31_DEFAULT_ADDR) < 0) {
    printf("ERROR:- Connecting to sht31 I2C address 0x%02hhx\n", SHT31_DEFAULT_ADDR);
    exit(1);
  }

  softreset(file);
  printtempandhumidity(file);
  printstatus(file);

  close(file);

  return 0;
}