This commit is contained in:
Adam Bright 2017-05-08 01:30:54 +00:00 committed by GitHub
commit a4d939f70e
2 changed files with 501 additions and 0 deletions

119
samples/mcp9808.go Normal file
View File

@ -0,0 +1,119 @@
package main
import (
"bufio"
"fmt"
"os"
"github.com/kidoman/embd"
_ "github.com/kidoman/embd/host/rpi"
"github.com/kidoman/embd/sensor/mcp9808"
)
func main() {
bus := embd.NewI2CBus(1)
defer embd.CloseI2C()
therm, _ := mcp9808.New(bus)
// set sensor to low power mode when we're done
defer therm.SetShutdownMode(true)
if id, err := therm.ManufacturerID(); err == nil {
fmt.Printf("Manufacturer ID: 0x%x\n", id)
}
if devID, rev, err := therm.DeviceID(); err == nil {
fmt.Printf("Device ID: 0x%x rev. 0x%x\n", devID, rev)
}
therm.SetShutdownMode(false)
therm.SetCriticalTempLock(false)
therm.SetWindowTempLock(false)
therm.SetAlertMode(true)
therm.SetInterruptClear(true)
therm.SetAlertStatus(true)
therm.SetAlertControl(true)
therm.SetAlertSelect(false)
therm.SetAlertPolarity(true)
// get faster results (130ms vs 250ms default)
therm.SetTempResolution(mcp9808.EighthC)
config, _ := therm.Config()
fmt.Printf("New Config: %b\n", config)
if err := therm.SetCriticalTemp(TempFToC(95)); err != nil {
panic(err)
}
critTemp, err := therm.CriticalTemp()
if err != nil {
fmt.Printf("Error reading critical temp limit: %s\n", err.Error())
}
fmt.Printf("Critical Temp set to: %fC\n", critTemp)
if err := therm.SetWindowTempLower(TempFToC(60)); err != nil {
panic(err)
}
lowerTemp, err := therm.WindowTempLower()
if err != nil {
fmt.Printf("Error reading lower temp limit: %s\n", err.Error())
}
fmt.Printf("Lower Temp Limit set to: %fC\n", lowerTemp)
if err := therm.SetWindowTempUpper(TempFToC(80)); err != nil {
panic(err)
}
upperTemp, _ := therm.WindowTempUpper()
fmt.Printf("Upper Temp Limit set to: %fC\n", upperTemp)
therm.SetCriticalTempLock(true)
therm.SetWindowTempLock(true)
alert, err := embd.NewDigitalPin(23)
if err != nil {
panic(err)
}
defer alert.Close()
alert.SetDirection(embd.In)
alert.ActiveLow(false)
err = alert.Watch(embd.EdgeRising, func(alert embd.DigitalPin) {
fmt.Printf("Temperature is outside the specified window!\n")
therm.SetInterruptClear(true)
therm.Config()
})
if err != nil {
panic(err)
}
cancel := make(chan bool)
go func() {
reader := bufio.NewReader(os.Stdin)
reader.ReadString('\n')
cancel <- true
}()
for {
select {
case <-cancel:
return
default:
temp, err := therm.AmbientTemp()
if err != nil {
fmt.Printf("Error reading temp: %s\n", err.Error())
} else {
fmt.Printf("Current temp is: %fF (%fC), Window Alert: %v, Critical Alert: %v\n",
TempCToF(temp.CelsiusDeg), temp.CelsiusDeg, temp.AboveUpper || temp.BelowLower, temp.AboveCritical)
}
}
}
}
func TempCToF(tempC float64) float64 {
return tempC*9/5 + 32
}
func TempFToC(tempF float64) float64 {
return (tempF - 32) * 5 / 9
}

382
sensor/mcp9808/mcp9808.go Normal file
View File

@ -0,0 +1,382 @@
// Package mcp9808 is a driver for the MCP9808 temperature sensor
// and all code is based off of the documentation found here:
// http://ww1.microchip.com/downloads/en/DeviceDoc/25095A.pdf
package mcp9808
import "github.com/kidoman/embd"
const (
// default I2C address for device
address = 0x18
// Register addresses.
regConfig = iota // starts at 1, this is what we want
regUpperTemp
regLowerTemp
regCriticalTemp
regAmbientTemp
regManufID
regDeviceID
regResolution
)
const (
configAlertMode uint16 = 1 << iota
configAlertPolarity
configAlertSelect
configAlertControl
configAlertStatus
configInterruptClear
configWindowTempLock
configCriticalTempLock
configShutDown
)
// MCP9808 represents a MCP9808 temperature sensor.
type MCP9808 struct {
Bus embd.I2CBus
config uint16
}
// New returns a handle to a MCP9808 sensor.
func New(bus embd.I2CBus) (*MCP9808, error) {
d := &MCP9808{Bus: bus}
// initialize the configuration
_, err := d.Config()
return d, err
}
// ManufacturerID reads the device manufacturer ID
func (d *MCP9808) ManufacturerID() (uint16, error) {
return d.Bus.ReadWordFromReg(address, regManufID)
}
// DeviceID reads the device ID and revision
func (d *MCP9808) DeviceID() (uint8, uint8, error) {
devIDRev, err := d.Bus.ReadWordFromReg(address, regDeviceID)
if err != nil {
return 0, 0, err
}
return uint8(devIDRev >> 8), uint8(devIDRev & 0xFF), nil
}
// Config gets the config word from the device.
func (d *MCP9808) Config() (uint16, error) {
config, err := d.Bus.ReadWordFromReg(address, regConfig)
if err != nil {
return 0, err
}
d.config = config
return d.config, nil
}
// setConfig writes the sensor's config word to the device and returns the resulting config
func (d *MCP9808) setConfig() error {
return d.Bus.WriteWordToReg(address, regConfig, d.config)
}
// flipConfig bit sets (1, set = true) or clears (0, set = false) a bit within the config word
func (d *MCP9808) flipConfigBit(val uint16, set bool) error {
if set {
d.config |= val
} else {
d.config &= ^val
}
return d.setConfig()
}
func (d *MCP9808) readConfigValue(val uint16) (bool, error) {
config, err := d.Config()
return !(config&(1<<val) == 0), err
}
// Hysteresis applies for decreasing temperature only (hot to cold) or as temperature
// drifts below the specified limit.
type Hysteresis uint8
const (
// Zero hysteresis represents 0 degrees Celsius of hysteresis compensation
Zero Hysteresis = iota
// Plus1pt5 hysteresis represents +1.5 degrees Celsius of hysteresis compensation
Plus1pt5
// Plus3pt0 hysteresis represents +3.0 degrees Celsius of hysteresis compensation
Plus3pt0
// Plus6pt0 hysteresis represents +6.0 degrees Celsius of hysteresis compensation
Plus6pt0
)
// TempHysteresis - TUPPER and TLOWER Limit Hysteresis bits
// 00 = 0°C (power-up default)
// 01 = +1.5°C
// 10 = +3.0°C
// 11 = +6.0°C
// The hysteresis applies for decreasing temperature only (hot to cold) or as temperature
// drifts below the specified limit.
func (d *MCP9808) TempHysteresis() (Hysteresis, error) {
_, err := d.Config()
return Hysteresis(d.config >> 9), err
}
// SetTempHysteresis - TUPPER and TLOWER Limit Hysteresis bits
// 00 = 0°C (power-up default)
// 01 = +1.5°C
// 10 = +3.0°C
// 11 = +6.0°C
// The hysteresis applies for decreasing temperature only (hot to cold) or as temperature
// drifts below the specified limit.
// This bit can not be altered when either of the Lock bits are set (bit 6 and bit 7).
// Thi s bit can be programmed in Shutdown mode.
func (d *MCP9808) SetTempHysteresis(val Hysteresis) error {
d.config = d.config - d.config&^(d.config>>9) + uint16(val)<<9
return d.setConfig()
}
// ShutdownMode bit
// 0 (false) = Continuous conversion (power-up default)
// 1 (true) = Shutdown (Low-Power mode)
// In shutdown, all power-consuming activities are disabled, though all registers can be written to or read.
func (d *MCP9808) ShutdownMode() (bool, error) {
return d.readConfigValue(configShutDown)
}
// SetShutdownMode bit
// 0 (false) = Continuous conversion (power-up default)
// 1 (true) = Shutdown (Low-Power mode)
// In shutdown, all power-consuming activities are disabled, though all registers can be written to or read.
// This bit cannot be set to 1 when either of the Lock bits is set (bit 6 and bit 7). However, it can be
// cleared to 0 for continuous conversion while locked
func (d *MCP9808) SetShutdownMode(set bool) error {
return d.flipConfigBit(configShutDown, set)
}
// CriticalTempLock - TCRIT Lock bit
// 0 (false) = Unlocked. TCRIT register can be written (power-up default)
// 1 (true) = Locked. TCRIT register can not be written
// When enabled, this bit remains set to 1 or locked until cleared by an internal Reset
func (d *MCP9808) CriticalTempLock() (bool, error) {
return d.readConfigValue(configCriticalTempLock)
}
// SetCriticalTempLock - TCRIT Lock bit
// 0 (false) = Unlocked. TCRIT register can be written (power-up default)
// 1 (true) = Locked. TCRIT register can not be written
// When enabled, this bit remains set to 1 or locked until cleared by an internal Reset
// This bit can be programmed in Shutdown mode.
func (d *MCP9808) SetCriticalTempLock(locked bool) error {
return d.flipConfigBit(configCriticalTempLock, locked)
}
// WindowTempLock - TUPPER and TLOWER Window Lock bit
// 0 (false) = Unlocked; TUPPER and TLOWER registers can be written (power-up default)
// 1 (true) = Locked; TUPPER and TLOWER registers can not be written
// When enabled, this bit remains set to 1 or locked until cleared by a Power-on Reset
func (d *MCP9808) WindowTempLock() (bool, error) {
return d.readConfigValue(configWindowTempLock)
}
// SetWindowTempLock - TUPPER and TLOWER Window Lock bit
// 0 (false) = Unlocked; TUPPER and TLOWER registers can be written (power-up default)
// 1 (true) = Locked; TUPPER and TLOWER registers can not be written
// When enabled, this bit remains set to 1 or locked until cleared by a Power-on Reset
// This bit can be programmed in Shutdown mode.
func (d *MCP9808) SetWindowTempLock(locked bool) error {
return d.flipConfigBit(configWindowTempLock, locked)
}
// InterruptClear - Interrupt Clear bit
// 0 (false) = No effect (power-up default)
// 1 (true) = Clear interrupt output; when read, this bit returns to 0
func (d *MCP9808) InterruptClear() (bool, error) {
return d.readConfigValue(configInterruptClear)
}
// SetInterruptClear - Interrupt Clear bit
// 0 (false) = No effect (power-up default)
// 1 (true) = Clear interrupt output; when read, this bit returns to 0
// This bit can not be set to 1 in Shutdown mode, but it can be cleared after the device enters Shutdown
// mode.
func (d *MCP9808) SetInterruptClear(set bool) error {
return d.flipConfigBit(configInterruptClear, set)
}
// AlertStatus Alert Output Status bit
// 0 (false) = Alert output is not asserted by the device (power-up default)
// 1 (true) = Alert output is asserted as a comparator/Interrupt or critical temperature output
func (d *MCP9808) AlertStatus() (bool, error) {
return d.readConfigValue(configAlertStatus)
}
// SetAlertStatus Alert Output Status bit
// 0 (false) = Alert output is not asserted by the device (power-up default)
// 1 (true) = Alert output is asserted as a comparator/Interrupt or critical temperature output
// This bit can not be set to 1 or cleared to 0 in Shutdown mode. However, if the Alert output is configured
// as Interrupt mode, and if the host controller clears to 0, the interrupt, using bit 5 while the device
// is in Shutdown mode, then this bit will also be cleared 0.
func (d *MCP9808) SetAlertStatus(set bool) error {
return d.flipConfigBit(configAlertStatus, set)
}
// AlertControl - Alert Output Control bit
// 0 (false) = Disabled (power-up default)
// 1 (true) = Enabled
func (d *MCP9808) AlertControl() (bool, error) {
return d.readConfigValue(configAlertControl)
}
// SetAlertControl - Alert Output Control bit
// 0 (false) = Disabled (power-up default)
// 1 (true) = Enabled
// This bit can not be altered when either of the Lock bits are set (bit 6 and bit 7).
// This bit can be programmed in Shutdown mode, but the Alert output will not assert or deassert.
func (d *MCP9808) SetAlertControl(set bool) error {
return d.flipConfigBit(configAlertControl, set)
}
// AlertSelect - Alert Output Select bit
// 0 (false) = Alert output for TUPPER, TLOWER and TCRIT (power-up default)
// 1 (true) = TA > TCRIT only (TUPPER and TLOWER temperature boundaries are disabled)
func (d *MCP9808) AlertSelect() (bool, error) {
return d.readConfigValue(configAlertSelect)
}
// SetAlertSelect - Alert Output Select bit
// 0 (false) = Alert output for TUPPER, TLOWER and TCRIT (power-up default)
// 1 (true) = TA > TCRIT only (TUPPER and TLOWER temperature boundaries are disabled)
// When the Alarm Window Lock bit is set, this bit cannot be altered until unlocked (bit 6).
// This bit can be programmed in Shutdown mode, but the Alert output will not assert or deassert.
func (d *MCP9808) SetAlertSelect(set bool) error {
return d.flipConfigBit(configAlertSelect, set)
}
// AlertPolarity - Alert Output Polarity bit
// 0 (false) = Active-low (power-up default; pull-up resistor required)
// 1 (true) = Active-high
func (d *MCP9808) AlertPolarity() (bool, error) {
return d.readConfigValue(configAlertPolarity)
}
// SetAlertPolarity - Alert Output Polarity bit
// 0 (false) = Active-low (power-up default; pull-up resistor required)
// 1 (true) = Active-high
// This bit cannot be altered when either of the Lock bits are set (bit 6 and bit 7).
// This bit can be programmed in Shutdown mode, but the Alert output will not assert or deassert.
func (d *MCP9808) SetAlertPolarity(set bool) error {
return d.flipConfigBit(configAlertPolarity, set)
}
// AlertMode - Alert Output Mode bit
// 0 (false) = Comparator output (power-up default)
// 1 (true) = Interrupt output
func (d *MCP9808) AlertMode() (bool, error) {
return d.readConfigValue(configAlertMode)
}
// SetAlertMode - Alert Output Mode bit
// 0 (false) = Comparator output (power-up default)
// 1 (true) = Interrupt output
// This bit cannot be altered when either of the Lock bits are set (bit 6 and bit 7).
// This bit can be programmed in Shutdown mode, but the Alert output will not assert or deassert.
func (d *MCP9808) SetAlertMode(set bool) error {
return d.flipConfigBit(configAlertMode, set)
}
// Temperature contains the ambient temperature along with alert values.
type Temperature struct {
CelsiusDeg float64
AboveCritical, AboveUpper, BelowLower bool
}
// readTempC reads from the reg temperature register and returns the current temperature value in celsius
func (d *MCP9808) readTempC(reg byte) (float64, error) {
temp, err := d.Bus.ReadWordFromReg(address, reg)
if err != nil {
return 0, err
}
return convertWordToTempC(temp), nil
}
func convertWordToTempC(temp uint16) float64 {
return float64(int16(temp<<3)>>3) / 16
}
func (d *MCP9808) setTemp(reg byte, newTemp float64) error {
return d.Bus.WriteWordToReg(address, reg, uint16(newTemp*16+2)&0x1ffc)
}
// AmbientTemp reads the current sensor value along with the flags denoting what boundaries the
// current temperature exceeds.
func (d *MCP9808) AmbientTemp() (*Temperature, error) {
temp, err := d.Bus.ReadWordFromReg(address, regAmbientTemp)
if err != nil {
return nil, err
}
tempResult := &Temperature{
AboveCritical: temp&0x8000 == 0x8000,
AboveUpper: temp&0x4000 == 0x4000,
BelowLower: temp&0x2000 == 0x2000}
tempResult.CelsiusDeg = convertWordToTempC(temp)
return tempResult, nil
}
// CriticalTemp reads the current temperature set in the critical temperature register.
func (d *MCP9808) CriticalTemp() (float64, error) {
return d.readTempC(regCriticalTemp)
}
// SetCriticalTemp when the temperature goes above the set value the alert will be
// triggered if enabled. This has no effect if CriticalTempLock is set.
func (d *MCP9808) SetCriticalTemp(newTemp float64) error {
return d.setTemp(regCriticalTemp, newTemp)
}
// WindowTempUpper reads the current temperature set in the upper window temperature register.
func (d *MCP9808) WindowTempUpper() (float64, error) {
return d.readTempC(regUpperTemp)
}
// SetWindowTempUpper when the temperature goes above the set value the alert will be
// triggered if enabled. This has no effect if WindowTempLock is set.
func (d *MCP9808) SetWindowTempUpper(newTemp float64) error {
return d.setTemp(regUpperTemp, newTemp)
}
// WindowTempLower reads the current temperature set in the lower window temperature register.
func (d *MCP9808) WindowTempLower() (float64, error) {
return d.readTempC(regLowerTemp)
}
// SetWindowTempLower when the temperature goes below the set value the alert will be
// triggered if enabled. This has no effect if WindowTempLock is set.
func (d *MCP9808) SetWindowTempLower(newTemp float64) error {
return d.setTemp(regLowerTemp, newTemp)
}
// TempResolution reads the current temperature accuracy from the sensor (affects temperature read speed)
// 0 - +/- .5 degrees C (~30ms)
// 1 - +/- .25 degrees C (~65ms)
// 2 - +/- .125 degrees C (~130ms)
// 3 (default) - +/- .0625 degrees C (~250ms)
type TempResolution uint8
const (
HalfC TempResolution = iota
QuarterC
EighthC
SixteenthC
)
// TempResolution reads the temperature resolution from the sensor.
func (d *MCP9808) TempResolution() (TempResolution, error) {
res, err := d.Bus.ReadByteFromReg(address, regResolution)
return TempResolution(res), err
}
// SetTempResolution writes a new temperature resolution to the sensor
func (d *MCP9808) SetTempResolution(res TempResolution) error {
return d.Bus.WriteByteToReg(address, regResolution, byte(res))
}