Created
January 23, 2020 21:18
-
-
Save smittytone/f32970ab10e9ee0f528ea9c2d1862df7 to your computer and use it in GitHub Desktop.
Squirrel driver for TSL2561 light-level sensor (I2C)
This file contains 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
class TSL2561 { | |
static version = "1.0.0"; | |
// TSL2561 register values | |
static TSL2561_COMMAND_BIT = "\x80"; // Command register. Bit 7 must be 1 | |
static TSL2561_CONTROL_POWERON = "\x03"; // Power on setting | |
static TSL2561_CONTROL_POWEROFF = "\x00"; // Power off setting | |
static TSL2561_REGISTER_TIMING = "\x81"; // Access timing register | |
static TSL2561_REGISTER_ADC0_LSB = "\xAC"; // LSB of sensor's two-byte ADC value | |
static TSL2561_REGISTER_ADC1_LSB = "\xAE"; // LSB of sensor's two-byte ADC value | |
static TSL2561_REGISTER_THRESHLOW_LSB = "\xA2"; // LSB of sensor's interrupt lower threshold | |
static TSL2561_REGISTER_THRESHHI_LSB = "\xA4"; // LSB of sensor's interrupt upper threshold | |
static TSL2561_CONTROL_INTERRUPT = "\x86"; // LSB of sensor's interrupt control | |
// Lux calculation values | |
static LUX_SCALE = 14; // Scale by 2^14 | |
static RATIO_SCALE = 9; // Scale ratio by 2^9 | |
// Class properties | |
_i2c = null; | |
_i2cAddr = null; | |
_interruptPin = null; | |
_interruptFlag = null; | |
_debugFlag = false; | |
_gain = 0; | |
_integ = 0; | |
constructor(i2cBus = null, i2cAddress = 0x39, debug = false) { | |
if (i2cBus == null) { | |
server.error("I2C bus undefined"); | |
return null; | |
} | |
if (i2cAddress < 0x00 || i2cAddress > 0xFF) { | |
server.error("I2C address out of range"); | |
return null; | |
} | |
_i2c = i2cBus; | |
_i2cAddr = i2cAddress << 1; | |
_debugFlag = debug; | |
} | |
function init(gain = 1, integration = 0) { | |
// Set command focus to the control register | |
_i2c.write(_i2cAddr, TSL2561_COMMAND_BIT); | |
// Send power up command | |
_i2c.write(_i2cAddr, TSL2561_CONTROL_POWERON); | |
// Check the power is on: this will return string '3' or '51' if it is | |
if (_debugFlag) { | |
local result = _i2c.read(_i2cAddr, TSL2561_CONTROL_POWERON, 1); | |
server.log("Power check: "+ result); | |
} | |
// Issue command: write (0x80) to the timing register (0x01) | |
// ie. TSL2561_REGISTER_TIMING = 0x81 | |
_i2c.write(_i2cAddr, TSL2561_REGISTER_TIMING); | |
// Set gain to low by writing a byte to the timing register | |
// bit 4 to the gain: 1 or 0 (gain high or low) | |
// bits 1,0 to the integration timing: 0,0 or 1,0 (timing 13.7ms or 101ms) | |
if (gain < 0 || gain > 1) gain = 0; | |
if (integration < 0 || integration > 2) integration = 0; | |
_gain = gain; | |
_integ = integration; | |
// Move gain value to bit 4 and add integration value (bits 0,1) | |
gain = gain << 4; | |
gain = gain + integration; | |
// Write the gain + integration timing data | |
_i2c.write(_i2cAddr, gain.tochar()); | |
if (_debugFlag) server.log("TSL2561 sensor ready"); | |
} | |
function getLux() { | |
// Set command focus to ADC 0 | |
_i2c.write(_i2cAddr, TSL2561_REGISTER_ADC0_LSB); | |
local lumo0 = readSensor0(); | |
if (_debugFlag) server.log("Light level: " + lumo0); | |
_i2c.write(_i2cAddr, TSL2561_REGISTER_ADC1_LSB); | |
local lumo1 = readSensor1(); | |
if (_debugFlag) server.log("IR level: " + lumo1); | |
local lux = _calculateLux(lumo0, lumo1); | |
if (_debugFlag) server.log("Lux: " + lux); | |
return lux; | |
} | |
function getVisible() { | |
// Convenience function for readSensor0() | |
return readSensor0(); | |
} | |
function getInfrared () { | |
// Convenience function for readSensor1() | |
return readSensor1(); | |
} | |
function readSensor0() { | |
local word = _i2c.read(_i2cAddr, TSL2561_REGISTER_ADC0_LSB, 2); | |
local lumo = (word[1] << 8) + word[0]; | |
return lumo; | |
} | |
function readSensor1() { | |
local word = _i2c.read(_i2cAddr, TSL2561_REGISTER_ADC1_LSB, 2); | |
local lumo = (word[1] << 8) + word[0]; | |
return lumo; | |
} | |
function setInterrupt(lower = 0, upper = 0xFFFF, intPin = null, callback = null) { | |
if (intPin == null) return; | |
if (lower < 0) lower = 0; | |
if (upper > 0xFFFF) upper = 0xFFFF; | |
if (lower > upper) { | |
local a = upper; | |
upper = lower; | |
lower = a; | |
} | |
// Configure interrupt pin | |
_interruptPin = intPin; | |
intPin.configure(DIGITAL_IN , callback.bindenv(this)); | |
// Write lower and upper threshold values | |
_i2c.write(_i2cAddr, TSL2561_REGISTER_THRESHLOW_LSB); | |
_i2c.write(_i2cAddr, (lower & 0xFF).tochar() + (lower >> 8).tochar()); | |
_i2c.write(_i2cAddr, TSL2561_REGISTER_THRESHHI_LSB); | |
_i2c.write(_i2cAddr, (upper & 0xFF).tochar() + (upper >> 8).tochar()); | |
// Activate the interrupt | |
_i2c.write(_i2cAddr, TSL2561_CONTROL_INTERRUPT); | |
_i2c.write(_i2cAddr, "\x25"); | |
_interruptFlag = true; | |
if (_debugFlag) server.log("TSL2561 interrupt set"); | |
} | |
function clearInterrupt() { | |
if (_interruptFlag == false) return; | |
_i2c.write(_i2cAddr, TSL2561_CONTROL_INTERRUPT); | |
_i2c.write(_i2cAddr, "\x00"); | |
_interruptFlag = false; | |
_interruptPin.configure(DIGITAL_OUT, 0); | |
if (_debugFlag) server.log("TSL2561 interrupt cleared"); | |
} | |
/* ------ Private Functions ------ */ | |
function _calculateLux(ch0, ch1) { | |
// Calculate the luminosity based on ADC Channel 0 (visible + IR) and | |
// Channel 1 (IR) values. Returns the luminosity. | |
local chScale = 0x0400; | |
if (_integ == 0) { | |
// 13.7ms | |
chScale = 0x7517; | |
} else if (_integ == 1) { | |
// 101ms | |
chScale = 0x0fe7; | |
} | |
if (_gain == 0) chScale = chScale << 4; | |
local channel0 = (ch0 * chScale) >> 10; | |
local channel1 = (ch1 * chScale) >> 10; | |
local ratio1 = 0; | |
if (channel0 != 0) ratio1 = (channel1 << (RATIO_SCALE + 1)) / channel0; | |
local ratio = (ratio1 + 1) >> 1; | |
local b = 0x0000; | |
local m = 0x0000; | |
if ((ratio >= 0) && (ratio <= 0x0043)) | |
{b=0x0204; m=0x01ad;} | |
else if (ratio <= 0x0085) | |
{b=0x0228; m=0x02c1;} | |
else if (ratio <= 0x00c8) | |
{b=0x0253; m=0x0363;} | |
else if (ratio <= 0x010a) | |
{b=0x0282; m=0x03df;} | |
else if (ratio <= 0x014d) | |
{b=0x0177; m=0x01dd;} | |
else if (ratio <= 0x019a) | |
{b=0x0101; m=0x0127;} | |
else if (ratio <= 0x029a) | |
{b=0x0037; m=0x002b;} | |
else if (ratio > 0x029a) | |
{b=0x0000; m=0x0000;} | |
local temp = ((channel0 * b) - (channel1 * m)); | |
if (temp < 0) temp = 0; | |
temp += (1 << (LUX_SCALE - 1)); | |
return (temp >> LUX_SCALE); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment