Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions common/crc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2025 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

// Package common contains functions used across multiple packages. For
// example, a CRC8 calculation
package common

// CRC8 calculates the 8-bit CRC of the byte slice parameter and returns the
// calculated value. CRC bytes are used in sensors from TI and Sensirion.
func CRC8(bytes []byte) byte {
var crc byte = 0xff
for _, val := range bytes {
crc ^= val
for range 8 {
if (crc & 0x80) == 0 {
crc <<= 1
} else {
crc = (byte)((crc << 1) ^ 0x31)
}
}
}
return crc
}
24 changes: 24 additions & 0 deletions common/crc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2025 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package common

import "testing"

func TestCRC8(t *testing.T) {
var tests = []struct {
bytes []byte
result byte
}{
{bytes: []byte{0xbe, 0xef}, result: 0x92},
{bytes: []byte{0x01, 0xa4}, result: 0x4d},
{bytes: []byte{0xab, 0xcd}, result: 0x6f},
}
for _, test := range tests {
res := CRC8(test.bytes)
if res != test.result {
t.Errorf("CRC8(%#v)!=0x%d received 0x%d", test.bytes, test.result, res)
}
}
}
32 changes: 9 additions & 23 deletions hdc302x/hdc302x.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/physic"
"periph.io/x/devices/v3/common"
)

type SampleRate uint16
Expand Down Expand Up @@ -203,21 +204,6 @@ func countToHumidity(bytes []byte) physic.RelativeHumidity {
return physic.RelativeHumidity(f * float64(physic.PercentRH))
}

func crc8(bytes []byte) byte {
var crc byte = 0xff
for _, val := range bytes {
crc ^= val
for range 8 {
if (crc & 0x80) == 0 {
crc <<= 1
} else {
crc = (byte)((crc << 1) ^ 0x31)
}
}
}
return crc
}

// Halt shuts down the device. If a SenseContinuous operation is in progress,
// its aborted. Implements conn.Resource
func (dev *Dev) Halt() error {
Expand Down Expand Up @@ -251,7 +237,7 @@ func (dev *Dev) Sense(env *physic.Env) error {
if err := dev.d.Tx(read, res); err != nil {
return fmt.Errorf("hdc302x: %w", err)
}
if crc8(res[:2]) != res[2] || crc8(res[3:5]) != res[5] {
if common.CRC8(res[:2]) != res[2] || common.CRC8(res[3:5]) != res[5] {
return errInvalidCRC
}
env.Temperature = countToTemperature(res)
Expand Down Expand Up @@ -320,7 +306,7 @@ func (dev *Dev) readSerialNumber() int64 {
// this is a 6 byte value read in 3 parts
for range 3 {
err := dev.d.Tx(cmd, r)
if err != nil || (crc8(r[:2]) != r[2]) {
if err != nil || (common.CRC8(r[:2]) != r[2]) {
return result
}
result = result<<16 | (int64(r[0])<<8 | int64(r[1]))
Expand Down Expand Up @@ -358,7 +344,7 @@ func (dev *Dev) readAlertValues(cfg *Configuration) error {
if err != nil {
return err
}
if crc8(r[:2]) != r[2] {
if common.CRC8(r[:2]) != r[2] {
return errInvalidCRC
}
wValue := uint16(r[0])<<8 | uint16(r[1])
Expand All @@ -381,7 +367,7 @@ func (dev *Dev) readOffsets(cfg *Configuration) error {
if err := dev.d.Tx(readSetOffsets, r); err != nil {
return fmt.Errorf("hdc302x: %w", err)
}
if crc8(r[:2]) != r[2] {
if common.CRC8(r[:2]) != r[2] {
return errInvalidCRC
}

Expand Down Expand Up @@ -424,7 +410,7 @@ func (dev *Dev) ReadStatus() (StatusWord, error) {
if err := dev.d.Tx(readStatus, r); err != nil {
return 0, err
}
if crc8(r[:2]) != r[2] {
if common.CRC8(r[:2]) != r[2] {
return 0, errInvalidCRC
}
_ = dev.d.Tx(clearStatus, nil)
Expand Down Expand Up @@ -463,7 +449,7 @@ func (dev *Dev) setOffsets(cfg *Configuration) error {
computeTemperatureOffsetByte(cfg.TemperatureOffset),
0,
}
w[4] = crc8(w[2:4])
w[4] = common.CRC8(w[2:4])
return dev.d.Tx(w, nil)
}

Expand Down Expand Up @@ -553,7 +539,7 @@ func (dev *Dev) setThresholds(typeAlert bool, tp *ThresholdPair) error {
wval := uint16(0)
wval = (humBits & 0xfe00) | tempBits>>7
w := []byte{cmds[pair][ix][0], cmds[pair][ix][1], byte(wval >> 8), byte(wval & 0xff), 0}
w[4] = crc8(w[2:4])
w[4] = common.CRC8(w[2:4])
err := dev.d.Tx(w, nil)
if err != nil {
return err
Expand Down Expand Up @@ -609,7 +595,7 @@ func (dev *Dev) SetHeater(powerLevel HeaterPower) error {
byte((powerLevel >> 8) & 0xff),
byte(powerLevel & 0xff),
0}
setValue[4] = crc8(setValue[2:4])
setValue[4] = common.CRC8(setValue[2:4])
err := dev.d.Tx(setValue, nil)
if err != nil {
return err
Expand Down
16 changes: 0 additions & 16 deletions hdc302x/hdc302x_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,22 +200,6 @@ func shutdown(t *testing.T) {
}
}

func TestCRC(t *testing.T) {
var tests = []struct {
bytes []byte
result byte
}{
{bytes: []byte{0xbe, 0xef}, result: 0x92},
{bytes: []byte{0xab, 0xcd}, result: 0x6f},
}
for _, test := range tests {
res := crc8(test.bytes)
if res != test.result {
t.Errorf("crc8(%#v)!=0x%d receieved 0x%d", test.bytes, test.result, res)
}
}
}

// TestConversions tests the various temperature/humidity functions
// for correct operation.
func TestConversions(t *testing.T) {
Expand Down
21 changes: 3 additions & 18 deletions scd4x/scd4x.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/physic"
"periph.io/x/devices/v3/common"
)

// PPM=Parts Per Million. Units of measure for CO2 concentration.
Expand Down Expand Up @@ -402,30 +403,14 @@ func (d *Dev) Reset(mode ResetMode) error {
return err
}

func calcCRC(bytes []byte) byte {
polynomial := byte(0x31)
crc := byte(0xff)
for ix := range len(bytes) {
crc ^= bytes[ix]
for crc_bit := byte(8); crc_bit > 0; crc_bit-- {
if (crc & 0x80) == 0x80 {
crc = (crc << 1) ^ polynomial
} else {
crc = (crc << 1)
}
}
}
return crc
}

// makeWriteData converts the slice of word values into byte values with the
// CRC following.
func makeWriteData(data []uint16) []byte {
bytes := make([]byte, len(data)*3)
for ix, val := range data {
bytes[ix*3] = byte((val >> 8) & 0xff)
bytes[ix*3+1] = byte(val & 0xff)
bytes[ix*3+2] = calcCRC(bytes[ix*3 : ix*3+2])
bytes[ix*3+2] = common.CRC8(bytes[ix*3 : ix*3+2])
}
return bytes
}
Expand Down Expand Up @@ -464,7 +449,7 @@ func (d *Dev) sendCommand(cmd command, writeData []uint16) ([]uint16, error) {
// verify the CRC as we go.
result := make([]uint16, cmd.responseSize/3)
for ix := range len(result) {
crc := calcCRC(r[ix*3 : ix*3+2])
crc := common.CRC8(r[ix*3 : ix*3+2])
if r[ix*3+2] != crc {
return nil, fmt.Errorf("scd4x cmd 0x%x: invalid crc", cmd.cmdWord)
}
Expand Down
16 changes: 0 additions & 16 deletions scd4x/scd4x_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,22 +154,6 @@ func shutdown(t *testing.T) {
}
}

func TestCRC(t *testing.T) {
tests := []struct {
bytes []byte
crc byte
}{
{bytes: []byte{0xbe, 0xef}, crc: 0x92},
{bytes: []byte{0x01, 0xa4}, crc: 0x4d},
}
for _, test := range tests {
res := calcCRC(test.bytes)
if res != test.crc {
t.Error(fmt.Errorf("crc calculation error bytes: %#v, result: 0x%x expected: 0x%x", test.bytes, res, test.crc))
}
}
}

func TestCountToTemperature(t *testing.T) {
tests := []struct {
count uint16
Expand Down
44 changes: 44 additions & 0 deletions sht4x/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2025 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package sht4x_test

import (
"log"
"time"

"periph.io/x/conn/v3/i2c/i2creg"
"periph.io/x/conn/v3/physic"
"periph.io/x/devices/v3/sht4x"
"periph.io/x/host/v3"
)

// Example shows creating an SHT-4X sensor and reading from it.
func Example() {
if _, err := host.Init(); err != nil {
log.Fatal("Error calling host.init()")
}
bus, err := i2creg.Open("")
if err != nil {
log.Fatal(err)
}
defer bus.Close()

dev, err := sht4x.New(bus, sht4x.DefaultAddress)
if err != nil {
log.Fatal(err)
}

env := &physic.Env{}

for range 10 {
err = dev.Sense(env)
if err != nil {
log.Println(err)
} else {
log.Printf("Temperature: %s Humidity: %s\n", env.Temperature, env.Humidity)
}
time.Sleep(time.Second)
}
}
Loading