mirror of
https://github.com/hybridgroup/gobot.git
synced 2025-04-24 13:48:49 +08:00
build out the ccs811 driver
This commit is contained in:
parent
e00e9a4672
commit
64ab6f6b8f
349
drivers/i2c/ccs811_driver.go
Normal file
349
drivers/i2c/ccs811_driver.go
Normal file
@ -0,0 +1,349 @@
|
||||
package i2c
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
)
|
||||
|
||||
// CCS811DriveMode type
|
||||
type CCS811DriveMode uint8
|
||||
|
||||
// Operating modes which dictate how often measurements are being made. If 0x00 is used as an operating mode, measurements will be disabled
|
||||
const (
|
||||
CCS811DriveModeIdle CCS811DriveMode = 0x00
|
||||
CCS811DriveMode1Sec = 0x01
|
||||
CCS811DriveMode10Sec = 0x02
|
||||
CCS811DriveMode60Sec = 0x03
|
||||
CCS811DriveMode250MS = 0x04
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
//DefaultAddress is the default I2C address for the ccs811
|
||||
ccs811DefaultAddress = 0x5A
|
||||
|
||||
//Registers, all definitions have been taken from the datasheet
|
||||
//Single byte read only register which indicates if a device is active, if new data is available or if an error occurred.
|
||||
ccs811RegStatus = 0x00
|
||||
//This is Single byte register, which is used to enable sensor drive mode and interrupts.
|
||||
ccs811RegMeasMode = 0x01
|
||||
//This multi-byte read only register contains the calculated eCO2 (ppm) and eTVOC (ppb) values followed by the STATUS register, ERROR_ID register and the RAW_DATA register.
|
||||
ccs811RegAlgResultData = 0x02
|
||||
//Two byte read only register which contains the latest readings from the sensor.
|
||||
ccs811RegRawData = 0x03
|
||||
//A multi-byte register that can be written with the current Humidity and Temperature values if known.
|
||||
ccs811RegEnvData = 0x05
|
||||
//Register that holds the NTC value used for temperature calcualtions
|
||||
ccs811RegNtc = 0x06
|
||||
//Asserting the SW_RESET will restart the CCS811 in Boot mode to enable new application firmware to be downloaded.
|
||||
ccs811RegSwReset = 0xFF
|
||||
//Single byte read only register which holds the HW ID which is 0x81 for this family of CCS81x devices.
|
||||
ccs811RegHwID = 0x20
|
||||
//Single byte read only register that contains the hardware version. The value is 0x1X
|
||||
ccs811RegHwVersion = 0x21
|
||||
//Two byte read only register which contain the version of the firmware bootloader stored in the CCS811 in the format Major.Minor.Trivial
|
||||
ccs811RegFwBootVersion = 0x23
|
||||
//Two byte read only register which contain the version of the firmware application stored in the CCS811 in the format Major.Minor.Trivial
|
||||
ccs811RegFwAppVersion = 0x24
|
||||
//To change the mode of the CCS811 from Boot mode to running the application, a single byte write of 0xF4 is required.
|
||||
ccs811RegAppStart = 0xF4
|
||||
|
||||
// Constants
|
||||
// The hardware ID code
|
||||
ccs811HwIDCode = 0x81
|
||||
)
|
||||
|
||||
var (
|
||||
// The sequence of bytes needed to do a software reset
|
||||
ccs811SwResetSequence = []byte{0x11, 0xE5, 0x72, 0x8A}
|
||||
)
|
||||
|
||||
// CCS811Status represents the current status of the device defined by the ccs811RegStatus.
|
||||
// The following definitions were taken from https://ams.com/documents/20143/36005/CCS811_DS000459_6-00.pdf/c7091525-c7e5-37ac-eedb-b6c6828b0dcf#page=15
|
||||
type CCS811Status struct {
|
||||
//There is some sort of error on the i2c bus or there is an error with the internal sensor
|
||||
HasError byte
|
||||
//A new data sample is ready in ccs811RegAlgResultData
|
||||
DataReady byte
|
||||
//Valid application firmware loaded
|
||||
AppValid byte
|
||||
//Firmware is in application mode. CCS811 is ready to take sensor measurements
|
||||
FwMode byte
|
||||
}
|
||||
|
||||
//NewCCS811Status returns a new instance of the package ccs811 status definiton
|
||||
func NewCCS811Status(data uint8) *CCS811Status {
|
||||
return &CCS811Status{
|
||||
HasError: data & 0x01,
|
||||
DataReady: (data >> 3) & 0x01,
|
||||
AppValid: (data >> 4) & 0x01,
|
||||
FwMode: (data >> 7) & 0x01,
|
||||
}
|
||||
}
|
||||
|
||||
//CCS811MeasMode represents the current measurement configuration.
|
||||
//The following definitions were taken from the bit fields of the ccs811RegMeasMode defined in
|
||||
//https://ams.com/documents/20143/36005/CCS811_DS000459_6-00.pdf/c7091525-c7e5-37ac-eedb-b6c6828b0dcf#page=16
|
||||
type CCS811MeasMode struct {
|
||||
//If intThresh is 1 a data measurement will only be taken when the sensor value mets the threshold constraint.
|
||||
//The threshold value is set in the threshold register (0x10)
|
||||
intThresh uint8
|
||||
//If intDataRdy is 1, the nINT signal (pin 3 of the device) will be driven low when new data is avaliable.
|
||||
intDataRdy uint8
|
||||
//driveMode represents the sampling rate of the sensor. If the value is 0, the measurement process is idle.
|
||||
driveMode CCS811DriveMode
|
||||
}
|
||||
|
||||
//NewCCS811MeasMode returns a new instance of the package ccs811 measurement mode configuration. This represents the desired intial
|
||||
//state of the measurement mode register.
|
||||
func NewCCS811MeasMode() *CCS811MeasMode {
|
||||
return &CCS811MeasMode{
|
||||
// Disable this by default as this library does not contain the functionality to use the internal interrupt feature.
|
||||
intThresh: 0x00,
|
||||
intDataRdy: 0x00,
|
||||
driveMode: CCS811DriveMode1Sec,
|
||||
}
|
||||
}
|
||||
|
||||
// GetMeasMode returns the measurement mode
|
||||
func (mm *CCS811MeasMode) GetMeasMode() byte {
|
||||
return (mm.intThresh << 2) | (mm.intDataRdy << 3) | uint8((mm.driveMode << 4))
|
||||
}
|
||||
|
||||
//CCS811Driver is the Gobot driver for the CCS811 (air quality sensor) Adafruit breakout board
|
||||
type CCS811Driver struct {
|
||||
name string
|
||||
connector Connector
|
||||
connection Connection
|
||||
measMode *CCS811MeasMode
|
||||
ntcResistanceValue uint32
|
||||
Config
|
||||
}
|
||||
|
||||
//NewCCS811Driver creates a new driver for the CCS811 (air quality sensor)
|
||||
func NewCCS811Driver(a Connector, options ...func(Config)) *CCS811Driver {
|
||||
l := &CCS811Driver{
|
||||
name: gobot.DefaultName("CCS811"),
|
||||
connector: a,
|
||||
measMode: NewCCS811MeasMode(),
|
||||
//Recommended resistance value is 100,000
|
||||
ntcResistanceValue: 100000,
|
||||
Config: NewConfig(),
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(l)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
//WithCCS811MeasMode sets the sampling rate of the device
|
||||
func WithCCS811MeasMode(mode CCS811DriveMode) func(Config) {
|
||||
return func(c Config) {
|
||||
d, _ := c.(*CCS811Driver)
|
||||
d.measMode.driveMode = mode
|
||||
}
|
||||
}
|
||||
|
||||
//WithCCS811NTCResistance sets reistor value used in the temperature calculations.
|
||||
//This resistor must be placed between pin 4 and pin 8 of the chip
|
||||
func WithCCS811NTCResistance(val uint32) func(Config) {
|
||||
return func(c Config) {
|
||||
d, _ := c.(*CCS811Driver)
|
||||
d.ntcResistanceValue = val
|
||||
}
|
||||
}
|
||||
|
||||
//Start initializes the sensor
|
||||
func (d *CCS811Driver) Start() (err error) {
|
||||
bus := d.GetBusOrDefault(d.connector.GetDefaultBus())
|
||||
address := d.GetAddressOrDefault(ccs811DefaultAddress)
|
||||
|
||||
if d.connection, err = d.connector.GetConnection(address, bus); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.initialize()
|
||||
}
|
||||
|
||||
//Name returns the Name for the Driver
|
||||
func (d *CCS811Driver) Name() string { return d.name }
|
||||
|
||||
//SetName sets the Name for the Driver
|
||||
func (d *CCS811Driver) SetName(n string) { d.name = n }
|
||||
|
||||
//Connection returns the connection for the Driver
|
||||
func (d *CCS811Driver) Connection() gobot.Connection { return d.connector.(gobot.Connection) }
|
||||
|
||||
//Halt returns true if devices is halted successfully
|
||||
func (d *CCS811Driver) Halt() (err error) { return }
|
||||
|
||||
//GetHardwareVersion returns the hardware version of the device in the form of 0x1X
|
||||
func (d *CCS811Driver) GetHardwareVersion() (uint8, error) {
|
||||
v, err := d.connection.ReadByteData(ccs811RegHwVersion)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
//GetFirmwareBootVersion returns the bootloader version
|
||||
func (d *CCS811Driver) GetFirmwareBootVersion() (uint16, error) {
|
||||
v, err := d.connection.ReadWordData(ccs811RegFwBootVersion)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
//GetFirmwareAppVersion returns the app code version
|
||||
func (d *CCS811Driver) GetFirmwareAppVersion() (uint16, error) {
|
||||
v, err := d.connection.ReadWordData(ccs811RegFwAppVersion)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
//GetStatus returns the current status of the device
|
||||
func (d *CCS811Driver) GetStatus() (*CCS811Status, error) {
|
||||
s, err := d.connection.ReadByteData(ccs811RegStatus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cs := NewCCS811Status(s)
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
//GetTemperature returns the device temperature in celcius.
|
||||
//If you do not have an NTC resistor installed, this function should not be called
|
||||
func (d *CCS811Driver) GetTemperature() (float32, error) {
|
||||
|
||||
buf, err := d.read(ccs811RegNtc, 4)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
vref := ((uint16(buf[0]) << 8) | uint16(buf[1]))
|
||||
vrntc := ((uint16(buf[2]) << 8) | uint16(buf[3]))
|
||||
rntc := (float32(vrntc) * float32(d.ntcResistanceValue) / float32(vref))
|
||||
|
||||
ntcTemp := float32(math.Log(float64(rntc / 10000.0)))
|
||||
ntcTemp /= 3380.0
|
||||
ntcTemp += 1.0 / (25 + 273.15)
|
||||
ntcTemp = 1.0 / ntcTemp
|
||||
ntcTemp -= 273.15
|
||||
|
||||
return ntcTemp, nil
|
||||
}
|
||||
|
||||
//GetGasData returns the data for the gas sensor.
|
||||
//eco2 is returned in ppm and tvoc is returned in ppb
|
||||
func (d *CCS811Driver) GetGasData() (uint16, uint16, error) {
|
||||
|
||||
data, err := d.read(ccs811RegAlgResultData, 4)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// Bit masks defined by https://ams.com/documents/20143/36005/CCS811_AN000369_2-00.pdf/25d0db9a-92b9-fa7f-362c-a7a4d1e292be#page=14
|
||||
eco2 := (uint16(data[0]) << 8) | uint16(data[1])
|
||||
tvoC := (uint16(data[2]) << 8) | uint16(data[3])
|
||||
|
||||
return eco2, tvoC, nil
|
||||
}
|
||||
|
||||
//HasData returns true if the device has not errored and temperature/gas data is avaliable
|
||||
func (d *CCS811Driver) HasData() (bool, error) {
|
||||
s, err := d.GetStatus()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !(s.DataReady == 0x01) || (s.HasError == 0x01) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
//EnableExternalInterrupt enables the external output hardware interrupt pin 3.
|
||||
func (d *CCS811Driver) EnableExternalInterrupt() error {
|
||||
d.measMode.intDataRdy = 1
|
||||
return d.connection.WriteByteData(ccs811RegMeasMode, d.measMode.GetMeasMode())
|
||||
}
|
||||
|
||||
//DisableExternalInterrupt disables the external output hardware interrupt pin 3.
|
||||
func (d *CCS811Driver) DisableExternalInterrupt() error {
|
||||
d.measMode.intDataRdy = 0
|
||||
return d.connection.WriteByteData(ccs811RegMeasMode, d.measMode.GetMeasMode())
|
||||
}
|
||||
|
||||
//updateMeasMode writes the current value of measMode to the measurement mode register.
|
||||
func (d *CCS811Driver) updateMeasMode() error {
|
||||
return d.connection.WriteByteData(ccs811RegMeasMode, d.measMode.GetMeasMode())
|
||||
}
|
||||
|
||||
//ResetDevice does a software reset of the device. After this operation is done,
|
||||
//the user must start the app code before the sensor can take any measurements
|
||||
func (d *CCS811Driver) resetDevice() error {
|
||||
return d.connection.WriteBlockData(ccs811RegSwReset, ccs811SwResetSequence)
|
||||
}
|
||||
|
||||
//startApp starts the app code in the device. This operation has to be done after a
|
||||
//software reset to start taking sensor measurements.
|
||||
func (d *CCS811Driver) startApp() error {
|
||||
//Write without data is needed to start the app code
|
||||
_, err := d.connection.Write([]byte{ccs811RegAppStart})
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *CCS811Driver) initialize() error {
|
||||
deviceID, err := d.connection.ReadByteData(ccs811RegHwID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get the device id from ccs811RegHwID with error: %s", err.Error())
|
||||
}
|
||||
|
||||
// Verify that the connected device is the CCS811 sensor
|
||||
if deviceID != ccs811HwIDCode {
|
||||
return fmt.Errorf("The fetched device id %d is not the known id %d with error", deviceID, ccs811HwIDCode)
|
||||
}
|
||||
|
||||
if err := d.resetDevice(); err != nil {
|
||||
return fmt.Errorf("Was not able to reset the device with error: %s", err.Error())
|
||||
}
|
||||
|
||||
// Required sleep to allow device to switch states
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
if err := d.startApp(); err != nil {
|
||||
return fmt.Errorf("Failed to start app code with error: %s", err.Error())
|
||||
}
|
||||
|
||||
if err := d.updateMeasMode(); err != nil {
|
||||
return fmt.Errorf("Failed to update the measMode register with error: %s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// An implementation of the ReadBlockData i2c operation. This code was copied from the BMP280Driver code
|
||||
func (d *CCS811Driver) read(reg byte, n int) ([]byte, error) {
|
||||
if _, err := d.connection.Write([]byte{reg}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := make([]byte, n)
|
||||
bytesRead, err := d.connection.Read(buf)
|
||||
if bytesRead != n || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
256
drivers/i2c/ccs811_driver_test.go
Normal file
256
drivers/i2c/ccs811_driver_test.go
Normal file
@ -0,0 +1,256 @@
|
||||
package i2c
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/gobottest"
|
||||
)
|
||||
|
||||
// The CCS811 Meets the Driver Definition
|
||||
var _ gobot.Driver = (*CCS811Driver)(nil)
|
||||
|
||||
// --------- HELPERS
|
||||
func initTestCCS811Driver() (driver *CCS811Driver) {
|
||||
driver, _ = initTestCCS811DriverWithStubbedAdaptor()
|
||||
return
|
||||
}
|
||||
|
||||
func initTestCCS811DriverWithStubbedAdaptor() (*CCS811Driver, *i2cTestAdaptor) {
|
||||
adaptor := newI2cTestAdaptor()
|
||||
return NewCCS811Driver(adaptor), adaptor
|
||||
}
|
||||
|
||||
// --------- BASE TESTS
|
||||
func TestNewCCS811Driver(t *testing.T) {
|
||||
// Does it return a pointer to an instance of CCS811Driver?
|
||||
var c interface{} = NewCCS811Driver(newI2cTestAdaptor())
|
||||
_, ok := c.(*CCS811Driver)
|
||||
if !ok {
|
||||
t.Errorf("NewCCS811Driver() should have returned a *CCS811Driver")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCCS811DriverSetName(t *testing.T) {
|
||||
// Does it change the name of the driver
|
||||
d := initTestCCS811Driver()
|
||||
d.SetName("TESTME")
|
||||
gobottest.Assert(t, d.Name(), "TESTME")
|
||||
}
|
||||
|
||||
func TestCCS811Connection(t *testing.T) {
|
||||
// Does it create an instance of gobot.Connection
|
||||
ccs811 := initTestCCS811Driver()
|
||||
gobottest.Refute(t, ccs811.Connection(), nil)
|
||||
}
|
||||
|
||||
// // --------- CONFIG OVERIDE TESTS
|
||||
|
||||
func TestCCS811DriverWithBus(t *testing.T) {
|
||||
// Can it update the bus
|
||||
d := NewCCS811Driver(newI2cTestAdaptor(), WithBus(2))
|
||||
gobottest.Assert(t, d.GetBusOrDefault(1), 2)
|
||||
}
|
||||
|
||||
func TestCCS811DriverWithAddress(t *testing.T) {
|
||||
// Can it update the address
|
||||
d := NewCCS811Driver(newI2cTestAdaptor(), WithAddress(0xFF))
|
||||
gobottest.Assert(t, d.GetAddressOrDefault(0x5a), 0xFF)
|
||||
}
|
||||
|
||||
func TestCCS811DriverWithCCS811MeasMode(t *testing.T) {
|
||||
// Can it update the measurement mode
|
||||
d := NewCCS811Driver(newI2cTestAdaptor(), WithCCS811MeasMode(CCS811DriveMode10Sec))
|
||||
gobottest.Assert(t, d.measMode.driveMode, CCS811DriveMode(CCS811DriveMode10Sec))
|
||||
}
|
||||
|
||||
func TestCCS811DriverWithCCS811NTCResistance(t *testing.T) {
|
||||
// Can it update the ntc resitor value used for temp calcuations
|
||||
d := NewCCS811Driver(newI2cTestAdaptor(), WithCCS811NTCResistance(0xFF))
|
||||
gobottest.Assert(t, d.ntcResistanceValue, uint32(0xFF))
|
||||
}
|
||||
|
||||
// // --------- DRIVER SPECIFIC TESTS
|
||||
|
||||
func TestCCS811DriverGetGasData(t *testing.T) {
|
||||
|
||||
cases := []struct {
|
||||
readReturn func([]byte) (int, error)
|
||||
eco2 uint16
|
||||
tvoc uint16
|
||||
err error
|
||||
}{
|
||||
// Can it compute the gas data with ideal values taken from the bus
|
||||
{
|
||||
readReturn: func(b []byte) (int, error) {
|
||||
copy(b, []byte{1, 156, 0, 86})
|
||||
return 4, nil
|
||||
},
|
||||
eco2: 412,
|
||||
tvoc: 86,
|
||||
err: nil,
|
||||
},
|
||||
// Can it compute the gas data with the max values possible taken from the bus
|
||||
{
|
||||
readReturn: func(b []byte) (int, error) {
|
||||
copy(b, []byte{255, 255, 255, 255})
|
||||
return 4, nil
|
||||
},
|
||||
eco2: 65535,
|
||||
tvoc: 65535,
|
||||
err: nil,
|
||||
},
|
||||
// Does it return an error when the i2c operation fails
|
||||
{
|
||||
readReturn: func(b []byte) (int, error) {
|
||||
copy(b, []byte{255, 255, 255, 255})
|
||||
return 4, errors.New("Error")
|
||||
},
|
||||
eco2: 0,
|
||||
tvoc: 0,
|
||||
err: errors.New("Error"),
|
||||
},
|
||||
}
|
||||
|
||||
d, adaptor := initTestCCS811DriverWithStubbedAdaptor()
|
||||
// Create stub function as it is needed by read submethod in driver code
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) { return 0, nil }
|
||||
|
||||
d.Start()
|
||||
for _, c := range cases {
|
||||
adaptor.i2cReadImpl = c.readReturn
|
||||
eco2, tvoc, err := d.GetGasData()
|
||||
gobottest.Assert(t, eco2, c.eco2)
|
||||
gobottest.Assert(t, tvoc, c.tvoc)
|
||||
gobottest.Assert(t, err, c.err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCCS811DriverGetTemperature(t *testing.T) {
|
||||
|
||||
cases := []struct {
|
||||
readReturn func([]byte) (int, error)
|
||||
temp float32
|
||||
err error
|
||||
}{
|
||||
// Can it compute the temperature data with ideal values taken from the bus
|
||||
{
|
||||
readReturn: func(b []byte) (int, error) {
|
||||
copy(b, []byte{10, 197, 0, 248})
|
||||
return 4, nil
|
||||
},
|
||||
temp: 27.811005,
|
||||
err: nil,
|
||||
},
|
||||
// Can it compute the temperature data without bus values overflowing
|
||||
{
|
||||
readReturn: func(b []byte) (int, error) {
|
||||
copy(b, []byte{129, 197, 10, 248})
|
||||
return 4, nil
|
||||
},
|
||||
temp: 29.48822,
|
||||
err: nil,
|
||||
},
|
||||
// Can it compute a negative temperature
|
||||
{
|
||||
readReturn: func(b []byte) (int, error) {
|
||||
copy(b, []byte{255, 255, 255, 255})
|
||||
return 4, nil
|
||||
},
|
||||
temp: -25.334152,
|
||||
err: nil,
|
||||
},
|
||||
// Does it return an error if the i2c bus errors
|
||||
{
|
||||
readReturn: func(b []byte) (int, error) {
|
||||
copy(b, []byte{129, 197, 0, 248})
|
||||
return 4, errors.New("Error")
|
||||
},
|
||||
temp: 0,
|
||||
err: errors.New("Error"),
|
||||
},
|
||||
}
|
||||
|
||||
d, adaptor := initTestCCS811DriverWithStubbedAdaptor()
|
||||
// Create stub function as it is needed by read submethod in driver code
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) { return 0, nil }
|
||||
|
||||
d.Start()
|
||||
for _, c := range cases {
|
||||
adaptor.i2cReadImpl = c.readReturn
|
||||
temp, err := d.GetTemperature()
|
||||
gobottest.Assert(t, temp, c.temp)
|
||||
gobottest.Assert(t, err, c.err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCCS811DriverHasData(t *testing.T) {
|
||||
|
||||
cases := []struct {
|
||||
readReturn func([]byte) (int, error)
|
||||
result bool
|
||||
err error
|
||||
}{
|
||||
// Does it return true for HasError = 0 and DataRdy = 1
|
||||
{
|
||||
readReturn: func(b []byte) (int, error) {
|
||||
copy(b, []byte{0x08})
|
||||
return 1, nil
|
||||
},
|
||||
result: true,
|
||||
err: nil,
|
||||
},
|
||||
// Does it return false for HasError = 1 and DataRdy = 1
|
||||
{
|
||||
readReturn: func(b []byte) (int, error) {
|
||||
copy(b, []byte{0x09})
|
||||
return 1, nil
|
||||
},
|
||||
result: false,
|
||||
err: nil,
|
||||
},
|
||||
// Does it return false for HasError = 1 and DataRdy = 0
|
||||
{
|
||||
readReturn: func(b []byte) (int, error) {
|
||||
copy(b, []byte{0x01})
|
||||
return 1, nil
|
||||
},
|
||||
result: false,
|
||||
err: nil,
|
||||
},
|
||||
// Does it return false for HasError = 0 and DataRdy = 0
|
||||
{
|
||||
readReturn: func(b []byte) (int, error) {
|
||||
copy(b, []byte{0x00})
|
||||
return 1, nil
|
||||
},
|
||||
result: false,
|
||||
err: nil,
|
||||
},
|
||||
// Does it return an error when the i2c read operation fails
|
||||
{
|
||||
readReturn: func(b []byte) (int, error) {
|
||||
copy(b, []byte{0x00})
|
||||
return 1, errors.New("Error")
|
||||
},
|
||||
result: false,
|
||||
err: errors.New("Error"),
|
||||
},
|
||||
}
|
||||
|
||||
d, adaptor := initTestCCS811DriverWithStubbedAdaptor()
|
||||
// Create stub function as it is needed by read submethod in driver code
|
||||
adaptor.i2cWriteImpl = func([]byte) (int, error) { return 0, nil }
|
||||
|
||||
d.Start()
|
||||
for _, c := range cases {
|
||||
adaptor.i2cReadImpl = c.readReturn
|
||||
result, err := d.HasData()
|
||||
gobottest.Assert(t, result, c.result)
|
||||
gobottest.Assert(t, err, c.err)
|
||||
}
|
||||
|
||||
}
|
76
examples/raspi_ccs811.go
Normal file
76
examples/raspi_ccs811.go
Normal file
@ -0,0 +1,76 @@
|
||||
// +build example
|
||||
//
|
||||
// Do not build by default.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/drivers/i2c"
|
||||
"gobot.io/x/gobot/platforms/raspi"
|
||||
)
|
||||
|
||||
func CCS811BootData(a *i2c.CCS811Driver) {
|
||||
v, err := a.GetHardwareVersion()
|
||||
if err != nil {
|
||||
fmt.Printf(err.Error())
|
||||
}
|
||||
|
||||
fmt.Printf("Hardare Version %#x\n", v)
|
||||
|
||||
d, err := a.GetFirmwareBootVersion()
|
||||
if err != nil {
|
||||
fmt.Printf(err.Error())
|
||||
}
|
||||
|
||||
fmt.Printf("Boot Version %#x\n", d)
|
||||
|
||||
d, err = a.GetFirmwareAppVersion()
|
||||
if err != nil {
|
||||
fmt.Printf(err.Error())
|
||||
}
|
||||
|
||||
fmt.Printf("App Version %#x\n\n", d)
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := raspi.NewAdaptor()
|
||||
ccs811Driver := i2c.NewCCS811Driver(r)
|
||||
|
||||
work := func() {
|
||||
CCS811BootData(ccs811Driver)
|
||||
gobot.Every(1*time.Second, func() {
|
||||
s, err := ccs811Driver.GetStatus()
|
||||
if err != nil {
|
||||
fmt.Printf("Error fetching data from the status register: %+v\n", err.Error())
|
||||
}
|
||||
fmt.Printf("Status %+v \n", s)
|
||||
|
||||
hd, err := ccs811Driver.HasData()
|
||||
if err != nil {
|
||||
fmt.Printf("Error fetching data from the status register: %+v\n", err.Error())
|
||||
}
|
||||
|
||||
if hd {
|
||||
ec02, tv0C, _ := ccs811Driver.GetGasData()
|
||||
fmt.Printf("Gas Data - eco2: %+v, tvoc: %+v \n", ec02, tv0C)
|
||||
|
||||
temp, _ := ccs811Driver.GetTemperature()
|
||||
fmt.Printf("Temperature %+v \n\n", temp)
|
||||
} else {
|
||||
fmt.Println("New data is not avaliable\n")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
robot := gobot.NewRobot("adaFruitBot",
|
||||
[]gobot.Connection{r},
|
||||
[]gobot.Device{ccs811Driver},
|
||||
work,
|
||||
)
|
||||
|
||||
robot.Start()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user