Skip to content

Instantly share code, notes, and snippets.

@smittytone
Created January 23, 2020 21:06
Show Gist options
  • Save smittytone/98091e6a7d63a7bf9f476064dca4d6b2 to your computer and use it in GitHub Desktop.
Save smittytone/98091e6a7d63a7bf9f476064dca4d6b2 to your computer and use it in GitHub Desktop.
Squirrel class for MCP9808 temperature sensor (I2C)
// 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