-
Notifications
You must be signed in to change notification settings - Fork 0
/
SFM3000.go
141 lines (108 loc) · 2.83 KB
/
SFM3000.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package sfm3000
import (
"encoding/binary"
"fmt"
"time"
"github.com/go-daq/crc8"
"periph.io/x/periph/conn"
)
const crcPolynomial = 0x31
const flowOffset = 32000
const flowScaleFactorAirN2 = 140
const flowScaleFactorO2 = 142.8
//SFM3000 is the i2C driver for the SFM3000 Low Pressure Drop Digital Flow Meter
type SFM3000 struct {
i2c conn.Conn
crcTable *crc8.Table
readMode bool
isAir bool
address uint8
label string
}
//NewSFM3000 create a new SFM3000 driver
func NewSFM3000(i2c conn.Conn, address uint8, isAir bool, label string) (*SFM3000, error) {
return &SFM3000{
i2c: i2c,
readMode: false,
crcTable: crc8.MakeTable(crcPolynomial),
isAir: isAir,
address: address,
label: label,
}, nil
}
//Label ..
func (e *SFM3000) Label() string {
return e.label
}
//SoftReset ..
func (e *SFM3000) SoftReset() error {
e.readMode = false
w := []byte{0x20, 0x00}
r := make([]byte, 0)
err := e.i2c.Tx(w, r)
if err != nil {
return fmt.Errorf("failed to write command: %w", err)
}
return nil
}
//GetSerial ..
func (e *SFM3000) GetSerial() ([4]byte, error) {
e.readMode = false
serial := [4]byte{}
w := []byte{0x31, 0xAE}
r := make([]byte, 4)
err := e.i2c.Tx(w, r)
if err != nil {
return serial, fmt.Errorf("failed to write command: %w", err)
}
if len(r) != 4 {
return serial, fmt.Errorf("response length unexpected (bytes), got %v, expected %v", len(r), 4)
}
copy(serial[:], r)
return serial, nil
}
//GetValue Returns data, crc, timestamp, error
func (e *SFM3000) GetValue() (float64, uint8, time.Time, error) {
value, crc, tstamp, err := e.getRaw()
if err != nil {
return 0, 0, tstamp, err
}
var scalefactor float64
if e.isAir {
scalefactor = flowScaleFactorAirN2
} else {
scalefactor = flowScaleFactorO2
}
flow := (float64(value) - flowOffset) / float64(scalefactor)
return flow, crc, tstamp, nil
}
//getRaw Returns data uint16, crc uint8, error
func (e *SFM3000) getRaw() (uint16, uint8, time.Time, error) {
if !(e.readMode) {
w := []byte{0x10, 00}
r := make([]byte, 0)
err := e.i2c.Tx(w, r)
if err != nil {
return 0, 0, time.Time{}, fmt.Errorf("failed to write command: %w", err)
}
time.Sleep(200 * time.Millisecond) // Wait for sensor to change mode
e.readMode = true
}
w := make([]byte, 0)
r := make([]byte, 3)
err := e.i2c.Tx(w, r)
if err != nil {
return 0, 0, time.Time{}, fmt.Errorf("failed to read command: %w", err)
}
if len(r) != 3 {
return 0, 0, time.Time{}, fmt.Errorf("response length unexpected (bytes), got %v, expected %v", len(r), 3)
}
timestamp := time.Now()
dataCRC := byte(crc8.Checksum(r[:2], e.crcTable))
sensorCRC := r[2]
if dataCRC != sensorCRC {
return 0, 0, time.Time{}, fmt.Errorf("CRC Check failed, got %v, expected %v", sensorCRC, dataCRC)
}
data := binary.BigEndian.Uint16(r[:2])
return data, dataCRC, timestamp, nil
}