Water Meter Electric Imp Code
// Create a data stream at data.sparkfun.com, then enter your keys here: | |
local publicKey = "xx" // enter public key here | |
local privateKey = "xx" // enter private key here | |
data_array <- array(800,[0,0,0,0]) | |
total <- 0 | |
conversion <- 4.95/476 | |
local req = http.get("https://data.sparkfun.com/output/"+o_publicKey+"/latest.json"); | |
local response = req.sendsync() | |
local data = http.jsondecode(response.body) | |
total = data[0].cnt_total.tointeger() | |
server.log(total) | |
function bufferRx(data) { | |
data_array <- data | |
local gpm = data_array[1][0].tofloat()*conversion*6.0 | |
local total_gal = total.tofloat()*conversion | |
// Prepare the phant headers | |
local phantHeaders = {"Phant-Private-Key": privateKey, "connection": "close"}; | |
// Create a post request | |
local req = http.post("http://data.sparkfun.com/input/"+publicKey, phantHeaders,"cnt="+gpm+"&cnt_total="+total_gal); | |
total += data_array[1][0] | |
//Send out the request! | |
server.log(gpm+" | "+total_gal+" | "+total+" | "+data_array[1][0]+" | "+data_array[1][1]+" | "+data_array[1][2]+" | "+req.sendsync().statuscode); | |
} | |
function request_handler(request, response) | |
{ | |
try | |
{ | |
// Process incoming http request | |
response.header("access-control-allow-origin", "*"); | |
// If everything worked as expected, send a standard 'OK' status code (200) | |
local r = "{" | |
local i=0 | |
local k=0 | |
r += "\"x\":[" | |
for (i=0;i<data_array[0].len();i++) { | |
if (i!=0) {r += ","} | |
r += data_array[0][i] | |
} | |
r += "]" | |
r+=",\"cnt\":"+data_array[1][0] | |
r+=",\"cnt_hf\":"+data_array[1][1] | |
r+=",\"cnt_lf\":"+data_array[1][2] | |
r+=",\"kalman\":"+data_array[1][3] | |
r+=",\"max\":"+data_array[1][4] | |
r+=",\"min\":"+data_array[1][5] | |
r+=",\"spread\":"+data_array[1][6] | |
r+=",\"amax\":"+data_array[1][7] | |
r+=",\"amin\":"+data_array[1][8] | |
r+=",\"thresh\":"+data_array[1][9] | |
r+="}" | |
response.send(200, r); | |
} | |
catch (exception) | |
{ | |
response.send(500, "Internal Server Error: " + exception); | |
} | |
} | |
device.on("buffer",bufferRx); | |
http.onrequest(request_handler); |
// Define a class for a ring buffer object with some analysis functions | |
class ring_buffer { | |
_depth = 0 | |
_index = 0 | |
_data = 0 | |
_buff = 0 | |
_fill = 0 | |
constructor(depth) { | |
_depth = depth | |
_data = array(depth,0) | |
_index = 0 | |
} | |
function insert(value) { | |
if (_index < _depth) | |
{ | |
_data[_index] = value | |
_index++ | |
} | |
else | |
{ | |
_data[0] = value | |
_index = 1 | |
} | |
if (_fill<=_depth){ | |
_fill++ | |
} | |
} | |
function z(relative_index){ | |
if ((_index-1-relative_index)>=0) | |
{ | |
return _data[_index-1-relative_index] | |
} | |
else | |
{ | |
return _data[_index-1-relative_index+_depth] | |
} | |
} | |
function dump(){ | |
local i = 0 | |
local buff = array(_fill,0) | |
for (i=0;i<_fill;i++) { | |
buff[i] = z(i) | |
} | |
return buff | |
} | |
function stringify(){ | |
local d = dump() | |
local string = "" | |
local i=0 | |
for (i=0;i<d.len();i++) { | |
string += d[i]+" " | |
} | |
return string | |
} | |
function stats(d){ | |
local min = 32767 | |
local max = -32767 | |
local i | |
for (i=0;i<d;i++) { | |
local t = z(i) | |
if (t>max){max=t} | |
if (t<min){min=t} | |
} | |
return [min,max,max-min] | |
} | |
function fill(){ | |
return _fill | |
} | |
function rolled_over(){ | |
return _fill>_depth | |
} | |
function reset(){ | |
_index = 0 | |
_fill = 0 | |
} | |
} | |
// Define the Sensor Address | |
const ADDR = 0x1C; | |
// Define register map | |
const OUT_X_MSB = "\x01"; | |
const CTRL_REG1 = "\x10"; | |
const CTRL_REG2 = "\x11"; | |
// Some useful bitmasks | |
const ACTIVE = "\x01"; | |
const AUTO_MRST_EN = "\x80"; | |
// Define the i2c periphrial being used | |
i2c <- hardware.i2c89; | |
// Interupt pin | |
int <- hardware.pin7; | |
// Config the i2c periph | |
i2c.configure(CLOCK_SPEED_400_KHZ); | |
// Set to auto reset int flag on read | |
i2c.write(ADDR,CTRL_REG2+AUTO_MRST_EN) | |
// Activate the MAG3110, sample at 80Hz | |
i2c.write(ADDR,CTRL_REG1+ACTIVE) | |
// Read the initial data to clear the isr flag | |
local data = i2c.read(ADDR,OUT_X_MSB,6); | |
// create a buffer for storing data | |
data_buffer <- ring_buffer(800); | |
// We're going to do some global min and max calculations | |
// So init the variables | |
min <- 32767 | |
max <- -32767 | |
// Initialize the threshold | |
thresh <- 0 | |
spread_thresh <- 100 | |
// Debouncing parameter | |
debounce <- 120 | |
// Some bools for oneshots | |
x_lf <- false | |
x_hf <- false | |
// Counters for counting the number of triggers | |
trigs <- 0 | |
x_trigs_lf <- 0 | |
x_trigs_hf <- 0 | |
k <- 0 | |
x_int <- 0 | |
x_int_neg <- 0 | |
// Interupt routine for getting MAG3110 measurements | |
function mag_isr() { | |
// Check for the rising edge | |
if (int.read()==1) | |
{ | |
// Read data from i2c | |
local data = i2c.read(ADDR,OUT_X_MSB,6); | |
// Combine the MSB and LSB into a 16bit value | |
local x = data[0]<<8 | data[1] | |
// Convert 2's compliment | |
if (x & 0x8000) {x = -((~x & 0x7FFF) + 1);} | |
// Detect for min and max, then calculate a new threshold | |
if (x<min) {min = x; thresh = min + (max-min)/2} | |
if (x>max) {max = x; thresh = min + (max-min)/2} | |
// Put data in buffer | |
data_buffer.insert(x) | |
// Get the count spread from the last 20 items | |
local stats = data_buffer.stats(20) | |
local spread = stats[2] | |
// Detect if measured value is above or below the threshold | |
if (x>thresh) { | |
// Logic for oneshot to count the crossing | |
if (!x_hf){ | |
x_hf = true | |
// Increment the high frequency counter | |
x_trigs_hf++ | |
if (spread > spread_thresh){trigs++} | |
} | |
// Integrate the measurement, this is for the debounce algorithim | |
x_int += (x-thresh) | |
// If integral exceeds the debounce parameter, time for the lf count | |
if (x_int>debounce) { | |
// Logic for oneshot to count the crossing | |
if (!x_lf) { | |
x_lf=true | |
// Increment the high frequency counter | |
x_trigs_lf++ | |
if (spread <= spread_thresh){trigs++} | |
// Reset the negative integral | |
x_int_neg = 0 | |
} | |
} | |
} | |
else { | |
// Reset the hf oneshot | |
x_hf = false | |
// Integrate the measurement under the threshold | |
x_int_neg += (thresh-x) | |
// If integral exceeds the threshold, time to reset the lf oneshot | |
if (x_int_neg>debounce) { | |
x_lf = false | |
// Reset the positive integral | |
x_int = 0 | |
} | |
} | |
//determine if buffer has reached the end | |
if (data_buffer.rolled_over()) | |
{ | |
if (spread>spread_thresh) { | |
k = x_trigs_hf | |
} | |
else { | |
k = x_trigs_lf | |
} | |
// Make sure min, max and threshold are stabilized before sending data | |
if ((max-min)>spread_thresh) | |
{ | |
// buffer is at end, send the data back to the agent | |
agent.send("buffer",[data_buffer.dump(),[trigs,x_trigs_hf,x_trigs_lf,k,stats[1],stats[0],stats[2],max,min,thresh]]) | |
} | |
// Reset the trigger counters | |
x_trigs_hf = 0 | |
x_trigs_lf = 0 | |
trigs = 0 | |
//reset the buffer and start all over again | |
data_buffer.reset() | |
} | |
} | |
} | |
// Configure the interrupt pin to run the mag_isr callback | |
int.configure(DIGITAL_IN, mag_isr) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment