From eb44392b0aca501bc2659503dd50b551f5e38fda Mon Sep 17 00:00:00 2001 From: Adam Bright Date: Mon, 12 Sep 2016 21:27:06 +0000 Subject: [PATCH 01/10] sensor: added driver for MCP9808 temperature sensor --- samples/mcp9808.go | 112 +++++++++ sensor/mcp9808/mcp9808.go | 484 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 596 insertions(+) create mode 100644 samples/mcp9808.go create mode 100644 sensor/mcp9808/mcp9808.go diff --git a/samples/mcp9808.go b/samples/mcp9808.go new file mode 100644 index 0000000..17fad1c --- /dev/null +++ b/samples/mcp9808.go @@ -0,0 +1,112 @@ +package main + +import ( + "bufio" + "fmt" + "log" + "os" + "time" + + "github.com/kidoman/embd" + _ "github.com/kidoman/embd/host/rpi" + "github.com/kidoman/embd/sensor/mcp9808" + "github.com/stianeikeland/go-rpio" +) + +func main() { + if err := embd.InitI2C(); err != nil { + panic(err) + } + defer embd.CloseI2C() + + bus := embd.NewI2CBus(1) + + therm := mcp9808.New(bus) + + 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.SetWindowTempLock(false) + // therm.SetCriticalTempLock(false) + + config, err := therm.WriteConfig() + if err != nil { + panic(err) + } + fmt.Printf("New Config: %b\n", config) + therm.SetAlertControl(true) + //therm.SetInterruptClear(true) + //therm.SetAlertStatus(true) + //therm.SetAlertSelect(false) + //therm.SetAlertPolarity(true) + //therm.SetAlertMode(true) + config, err = therm.WriteConfig() + if err != nil { + panic(err) + } + fmt.Printf("New Config: %b\n", config) + + if err := therm.SetWindowTempUpper(TempFToC(80)); err != nil { + panic(err) + } + + temp, err := therm.AmbientTemp() + if err != nil { + panic(err) + } + fmt.Printf("Temp is %f\n", TempCToF(temp.CelsiusDeg)) + + if err := embd.InitGPIO(); err != nil { + panic(err) + } + if err := rpio.Open(); err != nil { + log.Fatalf("Error: %v\n", err) + } + defer rpio.Close() + defer embd.CloseGPIO() + + alert := rpio.Pin(4) + alert.Input() + alert.PullDown() + + timer := time.Tick(time.Duration(5) * time.Second) + + cancel := make(chan bool) + go func() { + reader := bufio.NewReader(os.Stdin) + reader.ReadString('\n') + cancel <- true + }() + for { + select { + case <-timer: + temp, err := therm.AmbientTemp() + if err == nil { + fmt.Printf("Ambient temp is: %f\n", TempCToF(temp.CelsiusDeg)) + } + status := alert.Read() + fmt.Printf("Status: %d\n\n", status) + if status == rpio.High { + fmt.Println("Alert temp has been reached.") + return + } + case <-cancel: + return + } + } +} + +func TempCToF(tempC float64) float64 { + return tempC*9/5 + 32 +} + +func TempFToC(tempF float64) float64 { + return (tempF - 32) * 5 / 9 +} diff --git a/sensor/mcp9808/mcp9808.go b/sensor/mcp9808/mcp9808.go new file mode 100644 index 0000000..96f7de6 --- /dev/null +++ b/sensor/mcp9808/mcp9808.go @@ -0,0 +1,484 @@ +// 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 ( + "log" + "sync" + "time" + + "github.com/kidoman/embd" +) + +const ( + // default I2C address for device + address = 0x18 + + // Register addresses. + regConfig = 0x01 + regUpperTemp = 0x02 + regLowerTemp = 0x03 + regCriticalTemp = 0x04 + regAmbientTemp = 0x05 + regManufID = 0x06 + regDeviceID = 0x07 + regResolution = 0x08 +) + +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 + Poll time.Duration + + cmu sync.RWMutex + + config uint16 + synced bool + + temps chan uint16 +} + +// New returns a handle to a MCP9808 sensor. +func New(bus embd.I2CBus) *MCP9808 { + return &MCP9808{Bus: bus, Poll: time.Second, synced: false} +} + +// ManufacturerID reads the device manufacturer ID +func (d *MCP9808) ManufacturerID() (uint16, error) { + d.cmu.Lock() + defer d.cmu.Unlock() + + return d.Bus.ReadWordFromReg(address, regManufID) +} + +// DeviceID reads the device ID and revision +func (d *MCP9808) DeviceID() (uint8, uint8, error) { + d.cmu.Lock() + defer d.cmu.Unlock() + + devIDRev, err := d.Bus.ReadWordFromReg(address, regDeviceID) + if err != nil { + return 0, 0, err + } + + return uint8(devIDRev >> 8), uint8(devIDRev & 0xFF), nil +} + +// Config returns the current word value of the sensor config struct and whether +// that value reflects what is set on the sensor +func (d *MCP9808) Config() (uint16, bool) { + return d.config, d.synced +} + +// ReadConfig reads the config word from the device and writes it to the config attribute +// this overwrites any changes that may have been made to the config attribute +func (d *MCP9808) ReadConfig() (uint16, error) { + d.cmu.RLock() + defer d.cmu.RUnlock() + config, err := d.Bus.ReadWordFromReg(address, regConfig) + if err != nil { + return 0, err + } + d.config = config + d.synced = true + return d.config, nil +} + +// WriteConfig writes the sensor's config word to the device and returns the resulting config +func (d *MCP9808) WriteConfig() (uint16, error) { + d.cmu.Lock() + if err := d.Bus.WriteWordToReg(address, regConfig, d.config); err != nil { + return 0, err + } + d.cmu.Unlock() + + // read the config after write in case some changes were invalid + return d.ReadConfig() +} + +// 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) { + if set { + d.config |= val + } else { + d.config &= ^val + } + d.synced = false +} + +func (d *MCP9808) readConfigValue(val uint16) bool { + return !(d.config&(1<> 9) +} + +// 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) { + d.config = d.config - d.config&^(d.config>>9) + uint16(val)<<9 + d.synced = false +} + +// 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 { + 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) { + 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 { + 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) { + 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 { + 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) { + 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 { + 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) { + 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 { + 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) { + d.flipConfigBit(configAlertStatus, set) +} + +// AlertControl - Alert Output Control bit +// 0 (false) = Disabled (power-up default) +// 1 (true) = Enabled +func (d *MCP9808) AlertControl() bool { + 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) { + 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 { + 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) { + 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 { + 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) { + 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 { + 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) { + d.flipConfigBit(configAlertMode, set) +} + +// Temperature contains the ambient temperature along with alert values. +type Temperature struct { + CelsiusDeg float64 + AboveCritical, AboveUpper, BelowLower bool +} + +// 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 + } + + tempC := temp &^ 0xF000 + + wholeNum := float64(tempC&^0xF) / 16.0 + fraction := tempC &^ 0xFF0 + + tempResult := &Temperature{ + AboveCritical: !(temp&(1<<15) == 0), + AboveUpper: !(temp&(1<<14) == 0), + BelowLower: !(temp&(1<<13) == 0)} + tempResult.CelsiusDeg = wholeNum + (float64(fraction) / 10000.0) + if !(temp&(1<<12) == 0) { // read sign bit + tempResult.CelsiusDeg *= -1 + } + return tempResult, nil +} + +// readTempC reads from the ambient 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 + } + + log.Printf("\nTA vs TCrit: %v\nTA vs TUpper: %v\nTA vs TLower: %v\n", !(temp&(1<<15) == 0), !(temp&(1<<14) == 0), !(temp&(1<<13) == 0)) + + wholeTempC := temp &^ 0xF000 + + wholeNum := float64(wholeTempC&^0xF) / 16.0 + fraction := wholeTempC &^ 0xFF0 + + tempRead := wholeNum + (float64(fraction) / 10000.0) + if !(temp&(1<<12) == 0) { // read sign bit + tempRead *= -1 + } + return tempRead, nil +} + +func (d *MCP9808) setTemp(reg byte, newTemp float64) error { + var signBit uint16 + if newTemp < 0 { + newTemp *= -1 + signBit = 0x1000 + } + newTempWord := uint16(newTemp)*15 + uint16(newTemp*16) + signBit + + if err := d.Bus.WriteWordToReg(address, reg, newTempWord); err != nil { + return err + } + return nil +} + +// CriticalTempUpper reads the current temperature set in the critical temperature register. +func (d *MCP9808) CriticalTempUpper() (float64, error) { + return d.readTempC(regCriticalTemp) +} + +// SetCriticalTemp when the temperature goes above the set value the alert will be +// triggered if enabled. +func (d *MCP9808) SetCriticalTemp(newTemp float64) error { + d.cmu.Lock() + defer d.cmu.Unlock() + + d.SetCriticalTempLock(false) + if _, err := d.WriteConfig(); err != nil { + return err + } + + if err := d.setTemp(regCriticalTemp, newTemp); err != nil { + return err + } + + d.SetCriticalTempLock(true) + _, err := d.WriteConfig() + return err +} + +// 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. +func (d *MCP9808) SetWindowTempUpper(newTemp float64) error { + d.cmu.Lock() + defer d.cmu.Unlock() + + d.SetWindowTempLock(false) + if _, err := d.WriteConfig(); err != nil { + return err + } + + if err := d.setTemp(regUpperTemp, newTemp); err != nil { + return err + } + + d.SetWindowTempLock(true) + _, err := d.WriteConfig() + return err +} + +// 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. +func (d *MCP9808) SetWindowTempLower(newTemp float64) error { + d.cmu.Lock() + defer d.cmu.Unlock() + + d.SetWindowTempLock(false) + if _, err := d.WriteConfig(); err != nil { + return err + } + + if err := d.setTemp(regLowerTemp, newTemp); err != nil { + return err + } + + d.SetWindowTempLock(true) + _, err := d.WriteConfig() + return err +} + +// 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 + Eighth + Sixteenth +) + +// TempResolution reads the temperature resolution from the sensor. +func (d *MCP9808) TempResolution() (TempResolution, error) { + d.cmu.Lock() + defer d.cmu.Unlock() + + 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 { + d.cmu.Lock() + defer d.cmu.Unlock() + + return d.Bus.WriteByteToReg(address, regResolution, byte(res)) +} From d938e007ff2da79e2af0f69e9153625f5982d167 Mon Sep 17 00:00:00 2001 From: Adam Bright Date: Wed, 21 Sep 2016 17:49:09 +0000 Subject: [PATCH 02/10] code review changes and bug fixes --- samples/mcp9808.go | 75 ++++++------ sensor/mcp9808/mcp9808.go | 236 ++++++++++++++++++-------------------- 2 files changed, 143 insertions(+), 168 deletions(-) diff --git a/samples/mcp9808.go b/samples/mcp9808.go index 17fad1c..1db1189 100644 --- a/samples/mcp9808.go +++ b/samples/mcp9808.go @@ -10,18 +10,15 @@ import ( "github.com/kidoman/embd" _ "github.com/kidoman/embd/host/rpi" "github.com/kidoman/embd/sensor/mcp9808" - "github.com/stianeikeland/go-rpio" ) func main() { - if err := embd.InitI2C(); err != nil { - panic(err) - } - defer embd.CloseI2C() - 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) @@ -32,51 +29,36 @@ func main() { } therm.SetShutdownMode(false) + therm.SetAlertMode(true) + therm.SetInterruptClear(true) + therm.SetAlertStatus(true) + therm.SetAlertControl(true) + therm.SetAlertSelect(false) + therm.SetAlertPolarity(false) - //therm.SetWindowTempLock(false) - // therm.SetCriticalTempLock(false) - - config, err := therm.WriteConfig() - if err != nil { - panic(err) - } + config, _ := therm.Config() fmt.Printf("New Config: %b\n", config) - therm.SetAlertControl(true) - //therm.SetInterruptClear(true) - //therm.SetAlertStatus(true) - //therm.SetAlertSelect(false) - //therm.SetAlertPolarity(true) - //therm.SetAlertMode(true) - config, err = therm.WriteConfig() - if err != nil { + + if err := therm.SetCriticalTemp(TempFToC(90)); err != nil { panic(err) } - fmt.Printf("New Config: %b\n", config) if err := therm.SetWindowTempUpper(TempFToC(80)); err != nil { panic(err) } + fmt.Printf("Set upper temp to %fC\n", TempFToC(80)) - temp, err := therm.AmbientTemp() - if err != nil { - panic(err) - } - fmt.Printf("Temp is %f\n", TempCToF(temp.CelsiusDeg)) + upperTemp, _ := therm.WindowTempUpper() + fmt.Printf("Upper Temp Limit set to: %fC\n", upperTemp) - if err := embd.InitGPIO(); err != nil { + alert, err := embd.NewDigitalPin(23) + if err != nil { panic(err) } - if err := rpio.Open(); err != nil { - log.Fatalf("Error: %v\n", err) - } - defer rpio.Close() defer embd.CloseGPIO() - alert := rpio.Pin(4) - alert.Input() - alert.PullDown() - - timer := time.Tick(time.Duration(5) * time.Second) + alert.SetDirection(embd.In) + alert.PullUp() cancel := make(chan bool) go func() { @@ -84,17 +66,26 @@ func main() { reader.ReadString('\n') cancel <- true }() + + timer := time.Tick(time.Duration(5) * time.Second) for { select { case <-timer: temp, err := therm.AmbientTemp() - if err == nil { - fmt.Printf("Ambient temp is: %f\n", TempCToF(temp.CelsiusDeg)) + 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) + } + status, err := alert.Read() + if err != nil { + log.Printf("Error reading pin: %s\n", err.Error()) + continue } - status := alert.Read() fmt.Printf("Status: %d\n\n", status) - if status == rpio.High { - fmt.Println("Alert temp has been reached.") + if status == embd.High { + fmt.Println("Alert temp has been reached!") return } case <-cancel: diff --git a/sensor/mcp9808/mcp9808.go b/sensor/mcp9808/mcp9808.go index 96f7de6..b456235 100644 --- a/sensor/mcp9808/mcp9808.go +++ b/sensor/mcp9808/mcp9808.go @@ -4,9 +4,8 @@ package mcp9808 import ( - "log" + "math" "sync" - "time" "github.com/kidoman/embd" ) @@ -16,14 +15,14 @@ const ( address = 0x18 // Register addresses. - regConfig = 0x01 - regUpperTemp = 0x02 - regLowerTemp = 0x03 - regCriticalTemp = 0x04 - regAmbientTemp = 0x05 - regManufID = 0x06 - regDeviceID = 0x07 - regResolution = 0x08 + regConfig = iota // starts at 1, this is what we want + regUpperTemp + regLowerTemp + regCriticalTemp + regAmbientTemp + regManufID + regDeviceID + regResolution ) const ( @@ -40,20 +39,14 @@ const ( // MCP9808 represents a MCP9808 temperature sensor. type MCP9808 struct { - Bus embd.I2CBus - Poll time.Duration - - cmu sync.RWMutex - + Bus embd.I2CBus + cmu sync.Mutex config uint16 - synced bool - - temps chan uint16 } // New returns a handle to a MCP9808 sensor. func New(bus embd.I2CBus) *MCP9808 { - return &MCP9808{Bus: bus, Poll: time.Second, synced: false} + return &MCP9808{Bus: bus} } // ManufacturerID reads the device manufacturer ID @@ -79,48 +72,41 @@ func (d *MCP9808) DeviceID() (uint8, uint8, error) { // Config returns the current word value of the sensor config struct and whether // that value reflects what is set on the sensor -func (d *MCP9808) Config() (uint16, bool) { - return d.config, d.synced +func (d *MCP9808) Config() (uint16, error) { + return d.readConfig() } // ReadConfig reads the config word from the device and writes it to the config attribute // this overwrites any changes that may have been made to the config attribute -func (d *MCP9808) ReadConfig() (uint16, error) { - d.cmu.RLock() - defer d.cmu.RUnlock() +func (d *MCP9808) readConfig() (uint16, error) { config, err := d.Bus.ReadWordFromReg(address, regConfig) if err != nil { return 0, err } d.config = config - d.synced = true return d.config, nil } // WriteConfig writes the sensor's config word to the device and returns the resulting config -func (d *MCP9808) WriteConfig() (uint16, error) { - d.cmu.Lock() - if err := d.Bus.WriteWordToReg(address, regConfig, d.config); err != nil { - return 0, err - } - d.cmu.Unlock() - - // read the config after write in case some changes were invalid - return d.ReadConfig() +func (d *MCP9808) writeConfig() 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) { +func (d *MCP9808) flipConfigBit(val uint16, set bool) error { + d.cmu.Lock() + defer d.cmu.Unlock() if set { d.config |= val } else { d.config &= ^val } - d.synced = false + return d.writeConfig() } -func (d *MCP9808) readConfigValue(val uint16) bool { - return !(d.config&(1<> 9) +func (d *MCP9808) TempHysteresis() (Hysteresis, error) { + _, err := d.readConfig() + return Hysteresis(d.config >> 9), err } // SetTempHysteresis - TUPPER and TLOWER Limit Hysteresis bits @@ -158,16 +145,19 @@ func (d *MCP9808) TempHysteresis() Hysteresis { // 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) { +func (d *MCP9808) SetTempHysteresis(val Hysteresis) error { + d.cmu.Lock() + defer d.cmu.Unlock() + d.config = d.config - d.config&^(d.config>>9) + uint16(val)<<9 - d.synced = false + return d.writeConfig() } // 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 { +func (d *MCP9808) ShutdownMode() (bool, error) { return d.readConfigValue(configShutDown) } @@ -177,15 +167,15 @@ func (d *MCP9808) ShutdownMode() bool { // 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) { - d.flipConfigBit(configShutDown, set) +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 { +func (d *MCP9808) CriticalTempLock() (bool, error) { return d.readConfigValue(configCriticalTempLock) } @@ -194,31 +184,31 @@ func (d *MCP9808) CriticalTempLock() bool { // 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) { - d.flipConfigBit(configCriticalTempLock, locked) +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 { +func (d *MCP9808) WindowTempLock() (bool, error) { return d.readConfigValue(configWindowTempLock) } -// SetWindowTempLock - TUPPER and TLOWER Window Lock bit +// 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) { - d.flipConfigBit(configWindowTempLock, locked) +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 { +func (d *MCP9808) InterruptClear() (bool, error) { return d.readConfigValue(configInterruptClear) } @@ -227,14 +217,14 @@ func (d *MCP9808) InterruptClear() bool { // 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) { - d.flipConfigBit(configInterruptClear, set) +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 { +func (d *MCP9808) AlertStatus() (bool, error) { return d.readConfigValue(configAlertStatus) } @@ -244,14 +234,14 @@ func (d *MCP9808) AlertStatus() bool { // 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) { - d.flipConfigBit(configAlertStatus, set) +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 { +func (d *MCP9808) AlertControl() (bool, error) { return d.readConfigValue(configAlertControl) } @@ -260,14 +250,14 @@ func (d *MCP9808) AlertControl() bool { // 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) { - d.flipConfigBit(configAlertControl, set) +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 { +func (d *MCP9808) AlertSelect() (bool, error) { return d.readConfigValue(configAlertSelect) } @@ -276,14 +266,14 @@ func (d *MCP9808) AlertSelect() bool { // 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) { - d.flipConfigBit(configAlertSelect, set) +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 { +func (d *MCP9808) AlertPolarity() (bool, error) { return d.readConfigValue(configAlertPolarity) } @@ -292,14 +282,14 @@ func (d *MCP9808) AlertPolarity() bool { // 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) { - d.flipConfigBit(configAlertPolarity, set) +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 { +func (d *MCP9808) AlertMode() (bool, error) { return d.readConfigValue(configAlertMode) } @@ -308,8 +298,8 @@ func (d *MCP9808) AlertMode() bool { // 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) { - d.flipConfigBit(configAlertMode, set) +func (d *MCP9808) SetAlertMode(set bool) error { + return d.flipConfigBit(configAlertMode, set) } // Temperature contains the ambient temperature along with alert values. @@ -318,58 +308,53 @@ type Temperature struct { AboveCritical, AboveUpper, BelowLower bool } -// 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 - } - - tempC := temp &^ 0xF000 - - wholeNum := float64(tempC&^0xF) / 16.0 - fraction := tempC &^ 0xFF0 - - tempResult := &Temperature{ - AboveCritical: !(temp&(1<<15) == 0), - AboveUpper: !(temp&(1<<14) == 0), - BelowLower: !(temp&(1<<13) == 0)} - tempResult.CelsiusDeg = wholeNum + (float64(fraction) / 10000.0) - if !(temp&(1<<12) == 0) { // read sign bit - tempResult.CelsiusDeg *= -1 - } - return tempResult, nil -} - -// readTempC reads from the ambient temperature register and returns the current temperature value in celsius +// 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 } - log.Printf("\nTA vs TCrit: %v\nTA vs TUpper: %v\nTA vs TLower: %v\n", !(temp&(1<<15) == 0), !(temp&(1<<14) == 0), !(temp&(1<<13) == 0)) + return convertWordToTempC(temp), nil +} - wholeTempC := temp &^ 0xF000 +func convertWordToTempC(temp uint16) float64 { + wholeNum := float64(temp&0xFF0) / 16.0 + fraction := float64(temp&0xF) * .0625 - wholeNum := float64(wholeTempC&^0xF) / 16.0 - fraction := wholeTempC &^ 0xFF0 + tempRead := wholeNum + fraction - tempRead := wholeNum + (float64(fraction) / 10000.0) if !(temp&(1<<12) == 0) { // read sign bit tempRead *= -1 } - return tempRead, nil + return tempRead } func (d *MCP9808) setTemp(reg byte, newTemp float64) error { + d.cmu.Lock() + defer d.cmu.Unlock() + var signBit uint16 if newTemp < 0 { newTemp *= -1 signBit = 0x1000 } - newTempWord := uint16(newTemp)*15 + uint16(newTemp*16) + signBit + + wholeNum, fraction := math.Modf(newTemp) + var roundedFrac uint16 + switch { + case fraction < .125: + roundedFrac = 0 + case fraction < .375: + roundedFrac = 1 + case fraction < .625: + roundedFrac = 2 + case fraction < .875: + roundedFrac = 3 + default: + roundedFrac = 4 + } + newTempWord := signBit + uint16(wholeNum)*16 + roundedFrac*4 if err := d.Bus.WriteWordToReg(address, reg, newTempWord); err != nil { return err @@ -377,6 +362,23 @@ func (d *MCP9808) setTemp(reg byte, newTemp float64) error { return nil } +// 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&(1<<15) == 0), + AboveUpper: !(temp&(1<<14) == 0), + BelowLower: !(temp&(1<<13) == 0)} + tempResult.CelsiusDeg = convertWordToTempC(temp) + + return tempResult, nil +} + // CriticalTempUpper reads the current temperature set in the critical temperature register. func (d *MCP9808) CriticalTempUpper() (float64, error) { return d.readTempC(regCriticalTemp) @@ -385,11 +387,7 @@ func (d *MCP9808) CriticalTempUpper() (float64, error) { // SetCriticalTemp when the temperature goes above the set value the alert will be // triggered if enabled. func (d *MCP9808) SetCriticalTemp(newTemp float64) error { - d.cmu.Lock() - defer d.cmu.Unlock() - - d.SetCriticalTempLock(false) - if _, err := d.WriteConfig(); err != nil { + if err := d.setCriticalTempLock(false); err != nil { return err } @@ -397,9 +395,7 @@ func (d *MCP9808) SetCriticalTemp(newTemp float64) error { return err } - d.SetCriticalTempLock(true) - _, err := d.WriteConfig() - return err + return d.setCriticalTempLock(true) } // WindowTempUpper reads the current temperature set in the upper window temperature register. @@ -410,11 +406,7 @@ func (d *MCP9808) WindowTempUpper() (float64, error) { // SetWindowTempUpper when the temperature goes above the set value the alert will be // triggered if enabled. func (d *MCP9808) SetWindowTempUpper(newTemp float64) error { - d.cmu.Lock() - defer d.cmu.Unlock() - - d.SetWindowTempLock(false) - if _, err := d.WriteConfig(); err != nil { + if err := d.setWindowTempLock(false); err != nil { return err } @@ -422,9 +414,7 @@ func (d *MCP9808) SetWindowTempUpper(newTemp float64) error { return err } - d.SetWindowTempLock(true) - _, err := d.WriteConfig() - return err + return d.setWindowTempLock(true) } // WindowTempLower reads the current temperature set in the lower window temperature register. @@ -435,11 +425,7 @@ func (d *MCP9808) WindowTempLower() (float64, error) { // SetWindowTempLower when the temperature goes below the set value the alert will be // triggered if enabled. func (d *MCP9808) SetWindowTempLower(newTemp float64) error { - d.cmu.Lock() - defer d.cmu.Unlock() - - d.SetWindowTempLock(false) - if _, err := d.WriteConfig(); err != nil { + if err := d.setWindowTempLock(false); err != nil { return err } @@ -447,9 +433,7 @@ func (d *MCP9808) SetWindowTempLower(newTemp float64) error { return err } - d.SetWindowTempLock(true) - _, err := d.WriteConfig() - return err + return d.setWindowTempLock(true) } // TempResolution reads the current temperature accuracy from the sensor (affects temperature read speed) From afc8c08cfffea0d6f893bd80705c53b97bcb6211 Mon Sep 17 00:00:00 2001 From: Adam Bright Date: Mon, 26 Sep 2016 02:29:10 +0000 Subject: [PATCH 03/10] simplify sign bit extraction and remove extraneous error check' ' --- samples/mcp9808.go | 37 +++++++++++++++++++++---------------- sensor/mcp9808/mcp9808.go | 9 +++------ 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/samples/mcp9808.go b/samples/mcp9808.go index 1db1189..5553344 100644 --- a/samples/mcp9808.go +++ b/samples/mcp9808.go @@ -3,7 +3,6 @@ package main import ( "bufio" "fmt" - "log" "os" "time" @@ -34,7 +33,7 @@ func main() { therm.SetAlertStatus(true) therm.SetAlertControl(true) therm.SetAlertSelect(false) - therm.SetAlertPolarity(false) + therm.SetAlertPolarity(true) config, _ := therm.Config() fmt.Printf("New Config: %b\n", config) @@ -43,11 +42,18 @@ func main() { panic(err) } - if err := therm.SetWindowTempUpper(TempFToC(80)); err != nil { + if err := therm.SetWindowTempLower(TempFToC(-40)); err != nil { panic(err) } - fmt.Printf("Set upper temp to %fC\n", TempFToC(80)) + 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) @@ -55,10 +61,19 @@ func main() { if err != nil { panic(err) } - defer embd.CloseGPIO() + defer alert.Close() alert.SetDirection(embd.In) - alert.PullUp() + 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() { @@ -78,16 +93,6 @@ func main() { 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) } - status, err := alert.Read() - if err != nil { - log.Printf("Error reading pin: %s\n", err.Error()) - continue - } - fmt.Printf("Status: %d\n\n", status) - if status == embd.High { - fmt.Println("Alert temp has been reached!") - return - } case <-cancel: return } diff --git a/sensor/mcp9808/mcp9808.go b/sensor/mcp9808/mcp9808.go index b456235..9bb5725 100644 --- a/sensor/mcp9808/mcp9808.go +++ b/sensor/mcp9808/mcp9808.go @@ -324,7 +324,8 @@ func convertWordToTempC(temp uint16) float64 { tempRead := wholeNum + fraction - if !(temp&(1<<12) == 0) { // read sign bit + // read sign bit + if temp>>12 == 1 { tempRead *= -1 } return tempRead @@ -355,11 +356,7 @@ func (d *MCP9808) setTemp(reg byte, newTemp float64) error { roundedFrac = 4 } newTempWord := signBit + uint16(wholeNum)*16 + roundedFrac*4 - - if err := d.Bus.WriteWordToReg(address, reg, newTempWord); err != nil { - return err - } - return nil + return d.Bus.WriteWordToReg(address, reg, newTempWord) } // AmbientTemp reads the current sensor value along with the flags denoting what boundaries the From 606555ca7fde729cd24d20a80aa2a7477211069b Mon Sep 17 00:00:00 2001 From: Adam Bright Date: Mon, 26 Sep 2016 04:01:22 +0000 Subject: [PATCH 04/10] simplify temp set and read functions --- sensor/mcp9808/mcp9808.go | 48 ++++++--------------------------------- 1 file changed, 7 insertions(+), 41 deletions(-) diff --git a/sensor/mcp9808/mcp9808.go b/sensor/mcp9808/mcp9808.go index 9bb5725..fc0b2f0 100644 --- a/sensor/mcp9808/mcp9808.go +++ b/sensor/mcp9808/mcp9808.go @@ -4,7 +4,6 @@ package mcp9808 import ( - "math" "sync" "github.com/kidoman/embd" @@ -319,44 +318,11 @@ func (d *MCP9808) readTempC(reg byte) (float64, error) { } func convertWordToTempC(temp uint16) float64 { - wholeNum := float64(temp&0xFF0) / 16.0 - fraction := float64(temp&0xF) * .0625 - - tempRead := wholeNum + fraction - - // read sign bit - if temp>>12 == 1 { - tempRead *= -1 - } - return tempRead + return float64(int16(temp<<3)>>3) / 16 } func (d *MCP9808) setTemp(reg byte, newTemp float64) error { - d.cmu.Lock() - defer d.cmu.Unlock() - - var signBit uint16 - if newTemp < 0 { - newTemp *= -1 - signBit = 0x1000 - } - - wholeNum, fraction := math.Modf(newTemp) - var roundedFrac uint16 - switch { - case fraction < .125: - roundedFrac = 0 - case fraction < .375: - roundedFrac = 1 - case fraction < .625: - roundedFrac = 2 - case fraction < .875: - roundedFrac = 3 - default: - roundedFrac = 4 - } - newTempWord := signBit + uint16(wholeNum)*16 + roundedFrac*4 - return d.Bus.WriteWordToReg(address, reg, newTempWord) + return d.Bus.WriteWordToReg(address, reg, uint16((newTemp)*16)&0x1fff) } // AmbientTemp reads the current sensor value along with the flags denoting what boundaries the @@ -368,16 +334,16 @@ func (d *MCP9808) AmbientTemp() (*Temperature, error) { } tempResult := &Temperature{ - AboveCritical: !(temp&(1<<15) == 0), - AboveUpper: !(temp&(1<<14) == 0), - BelowLower: !(temp&(1<<13) == 0)} + AboveCritical: temp&0x8000 == 0x8000, + AboveUpper: temp&0x4000 == 0x4000, + BelowLower: temp&0x2000 == 0x2000} tempResult.CelsiusDeg = convertWordToTempC(temp) return tempResult, nil } -// CriticalTempUpper reads the current temperature set in the critical temperature register. -func (d *MCP9808) CriticalTempUpper() (float64, error) { +// CriticalTemp reads the current temperature set in the critical temperature register. +func (d *MCP9808) CriticalTemp() (float64, error) { return d.readTempC(regCriticalTemp) } From bb7032efe80daae72ad275285a3a1e87dcc2df0b Mon Sep 17 00:00:00 2001 From: Adam Bright Date: Mon, 26 Sep 2016 04:06:41 +0000 Subject: [PATCH 05/10] remove unnecessary parens --- sensor/mcp9808/mcp9808.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sensor/mcp9808/mcp9808.go b/sensor/mcp9808/mcp9808.go index fc0b2f0..66d9291 100644 --- a/sensor/mcp9808/mcp9808.go +++ b/sensor/mcp9808/mcp9808.go @@ -322,7 +322,7 @@ func convertWordToTempC(temp uint16) float64 { } func (d *MCP9808) setTemp(reg byte, newTemp float64) error { - return d.Bus.WriteWordToReg(address, reg, uint16((newTemp)*16)&0x1fff) + return d.Bus.WriteWordToReg(address, reg, uint16(newTemp*16)&0x1fff) } // AmbientTemp reads the current sensor value along with the flags denoting what boundaries the From c68eca29cc3b739000bdc2d0bdca5c6dd8726f15 Mon Sep 17 00:00:00 2001 From: Adam Bright Date: Mon, 26 Sep 2016 04:59:42 +0000 Subject: [PATCH 06/10] fix rounding and precision when setting temperature --- sensor/mcp9808/mcp9808.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sensor/mcp9808/mcp9808.go b/sensor/mcp9808/mcp9808.go index 66d9291..53b68a2 100644 --- a/sensor/mcp9808/mcp9808.go +++ b/sensor/mcp9808/mcp9808.go @@ -322,7 +322,11 @@ func convertWordToTempC(temp uint16) float64 { } func (d *MCP9808) setTemp(reg byte, newTemp float64) error { - return d.Bus.WriteWordToReg(address, reg, uint16(newTemp*16)&0x1fff) + rounder := 2.0 + if newTemp < 0 { + rounder = 0.0 + } + return d.Bus.WriteWordToReg(address, reg, uint16(newTemp*16+rounder)&0x1ffc) } // AmbientTemp reads the current sensor value along with the flags denoting what boundaries the From 0c25e20721f123f1a7196e7d658b81359962cafd Mon Sep 17 00:00:00 2001 From: Adam Bright Date: Mon, 26 Sep 2016 05:31:51 +0000 Subject: [PATCH 07/10] better negative rounding --- sensor/mcp9808/mcp9808.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sensor/mcp9808/mcp9808.go b/sensor/mcp9808/mcp9808.go index 53b68a2..35dd149 100644 --- a/sensor/mcp9808/mcp9808.go +++ b/sensor/mcp9808/mcp9808.go @@ -322,11 +322,7 @@ func convertWordToTempC(temp uint16) float64 { } func (d *MCP9808) setTemp(reg byte, newTemp float64) error { - rounder := 2.0 - if newTemp < 0 { - rounder = 0.0 - } - return d.Bus.WriteWordToReg(address, reg, uint16(newTemp*16+rounder)&0x1ffc) + 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 From 95e4b68b7478f3efbc478a8fc75179da55cc02f5 Mon Sep 17 00:00:00 2001 From: Adam Bright Date: Wed, 28 Sep 2016 16:33:45 +0000 Subject: [PATCH 08/10] make names more idiomatic, remove redundant mutex lock --- samples/mcp9808.go | 4 +-- sensor/mcp9808/mcp9808.go | 51 +++++++++------------------------------ 2 files changed, 13 insertions(+), 42 deletions(-) diff --git a/samples/mcp9808.go b/samples/mcp9808.go index 5553344..aa64526 100644 --- a/samples/mcp9808.go +++ b/samples/mcp9808.go @@ -42,7 +42,7 @@ func main() { panic(err) } - if err := therm.SetWindowTempLower(TempFToC(-40)); err != nil { + if err := therm.SetWindowTempLower(TempFToC(32)); err != nil { panic(err) } lowerTemp, err := therm.WindowTempLower() @@ -51,7 +51,7 @@ func main() { } fmt.Printf("Lower Temp Limit set to: %fC\n", lowerTemp) - if err := therm.SetWindowTempUpper(TempFToC(80)); err != nil { + if err := therm.SetWindowTempUpper(TempFToC(75)); err != nil { panic(err) } upperTemp, _ := therm.WindowTempUpper() diff --git a/sensor/mcp9808/mcp9808.go b/sensor/mcp9808/mcp9808.go index 35dd149..a5882dd 100644 --- a/sensor/mcp9808/mcp9808.go +++ b/sensor/mcp9808/mcp9808.go @@ -3,11 +3,7 @@ // http://ww1.microchip.com/downloads/en/DeviceDoc/25095A.pdf package mcp9808 -import ( - "sync" - - "github.com/kidoman/embd" -) +import "github.com/kidoman/embd" const ( // default I2C address for device @@ -39,7 +35,6 @@ const ( // MCP9808 represents a MCP9808 temperature sensor. type MCP9808 struct { Bus embd.I2CBus - cmu sync.Mutex config uint16 } @@ -50,17 +45,11 @@ func New(bus embd.I2CBus) *MCP9808 { // ManufacturerID reads the device manufacturer ID func (d *MCP9808) ManufacturerID() (uint16, error) { - d.cmu.Lock() - defer d.cmu.Unlock() - return d.Bus.ReadWordFromReg(address, regManufID) } // DeviceID reads the device ID and revision func (d *MCP9808) DeviceID() (uint8, uint8, error) { - d.cmu.Lock() - defer d.cmu.Unlock() - devIDRev, err := d.Bus.ReadWordFromReg(address, regDeviceID) if err != nil { return 0, 0, err @@ -69,15 +58,8 @@ func (d *MCP9808) DeviceID() (uint8, uint8, error) { return uint8(devIDRev >> 8), uint8(devIDRev & 0xFF), nil } -// Config returns the current word value of the sensor config struct and whether -// that value reflects what is set on the sensor +// Config gets the config word from the device. func (d *MCP9808) Config() (uint16, error) { - return d.readConfig() -} - -// ReadConfig reads the config word from the device and writes it to the config attribute -// this overwrites any changes that may have been made to the config attribute -func (d *MCP9808) readConfig() (uint16, error) { config, err := d.Bus.ReadWordFromReg(address, regConfig) if err != nil { return 0, err @@ -86,26 +68,24 @@ func (d *MCP9808) readConfig() (uint16, error) { return d.config, nil } -// WriteConfig writes the sensor's config word to the device and returns the resulting config -func (d *MCP9808) writeConfig() error { +// 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 { - d.cmu.Lock() - defer d.cmu.Unlock() if set { d.config |= val } else { d.config &= ^val } - return d.writeConfig() + return d.setConfig() } func (d *MCP9808) readConfigValue(val uint16) (bool, error) { - _, err := d.readConfig() - return !(d.config&(1<> 9), err } @@ -145,11 +125,8 @@ func (d *MCP9808) TempHysteresis() (Hysteresis, error) { // 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.cmu.Lock() - defer d.cmu.Unlock() - d.config = d.config - d.config&^(d.config>>9) + uint16(val)<<9 - return d.writeConfig() + return d.setConfig() } // ShutdownMode bit @@ -409,23 +386,17 @@ type TempResolution uint8 const ( HalfC TempResolution = iota QuarterC - Eighth - Sixteenth + EighthC + SixteenthC ) // TempResolution reads the temperature resolution from the sensor. func (d *MCP9808) TempResolution() (TempResolution, error) { - d.cmu.Lock() - defer d.cmu.Unlock() - 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 { - d.cmu.Lock() - defer d.cmu.Unlock() - return d.Bus.WriteByteToReg(address, regResolution, byte(res)) } From 24db7580a425fff8343c265b3bba85ee0822dc9a Mon Sep 17 00:00:00 2001 From: Adam Bright Date: Thu, 29 Sep 2016 05:51:00 +0000 Subject: [PATCH 09/10] initialize configuration so we aren't unintentionally overwriting something on the first configuration change --- samples/mcp9808.go | 13 +++++++++---- sensor/mcp9808/mcp9808.go | 8 ++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/samples/mcp9808.go b/samples/mcp9808.go index aa64526..104c52d 100644 --- a/samples/mcp9808.go +++ b/samples/mcp9808.go @@ -15,7 +15,7 @@ func main() { bus := embd.NewI2CBus(1) defer embd.CloseI2C() - therm := mcp9808.New(bus) + therm, _ := mcp9808.New(bus) // set sensor to low power mode when we're done defer therm.SetShutdownMode(true) @@ -38,11 +38,16 @@ func main() { config, _ := therm.Config() fmt.Printf("New Config: %b\n", config) - if err := therm.SetCriticalTemp(TempFToC(90)); err != nil { + 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(32)); err != nil { + if err := therm.SetWindowTempLower(TempFToC(50)); err != nil { panic(err) } lowerTemp, err := therm.WindowTempLower() @@ -51,7 +56,7 @@ func main() { } fmt.Printf("Lower Temp Limit set to: %fC\n", lowerTemp) - if err := therm.SetWindowTempUpper(TempFToC(75)); err != nil { + if err := therm.SetWindowTempUpper(TempFToC(80)); err != nil { panic(err) } upperTemp, _ := therm.WindowTempUpper() diff --git a/sensor/mcp9808/mcp9808.go b/sensor/mcp9808/mcp9808.go index a5882dd..24bc85e 100644 --- a/sensor/mcp9808/mcp9808.go +++ b/sensor/mcp9808/mcp9808.go @@ -39,8 +39,12 @@ type MCP9808 struct { } // New returns a handle to a MCP9808 sensor. -func New(bus embd.I2CBus) *MCP9808 { - return &MCP9808{Bus: bus} +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 From 31f3386652c8cdc9f18c2e539dec6ac4f1ee40ae Mon Sep 17 00:00:00 2001 From: Adam Bright Date: Thu, 29 Sep 2016 06:07:22 +0000 Subject: [PATCH 10/10] make SetCriticalTempLock and SetWindowTempLock exported and don't auto lock when setting temps since the device doesn't seem to respond well to it --- samples/mcp9808.go | 18 +++++++++++------ sensor/mcp9808/mcp9808.go | 42 +++++++++------------------------------ 2 files changed, 21 insertions(+), 39 deletions(-) diff --git a/samples/mcp9808.go b/samples/mcp9808.go index 104c52d..03a7cc9 100644 --- a/samples/mcp9808.go +++ b/samples/mcp9808.go @@ -4,7 +4,6 @@ import ( "bufio" "fmt" "os" - "time" "github.com/kidoman/embd" _ "github.com/kidoman/embd/host/rpi" @@ -28,6 +27,8 @@ func main() { } therm.SetShutdownMode(false) + therm.SetCriticalTempLock(false) + therm.SetWindowTempLock(false) therm.SetAlertMode(true) therm.SetInterruptClear(true) therm.SetAlertStatus(true) @@ -35,6 +36,9 @@ func main() { 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) @@ -47,7 +51,7 @@ func main() { } fmt.Printf("Critical Temp set to: %fC\n", critTemp) - if err := therm.SetWindowTempLower(TempFToC(50)); err != nil { + if err := therm.SetWindowTempLower(TempFToC(60)); err != nil { panic(err) } lowerTemp, err := therm.WindowTempLower() @@ -62,6 +66,9 @@ func main() { 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) @@ -87,10 +94,11 @@ func main() { cancel <- true }() - timer := time.Tick(time.Duration(5) * time.Second) for { select { - case <-timer: + case <-cancel: + return + default: temp, err := therm.AmbientTemp() if err != nil { fmt.Printf("Error reading temp: %s\n", err.Error()) @@ -98,8 +106,6 @@ func main() { 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) } - case <-cancel: - return } } } diff --git a/sensor/mcp9808/mcp9808.go b/sensor/mcp9808/mcp9808.go index 24bc85e..8b406ff 100644 --- a/sensor/mcp9808/mcp9808.go +++ b/sensor/mcp9808/mcp9808.go @@ -164,7 +164,7 @@ func (d *MCP9808) CriticalTempLock() (bool, error) { // 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 { +func (d *MCP9808) SetCriticalTempLock(locked bool) error { return d.flipConfigBit(configCriticalTempLock, locked) } @@ -176,12 +176,12 @@ func (d *MCP9808) WindowTempLock() (bool, error) { return d.readConfigValue(configWindowTempLock) } -// setWindowTempLock - TUPPER and TLOWER Window Lock bit +// 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 { +func (d *MCP9808) SetWindowTempLock(locked bool) error { return d.flipConfigBit(configWindowTempLock, locked) } @@ -329,17 +329,9 @@ func (d *MCP9808) CriticalTemp() (float64, error) { } // SetCriticalTemp when the temperature goes above the set value the alert will be -// triggered if enabled. +// triggered if enabled. This has no effect if CriticalTempLock is set. func (d *MCP9808) SetCriticalTemp(newTemp float64) error { - if err := d.setCriticalTempLock(false); err != nil { - return err - } - - if err := d.setTemp(regCriticalTemp, newTemp); err != nil { - return err - } - - return d.setCriticalTempLock(true) + return d.setTemp(regCriticalTemp, newTemp) } // WindowTempUpper reads the current temperature set in the upper window temperature register. @@ -348,17 +340,9 @@ func (d *MCP9808) WindowTempUpper() (float64, error) { } // SetWindowTempUpper when the temperature goes above the set value the alert will be -// triggered if enabled. +// triggered if enabled. This has no effect if WindowTempLock is set. func (d *MCP9808) SetWindowTempUpper(newTemp float64) error { - if err := d.setWindowTempLock(false); err != nil { - return err - } - - if err := d.setTemp(regUpperTemp, newTemp); err != nil { - return err - } - - return d.setWindowTempLock(true) + return d.setTemp(regUpperTemp, newTemp) } // WindowTempLower reads the current temperature set in the lower window temperature register. @@ -367,17 +351,9 @@ func (d *MCP9808) WindowTempLower() (float64, error) { } // SetWindowTempLower when the temperature goes below the set value the alert will be -// triggered if enabled. +// triggered if enabled. This has no effect if WindowTempLock is set. func (d *MCP9808) SetWindowTempLower(newTemp float64) error { - if err := d.setWindowTempLock(false); err != nil { - return err - } - - if err := d.setTemp(regLowerTemp, newTemp); err != nil { - return err - } - - return d.setWindowTempLock(true) + return d.setTemp(regLowerTemp, newTemp) } // TempResolution reads the current temperature accuracy from the sensor (affects temperature read speed)