piframe-go/vendor/github.com/d2r2/go-bh1750/bh1750.go

324 lines
8.7 KiB
Go

//--------------------------------------------------------------------------------------------------
//
// Copyright (c) 2018 Denis Dyakov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial
// portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//--------------------------------------------------------------------------------------------------
package bh1750
import (
"encoding/binary"
"errors"
"time"
i2c "github.com/d2r2/go-i2c"
"github.com/davecgh/go-spew/spew"
)
// Command bytes
const (
// No active state.
CMD_POWER_DOWN = 0x00
// Waiting for measurement command.
CMD_POWER_ON = 0x01
// Reset Data register value.
// Reset command is not acceptable in Power Down mode.
CMD_RESET = 0x07
// Start measurement at 1lx resolution.
// Measurement Time is typically 120ms.
CMD_CONTINUOUSLY_H_RES_MODE = 0x10
// Start measurement at 0.5lx resolution.
// Measurement Time is typically 120ms.
CMD_CONTINUOUSLY_H_RES_MODE2 = 0x11
// Start measurement at 4lx resolution.
// Measurement Time is typically 16ms.
CMD_CONTINUOUSLY_L_RES_MODE = 0x13
// Start measurement at 1lx resolution.
// Measurement Time is typically 120ms.
// It is automatically set to Power Down mode after measurement
CMD_ONE_TIME_H_RES_MODE = 0x20
// Start measurement at 0.5lx resolution.
// Measurement Time is typically 120ms.
// It is automatically set to Power Down mode after measurement.
CMD_ONE_TIME_H_RES_MODE2 = 0x21
// Start measurement at 4lx resolution.
// Measurement Time is typically 16ms.
// It is automatically set to Power Down mode after measurement.
CMD_ONE_TIME_L_RES_MODE = 0x23
// Change measurement time. 01000_MT[7,6,5]
CMD_CHANGE_MEAS_TIME_HIGH = 0x40
// Change measurement time. 011_MT[4,3,2,1,0]
CMD_CHANGE_MEAS_TIME_LOW = 0x60
)
// ResolutionMode define sensor sensitivity
// and measure time. Be aware, that improving
// sensitivity lead to increasing of measurement time.
type ResolutionMode int
const (
// LowResolution precision 4 lx, 16 ms measurement time
LowResolution ResolutionMode = iota + 1
// HighResolution precision 1 lx, 120 ms measurement time
HighResolution
// HighestResolution precision 0.5 lx, 120 ms measurement time
HighestResolution
)
// String define stringer interface.
func (v ResolutionMode) String() string {
switch v {
case LowResolution:
return "Low Resolution"
case HighResolution:
return "High Resolution"
case HighestResolution:
return "Highest Resolution"
default:
return "<unknown>"
}
}
// BH1750 it's a sensor itself.
type BH1750 struct {
// Since sensor have no register
// to report current state, we save
// last issued command to fill this gap.
lastCmd byte
lastResolution ResolutionMode
factor byte
}
// NewBH1750 return new sensor instance.
func NewBH1750() *BH1750 {
v := &BH1750{}
v.factor = v.GetDefaultSensivityFactor()
return v
}
// Reset clear ambient light register value.
func (v *BH1750) Reset(i2c *i2c.I2C) error {
lg.Debug("Reset sensor...")
_, err := i2c.WriteBytes([]byte{CMD_RESET})
if err != nil {
return err
}
v.lastCmd = CMD_RESET
time.Sleep(time.Microsecond * 3)
return nil
}
// PowerDown return register to idle state.
func (v *BH1750) PowerDown(i2c *i2c.I2C) error {
lg.Debug("Power down sensor...")
_, err := i2c.WriteBytes([]byte{CMD_POWER_DOWN})
if err != nil {
return err
}
v.lastCmd = CMD_POWER_DOWN
return nil
}
// PowerOn activate sensor.
func (v *BH1750) PowerOn(i2c *i2c.I2C) error {
lg.Debug("Power on sensor...")
_, err := i2c.WriteBytes([]byte{CMD_POWER_ON})
if err != nil {
return err
}
v.lastCmd = CMD_POWER_ON
return nil
}
// Get internal parameters used to program sensor.
func (v *BH1750) getResolutionData(resolution ResolutionMode) (cmd byte,
wait time.Duration, divider uint32) {
switch resolution {
case LowResolution:
cmd = CMD_ONE_TIME_L_RES_MODE
divider = 1
// typical measure time is 16 ms,
// but as it was found 24 ms max time
// gives better results
wait = time.Millisecond * 24
case HighResolution:
cmd = CMD_ONE_TIME_H_RES_MODE
divider = 1
// typical measure time
wait = time.Millisecond * 120
case HighestResolution:
cmd = CMD_ONE_TIME_H_RES_MODE2
divider = 2
// typical measure time
wait = time.Millisecond * 120
}
wait = wait * time.Duration(v.factor) /
time.Duration(v.GetDefaultSensivityFactor())
return cmd, wait, divider
}
// MeasureAmbientLight measure and return ambient light once in lux.
func (v *BH1750) MeasureAmbientLight(i2c *i2c.I2C,
resolution ResolutionMode) (uint16, error) {
lg.Debug("Run one time measure...")
cmd, wait, divider := v.getResolutionData(resolution)
v.lastCmd = cmd
v.lastResolution = resolution
_, err := i2c.WriteBytes([]byte{cmd})
if err != nil {
return 0, err
}
time.Sleep(wait)
var data struct {
Data [2]byte
}
err = readDataToStruct(i2c, 2, binary.BigEndian, &data)
if err != nil {
return 0, err
}
amb := uint16(uint32(uint16(data.Data[0])<<8|uint16(data.Data[1])) *
5 / 6 / divider)
return amb, nil
}
// StartMeasureAmbientLightContinuously start continuous
// measurement process. Use FetchMeasuredAmbientLight to get
// average ambient light amount collected and calculated over a time.
// Use PowerDown to stop measurements and return sensor to idle state.
func (v *BH1750) StartMeasureAmbientLightContinuously(i2c *i2c.I2C,
resolution ResolutionMode) (wait time.Duration, err error) {
lg.Debug("Start measures continuously...")
cmd, wait, _ := v.getResolutionData(resolution)
v.lastCmd = cmd
v.lastResolution = resolution
_, err = i2c.WriteBytes([]byte{cmd})
if err != nil {
return 0, err
}
// Wait first time to collect necessary
// amount of light for correct results.
// It's not necessary to wait next time
// same amount of time, because
// sensor accumulate average lux amount
// without any overwrite old value.
time.Sleep(wait)
// In any case we are returning same
// recommended amount of time to wait
// between measures.
return wait, nil
}
// FetchMeasuredAmbientLight return current average ambient light in lux.
// Previous command should be any continuous measurement initiation,
// otherwise error will be reported.
func (v *BH1750) FetchMeasuredAmbientLight(i2c *i2c.I2C) (uint16, error) {
lg.Debug("Fetch measured data...")
cmd, _, divider := v.getResolutionData(v.lastResolution)
if v.lastCmd != cmd {
return 0, errors.New(
"can't fetch measured ambient light, since last command doesn't match")
}
var data struct {
Data [2]byte
}
err := readDataToStruct(i2c, 2, binary.BigEndian, &data)
if err != nil {
return 0, err
}
amb := uint16(uint32(uint16(data.Data[0])<<8|uint16(data.Data[1])) *
5 / 6 / divider)
return amb, nil
}
// GetDefaultSensivityFactor return factor value
// used when your sensor have no any protection cover.
// This is default setting according to specification.
func (v *BH1750) GetDefaultSensivityFactor() byte {
return 69
}
// ChangeSensivityFactor used when you close sensor
// with protection cover, which change (ordinary decrease)
// expected amount of light falling on the sensor.
// In this case you should calibrate you sensor and find
// appropriate factor to get in output correct ambient light value.
// Be aware, that improving sensitivity will increase
// measurement time.
func (v *BH1750) ChangeSensivityFactor(i2c *i2c.I2C, factor byte) error {
lg.Debug("Change sensitivity factor...")
// minimum limit
const minValue = 31
// maximum limit
const maxValue = 254
if factor < minValue || factor > maxValue {
return errors.New(spew.Sprintf("sensitivity factor value exceed range [%d..%d]",
minValue, maxValue))
}
high := (factor & 0xE0) >> 5
_, err := i2c.WriteBytes([]byte{CMD_CHANGE_MEAS_TIME_HIGH | high})
if err != nil {
return err
}
low := (factor & 0x1F)
_, err = i2c.WriteBytes([]byte{CMD_CHANGE_MEAS_TIME_LOW | low})
if err != nil {
return err
}
v.factor = factor
return nil
}