Electric Imp code (agent & device) for a laundry monitoring device. Read more at $BLOG_POST (tbd)
// Run on Agent | |
// Thresholds to adjust for better accuracy | |
dataThreshold <- 300; // Minimum accelerometer value to count as ON | |
onThreshold <- 24; // Number of ON samples before machine enters RUNNING state | |
offThreshold <- 60; // Number of OFF samples before machine enters OFF state | |
// State variable | |
running <- false; | |
// Keep track of counts | |
onCount <- 0; | |
offCount <- 0; | |
// Twilio | |
twilioURL <- "https://USER:PASS@api.twilio.com/2010-04-01/Accounts/ID/Messages.json"; | |
twilioHeaders <- { "Content-Type": "application/x-www-form-urlencoded" }; | |
twilioNumber <- "+14155551212"; | |
// Array of phone numbers to be contacted with the laundry is done | |
phoneNumbers <- ["+14155555555", "+14155555556"]; | |
// Firebase | |
logToFirebase <- false; | |
firebaseURL <- "https://FIREBASE_URL.firebaseIO.com/data.json"; | |
firebaseHeaders <- { "Content-Type": "application/json" }; | |
// Called every time the imp emits data | |
device.on("data", function(data) { | |
// Is there enough accelerometer activity for the device to be considered ON? | |
if (data >= dataThreshold) { | |
onCount = onCount + 1; | |
// Prevent overflow errors by resetting onCount when it gets close to limit | |
if (onCount >= 65500) { | |
onCount = onThreshold; | |
} | |
// If the device has been ON for long enough, set running state to be true | |
if (onCount >= onThreshold) { | |
running = true; | |
// Running, so reset offCount | |
offCount = 0; | |
} | |
// debug / logs | |
if (running == true) { | |
server.log(format("ON - RUNNING (%f), onCount (%d), offCount (%d)", data, onCount, offCount)); | |
} else { | |
server.log(format("ON (%f), onCount (%d), offCount (%d)", data, onCount, offCount)); | |
} | |
// Imp is not recording much accelerometer movement, so device seems to be OFF | |
} else { | |
offCount = offCount + 1; | |
// Prevent overflow errors by resetting offCount when it gets close to limit | |
if (offCount >= 65500) { | |
offCount = offThreshold; | |
} | |
// Has the device been off for long enough to be "done"? | |
if (offCount >= offThreshold) { | |
// Was the device previously running? | |
if (running == true) { | |
// This means that the laundry had been running, and is now done. | |
// Send an SMS to each phone number in the phoneNumbers array. | |
foreach (number in phoneNumbers) { | |
local body = format("To=%s&From=%s&Body=The%%20laundry%%20is%%20done.", number, twilioNumber); | |
local request = http.post(twilioURL, twilioHeaders, body); | |
local response = request.sendasync(function(done) {}); | |
} | |
// debug / logs | |
server.log("!!!! Emitting OFF event !!!!"); | |
} | |
// Reset on count | |
onCount = 0; | |
// Machine is no longer running | |
running = false; | |
} | |
// debug / logs | |
if (running == true) { | |
server.log(format("OFF - WAS RUNNING (%f), onCount (%d), offCount (%d)", data, onCount, offCount)); | |
} else { | |
server.log(format("OFF (%f), onCount (%d), offCount (%d)", data, onCount, offCount)); | |
} | |
} | |
if (logToFirebase == true) { | |
// Build a post request to Firebase to log the data | |
local body = format("{\"amount\": %f, \"running\": %s, \".priority\": %d}", data, running ? "true" : "false", time()); | |
local request = http.post(firebaseURL, firebaseHeaders, body); | |
// Send the data to Firebase async | |
local response = request.sendasync(function(done) {}); | |
} | |
}); |
total <- 0; // Sum of % change from all samples | |
n <- 0; // Counter for number of samples read | |
last <- 1; // Previous reading of magnitude | |
function readSensor() { | |
// Time interval | |
local interval = 0.02; | |
// Get source voltage, should be 3.3V | |
local vRef = hardware.voltage(); | |
// Read in ADC values from accelerometer | |
local adcRx = hardware.pin1.read(); | |
local adcRy = hardware.pin2.read(); | |
local adcRz = hardware.pin5.read(); | |
// server.log(format("Raw ADC values: %f, %f, %f", adcRx, adcRy, adcRz)); | |
// Convert 16bit ADC accelerometer values (0-65535) into voltage | |
local voltsRx = (adcRx * vRef) / 65535.0; | |
local voltsRy = (adcRy * vRef) / 65535.0; | |
local voltsRz = (adcRz * vRef) / 65535.0; | |
// server.log(format("Voltages: %f, %f %f", voltsRx, voltsRy, voltsRz)); | |
// Subtract 0g (1.5V at 3V, 1.65V at 3.3V) | |
local deltaVoltsRx = voltsRx - (vRef / 2); | |
local deltaVoltsRy = voltsRy - (vRef / 2); | |
local deltaVoltsRz = voltsRz - (vRef / 2); | |
// server.log(format("Adjusted voltages %f, %f, %f", deltaVoltsRx, deltaVoltsRy, deltaVoltsRz)); | |
// Convert from voltage to g, using sensitivity of 300mV | |
local rX = deltaVoltsRx / 0.3; | |
local rY = deltaVoltsRy / 0.3; | |
local rZ = deltaVoltsRz / 0.3; | |
// server.log(format("Gs: %f, %f, %f", rX, rY, rZ)); | |
// Compute magnitude of force | |
local magnitude = math.sqrt(math.pow(rX,2) + math.pow(rY, 2) + math.pow(rZ, 2)); | |
// Compute % change since last reading | |
local change = math.fabs((magnitude - last)/last) * 100.0; | |
// Store magnitude in last for next time | |
last = magnitude; | |
// Log magnitude and percent change | |
// server.log(format("magnitude: %f, change amount: %f", magnitude, change)); | |
// Increment total with % change, increment N | |
total = total + change; | |
n = n + 1; | |
// Log data to server once ever 250 samples (5 seconds) | |
if (n >= 250) { | |
agent.send("data", total); | |
n = 0; | |
total = 0; | |
} | |
// Sleep until time to read sensor again | |
imp.wakeup(interval, readSensor); | |
} | |
// X input | |
hardware.pin1.configure(ANALOG_IN); | |
// Y input | |
hardware.pin2.configure(ANALOG_IN); | |
// Z input | |
hardware.pin5.configure(ANALOG_IN); | |
// Start reading the sensor | |
readSensor(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment