Last active
May 28, 2024 15:02
-
-
Save alexbain/8392153 to your computer and use it in GitHub Desktop.
Electric Imp code (agent & device) for a laundry monitoring device. Read more at $BLOG_POST (tbd)
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
// 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) {}); | |
} | |
}); |
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
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