Forked from ersatzavian/envsense-tail.device.nut
Last active
August 29, 2015 14:13
-
-
Save blindman2k/1674fccf8696583b63d6 to your computer and use it in GitHub Desktop.
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
/* Environmental Sensor Tail Firmware | |
* Ambient Light Sensor: APDS-9007-020 (http://www.avagotech.com/docs/AV02-0512EN) | |
* Air Pressure Sensor: LPS25HTR (http://www.st.com/web/en/resource/technical/document/datasheet/DM00066332.pdf) | |
* Humidity/Temp Sensor: SI7020-A10-GMR (http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7020.pdf) | |
*/ | |
const LPS25H_ADDR = 0xB8; // 8-bit I2C Student Address for LPS25HTR | |
const SI7020_ADDR = 0x80; // 8-bit I2C Student Address for SI7020 | |
const ALS_RLOAD = 47000.0; // load resistor value on ALS | |
const READING_INTERVAL = 3; // seconds between readings | |
/* CLASS AND GLOBAL FUNCTION DEFINITIONS ------------------------------------ */ | |
// Ambient Light Sensor APDS-9007-020 | |
// http://www.avagotech.com/docs/AV02-0512EN | |
// Logarithmic Analog Current Output; drive into load resistor and buffer | |
// Read with Analog Input | |
class APDS9007 { | |
static WAIT_BEFORE_READ = 5.0; | |
RLOAD = null; // value of load resistor on ALS (device has current output) | |
_als_pin = null; | |
_als_en = null; | |
_points_per_read = null; | |
// ------------------------------------------------------------------------- | |
constructor(als_pin, rload, als_en = null, points_per_read = 10) { | |
_als_pin = als_pin; | |
_als_en = als_en; | |
RLOAD = rload; | |
_points_per_read = points_per_read * 1.0; //force to a float | |
} | |
// ------------------------------------------------------------------------- | |
// read the ALS and return value in lux | |
function read() { | |
if (_als_en) { | |
_als_en.write(1); | |
imp.sleep(WAIT_BEFORE_READ); | |
} | |
local Vpin = 0; | |
local Vcc = 0; | |
// average several readings for improved precision | |
for (local i = 0; i < _points_per_read; i++) { | |
Vpin += _als_pin.read(); | |
Vcc += hardware.voltage(); | |
} | |
Vpin = (Vpin * 1.0) / _points_per_read; | |
Vcc = (Vcc * 1.0) / _points_per_read; | |
Vpin = (Vpin / 65535.0) * Vcc; | |
local Iout = (Vpin / RLOAD) * 1000000.0; // current in µA | |
if (_als_en) _als_en.write(0); | |
return (math.pow(10.0,(Iout/10.0))); | |
} | |
} | |
// Air Pressure Sensor LPS25H | |
// http://www.st.com/web/en/resource/technical/document/datasheet/DM00066332.pdf | |
class LPS25H { | |
static MEAS_TIME = 0.5; // seconds; time to complete pressure conversion | |
_i2c = null; | |
_addr = null; | |
// ------------------------------------------------------------------------- | |
constructor(i2c, addr = 0xB8) { | |
_i2c = i2c; | |
_addr = addr; | |
init(); | |
} | |
// ------------------------------------------------------------------------- | |
function init() { | |
enum LPS25H_REG { | |
REF_P_XL = 0x08, | |
REF_P_L = 0X09, | |
REF_P_H = 0x0A, | |
WHO_AM_I = 0x0F, | |
RES_CONF = 0x10, | |
CTRL_REG1 = 0x20, | |
CTRL_REG2 = 0x21, | |
CTRL_REG3 = 0x22, | |
CTRL_REG4 = 0x23, | |
INT_CFG = 0x24, | |
INT_SOURCE = 0x25, | |
STATUS_REG = 0x27, | |
PRESS_OUT_XL = 0x28, | |
PRESS_OUT_L = 0x29, | |
PRESS_OUT_H = 0x2A, | |
TEMP_OUT_L = 0x2B, | |
TEMP_OUT_H = 0x2C, | |
FIFO_CTRL = 0x2E, | |
FIFO_STATUS = 0x2F, | |
THS_P_L = 0x30, | |
THS_P_H = 0x31, | |
RPDS_L = 0x39, | |
RPDS_H = 0x3A | |
} | |
} | |
// ------------------------------------------------------------------------- | |
function _twosComp(value, mask) { | |
value = ~(value & mask) + 1; | |
return -1 * (value & mask); | |
} | |
// ------------------------------------------------------------------------- | |
function _read(reg, numBytes) { | |
local result = _i2c.read(_addr, reg.tochar(), numBytes); | |
if (result == null) { | |
server.error("I2C read error: " + _i2c.readerror()); | |
} | |
return result; | |
} | |
// ------------------------------------------------------------------------- | |
function _write(reg, ...) { | |
local s = reg.tochar(); | |
foreach (b in vargv) { | |
s += b.tochar(); | |
} | |
local result = _i2c.write(_addr, s); | |
if (result) { | |
server.error("I2C write error: " + result); | |
} | |
return result; | |
} | |
// ------------------------------------------------------------------------- | |
function getDeviceID() { | |
return _read(LPS25H_REG.WHO_AM_I, 1); | |
} | |
// ------------------------------------------------------------------------- | |
function enable(val) { | |
local reg = _read(LPS25H_REG.CTRL_REG1, 1); | |
local res = _write(LPS25H_REG.CTRL_REG1, reg[0] | (0x80 & val << 7)); | |
} | |
// ------------------------------------------------------------------------- | |
function getReferencePressure() { | |
local data = _read(LPS25H_REG.REF_P_XL, 3); | |
return (data[2] << 16) | (data[1] << 8) | data[0]; | |
} | |
// ------------------------------------------------------------------------- | |
// Set the number of readings taken and internally averaged to give a pressure result | |
// Selector field is 2 bits | |
function setPressNpts(npts) { | |
if (npts <= 8) { | |
// Average 8 readings | |
npts = 0x00; | |
} else if (npts <= 32) { | |
// Average 32 readings | |
npts = 0x01 | |
} else if (npts <= 128) { | |
// Average 128 readings | |
npts = 0x02; | |
} else { | |
// Average 512 readings | |
npts = 0x03; | |
} | |
local val = _read(LPS25H_REG.RES_CONF, 1)[0]; | |
local res = _write(LPS25H_REG.RES_CONF, (val & 0xFC) | npts); | |
} | |
// ------------------------------------------------------------------------- | |
// Set the number of readings taken and internally averaged to give a temperature result | |
// Selector field is 2 bits | |
function setTempNpts(npts) { | |
if (npts <= 8) { | |
// Average 8 readings | |
npts = 0x00; | |
} else if (npts <= 16) { | |
// Average 16 readings | |
npts = 0x01 | |
} else if (npts <= 32) { | |
// Average 32 readings | |
npts = 0x02; | |
} else { | |
// Average 64 readings | |
npts = 0x03; | |
} | |
local val = _read(LPS25H_REG.RES_CONF, 1); | |
local res = _write(LPS25H_REG.RES_CONF, (val & 0xF3) | (npts << 2)); | |
} | |
// ------------------------------------------------------------------------- | |
function setIntEnable(state) { | |
local val = _read(LPS25H_REG.CTRL_REG1, 1); | |
if (state == 0) { | |
val = val & 0xF7; | |
} else { | |
val = val | 0x08; | |
} | |
local res = _write(LPS25H_REG.CTRL_REG1, val & 0xFF); | |
} | |
// ------------------------------------------------------------------------- | |
function setFifoEnable(state) { | |
local val = _read(LPS25H_REG.CTRL_REG2, 1)[0]; | |
if (state == 0) { | |
val = val & 0xAF; | |
} else { | |
val = val | 0x40; | |
} | |
local res = _write(LPS25H_REG.CTRL_REG2, val & 0xFF); | |
} | |
// ------------------------------------------------------------------------- | |
function softReset(state) { | |
local res = _write(LPS25H_REG.CTRL_REG2, 0x04); | |
} | |
// ------------------------------------------------------------------------- | |
function setIntActivehigh(state) { | |
local val = _read(LPS25H_REG.CTRL_REG3, 1)[0]; | |
if (state == 0) { | |
val = val | 0x80; | |
} else { | |
val = val & 0x7F; | |
} | |
local res = _write(LPS25H_REG.CTRL_REG3, val & 0xFF); | |
} | |
// ------------------------------------------------------------------------- | |
function setIntPushpull(state) { | |
local val = _read(LPS25H_REG.CTRL_REG3, 1)[0]; | |
if (state == 0) { | |
val = val | 0x40; | |
} else { | |
val = val & 0xBF; | |
} | |
local res = _write(LPS25H_REG.CTRL_REG3, val & 0xFF); | |
} | |
// ------------------------------------------------------------------------- | |
function setIntConfig(latch, diff_press_low, diff_press_high) { | |
local val = _read(LPS25H_REG.CTRL_REG1, 1)[0]; | |
if (latch) { | |
val = val | 0x04; | |
} | |
if (diff_press_low) { | |
val = val & 0x02; | |
} | |
if (diff_press_high) { | |
val = val | 0x01; | |
} | |
local res = _write(LPS25H_REG.CTRL_REG1, val & 0xFF); | |
} | |
// ------------------------------------------------------------------------- | |
function setPressThresh(press_thresh) { | |
_write(LPS25H.THS_P_H, (press_thresh & 0xff00) >> 8); | |
_write(LPS25H.THS_P_L, press_thresh & 0xff); | |
} | |
// ------------------------------------------------------------------------- | |
// Returns raw pressure register values | |
function getRawPressure() { | |
local low = _read(LPS25H_REG.PRESS_OUT_XL, 1); | |
local mid = _read(LPS25H_REG.PRESS_OUT_L, 1); | |
local high = _read(LPS25H_REG.PRESS_OUT_H, 1); | |
return ((high[0] << 16) | (mid[0] << 8) | low[0]); | |
} | |
// ------------------------------------------------------------------------- | |
function getPressureHPa(cb = null) { | |
// Wake up the sensor | |
enable(1); | |
// Start a one-shot measurement | |
_write(LPS25H_REG.CTRL_REG2, 0x01); | |
if (cb) { | |
imp.wakeup(MEAS_TIME, function() { | |
local pressure = (getRawPressure() - getReferencePressure()) / 4096.0; | |
enable(0); | |
cb(pressure); | |
}.bindenv(this)); | |
} else { | |
imp.sleep(MEAS_TIME); | |
local pressure = (getRawPressure() - getReferencePressure()) / 4096.0; | |
enable(0); | |
return pressure; | |
} | |
} | |
// ------------------------------------------------------------------------- | |
// Returns Pressure in kPa | |
function getPressureKPa() { | |
return gePressureHPa() / 10.0; | |
} | |
// ------------------------------------------------------------------------- | |
// Returns Pressure in inches of Hg | |
function getPressureInHg() { | |
return getPressureHPa() * 0.0295333727; | |
} | |
// ------------------------------------------------------------------------- | |
function getTemp() { | |
enable(1); | |
local temp_l = _read(LPS25H_REG.TEMP_OUT_L, 1)[0]; | |
local temp_h = _read(LPS25H_REG.TEMP_OUT_H, 1)[0]; | |
enable(0); | |
local temp_raw = (temp_h << 8) | temp_l; | |
if (temp_raw & 0x8000) { | |
temp_raw = _twosComp(temp_raw, 0xFFFF); | |
} | |
return (42.5 + (temp_raw / 480.0)); | |
} | |
} | |
// Humidity/Temp Sensor | |
class SI7021 { | |
static READ_RH = "\xF5"; | |
static READ_TEMP = "\xF3"; | |
static PREV_TEMP = "\xE0"; | |
static RH_MULT = 125.0/65536.0; | |
static RH_ADD = -6; | |
static TEMP_MULT = 175.72/65536.0; | |
static TEMP_ADD = -46.85; | |
_i2c = null; | |
_addr = null; | |
// class constructor | |
// Input: | |
// _i2c: hardware i2c bus, must pre-configured | |
// _addr: slave address (optional) | |
// Return: (None) | |
constructor(i2c, addr = 0x80) | |
{ | |
_i2c = i2c; | |
_addr = addr; | |
} | |
// read the humidity | |
// Input: (none) | |
// Return: relative humidity (float) | |
function readRH() { | |
_i2c.write(_addr, READ_RH); | |
local reading = _i2c.read(_addr, "", 2); | |
while (reading == null) { | |
reading = _i2c.read(_addr, "", 2); | |
} | |
local humidity = RH_MULT*((reading[0] << 8) + reading[1]) + RH_ADD; | |
return humidity; | |
} | |
// read the temperature | |
// Input: (none) | |
// Return: temperature in celsius (float) | |
function readTemp() { | |
_i2c.write(_addr, READ_TEMP); | |
local reading = _i2c.read(_addr, "", 2); | |
while (reading == null) { | |
reading = _i2c.read(_addr, "", 2); | |
} | |
local temperature = TEMP_MULT*((reading[0] << 8) + reading[1]) + TEMP_ADD; | |
return temperature; | |
} | |
// read the temperature from previous rh measurement | |
// this method does not have to recalculate temperature so it is faster | |
// Input: (none) | |
// Return: temperature in celsius (float) | |
function readPrevTemp() { | |
_i2c.write(_addr, PREV_TEMP); | |
local reading = _i2c.read(_addr, "", 2); | |
local temperature = TEMP_MULT*((reading[0] << 8) + reading[1]) + TEMP_ADD; | |
return temperature; | |
} | |
} | |
function aps_int() { | |
if (press_int.read()) { | |
server.log("LPS25HTR threw interrupt"); | |
} | |
} | |
function logloop() { | |
imp.wakeup(READING_INTERVAL,logloop); | |
data <- {}; | |
data.rh <- rh.readRH(); | |
data.temp <- rh.readTemp(); | |
data.lux <- als.read(); | |
data.press <- aps.getPressureInHg(); | |
server.log(format("SI7020: RH = %0.2f%s, Temp = %0.2fºC",data.rh,"%",data.temp)); | |
server.log(format("APDS-9007: %0.2f Lux",data.lux)); | |
server.log(format("LPS25H: Press = %0.2f\" Hg (%0.2f mBar), Temp = %0.2fºC",data.press,aps.getPressureHPa(),aps.getTemp())); | |
agent.send("insights", data); | |
} | |
/* AGENT CALLBACKS ---------------------------------------------------------- */ | |
/* RUNTIME START ------------------------------------------------------------ */ | |
press_int <- hardware.pin1; // interrupt from air pressure sensor | |
led <- hardware.pin2; // high-side drive for LED | |
als_out <- hardware.pin5; // ambient light sensor output (analog) | |
als_en <- hardware.pin7; // ambient light sensor enable (active high) | |
i2c <- hardware.i2c89; | |
press_int.configure(DIGITAL_IN, aps_int); | |
led.configure(PWM_OUT,2,0.2); // blink once every 2 seconds for 400ms | |
als_out.configure(ANALOG_IN); | |
als_en.configure(DIGITAL_OUT); | |
i2c.configure(CLOCK_SPEED_400_KHZ); | |
als <- APDS9007(als_out, ALS_RLOAD, als_en); | |
aps <- LPS25H(i2c, LPS25H_ADDR); | |
rh <- SI7021(i2c, SI7020_ADDR); | |
server.log("SW: "+imp.getsoftwareversion()); | |
server.log("Memory Free: "+imp.getmemoryfree()); | |
imp.enableblinkup(true); | |
imp.setpowersave(true); | |
logloop(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment