Go实现树莓派I2C读取SHT30温度&湿度传感器

简介

树莓派其实本身包含很多资源引脚, 合理利用其实可以自制智能家居的一部分,本身硬件和Linux系统等高级语言支持加生态, 不说了,

做就好了...

I2C 功能开启

参考之前的文章就可以了 Go实现树莓派读取bh1750光照强度

查看I2C总线上SHT30的设备地址

树莓派上两路i2c总线, 我们连接的是第一路,指令也是 -y 1, 如下

i2cdetect -y 1

为什么是0x44, 接着向下看, SHT3x默认地址就是0x44, 当Addr引脚接地则地址是0x44, 接VCC则是0x45, 电路图如下, 所以地址是0x44

代码

sht30.go

go 复制代码
package sensor

import (
	"context"
	"periph.io/x/conn/v3/gpio"
	"periph.io/x/conn/v3/gpio/gpioreg"
	"time"
)

type SHT3xOption struct {
	I2COption
	OnAlerted   func()
	AlertPinNum PinNum
}

type SHT30TemperatureValue struct {
	value int64
}

func (v SHT30TemperatureValue) Fahrenheit() float32 {
	return -49.0 + (315.0 * float32(v.value) / 65535.0)
}

func (v SHT30TemperatureValue) Celsius() float32 {
	return -49.0 + (175.0 * float32(v.value) / 65535.0)
}

type SHT30Value struct {
	temperature SHT30TemperatureValue
	humidity    float32
}

func (s *SHT30Value) setValues(temp int64, hum int64) {
	s.temperature.value = temp
	s.humidity = 100.0 * (float32(hum) / 65535.0)
}

func (s *SHT30Value) Humidity() float32 {
	return s.humidity
}

func (s *SHT30Value) Temperature() SHT30TemperatureValue {
	return s.temperature
}

type SHT30Sensor struct {
	opt      SHT3xOption
	alertPin gpio.PinIn
	i2cDevice

	cancalFunc context.CancelFunc
}

func NewSHT30Sensor(opt SHT3xOption) (*SHT30Sensor, error) {
	var (
		sensor = &SHT30Sensor{
			opt: opt,
		}
	)
	if opt.AlertPinNum > 0 {
		sensor.alertPin = gpioreg.ByName(opt.AlertPinNum.String())
		if nil == sensor.alertPin {
			return nil, CantFindPinError
		}
	}
	sensor.setDeviceInfo(opt.I2COption)

	return sensor, nil
}

func (sensor *SHT30Sensor) Init() (err error) {
	if err = sensor.init(); nil != err {
		return err
	}

	if nil != sensor.opt.OnAlerted && nil != sensor.alertPin {
		err = sensor.alertPin.In(gpio.PullNoChange, gpio.NoEdge)
		if nil != err {
			return err
		}

		var ctx context.Context
		ctx, sensor.cancalFunc = context.WithCancel(context.Background())

		go sensor.monitorAlertPin(ctx)
	}

	//if err = sensor.reset(); nil != err {
	//	return err
	//}
	return
}

func (sensor *SHT30Sensor) Destroy() error {
	if nil != sensor.cancalFunc {
		sensor.cancalFunc()
		sensor.cancalFunc = nil
	}
	return nil
}

func (sensor *SHT30Sensor) GetValue() (v SHT30Value, err error) {
	var (
		sendBytes = []byte{0xE0, 0x00} // read command
		recvBytes = make([]byte, 6)

		temp, hum int64
	)

	err = sensor.dev.Tx(sendBytes, recvBytes)
	if nil != err {
		return
	}

	if !sensor.checksumCompare(recvBytes[:2], recvBytes[2]) {
		err = CRCCheckFailedError
		return
	}

	if !sensor.checksumCompare(recvBytes[3:5], recvBytes[5]) {
		err = CRCCheckFailedError
		return
	}

	temp = int64(recvBytes[0])<<8 | int64(recvBytes[1])
	v.temperature.value = temp

	hum = int64(recvBytes[3])<<8 | int64(recvBytes[4])
	v.setValues(temp, hum)

	return
}

/*
同硬件上nReset相同, 但这里是软件发送指令, 硬件是引脚触发, 不再响应指令
目前调用就会报错,所以直接返回
*/
func (sensor *SHT30Sensor) reset() error {
	var (
		sendBytes = []byte{0x30, 0xA2} // 软重置
	)

	_, err := sensor.dev.Write(sendBytes)
	if nil != err {
		return err
	}
	time.Sleep(time.Millisecond * (15 + 1)) // 软重置最长时间 1ms, 可能后续需要考虑指令取消,  最长15ms,目前先跟数据手册单个指令时间来

	return err
}

