|
| 1 | +package honeyhsc |
| 2 | + |
| 3 | +import ( |
| 4 | + "errors" |
| 5 | + "math" |
| 6 | + |
| 7 | + "tinygo.org/x/drivers" |
| 8 | +) |
| 9 | + |
| 10 | +var ( |
| 11 | + errSensorMissing = errors.New("hsc: not connected") |
| 12 | + errDiagnostic = errors.New("hsc: diagnostic error") |
| 13 | +) |
| 14 | + |
| 15 | +const ( |
| 16 | + measuremask = drivers.Pressure | drivers.Temperature |
| 17 | + statusMask = 0b1100_0000 |
| 18 | + statusOffset = 6 |
| 19 | +) |
| 20 | + |
| 21 | +// DevI2C is the TruStability® High Accuracy Silicon Ceramic (HSC) Series is a piezoresistive silicon pressure sensor offering a ratiometric |
| 22 | +// analog or digital output for reading pressure over the specified full scale pressure span and temperature range. |
| 23 | +type DevI2C struct { |
| 24 | + bus drivers.I2C |
| 25 | + dev |
| 26 | + addr uint8 |
| 27 | + buf [6]byte |
| 28 | +} |
| 29 | + |
| 30 | +// NewDevI2C creates and returns a new DevI2C that communicates with an HSC device over the provided I2C bus. |
| 31 | +// Parameters: |
| 32 | +// - bus: the I2C bus to use. |
| 33 | +// - addr: the 7-bit I2C address of the sensor. |
| 34 | +// - outMin, outMax: raw output code range (counts) corresponding to the pressure span. Depends on sensor model. |
| 35 | +// - pMin, pMax: pressure range endpoints in millipascals (mPa). Depends on sensor model. |
| 36 | +// |
| 37 | +// The returned DevI2C will use these calibration parameters to convert raw bridge counts to pressure. |
| 38 | +func NewDevI2C(bus drivers.I2C, addr, outMin, outMax uint16, pMin, pMax int32) *DevI2C { |
| 39 | + h := &DevI2C{ |
| 40 | + bus: bus, |
| 41 | + addr: uint8(addr), |
| 42 | + dev: dev{ |
| 43 | + cmin: outMin, |
| 44 | + cmax: outMax, |
| 45 | + pmin: pMin, |
| 46 | + pmax: pMax, |
| 47 | + }, |
| 48 | + } |
| 49 | + return h |
| 50 | +} |
| 51 | + |
| 52 | +// ReadTemperature reads and returns the temperature in milliKelvin (mC) from the I2C-attached HSC device. |
| 53 | +// It performs an Update internally to get the latest temperature value. |
| 54 | +func (h *DevI2C) ReadTemperature() (int32, error) { |
| 55 | + err := h.Update(drivers.Temperature) |
| 56 | + if err != nil { |
| 57 | + return 0, err |
| 58 | + } |
| 59 | + return h.Temperature(), nil |
| 60 | +} |
| 61 | + |
| 62 | +// Update reads both temperature and pressure data from the I2C-attached HSC device when |
| 63 | +// the requested measurement mask includes pressure or temperature. |
| 64 | +// If neither pressure nor temperature is requested, Update is a no-op. |
| 65 | +func (d *DevI2C) Update(which drivers.Measurement) error { |
| 66 | + // Update performs an I2C transaction to read 4 bytes, parses the status bits, 14-bit bridge data and |
| 67 | + // temperature bits, and forwards them to the internal update routine. Any I2C transport error is returned, |
| 68 | + // as well as errors produced by the internal update (e.g. errSensorMissing, errDiagnostic). |
| 69 | + if which&measuremask == 0 { |
| 70 | + return nil |
| 71 | + } |
| 72 | + rbuf := d.buf[:4] |
| 73 | + wbuf := d.buf[4:6] |
| 74 | + const reg = 0 |
| 75 | + value := (d.addr << 1) | 1 |
| 76 | + wbuf[0] = reg |
| 77 | + wbuf[1] = value |
| 78 | + err := d.bus.Tx(uint16(d.addr), wbuf, rbuf) |
| 79 | + if err != nil { |
| 80 | + return err |
| 81 | + } |
| 82 | + status := (rbuf[0] & statusMask) >> statusOffset |
| 83 | + bridgeData := (uint16(rbuf[0]&^statusMask) << 8) | uint16(rbuf[1]) |
| 84 | + tempData := uint16(rbuf[2])<<8 | uint16(rbuf[3]&0xe0)>>5 |
| 85 | + return d.dev.update(status, bridgeData, tempData) |
| 86 | +} |
| 87 | + |
| 88 | +type pinout func(level bool) |
| 89 | + |
| 90 | +// DevI2C is the TruStability® High Accuracy Silicon Ceramic (HSC) Series is a piezoresistive silicon pressure sensor offering a ratiometric |
| 91 | +// analog or digital output for reading pressure over the specified full scale pressure span and temperature range. |
| 92 | +type DevSPI struct { |
| 93 | + spi drivers.SPI |
| 94 | + cs pinout |
| 95 | + dev |
| 96 | + buf [4]byte |
| 97 | +} |
| 98 | + |
| 99 | +// NewDevSPI creates and returns a new DevSPI that communicates with an HSC device over SPI. |
| 100 | +// Parameters: |
| 101 | +// - conn: the SPI connection to use. |
| 102 | +// - cs: a chip-select function that drives the device select line low/high. |
| 103 | +// - outMin, outMax: raw output code range (counts) corresponding to the pressure span. Depends on sensor model. |
| 104 | +// - pMin, pMax: pressure range endpoints in millipascals (mPa). Depends on sensor model. |
| 105 | +// |
| 106 | +// The function returns the constructed DevSPI and an error value (currently always nil). |
| 107 | +func NewDevSPI(conn drivers.SPI, cs pinout, outMin, outMax uint16, pMin, pMax int32) (*DevSPI, error) { |
| 108 | + h := &DevSPI{ |
| 109 | + spi: conn, |
| 110 | + cs: cs, |
| 111 | + dev: dev{ |
| 112 | + cmin: outMin, |
| 113 | + cmax: outMax, |
| 114 | + pmin: pMin, |
| 115 | + pmax: pMax, |
| 116 | + }, |
| 117 | + } |
| 118 | + return h, nil |
| 119 | +} |
| 120 | + |
| 121 | +// ReadTemperature reads and returns the temperature in milliKelvin (mC) from the SPI-attached HSC device. |
| 122 | +// It performs an Update internally to get the latest temperature value. |
| 123 | +func (h *DevSPI) ReadTemperature() (int32, error) { |
| 124 | + err := h.Update(drivers.Temperature) |
| 125 | + if err != nil { |
| 126 | + return 0, err |
| 127 | + } |
| 128 | + return h.Temperature(), nil |
| 129 | +} |
| 130 | + |
| 131 | +// Update reads pressure and temperature data from the SPI-attached HSC device when the requested measurement mask includes |
| 132 | +// pressure or temperature. If neither pressure nor temperature is requested, Update is a no-op. |
| 133 | +func (h *DevSPI) Update(which drivers.Measurement) error { |
| 134 | + // It toggles the provided chip-select, performs an SPI transfer to read 4 bytes, parses the status bits, |
| 135 | + // 14-bit bridge data and temperature bits, and forwards them to the internal update routine. Any SPI |
| 136 | + // transport error is returned, as well as errors produced by the internal update (e.g. errSensorMissing, errDiagnostic). |
| 137 | + if which&measuremask == 0 { |
| 138 | + return nil |
| 139 | + } |
| 140 | + buf := &h.buf |
| 141 | + h.cs(false) |
| 142 | + err := h.spi.Tx(nil, buf[:4]) |
| 143 | + h.cs(true) |
| 144 | + if err != nil { |
| 145 | + return err |
| 146 | + } |
| 147 | + // First two bits are status bits. |
| 148 | + status := (buf[0] & statusMask) >> statusOffset |
| 149 | + bridgeData := (uint16(buf[0]&^statusMask) << 8) | uint16(buf[1]) |
| 150 | + |
| 151 | + tempData := uint16(buf[2])<<8 | uint16(buf[3]&0xe0)>>5 |
| 152 | + return h.dev.update(status, bridgeData, tempData) |
| 153 | +} |
| 154 | + |
| 155 | +type dev struct { |
| 156 | + pressure int32 |
| 157 | + temp int32 |
| 158 | + cmin, cmax uint16 |
| 159 | + pmin, pmax int32 |
| 160 | +} |
| 161 | + |
| 162 | +// Pressure returns the most recently computed pressure value in millipascals (mPa). |
| 163 | +// The value is taken from the last successful Update. |
| 164 | +func (d *dev) Pressure() int32 { |
| 165 | + return d.pressure |
| 166 | +} |
| 167 | + |
| 168 | +// Temperature returns the most recently read temperature value in milliKelvin (mC). |
| 169 | +// The value is taken from the last successful Update. |
| 170 | +func (d *dev) Temperature() int32 { |
| 171 | + return d.temp + 273_150 |
| 172 | +} |
| 173 | + |
| 174 | +// update interprets raw sensor fields (status, bridgeData, tempData) and updates the dev's stored |
| 175 | +// pressure and temperature. It returns errSensorMissing when the temperature raw value indicates no sensor |
| 176 | +// (tempData == math.MaxUint16), errDiagnostic when the status indicates a device diagnostic condition |
| 177 | +// (status == 3), or nil on success. Pressure is computed with integer arithmetic using the configured |
| 178 | +// cmin/cmax -> pmin/pmax linear mapping in order to avoid overflows. |
| 179 | +func (d *dev) update(status uint8, bridgeData, tempData uint16) error { |
| 180 | + if tempData == math.MaxUint16 { |
| 181 | + return errSensorMissing |
| 182 | + } else if status == 3 { |
| 183 | + return errDiagnostic |
| 184 | + } |
| 185 | + |
| 186 | + // Take care not to overflow here. |
| 187 | + p := (int32(bridgeData)-int32(d.cmin))*(d.pmax-d.pmin)/int32(d.cmax-d.cmin) + d.pmin |
| 188 | + d.temp = int32(tempData) |
| 189 | + d.pressure = p |
| 190 | + return nil |
| 191 | +} |
0 commit comments