精帖  树莓派使用SHT30温度/湿度传感器 通过I2C获取温湿度教程温度传感器,温湿度传感器,GPIO编程,Python,传感器

By 该哈哈哈

2023-05-11 23:38:48

浏览量103205

已赞10

本人小白,在使用SHT3XX 系列的温度传感的一些过程,特此分享给大家。首先我们得了解树莓派外部GPIO的I2C接口。

Image

一:传感器与树莓派接线

sht30的vcc——树莓派的3.3V

sht30的gnd——树莓派的GND

sht30的sda——树莓派的SDA(BMC编码2)

sht30的scl——树莓派的SCL(BMC编码3)

Image

二:开启树莓派的i2c接口

我们所用的sht30传感器是i2c通信的,所以我们还必须将树莓派的i2c接口打开才能使用它,输入下面命令进入树莓派设置界面

sudo raspi-config

选择第五项:Interfacing Options ,在选择 I2C 按回车确认开启

Image

Image

Image

然后输入sudo reboot 把树莓派重启一下。这就完成树莓派的设置。

二、sht30获取温湿度的命令地址

关于sht30的官方解释文档我们可以查看官网的产品介绍

传感器获取温湿度的命令地址如下 single shot:在这种模式下,一个发出的测量命令触发- -个数据对的采集。每个数据对由一个16 位温度,和一个16位湿度值(按此顺序)组成。在传输过程中,每个数据值总是紧跟着一个CRC校验和,可以选择不同的测量命令。16 位命令如表8所示。它们与可重复性(低、中、高)和时钟拉伸(启用或禁用)不同。传感器完成测量后,主程序可以通过发送一一个START条件和一个I2C读取头来读取测量结果(RH& T对)。传感器将接收读头的接收,并发送两个字节的数据(温度),接着是一个字节的CRC校验和另外两个字节的数据(相对湿度),然后是一个字节的CRC校验和。每个字节必须承认微控制器与ACK条件传感器继续发送数据。如果传感器在任何字节的数据之后没有从主程序接收到ACK,它就不会继续发送数据。传感器将首先发送温度值,然后发送相对湿度值。在收到湿度值的校验和后,就应该发送一个NACK和停止条件。

Image

Periodic Data Acquisition Mode:在这种模式下,一个发出的测量命令产生一对数据流。每个数据对由一个16 位温度和一个16位湿度值(按此顺序)组成。在周期模式下,可以选择不同的测量命令。相应的16位命令如表9所示。它们与重复性(低、中、高)和数据采集频率(测量每秒0.5, 1, 2, 4和10, MPS)不同。在这种模式下不能选择时钟沿伸。

测:量数据的传输可以通过表10 所示的获取数据命令来启动。如果没有测量数据,I2C 读取头会反应出一个NACK (表10 中的9位),从而停止通信。在读出命令获取数据之后,数据存储器被清除,即测量数据不会存在。


三、C代码

当树莓派的硬件完成好后,我们就可以开始编程了。c编程当然离不开ictol这个函数,它提供了强大的功能,能够让开发者调用它获取到底层的数据。设备连接好后我们就可以查找传感器的i2c通信地址。

安装好i2c库和工具

sudo apt-get install i2c-tools

之后我们就会发现出现了这个文件

Image

然后查看传感器地址

sudo i2cdetect -y -a 1
Image

0x44就是sht30的通信地址,之后我们就要打开上面我们看到的文件i2c-1

int sht_open(int i2c_addr, uint8_t sht_addr)
{
    char    i2c_filename[10];
    int     fd = -1;
    int     rv = -1;

    snprintf(i2c_filename, 19, "/dev/i2c-%d", i2c_addr);
    fd=open(i2c_filename, O_RDWR);
    if(fd < 0)
    {
        printf("open %s fialeure\n", i2c_filename);
        return -1;
    }

    return fd;
}
打开文件记得给读写的权限,然后开始往i2c设备也就是sht30传感器写入命令,我们要注意的是,sht30的通信方式是一个字节一个字节的传输的,而我们获取温度的命令地址为16位的,所以我们需要将它拆分,并且通信的时候是高地址为先传送的。我们需要将地址简单处理下

send[0]=(read_model>>8) & 0xff;
    send[1]=read_model & 0xff;
利用ioctl函数之前我们得了解下下面的结构体

 struct i2c_rdwr_ioctl_data {

             struct i2c_msg __user *msgs; /* pointers to i2c_msgs */

             __u32 nmsgs; /* number of i2c_msgs */

         };

         struct i2c_msg {

             _ _u16 addr; /* slave address */

             _ _u16 flags; /* 标志(读、写) */

             _ _u16 len; /* msg length */

             _ _u8 *buf; /* pointer to msg data */

         };
这结构体就是存储着树莓派向传感器同行的所有信息,和i2c设备通信就是通过修改结构体的内容来实现的,将对应的参数写入然后掉要ioctl函数

data.nmsgs = 1;//消息的数目
    msgs.len = sendsize;//写入目标的字节数
    msgs.addr = sht_addr;//i2c设备地址  
    msgs.flags = 0;//flags为0:表示写;为1:表示读 
    msgs.buf = send;//发送的数据
    data.msgs = &msgs;
    rv=ioctl(fd, I2C_RDWR, &data);
获取温湿度的后,传感器将会给我们6个字节的数据,前面三个字节为温度两个字节后面跟着一个crc码,后面三个字节为湿度两个字节后面跟着一个crc码,中间可以使用定时函数延时下,等待温度获取成功

在这里插入代码data.nmsgs =1;
    msgs.len = readsize;
    msgs.addr = sht_addr;
    msgs.flags = 1;
    msgs.buf = buf;
    data.msgs = &msgs;
    rv=ioctl(fd, I2C_RDWR, &data);片
