Skip to content

Instantly share code, notes, and snippets.

@blindman2k
Forked from ersatzavian/envsense-tail.device.nut
Last active August 29, 2015 14:13
Show Gist options
  • Save blindman2k/1674fccf8696583b63d6 to your computer and use it in GitHub Desktop.
Save blindman2k/1674fccf8696583b63d6 to your computer and use it in GitHub Desktop.
/* 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