tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/dht/timesafethermometer.go (about)

     1  //go:build tinygo
     2  
     3  // Package dht provides a driver for DHTXX family temperature and humidity sensors.
     4  //
     5  // [1] Datasheet DHT11: https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
     6  // [2] Datasheet DHT22: https://cdn-shop.adafruit.com/datasheets/Digital+humidity+and+temperature+sensor+AM2302.pdf
     7  // Adafruit C++ driver: https://github.com/adafruit/DHT-sensor-library
     8  
     9  package dht // import "tinygo.org/x/drivers/dht"
    10  
    11  import (
    12  	"machine"
    13  	"time"
    14  )
    15  
    16  // Device interface provides main functionality of the DHTXX sensors.
    17  type Device interface {
    18  	DummyDevice
    19  	Configure(policy UpdatePolicy)
    20  }
    21  
    22  // managedDevice struct provides time control and optional automatic data retrieval from the sensor.
    23  // It delegates all the functionality to device
    24  type managedDevice struct {
    25  	t          device
    26  	lastUpdate time.Time
    27  	policy     UpdatePolicy
    28  }
    29  
    30  // Measurements returns both measurements: temperature and humidity as they sent by the device.
    31  // Depending on the UpdatePolicy of the device may update cached measurements.
    32  func (m *managedDevice) Measurements() (temperature int16, humidity uint16, err error) {
    33  	err = m.checkForUpdateOnDataRequest()
    34  	if err != nil {
    35  		return 0, 0, err
    36  	}
    37  	return m.t.Measurements()
    38  }
    39  
    40  // Getter for temperature. Temperature method returns temperature as it is sent by device.
    41  // The temperature is measured temperature in Celsius multiplied by 10.
    42  // Depending on the UpdatePolicy of the device may update cached measurements.
    43  func (m *managedDevice) Temperature() (temp int16, err error) {
    44  	err = m.checkForUpdateOnDataRequest()
    45  	if err != nil {
    46  		return 0, err
    47  	}
    48  	temp, err = m.t.Temperature()
    49  	return
    50  }
    51  
    52  func (m *managedDevice) checkForUpdateOnDataRequest() (err error) {
    53  	// update if necessary
    54  	if m.policy.UpdateAutomatically {
    55  		err = m.ReadMeasurements()
    56  	}
    57  	// ignore error if the data was updated recently
    58  	// interface comparison does not work in tinygo. Therefore need to cast to explicit type
    59  	if code, ok := err.(ErrorCode); ok && code == UpdateError {
    60  		err = nil
    61  	}
    62  	// add error if the data is not initialized
    63  	if !m.t.initialized {
    64  		err = UninitializedDataError
    65  	}
    66  	return err
    67  }
    68  
    69  // Getter for temperature. TemperatureFloat returns temperature in a given scale.
    70  // Depending on the UpdatePolicy of the device may update cached measurements.
    71  func (m *managedDevice) TemperatureFloat(scale TemperatureScale) (float32, error) {
    72  	err := m.checkForUpdateOnDataRequest()
    73  	if err != nil {
    74  		return 0, err
    75  	}
    76  	return m.t.TemperatureFloat(scale)
    77  }
    78  
    79  // Getter for humidity. Humidity returns humidity as it is sent by device.
    80  // The humidity is measured in percentages multiplied by 10.
    81  // Depending on the UpdatePolicy of the device may update cached measurements.
    82  func (m *managedDevice) Humidity() (hum uint16, err error) {
    83  	err = m.checkForUpdateOnDataRequest()
    84  	if err != nil {
    85  		return 0, err
    86  	}
    87  	return m.t.Humidity()
    88  }
    89  
    90  // Getter for humidity. HumidityFloat returns humidity in percentages.
    91  // Depending on the UpdatePolicy of the device may update cached measurements.
    92  func (m *managedDevice) HumidityFloat() (float32, error) {
    93  	err := m.checkForUpdateOnDataRequest()
    94  	if err != nil {
    95  		return 0, err
    96  	}
    97  	return m.t.HumidityFloat()
    98  }
    99  
   100  // ReadMeasurements reads data from the sensor.
   101  // The function will return UpdateError if it is called more frequently than specified in UpdatePolicy
   102  func (m *managedDevice) ReadMeasurements() (err error) {
   103  	timestamp := time.Now()
   104  	if !m.t.initialized || timestamp.Sub(m.lastUpdate) > m.policy.UpdateTime {
   105  		err = m.t.ReadMeasurements()
   106  	} else {
   107  		err = UpdateError
   108  	}
   109  	if err == nil {
   110  		m.lastUpdate = timestamp
   111  	}
   112  	return
   113  }
   114  
   115  // Configure configures UpdatePolicy for Device.
   116  // Configure checks for policy.UpdateTime and prevent from updating more frequently than specified in [1][2]
   117  // to prevent undefined behaviour of the sensor.
   118  func (m *managedDevice) Configure(policy UpdatePolicy) {
   119  	if policy.UpdateAutomatically && policy.UpdateTime < time.Second*2 {
   120  		policy.UpdateTime = time.Second * 2
   121  	}
   122  	m.policy = policy
   123  }
   124  
   125  // Constructor of the Device implementation.
   126  // This implementation updates data every 2 seconds during data access.
   127  func New(pin machine.Pin, deviceType DeviceType) Device {
   128  	pin.High()
   129  	return &managedDevice{
   130  		t: device{
   131  			pin:          pin,
   132  			measurements: deviceType,
   133  			initialized:  false,
   134  		},
   135  		lastUpdate: time.Time{},
   136  		policy: UpdatePolicy{
   137  			UpdateTime:          time.Second * 2,
   138  			UpdateAutomatically: true,
   139  		},
   140  	}
   141  }
   142  
   143  // Constructor of the Device implementation with given UpdatePolicy
   144  func NewWithPolicy(pin machine.Pin, deviceType DeviceType, updatePolicy UpdatePolicy) Device {
   145  	pin.High()
   146  	result := &managedDevice{
   147  		t: device{
   148  			pin:          pin,
   149  			measurements: deviceType,
   150  			initialized:  false,
   151  		},
   152  		lastUpdate: time.Time{},
   153  	}
   154  	result.Configure(updatePolicy)
   155  	return result
   156  }