-
Notifications
You must be signed in to change notification settings - Fork 230
Sensor interface PR proof of concept #335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now it only reads voltage, can read more things?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why this name? Shall be "Voltage"?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
| } | ||
| 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 | ||
| } |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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.
There was a problem hiding this comment.
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.EOFsentinel like error for the case you mention Yurii, for example:drivers.ErrNoMeasurement.