Created
January 23, 2020 21:06
-
-
Save smittytone/98091e6a7d63a7bf9f476064dca4d6b2 to your computer and use it in GitHub Desktop.
Squirrel class for MCP9808 temperature sensor (I2C)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MCP9808 'constants' | |
const MCP9808_REG_CONFIG = 0x01; | |
const MCP9808_REG_UPPER_TEMP = 0x02; | |
const MCP9808_REG_LOWER_TEMP = 0x03; | |
const MCP9808_REG_CRIT_TEMP = 0x04; | |
const MCP9808_REG_TEMP = 0x05; | |
const MCP9808_REG_MANUF_ID = 0x06; | |
const MCP9808_REG_DEVICE_ID = 0x07; | |
const MCP9808_REG_RESOLUTION = 0x08; | |
const MCP9808_REG_CONFIG_ALERTMODE = 0x0001; | |
const MCP9808_REG_CONFIG_ALERTPOL = 0x0002; | |
const MCP9808_REG_CONFIG_ALERTSEL = 0x0004; | |
const MCP9808_REG_CONFIG_ALERTCTRL = 0x0008; | |
const MCP9808_REG_CONFIG_ALERTSTAT = 0x0010; | |
const MCP9808_REG_CONFIG_INTCLR = 0x0020; | |
const MCP9808_REG_CONFIG_WINLOCK = 0x0040; | |
const MCP9808_REG_CONFIG_CRITLOCK = 0x0080; | |
const MCP9808_REG_CONFIG_SHUTDOWN = 0x0100; | |
const MCP9808_ALERT_CRITICAL = 0x01; | |
const MCP9808_ALERT_WINDOW = 0x00; | |
const MCP9808_MODE_CONTINUOUS = 0x00; | |
const MCP9808_MODE_SHUTDOWN = 0x01; | |
class MCP9808 { | |
static VERSION = "1.0.0"; | |
// Instance variables | |
_i2cAddr = null; | |
_i2cBus = null; | |
_alertPin = null; | |
_shutdownMode = false; | |
_debug = false; | |
// Constructor | |
constructor (bus = null, address = 0x18, alertPin = null, debug = false) { | |
// Parameters: | |
// 1. An imp I2C bus | |
// 2. The MCP9808's 7-bit I2C address (default: 0x18) | |
// 3. An imp pin object which will sense alerts (default: none) | |
// 4. A Boolean to trigger extra debugging info (default: false) | |
if (address) { | |
_i2cAddr = address << 1; | |
} else { | |
throw "MCP9808 class requires a non-null I2C address"; | |
} | |
if (bus) { | |
_i2cBus = bus; | |
} else { | |
throw "MCP9808 class requires a non-null imp I2C bus"; | |
} | |
if (alertPin) { | |
_alertPin = alertPin; | |
_alertPin.configure(DIGITAL_IN); | |
} | |
_debug = debug; | |
} | |
function readTempCelsius(cb = null) { | |
// Parameters: None | |
// Returns: celsius reading (float), otherwise null to signal an error | |
local rawData = _readTwoBytes(MCP9808_REG_TEMP); | |
if (rawData) { | |
if (cb != null) { | |
cb(_rawToCelsius(rawData)); | |
return; | |
} else { | |
return _rawToCelsius(rawData); | |
} | |
} | |
if (_debug) server.error("MCP9808 I2C bus mis-read"); | |
return null; | |
} | |
function showInfo() { | |
// Simple debug function which displays the MCP9808's manufacturer ID | |
// and its device ID: 0x0054 and 0x0400, respectively | |
// Parameters: None | |
// Returns: Nothing | |
local rawData = _readTwoBytes(MCP9808_REG_MANUF_ID); | |
if (rawData) server.log(format("MCP9808 Manufacturer ID: 0x%04X", rawData)); | |
rawData = _readTwoBytes(MCP9808_REG_DEVICE_ID); | |
if (rawData) server.log(format("MCP9808 Device ID: 0x%04X", rawData)); | |
rawData = _readTwoBytes(MCP9808_REG_CRIT_TEMP); | |
if (rawData) server.log("MCP9808 Critical Alert Temp: " + _rawToCelsius(rawData)); | |
rawData = _readTwoBytes(MCP9808_REG_UPPER_TEMP); | |
if (rawData) server.log("MCP9808 Upper Alert Temp: " + _rawToCelsius(rawData)); | |
rawData = _readTwoBytes(MCP9808_REG_LOWER_TEMP); | |
if (rawData) server.log("MCP9808 Lower Alert Temp: " + _rawToCelsius(rawData)); | |
rawData = _readTwoBytes(MCP9808_REG_CONFIG); | |
if (rawData != null) { | |
local m = "Status: "; | |
m = m + (rawData & MCP9808_REG_CONFIG_SHUTDOWN ? "not " : "in shutdown mode, "); | |
m = m + "alert window " + (rawData & MCP9808_REG_CONFIG_WINLOCK ? "locked, " : "unlocked, "); | |
m = m + "critical alert " + (rawData & MCP9808_REG_CONFIG_CRITLOCK ? "locked, " : "unlocked, "); | |
m = m + "alerts " + (rawData & MCP9808_REG_CONFIG_ALERTCTRL ? "active, " : "inactive, "); | |
m = m + "output active " + (rawData & MCP9808_REG_CONFIG_ALERTPOL ? "high, " : "low, "); | |
m = m + "output on " + (rawData & MCP9808_REG_CONFIG_ALERTSEL ? "critical alert only, " : "all alerts, "); | |
m = m.slice(0, m.len() - 2); | |
server.log(m); | |
} | |
} | |
function setMode(mode = MCP9808_MODE_CONTINUOUS) { | |
// Activates or deactivates the MCP98080's low-power mode. No temperature | |
// readings are taken in Shutdown Mode, so the register contains the last | |
// temperature reading taken | |
// Parameters: | |
// 1. Boolean/integer (1 or 0) indicating whether to enter or leave this mode | |
// Returns: Nothing | |
shutdown(mode); | |
} | |
function shutdown(willBeShutdown) { | |
// Activates or deactivates the MCP98080's low-power mode. No temperature | |
// readings are taken in Shutdown Mode, so the register contains the last | |
// temperature reading taken | |
// Parameters: | |
// 1. Boolean/integer (1 or 0) indicating whether to enter or leave this mode | |
// Returns: Nothing | |
local config = _getConfig(); | |
if (config) { | |
if (willBeShutdown == true || willBeShutdown == 1) { | |
if (_checkLock(config)) { | |
// Either one or both Lock bits are set, so can't go into shutdown mode (see manual p30) | |
server.error("Unable to enter Shutdown Mode: MCP9808 locked"); | |
return; | |
} else { | |
config = config | MCP9808_REG_CONFIG_SHUTDOWN; | |
_shutdownMode = true; | |
if (_debug) server.log("MCP9808 now in Shutdown Mode"); | |
} | |
} | |
if (willBeShutdown == false || willBeShutdown == 0) { | |
config = config & (0xFFFF - MCP9808_REG_CONFIG_SHUTDOWN); | |
_shutdownMode = false; | |
if (_debug) server.log("MCP9808 now in Continuous Conversion Mode"); | |
} | |
_writeTwoBytes(MCP9808_REG_CONFIG, config); | |
} | |
} | |
function setResolution(resolution = 0.5) { | |
// Sets the MCP9808's temperature resolution/sample rate | |
// Parameters: | |
// 1. The desired resolution: 0.5 (default), 0.25, 0.125 or 0.0625 | |
// Check we have a valid resolution value | |
local values = [0.5, 0.25, 0.125, 0.0625]; | |
local match = -1; | |
foreach (index, value in values) { | |
if (resolution == value) match = index; | |
} | |
if (match != -1) { | |
_writeByte(MCP9808_REG_RESOLUTION, match); | |
} else { | |
server.error("MCP98008.setResolution() passed out-of-range value: " + resolution); | |
} | |
} | |
function setHysteresis(setting = 0) { | |
// Sets the MCP9808's hysteresis setting | |
// Parameters: | |
// 1. The desired resolution: 0 (default), 1.5, 3 or 6 | |
// Check we have a valid resolution value | |
local config = _getConfig(); | |
if (_checkLock(config)) { | |
// Lock bits are set, so can't set hysteresis (see manual p30) | |
if (_debug) server.error("Unable to set hysteresis: MCP9808 locked"); | |
} else { | |
// Check we have a valid value | |
local values = [0, 1.5, 3, 6]; | |
local match = -1; | |
foreach (index, value in values) { | |
if (setting == value) match = index; | |
} | |
if (match != -1) { | |
match = match << 9; | |
config = config | match; | |
_writeTwoBytes(MCP9808_REG_CONFIG, config); | |
if (_debug) server.log("MCP9808 hysteresis set: " + setting + "C"); | |
} else { | |
server.error("MCP9808.setHysteresis() passed an out-of-range value: " + setting); | |
} | |
} | |
} | |
function setCriticalAlert(temp, polarity = 0, criticalOnly = false, alertCallback = null) { | |
// Sets the MCP9808's temperature alert | |
// Parameters: | |
// 1. The desired critical temperature | |
// 2. The polarity... | |
// 3. A Boolean to indicate whether the alert is only triggered on passing the critical temp | |
// 4. An optional callback to be triggered when the alert pin fires (if set) (default: none) | |
// | |
// Returns: | |
// Nothing | |
if (_shutdownMode) { | |
// Can't set alert in shutdown mode | |
server.error("Unable to set alert: MCP9808 is in Shutdown Mode"); | |
return; | |
} | |
local config = _getConfig(); | |
if (_checkLock(config)) { | |
// One or both of the lock bits are set, so can't set the alert (see manual p30) | |
server.error("Unable to set alert: MCP9808 is locked"); | |
} else { | |
local critTemp = _celsiusToRaw(temp).tointeger(); | |
_writeTwoBytes(MCP9808_REG_CRIT_TEMP, critTemp); | |
if (_debug) server.log("MCP9808 alert critical temperature set"); | |
// Set the Alert Output Control bit (3) | |
config = config | MCP9808_REG_CONFIG_ALERTCTRL; | |
// Set/unset the Alert Output Mode bit (2) | |
config = criticalOnly ? config | MCP9808_REG_CONFIG_ALERTSEL : config & (0xFFFF - MCP9808_REG_CONFIG_ALERTSEL); | |
// Set/unset the Alert Polarity bit (1) | |
// Active High on ALRT pin else // Active Low on ALRT pin | |
config = polarity == 1 ? config | MCP9808_REG_CONFIG_ALERTPOL : config & (0xFFFF - MCP9808_REG_CONFIG_ALERTPOL); | |
_writeTwoBytes(MCP9808_REG_CONFIG, config); | |
if (_debug) server.log("MCP9808 critical alert enabled"); | |
if (_alertPin && alertCallback) { | |
_alertPin.configure(DIGITAL_IN_PULLUP, alertCallback); | |
} | |
} | |
} | |
function clearInterrupt() { | |
local config = _getConfig(); | |
if (_checkLock(config)) { | |
// One or both of the lock bits are set, so can't set alert (see manual p30) | |
if (_debug) server.log("Unable to set alert: MCP9808 is locked"); | |
} else { | |
// Unset the Interrupt Clear bit (5) | |
config = config & (0xFFFF - MCP9808_REG_CONFIG_INTCLR); | |
_writeTwoBytes(MCP9808_REG_CONFIG, config); | |
} | |
} | |
function setAlertWindow(lower, upper, polarity = 0, alertCallback = null) { | |
// Sets the MCP9808's temperature alert window: high and low values | |
// Parameters: | |
// 1. The desired low temperature | |
// 2. The desired high temperature | |
// 3. The polarity... | |
// 4. An optional callback to be triggered when the alert pin fires (if set) (default: none) | |
// | |
// Returns: | |
// Nothing | |
if (_shutdownMode) { | |
if (_debug) server.error("Unable to set alert: MCP9808 is in Shutdown Mode"); | |
return; | |
} | |
local config = _getConfig(); | |
if (_checkLock(config)) { | |
// One or both of the lock bits are set, so can't set the alert (see manual p30) | |
server.error("Unable to set alert: MCP9808 is locked"); | |
} else { | |
_writeTwoBytes(MCP9808_REG_LOWER_TEMP, _celsiusToRaw(lower).tointeger()); | |
_writeTwoBytes(MCP9808_REG_UPPER_TEMP, _celsiusToRaw(upper).tointeger()); | |
if (_debug) server.log("MCP9808 upper and lower alert temperatures set"); | |
// Set the Alert Output Control bit (3) | |
config = config | MCP9808_REG_CONFIG_ALERTCTRL; | |
// Unset the Alert Output Mode bit (2) | |
config = config & (0xFFFF - MCP9808_REG_CONFIG_ALERTSEL); | |
// Set/unset the Alert Polarity bit (1) | |
if (polarity == 1) { | |
// Active High on ALRT pin | |
config = config | MCP9808_REG_CONFIG_ALERTPOL; | |
} else { | |
// Active Low on ALRT pin | |
config = config & (0xFFFF - MCP9808_REG_CONFIG_ALERTPOL); | |
} | |
_writeTwoBytes(MCP9808_REG_CONFIG, config); | |
if (_debug) server.log("MCP9808 alert window enabled"); | |
if (_alertPin && alertCallback) { | |
_alertPin.configure(DIGITAL_IN, alertCallback); | |
} | |
} | |
} | |
function setCriticalAlertLock() { | |
local config = _getConfig(); | |
if (config) { | |
// Set the critical lock bit (7) - can only be cleared by reset | |
config = config | MCP9808_REG_CONFIG_CRITLOCK; | |
if (_debug) server.log("MCP9808 critical alert locked"); | |
_writeTwoBytes(MCP9808_REG_CONFIG, config); | |
} | |
} | |
function setAlertWindowLock() { | |
local config = _getConfig(); | |
if (config) { | |
// Set the window lock bit (6) - can only be cleared by power-cycle | |
config = config | MCP9808_REG_CONFIG_WINLOCK; | |
if (_debug) server.log("MCP9808 alert window locked"); | |
_writeTwoBytes(MCP9808_REG_CONFIG, config); | |
} | |
} | |
// *********** PRIVATE FUNCTIONS - DO NOT CALL *********** | |
function _writeTwoBytes(register, value) { | |
// Write 16-bit value to bus | |
_i2cBus.write(_i2cAddr, register.tochar() + (value >> 8).tochar() + (value & 0xFF).tochar()); | |
} | |
function _writeByte(register, value) { | |
// Write 16-bit value to bus | |
_i2cBus.write(_i2cAddr, register.tochar() + value.tochar()); | |
} | |
function _readTwoBytes(register) { | |
// Read 16-bit value from bus | |
local value = _i2cBus.read(_i2cAddr, register.tochar(), 2); | |
if (value && value.len() == 2) { | |
return ((value[0] << 8) + value[1]); | |
} else { | |
return null; | |
} | |
} | |
function _getConfig() { | |
// Reads the MCP9808's config register and returns it if it is read without error, | |
// or returns null if there has been an error | |
local config = _readTwoBytes(MCP9808_REG_CONFIG); | |
if (config != null && _debug) { | |
server.log(format("MCP9808 config register: 0x%04X", config)); | |
} | |
return config; | |
} | |
function _checkLock(config, lockType = 0) { | |
// Check lock state from a passed in reading of the MCP9808 config register | |
// Returns true if the lock or locks is or are set, false otherwise | |
if (config != null) { | |
if (lockType == 0) { | |
// Return true if either lock type is set | |
if ((config & MCP9808_REG_CONFIG_WINLOCK) || (config & MCP9808_REG_CONFIG_CRITLOCK)) { | |
return true; | |
} | |
} else { | |
// Return true if the requested lock type is set | |
if (config | lockType) return true; | |
} | |
} | |
return false; | |
} | |
function _celsiusToRaw(celsius) { | |
// Convert passed in celsius temperature to sensor format | |
return celsius < 0 ? (16.0 * celsius + 0x1000) | 0x1000 : 16.0 * celsius; | |
} | |
function _rawToCelsius(raw) { | |
// Convert sensor format value to celsius temperature | |
if (_debug) _printraw(raw) | |
return raw & 0x1000 ? (raw & 0x0FFF) / 16.0 - 256 : (raw & 0x0FFF) / 16.0; | |
} | |
function _printraw(raw) { | |
local bs = "0 0 0 "; | |
for (local i = 12 ; i >= 0 ; i--) { | |
local a = raw >> i; | |
bs = bs + (a & 0x01 ? "1 " : "0 "); | |
} | |
server.log("Raw: " + bs); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment