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
81 changes: 54 additions & 27 deletions dht/thermometer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
package dht // import "tinygo.org/x/drivers/dht"

import (
"machine"
"runtime/interrupt"
"time"

"tinygo.org/x/drivers"
"tinygo.org/x/drivers/internal/legacy"
"tinygo.org/x/drivers/internal/pin"
)

// DummyDevice provides a basic interface for DHT devices.
Expand All @@ -30,7 +33,8 @@ type DummyDevice interface {
// Since taking measurements from the sensor is time consuming procedure and blocks interrupts,
// user can avoid any hidden calls to the sensor.
type device struct {
pin machine.Pin
set pin.OutputFn
get pin.InputFn

measurements DeviceType
initialized bool
Expand All @@ -43,8 +47,8 @@ type device struct {
// According to documentation pin should be always, but the t *device restores pin to the state before call.
func (t *device) ReadMeasurements() error {
// initial waiting
state := powerUp(t.pin)
defer t.pin.Set(state)
state := powerUp(t.set, t.get)
defer t.set(state)
err := t.read()
if err == nil {
t.initialized = true
Expand Down Expand Up @@ -93,14 +97,11 @@ func (t *device) HumidityFloat() (float32, error) {
// Perform initialization of the communication protocol.
// Device lowers the voltage on pin for startingLow=20ms and starts listening for response
// Section 5.2 in [1]
func initiateCommunication(p machine.Pin) {
// Send low signal to the device
p.Configure(machine.PinConfig{Mode: machine.PinOutput})
p.Low()
func initiateCommunication(set pin.OutputFn) {
set.Low()
time.Sleep(startingLow)
// Set pin to high and wait for reply
p.High()
p.Configure(machine.PinConfig{Mode: machine.PinInput})
set.High()
}

// Measurements returns both measurements: temperature and humidity as they sent by the device.
Expand Down Expand Up @@ -131,14 +132,14 @@ func (t *device) read() error {
signals := signalsData[:]

// Start communication protocol with sensor
initiateCommunication(t.pin)
initiateCommunication(t.set)
// Wait for sensor's response and abort if sensor does not reply
err := waitForDataTransmission(t.pin)
err := waitForDataTransmission(t.get)
if err != nil {
return err
}
// count low and high cycles for sensor's reply
receiveSignals(t.pin, signals)
receiveSignals(t.get, signals)

// process received signals and store the result in the buffer. Abort if data transmission was interrupted and not
// all 40 bits were received
Expand All @@ -158,13 +159,13 @@ func (t *device) read() error {

// receiveSignals counts number of low and high cycles. The execution is time critical, so the function disables
// interrupts
func receiveSignals(pin machine.Pin, result []counter) {
func receiveSignals(get pin.InputFn, result []counter) {
i := uint8(0)
mask := interrupt.Disable()
defer interrupt.Restore(mask)
for ; i < 40; i++ {
result[i*2] = expectChange(pin, false)
result[i*2+1] = expectChange(pin, true)
result[i*2] = expectChange(get, false)
result[i*2+1] = expectChange(get, true)
}
}

Expand All @@ -189,33 +190,59 @@ func (t *device) extractData(signals []counter, buf []uint8) error {
// waitForDataTransmission waits for reply from the sensor.
// If no reply received, returns NoSignalError.
// For more details, see section 5.2 in [1]
func waitForDataTransmission(p machine.Pin) error {
func waitForDataTransmission(get pin.InputFn) error {
// wait for thermometer to pull down
if expectChange(p, true) == timeout {
if expectChange(get, true) == timeout {
return NoSignalError
}
//wait for thermometer to pull up
if expectChange(p, false) == timeout {
if expectChange(get, false) == timeout {
return NoSignalError
}
// wait for thermometer to pull down and start sending the data
if expectChange(p, true) == timeout {
if expectChange(get, true) == timeout {
return NoSignalError
}
return nil
}

// Constructor function for a DummyDevice implementation.
// This device provides full control to the user.
// It does not do any hidden measurements calls and does not check
// for 2 seconds delay between measurements.
func NewDummyDevice(pin machine.Pin, deviceType DeviceType) DummyDevice {
pin.High()
func newDevice(pin drivers.Pin, deviceType DeviceType) *device {
pin.Set(true)
// Pins are configured to maintain backward compatibility,
// When writing new drivers we assume that pins are configured in user code
// so the device initialization could be simplified like this:
// return &device{
// set: pin.Set,
// get: pin.Get,
// ...
// }
isOutput := true
return &device{
pin: pin,
set: func(level bool) {
if !isOutput {
legacy.ConfigurePinOut(pin)
isOutput = true
}
pin.Set(level)
},
get: func() bool {
if isOutput {
legacy.ConfigurePinInput(pin)
isOutput = false
}
return pin.Get()
},
measurements: deviceType,
initialized: false,
temperature: 0,
humidity: 0,
}
}

// Constructor function for a DummyDevice implementation.
// This device provides full control to the user.
// It does not do any hidden measurements calls and does not check
// for 2 seconds delay between measurements.
func NewDummyDevice(pin drivers.Pin, deviceType DeviceType) DummyDevice {
return newDevice(pin, deviceType)
}
23 changes: 8 additions & 15 deletions dht/timesafethermometer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
package dht // import "tinygo.org/x/drivers/dht"

import (
"machine"
"time"

"tinygo.org/x/drivers"
)

// Device interface provides main functionality of the DHTXX sensors.
Expand Down Expand Up @@ -124,14 +125,10 @@ func (m *managedDevice) Configure(policy UpdatePolicy) {

// Constructor of the Device implementation.
// This implementation updates data every 2 seconds during data access.
func New(pin machine.Pin, deviceType DeviceType) Device {
pin.High()
func New(pin drivers.Pin, deviceType DeviceType) Device {
pin.Set(true)
return &managedDevice{
t: device{
pin: pin,
measurements: deviceType,
initialized: false,
},
t: *newDevice(pin, deviceType),
lastUpdate: time.Time{},
policy: UpdatePolicy{
UpdateTime: time.Second * 2,
Expand All @@ -141,14 +138,10 @@ func New(pin machine.Pin, deviceType DeviceType) Device {
}

// Constructor of the Device implementation with given UpdatePolicy
func NewWithPolicy(pin machine.Pin, deviceType DeviceType, updatePolicy UpdatePolicy) Device {
pin.High()
func NewWithPolicy(pin drivers.Pin, deviceType DeviceType, updatePolicy UpdatePolicy) Device {
pin.Set(true)
result := &managedDevice{
t: device{
pin: pin,
measurements: deviceType,
initialized: false,
},
t: *newDevice(pin, deviceType),
lastUpdate: time.Time{},
}
result.Configure(updatePolicy)
Expand Down
13 changes: 7 additions & 6 deletions dht/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,24 @@
package dht // import "tinygo.org/x/drivers/dht"

import (
"machine"
"time"

"tinygo.org/x/drivers/internal/pin"
)

// Check if the pin is disabled
func powerUp(p machine.Pin) bool {
state := p.Get()
func powerUp(set pin.OutputFn, get pin.InputFn) bool {
state := get()
if !state {
p.High()
set.High()
time.Sleep(startTimeout)
}
return state
}

func expectChange(p machine.Pin, oldState bool) counter {
func expectChange(get pin.InputFn, oldState bool) counter {
cnt := counter(0)
for ; p.Get() == oldState && cnt != timeout; cnt++ {
for ; get() == oldState && cnt != timeout; cnt++ {
}
return cnt
}
Expand Down
4 changes: 3 additions & 1 deletion examples/onewire/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package main

import (
"encoding/hex"
"machine"
"time"

"tinygo.org/x/drivers/onewire"
)

Expand All @@ -18,7 +20,7 @@ func main() {
println()
println("Device:", machine.Device)

romIDs, err := ow.Search(onewire.SEARCH)
romIDs, err := ow.Search(onewire.SEARCH_ROM)
if err != nil {
println(err)
}
Expand Down
63 changes: 63 additions & 0 deletions examples/onewire_v2/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"encoding/hex"
"machine"
"time"

onewire "tinygo.org/x/drivers/onewire_v2"
)

type onewirePin struct {
p machine.Pin
isOutput bool
}

func (owp *onewirePin) Set(level bool) {
if level && owp.isOutput {
owp.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
owp.isOutput = false
}
if !level && !owp.isOutput {
owp.p.Configure(machine.PinConfig{Mode: machine.PinOutput})
owp.isOutput = true
}
}

func (owp *onewirePin) Get() bool {
if owp.isOutput {
owp.p.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
owp.isOutput = false
}
return owp.p.Get()
}

func main() {
pin := &onewirePin{p: machine.D2, isOutput: false}
ow := onewire.New(pin)

for {
time.Sleep(3 * time.Second)

println()
println("Device:", machine.Device)

romIDs, err := ow.Search(onewire.SEARCH_ROM)
if err != nil {
println(err)
}
for _, romid := range romIDs {
println(hex.EncodeToString(romid))
}

if len(romIDs) == 1 {
// only 1 device on bus
r, err := ow.ReadAddress()
if err != nil {
println(err)
}
println(hex.EncodeToString(r))

}
}
}
62 changes: 62 additions & 0 deletions internal/legacy/pinconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package legacy

import (
"errors"

"tinygo.org/x/drivers"
)

// The pingconfig group of files serve to abstract away
// pin configuration calls on the machine.Pin type.
// It was observed this way of developing drivers was
// non-portable and unusable on "big" Go projects so
// future projects should NOT configure pins in driver code.
// Users must configure pins before passing them as arguments
// to drivers.

// ConfigurePinOut is a legacy function used to configure pins as outputs.
//
// Deprecated: Do not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigurePinOut(po drivers.OutputPin) {
configurePinOut(po)
}

// ConfigurePinInput is a legacy function used to configure pins as inputs.
//
// Deprecated: Do not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigurePinInputPulldown(pi drivers.InputPin) {
configurePinInputPulldown(pi)
}

// ConfigurePinInput is a legacy function used to configure pins as inputs.
//
// Deprecated: Do not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigurePinInput(pi drivers.InputPin) {
configurePinInput(pi)
}

// ConfigurePinInput is a legacy function used to configure pins as inputs.
//
// Deprecated: Do not configure pins in drivers.
// This is a legacy feature and should only be used by drivers that
// previously configured pins in initialization to avoid breaking users.
func ConfigurePinInputPullup(pi drivers.InputPin) {
configurePinInputPullup(pi)
}

// PinIsNoPin returns true if the argument is a machine.Pin type and is the machine.NoPin predeclared type.
//
// Deprecated: Drivers do not require pin knowledge from now on.
func PinIsNoPin(pin drivers.Pin) bool {
return pinIsNoPin(pin)
}

var (
ErrConfigBeforeInstantiated = errors.New("device must be instantiated with New before calling Configure method")
)
15 changes: 15 additions & 0 deletions internal/legacy/pinconfig_go.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build !tinygo

package legacy

import "tinygo.org/x/drivers"

// This file compiles for non-tinygo builds
// for use with "big" or "upstream" Go where
// there is no machine package.

func configurePinOut(p drivers.OutputPin) {}
func configurePinInput(p drivers.InputPin) {}
func configurePinInputPulldown(p drivers.InputPin) {}
func configurePinInputPullup(p drivers.InputPin) {}
func pinIsNoPin(a drivers.Pin) bool { return false }
Loading