-
Notifications
You must be signed in to change notification settings - Fork 196
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
add Sensor interface and Measurement type #345
Conversation
It just occurred to me the arguments to func (d Dev) Temperature(unit units.Temperature) (T int32) {
switch unit {
case units.Kelvin:
return d.K
case units.Celsius:
return d.K-273
/// etc.
}
} This would probably clutter the |
That would be a good use for https://pkg.go.dev/periph.io/x/periph/conn/physic probably. |
This is the file in question here: https://github.com/google/periph/blob/v3.6.8/conn/physic/units.go If only it were in its own package with no other dependencies on periph's |
@deadprogram this original repository was split in 3 last year. I documented the changes at https://periph.io/news/2020/a_new_start/ The package "conn" is now a standalone repository at https://github.com/periph/conn. It does import one package mainly to make testing easier; https://github.com/periph/conn/blob/main/go.mod The current version is v3, so periph.io/x/conn/v3, instead of the previous repository with an additional "periph" in the path: periph.io/x/periph/conn. I sent you an email about that on May 29th 2021 but you haven't replied yet. |
embarrassed checking of enormous backlog of email is hereby promised. 😹 But what I mean is after clicking on those links, is if |
Do you mean periph.io/x/conn/v3/physic? My only big regret there is physic.Env that I want to move out in v4. Nothing else is a struct. |
We need to try this out then, seems like it could be really cool. |
So I've reconsidered my position about adding units to the interface: Maybe it's not such a good idea? My drive-by comment on adding units to driver logic seems like a bad idea as the driver would then have to have unit processing logic inside of it. Maybe it's best to let the user bring in unit logic if they so desire and let drivers be just drivers (although we should standardize units on all implementations) I'd imagine something of the sort: tempSens := peripheral123.New(i2c0) // this is the drivers package implementation
tempSensExternal := unit.NewCelsius(tempSens) // recieves a drivers.Thermometer interface
microKelvin := tempSens.Temperature()
Celsius := tempSensExternal.Temperature() |
Totally agree. Drivers should not be concerned with US units like Fahrenheit, for example. Instead, let's just use worldwide standards ;) However, some people prefer different units (Fahrenheit, Kelvin, ...). That's of course fine. But I feel strongly that drivers should not be concerned with this and instead defer this to common code.
Something like it, definitely. That's what I was aiming for with #332. However, I'm a bit concerned that the periph package might be a bit too heavy for our uses. For example, the |
To clarify, https://periph.io/x/conn/v3/physic and not the old package. For Temperature, I thought about using int32 but it was a bit more constraining on the range and I didn't want to enforce arbitrary limitations. Having a lot of precision makes it significantly more forgiving for poorly designed integer operations. I used Nano everywhere to be like time.Duration. It made everything more coherent; people don't have to guess the resolution when working in one metric versus another. Was it micro, milli, nano? It's easy to get confused for new users. Even a new user sees that all the units are in Nano, it reduces cognitive overhead. float32 versus float64 is a fair point. I think most soft float libraries do the calculation in 32 bits resolution anyway so I suspect it's likely mostly a storage issue. |
Hmm, that's a fair point. It's a bit of a convenience vs performance tradeoff here, I guess. It doesn't matter on 64-bit systems, but might matter (not sure about that) on 32-bit systems. Add/sub of int64 is fast on a 64-bit system (only half as slow in the ideal case) but mul/div can be significantly slower and cost more in code size because it needs to be implemented in software.
Also a fair point. Although that can be reduced a bit by providing related methods:
No. Floating point operations are very precisely defined, and the Go standard library depends on it. In fact In general, I'm leaning towards using periph.io/x/conn/v3/physic even though I have some concerns about its use on microcontrollers. |
I'd like to make a case for not having units be part of the One way of implementing this would be having pure functions which return physical quantity types, much like periph's physic. package sensor // or units, physic,
// Temperature represents millicelsius. This could be taken from physic directly.
type Temperature uint32
// the drivers.Thermometer type would be possible with this this PR.
func GetTemperature(sensor drivers.Thermometer) (Temperature, error) {
// ...
}
func (t Temperature) Farenheit() float64 { return t.Celsius()*celsiusToFarenheit } These functions could then be the goto examples on how to use TinyGo with sensors. They'd be easy to use and opt-in, which for me is a a huge advantage since I prefer to avoid float and other data transformations on the microcontroller and prefer to defer that to a remote computer. |
@soypat I think your points here are right on. How do you think we should we proceed from here? |
If needed, I'm open to refactor the periph.io/x/conn/v3 library, i.e. make a v4. |
@deadprogram Well, before any of this gets off the ground we should define tinygo's user-level API going forward.
|
This proposal could be linked with #491 which proposes a new organization level package called |
There's been a lot of attention guided towards the existing mpu6050 driver. It's been nearly a year since we started talking about changing the API of sensors in the drivers repo and things are still up in the air. I'm very keen on leading this towards a finished design. In my experience the This PR would be the first step in this direction as it would allow for new drivers to be written to follow this convention and not break existing drivers. I understand there were some reservations on where the design would go after this PR, but I feel that is besides the point. This is a first step and does not necessarily have to answer every question out there. It's simple enough to seem reasonably future proof and a huge improvement over existing APIs. Let's reach an agreement! |
I still quite like your original design. As for the discussion on the units: I still like specific units like
I was originally concerned about the extra bytes necessary for storage, but honestly I don't think that's a big deal. There are things that take up far more space than a few sensor readings. |
Initially I was very against floating points, as their processing on MCU as softfp is very expensive. I've softened my take over time as I understand using integer calculation is too difficult for most users. I'd still recommend something similar to what I did with conn/v3/physics, where the units are different from the measurement type. The main difference would be to use float32 instead of int64. |
@maruel I still think it's a really bad idea to require the use of floating point. In fact, I'd say |
I like where this is going but I've got to ask if you mean this PR's contents should be in the same The proposed
So that said, with the help of chatgpt here are a few suggestions for names for a package:
|
I don't have a strong opinion where all these things should be defined. I think I'd just put them in the bare drivers repo (both units and the (Perhaps we're misunderstanding, I don't think I suggested defining
Fully agree with this one.
Why would you want to put it in a separate package? I think putting the |
Agree 100%. I wasn't sold on the idea of defining a separate package. I feel they belong under
What I meant is that roughly you'd be able to compose sensors and sensor systems much easier. For example some patterns I image will arise when working with large complex systems: type DistillationPlant struct {
thermometers [TOTAL_THERMOMETERS]drivers.Thermometer
barometers [TOTAL_BAROMETERS]drivers.Barometer
// If one of these sensors fails we are in trouble.
missionCritical SensorGroup
redundant SensorGroup
// Data acquisition for analytics
telemetry SensorGroup
}
type SensorGroup struct {
senseMask uint32
sensors []drivers.Sensor
}
func (sg *SensorGroup) Update(which drivers.Measurement) error {
which &= sg.senseMask
for _, sensor := range sg.sensors {
err := sensor.Update(which)
if err != nil {
// store error, log some stuff, but keep updating them.
}
}
// Interesting to see how errors are composed so
// exact place of fault can be programatically determined.
return sg.makeErrorIfNotNil()
} Final question: Is this PR ready to add as is? Should something more be added? |
The PR looks good to me. Did one last call in the Slack channel, but otherwise I'd say just merge it. |
Thank you for working on this @soypat and to everyone who helped review. Now merging. |
In #587 I am adding support for the BMA42x accelerometer. This accelerometer also includes a step counter that is able to count steps while it consumes very little current. The question is now: should this be a separate measurement or should it update along with Reasons for making this a separate measurement:
Reasons for treating it as part of
The way I see it is that we should decide whether step counting (and perhaps other things, like tap gestures that are also part of the BMA425) are important enough to get their own bit in |
Maybe it's worth having a measurement type for this kind of discrete values? i.e:
Tried asking chat gippity and it wasn't too helpful heh. |
Re-PRing because I broke my local branch while trying to rebase #321 to latest dev. I'll copy and paste the contents:
Some Background
Hey there peeps, here's part of the
Update
method proposal. This comes with aSensor
interface which implements said method. This interface then could be embedded in all subsequent sensor interface types, i.e:Benefits
These changes were first discussed in
add AHRS interfaces for vehicle attitude control
and then added as a formal proposal inProposal: Update method call to read sensor data from a bus
. There has been little to no discussion on the matter. I will restate some of the benefits of theUpdate
proposal:To be clear, I think we should still have the
ReadMeasurement
methods which perform an update followed by measurement read which are usually much simpler to understand and work with for people who are new to embedded systems.Demonstrations and Proof of Concept
Comments on current methodology
The way it is currently done there are methods for drivers that look like so:
There are some benefits to having these
ReadPressure() (microbar int32)
(but discards error)ReadMeasurement
method requires no data to be stored before passing it to user. Nowadays I think storing a couple uint32's should not be a problem on modern controllers.Community Comments
Yurii Soldak on adding a
Measurement()
method on top of theReadMeasurement()
method:Ayke unsure about an
Audio
Measurement:Patricio on how
Update()
chooses to update measurements// Update reads sensor data requested. Does not guarantee only the sensors requested are read
. When callingMeasurement
, data is read straight from the buffer// Update reads only sensor data requested.
. This probably means you have to create struct field for each sensor data and only save to it the values requested. This takes up more space if also storing the buffer inside the struct. Goes from 24 bytes to 48 or so. A small benefit of this is that there need not be byte shifting and ORing onMeasurement
call sinceUpdate
already does that for us.