Skip to content
Draft
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
54 changes: 54 additions & 0 deletions bmp180/bmp180.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ type Device struct {
Address uint16
mode OversamplingMode
calibrationCoefficients calibrationCoefficients
temp int32
pressure int32
}

// New creates a new BMP180 connection. The I2C bus must already be
Expand Down Expand Up @@ -80,6 +82,58 @@ func (d *Device) Configure() {
d.calibrationCoefficients.md = readInt(data[20], data[21])
}

// Pressure returns the pressure in milli pascal (mPa).
func (d *Device) Pressure() int32 { return d.pressure }

// Temperature returns the temperature in celsius milli degrees (°C/1000).
func (d *Device) Temperature() int32 { return d.temp }

func (d *Device) Update(which drivers.Measurement) (err error) {
// Temperature is needed for presssure calculation.
if which|drivers.Temperature|drivers.Pressure == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall it not return some sort of error when measurement requested is not temperature or pressure?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason behind it not being an error is to allow for grouping of Sensors.

allSensors := []Sensor{imu, bmp, tempSens}
updateAll := func (w Measurement) {
   for _, sens := range allSensors {
     sens.Update(w) // illustrative, please handle your errors
   }
}

This way you can have precise requests of what sensor measurements you want to update. This is especially useful if the characteristic time of a sensor is very slow compared to other sensors in the group, i.e Temperature sensor vs. voltage sensor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth thinking about this a bit more. Maybe it's worth having a io.EOF sentinel like error for the case you mention Yurii, for example: drivers.ErrNoMeasurement.

return nil
}
// Temperature calculation.
rawTemp, err := d.rawTemp()
if err != nil {
return err
}
b5 := d.calculateB5(rawTemp)
t := (b5 + 8) >> 4
d.temp = 100 * t

// Pressure calculation.
if which|drivers.Pressure == 0 {
return nil
}
rawPressure, err := d.rawPressure(d.mode)
if err != nil {
return err
}

b6 := b5 - 4000
x1 := (int32(d.calibrationCoefficients.b2) * (b6 * b6 >> 12)) >> 11
x2 := (int32(d.calibrationCoefficients.ac2) * b6) >> 11
x3 := x1 + x2
b3 := (((int32(d.calibrationCoefficients.ac1)*4 + x3) << uint(d.mode)) + 2) >> 2
x1 = (int32(d.calibrationCoefficients.ac3) * b6) >> 13
x2 = (int32(d.calibrationCoefficients.b1) * ((b6 * b6) >> 12)) >> 16
x3 = ((x1 + x2) + 2) >> 2
b4 := (uint32(d.calibrationCoefficients.ac4) * uint32(x3+32768)) >> 15
b7 := uint32(rawPressure-b3) * (50000 >> uint(d.mode))
var p int32
if b7 < 0x80000000 {
p = int32((b7 << 1) / b4)
} else {
p = int32((b7 / b4) << 1)
}
x1 = (p >> 8) * (p >> 8)
x1 = (x1 * 3038) >> 16
x2 = (-7357 * p) >> 16
d.pressure = 1000 * (p + ((x1 + x2 + 3791) >> 4))
return nil
}

// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
func (d *Device) ReadTemperature() (temperature int32, err error) {
rawTemp, err := d.rawTemp()
Expand Down
102 changes: 102 additions & 0 deletions mcp3008/buffered.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package mcp3008

import (
"machine"

"tinygo.org/x/drivers"
)

const (
Chan0 = 1 << iota
Chan1
Chan2
Chan3
Chan4
Chan5
Chan6
Chan7

numChannels = iota
AllChannels = 0xff
)

// Buffered implementation of the MCP3008.
type BufferedDev struct {
active uint8
bus drivers.SPI
cs machine.Pin
tx [3]byte
rx [3]byte
data [numChannels]uint16
}

// New returns a new MCP3008 driver. Pass in a fully configured SPI bus.
// activeChannels informs which channels are to be measured.
//
// For example, to measure only on channels 0, 3 and 4 you'd call New as following:
// d := mcp3008.NewBuffered(spi, cs, mcp3008.Chan0|mcp3008.Chan3|mcp3008.Chan4)
func NewBuffered(b drivers.SPI, csPin machine.Pin, activeChannels uint8) *BufferedDev {
if activeChannels&AllChannels == 0 {
panic("mcp3008: no channels selected")
}
d := &BufferedDev{bus: b,
cs: csPin,
active: activeChannels,
}
csPin.High()
d.tx[0] = 0x01
d.tx[2] = 0x00
return d
}

// Configure sets up the device for communication
func (d *BufferedDev) Configure() {
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
}

// UNTESTED
func (d *BufferedDev) Update(which drivers.Measurement) error {
if which|drivers.Voltage == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now it only reads voltage, can read more things?
If only voltage ever, why have which drivers.Measurement at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above

return nil
}
for i := 0; i < numChannels; i++ {
if 1<<i&d.active != 0 { // measure only desired channels.
d.tx[1] = byte(8+i) << 4
d.cs.Low()
// This supposes the ADC supports autoincrement like for example the mcp3464 ADC (UNTESTED)
err := d.bus.Tx(d.tx[:], d.rx[:])
if err != nil {
return err
}
d.cs.High()
d.data[i] = uint16((d.rx[1]&0x3))<<(8+6) + uint16(d.rx[2])<<6
}
}
return nil
}

// Conversion Constants
const (
// Maximum value read by 10bit ADC after conversion to 16 bit value
maxVal = 0x3ff << 6
vRef = 5 // input voltage

convConst = 5 * 1000 / maxVal
convDiv = maxVal / 1000
convMul = 5
)

// Channel returns an ADC measurement for a specific channel. Must call Update Beforehand
func (d *BufferedDev) Channel(ch int) uint16 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this name? Shall be "Voltage"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So maybe this name should be Get, the reason it is not Voltage is because it does not represent the voltage, merely the bits read by the ADC. I have added the Voltage method separate as a conversion of Channel.

return d.data[ch]
}

func (d *BufferedDev) ReadChannel(ch int) (uint16, error) {
err := d.Update(drivers.Voltage)
return d.Channel(ch), err
}

// Voltage returns voltage in microvolts supposing 5V Vref.
func (d *BufferedDev) Voltage(ch int) int32 {
return int32(d.Channel(ch)) / convDiv * convMul
}
2 changes: 1 addition & 1 deletion mcp3008/mcp3008.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (p ADCPin) Get() uint16 {
p.d.bus.Tx(p.d.tx, p.d.rx)

// scale result to 16bit value like other ADCs
result := uint16((p.d.rx[1]&0x3))<<8 + uint16(p.d.rx[2])<<6
result := uint16((p.d.rx[1]&0x3))<<(8+6) + uint16(p.d.rx[2])<<6
p.d.cs.High()

return result
Expand Down
33 changes: 33 additions & 0 deletions sensor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package drivers

// Measurement specifies a type of measurement,
// for example: temperature, acceleration, pressure.
type Measurement uint32

// Sensor measurements
const (
Voltage Measurement = 1 << iota
Temperature
Humidity
Pressure
Distance
Acceleration
AngularVelocity
MagneticField
Luminosity
Time

// All Measurements is the OR of all Measurement defined in drivers package. This
// may change over time. Let user-defined Measurement flags be the MSB bits of Measurement.
AllMeasurements Measurement = 1<<iota - 1
)

// Sensor represents an object capable of making one
// or more measurements. A sensor will then have methods
// which read the last updated measurements.
//
// Many Sensors may be collected into
// one Sensor interface to synchronize measurements.
type Sensor interface {
Update(which Measurement) error
}