温度获取成功后我们可以进行crc的校检,看看数据是否正确然后利用官方提供的说明对温湿度的值进行加工

Image

程序运行

Image

源代码:

程序中写入的命令可以是我们介绍传感器中的图片的那些16为地址,当然还有这其它的功能,具体可参考给出的链接

/*********************************************************************************
 *      Copyright:  (C) 2020 longyongtu<longyongtu13@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  d_sht_temp_hmti.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(12/07/20)
 *         Author:  longyongtu <longyongtu13@qq.com>
 *      ChangeLog:  1, Release initial version on "12/07/20 14:45:20"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <string.h>
#include <linux/i2c-dev.h>
#include <time.h>
#include <sys/ioctl.h>
#include <time.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/i2c.h>


#define I2C_ADDR            1
#define SHT_ADDR            0X44
#define SHT_MEAS_MEDREP     0x240b
#define SHT_MEAS_LOWREP     0x2416


void delay_ms(unsigned int time);
uint8_t crc8(const uint8_t *data, int len);
int sht_open(int i2c_addr, uint8_t sht_addr);
int sht_read_write(int fd, int sht_addr, uint16_t read_model, uint8_t *buf, int readsize);
int get_temp_hmti(int i2c_addr, uint8_t sht_addr,  float *temp, float *hmti);


/* delay ms*/
void delay_ms(unsigned int time)
{
    struct timespec   sleeper, temp;
    sleeper.tv_sec = (time_t)(time/1000);
    sleeper.tv_nsec = (long)(time%1000)*1000000;
    nanosleep(&sleeper, &temp);
    return ;
}



uint8_t crc8(const uint8_t *data, int len)
{
    const uint8_t magic = 0x31;
    uint8_t       crc = 0xff;
    int           i;
    int           j;

    for(j=len; j; --j)
    {
        crc ^= *data++;
        for(i=8; i; --i)
        {
            crc=(crc & 0x80) ? (crc << 1) ^ magic : (crc << 1);
        }
    }
    return crc;
}


int sht_open(int i2c_addr, uint8_t sht_addr)
{
    char    i2c_filename[10];
    int     fd = -1;
    int     rv = -1;

    snprintf(i2c_filename, 19, "/dev/i2c-%d", i2c_addr);
    fd=open(i2c_filename, O_RDWR);
    if(fd < 0)
    {
        printf("open %s fialeure\n", i2c_filename);
        return -1;
    }

    return fd;
}

int sht_read_write(int fd, int sht_addr, uint16_t read_model, uint8_t *buf, int readsize)
{
    int                        rv = -1;
    int                        sendsize = 2;
    uint8_t                    send[2];
    struct i2c_msg             msgs;
    struct i2c_rdwr_ioctl_data data;


    send[0]=(read_model>>8) & 0xff;
    send[1]=read_model & 0xff;

    data.nmsgs = 1;//消息的数目
    msgs.len = sendsize;//写入目标的字节数
    msgs.addr = sht_addr;//i2c设备地址  
    msgs.flags = 0;//flags为0:表示写;为1:表示读 
    msgs.buf = send;//发送的数据
    data.msgs = &msgs;
    rv=ioctl(fd, I2C_RDWR, &data);
    if(rv < 0)
    {
        printf("write to sht_30 failure\n");
        return -1;
    }

    delay_ms(10);

    data.nmsgs =1;
    msgs.len = readsize;
    msgs.addr = sht_addr;
    msgs.flags = 1;
    msgs.buf = buf;
    data.msgs = &msgs;
    rv=ioctl(fd, I2C_RDWR, &data);
    if(rv < 0)
    {
        printf("read to sht_30 failure\n");
        return -1;
    }
    return 1;
}

int get_temp_hmti(int i2c_addr, uint8_t sht_addr,  float *temp, float *hmti)
{
    int             rv = -1;
    int             sht_fd = -1;
    uint8_t         buf[10];
    uint16_t        tmp_t, tmp_h;

    memset(buf, 0, sizeof(buf));

    sht_fd=sht_open(I2C_ADDR, SHT_ADDR);
    if(sht_fd < 0)
    {
        return -1;
    }

    rv=sht_read_write(sht_fd, sht_addr, SHT_MEAS_LOWREP, buf, 6);
    if(rv < 0)
    {
        return -1;
    }

    if(buf[2] != crc8(buf, 2) || buf[5] != crc8(buf+3, 2))
    {
        printf("crc check errno\n");
        return -1;
    }

    tmp_t = buf[0];
    tmp_t <<= 8;
    tmp_t |= buf[1];

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


    *temp = -45.0 + (175.0 * ((float) tmp_t / (float) 0xFFFF));
    *hmti = 100.0 * ((float) tmp_h / (float) 0xFFFF);

    close(sht_fd);
    return 1;
}






int main(int argc, char *argv[])
{
    float temp, hmti;
    get_temp_hmti(I2C_ADDR, SHT_ADDR, &temp, &hmti);
    printf("Temperature %.2fc\n", temp);
    printf("Humidity %.2f%%\n", hmti);
    return 0;
}





教程结束!



本文章最后由 超级版主2023-11-12 21:28 编辑
发表评论
请先 注册/登录 后参与评论

已有1 发布

默认   热门   正序   倒序
查看更多评论
(10) 分享
分享

扫二维码或复制链接分享该篇文章

取消
已有1次打赏
raspi.cc打赏给楼主1金币,2023-05-14 02:59:46
本站免责声明
1、本站资源,均来自网络或个人用户发布,版权归原作者,所有资源和文章仅限用于学习和研究目的 。
2、不得用于商业或非法用途,否则,一切责任由该用户承担 !

侵权删除请致信 E-Mail:cxg88@qq.com