/*
设置测量周期

	mps 0.5, 指令 0x20, 0x32/0x24/0x2F(High/Medium/Low)
	mps 1  , 指令 0x21, 0x30/0x26/0x2D(High/Medium/Low)
	mps 2  , 指令 0x22, 0x36/0x20/0x2B(High/Medium/Low)
	mps 4  , 指令 0x23, 0x34/0x22/0x29(High/Medium/Low)
	mps 10 , 指令 0x27, 0x37/0x21/0x2A(High/Medium/Low)
*/
func (sensor *SHT30Sensor) init() error {
	var (
		sendBytes = []byte{0x22, 0x36}
	)

	_, err := sensor.dev.Write(sendBytes)
	if nil != err {
		return err
	}
	time.Sleep(time.Millisecond * 15)

	return err
}

func (sensor *SHT30Sensor) checksumCompare(dat []byte, checksum byte) bool {
	var crc = sensor.crc8(dat)
	return crc == checksum
}

func (sensor *SHT30Sensor) crc8(dat []byte) byte {
	var polynomial = byte(0x31) // 多项式值
	var crc byte = 0xFF         // 初始化值
	for _, v := range dat {
		crc ^= v
		for i := 0; i < 8; i++ {
			if (crc & 0x80) != 0 {
				crc = (crc << 1) ^ polynomial
			} else {
				crc <<= 1
			}
			crc &= 0xFF // 保持 crc 为 8 位
		}
	}

	return crc
}

func (sensor *SHT30Sensor) monitorAlertPin(ctx context.Context) {
	var (
		triggered bool // 用于确保不会反复提醒...
	)
	for {
		if gpio.Low == sensor.alertPin.Read() {
			if !triggered {
				go sensor.opt.OnAlerted()
				triggered = true
			}
		} else {
			if triggered {
				triggered = false
			}
		}
		select {
		case <-ctx.Done():
			return
		case <-time.After(time.Millisecond * 10):
		}
	}
}

使用代码

main.go

go 复制代码
package main

import (
	"IntelligentAgriculture/sensor"
	"fmt"
	"periph.io/x/conn/v3/i2c/i2creg"
	"time"
)

const (
	AlertPinNum = 27
	MotorPinNum = 17
	LEDPinNum   = 22

	I2CSHT30Addr   = 0x44
	I2CAt24C02Addr = 0x50
	I2CBH1750Addr  = 0x23
)

func main() {
	i2cBus, err := i2creg.Open("")
	if nil != err {
		fmt.Println("i2creg.Open:", err)
		return
	}
	defer i2cBus.Close()

	s, err := sensor.NewSHT30Sensor(sensor.SHT3xOption{
		OnAlerted: func() {
			fmt.Println("alerted!!!")
		},
		AlertPinNum: AlertPinNum,
		I2COption: sensor.I2COption{
			I2CBus:        i2cBus,
			DeviceAddress: I2CSHT30Addr,
		},
	})
	if nil != err {
		fmt.Println("sensor.NewSHT30Sensor:", err)
		return
	}
	defer s.Destroy()
	err = s.Init()
	if nil != err {
		fmt.Println("sensor.Init:", err)
		return
	}

	for {
		v, err := s.GetValue()
		if nil != err {
			fmt.Println("sensor.GetValue: ", err)
			continue
		}
		fmt.Printf("%0.2f℃, %0.2f℉, %0.2f(RH)\n", v.Temperature().Celsius(), v.Temperature().Fahrenheit(), v.Humidity())
		time.Sleep(time.Second)
	}
}

其他文章

Go实现树莓派读取bh1750光照强度
Go实现树莓派读取at24c02 eeprom读写数据
Go实现树莓派控制舵机
Go实现树莓派超声波测距

相关推荐
杜杜的man3 小时前
【go从零单排】go中的结构体struct和method
开发语言·后端·golang
幼儿园老大*3 小时前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
半桶水专家3 小时前
go语言中package详解
开发语言·golang·xcode
llllinuuu3 小时前
Go语言结构体、方法与接口
开发语言·后端·golang
cookies_s_s3 小时前
Golang--协程和管道
开发语言·后端·golang
王大锤43913 小时前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
产幻少年3 小时前
golang函数
golang
为什么这亚子3 小时前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
半桶水专家3 小时前
用go实现创建WebSocket服务器
服务器·websocket·golang
材料苦逼不会梦到计算机白富美3 小时前
golang分布式缓存项目 Day 1
分布式·缓存·golang