tinygo-org / drivers

TinyGo drivers for sensors, displays, wireless adaptors, and other devices that use I2C, SPI, GPIO, ADC, and UART interfaces.
https://tinygo.org
BSD 3-Clause "New" or "Revised" License
608 stars 193 forks source link

Proposal: `Update` method call to read sensor data from a bus #310

Open soypat opened 3 years ago

soypat commented 3 years ago

Background

Sensors acquire measurements from their surrounding and store them as bytes in their registers for us to read via a communications bus, such as (but not limited to) i2c and SPI. Reading sensor data using these protocols carries some overhead that should be minimized if possible. This proposal seeks to provide TinyGo with a lean foundation to build sensing libraries upon.

The Update method

The benefits of having an Update method to schedule sensor measurements was discussed in PR #298 and #299.

Put simply, the Update method reads all requested sensor values into memory for later use using Measurement methods. Subsequent calls to update replace measurements.

The goal of this proposal is to clearly separate the I/O from the data. Most ICs work with auto-increment when reading registers which allows reading many sensor values in one transaction.

The benefits are many:

Advantages

Disadvantage

@aykevl Showed an example of what the API could look like

// in the drivers package
type Measurement uint32
const (
    Temperature = 1 << iota
    Humidity
    Rotation
    Acceleration
    MagneticField // not sure about this name
    // ...etc
)

// In a driver
// Update reads the various measurements in one batch, as far as possible.
func (d *Device) Update(which Measurement) error {
    if which & drivers.Temperature != 0 {
        // read temperature
    }
}

// In a user package
device := ...
for {
    err := device.Update(drivers.Acceleration | drivers.Rotation)
    if err != nil { handle }
    ax, ay, az := device.Acceleration()
    rx, ry, rz := device.Rotation()
    time.Sleep(time.Second)
}

TBD

Side effects of Update

It might be worthwhile to decide if Update has side effects or not before writing the implementations.

  1. Update reads sensor data requested. Does not guarantee only the sensors requested are read. When calling Measurement, data is read straight from the buffer
  2. 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 on Measurement call since Update already does that for us.
ysoldak commented 3 years ago

I'll read this more carefully later, but my initial thought: would be nice to see a PR demonstrating this in action, i.e. refactor existing (not necessary all, couple is enough) IMU drivers to satisfy common interface. There can be insights that very easy miss designing top-down.

soypat commented 3 years ago

@ysoldak Here's a PR that partially implements this design choice. The approach of this PR was to implement a type embedding the existing peripheral handle and add the new Update, Acceleration, Temperature, and Rotation methods. The PR has been tested and works fine. I've also written drivers for the MPU6050 for OS targets and this approach reduces "failed read" rates drastically for high request rates (4 failures/minute --> 1 failure every 6 minutes).