Created
December 7, 2010 01:47
-
-
Save corinnas/731342 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
/* MEAL Project (Mindful Eating through Ambient Light) | |
* | |
* This sketch takes input from 3 pressure sensors (FlexiForce 100 lb) | |
* and uses this data to track the weight of food consumed. | |
* It outputs changes in a light source in response (BlinkM RGB LEDs). | |
* | |
* For example: | |
* | |
* Plug in the device. | |
* Turn on the lamp by pressing the power button. Minimum load is recorded. | |
* Place a dish or individual items on the load sensing platform. | |
* Press the calibrate button to register the maximum load. | |
* Begin removing items from the platter. | |
* | |
* If the load decreases, we infer that the person is eating. | |
* The light source shifts from red (a color that stimulates appetite) | |
* to blue (a color that promotes relaxation), | |
* signalling the eater to pause and reflect. | |
* | |
* FlexiForce connections to Arduino | |
* Sensor pin 1 -- Analog In 0 | |
* Sensor pin 1 -- 2M ohm resistor -- 5V | |
* Sensor pin 2 -- Gnd | |
* | |
* BlinkM connections to Arduino | |
* PWR - -- gnd -- black -- Gnd | |
* PWR + -- +5V -- red -- 5V | |
* I2C d -- SDA -- green -- Analog In 4 | |
* I2C c -- SCK -- blue -- Analog In 5 | |
* | |
* Note: This sketch sends to the I2C "broadcast" address of 0, | |
* so all BlinkMs on the I2C bus will respond. | |
* | |
* by Corinna Sherman | |
* December 3, 2010 | |
*/ | |
#include <QueueArray.h> | |
#include <Wire.h> | |
#include "BlinkM_funcs.h" | |
// BlinkM constants | |
const int blinkmAddr = 0; // general call to all BlinkMs | |
const int initialHue = 20; // yellow | |
const int hueMin = 0; // red | |
const int hueMax = 160; // blue | |
const int brightnessMin = 0; | |
const int brightnessMax = 255; | |
const int saturationMin = 0; | |
const int saturationMax = 255; | |
// Pressure sensor constants | |
const int pressureSensorCount = 3; // number of pressure sensors | |
const int pressureSensorPin[pressureSensorCount] = { 0, 1, 2 }; // analog input pins for the pressure sensors | |
const int pressureSensorMin = 0; // minimum value that can be produced by a pressure sensor (when load sensed is 100 lbs) | |
const int pressureSensorMax = 1023; // maximum value that can be produced by a pressure sensor (when load sensed is 0) | |
const int queueSize = 5; // variable to hold the size of the queue | |
const int pressureChangeThreshold = 50; // variable to specify the threshold amount beyond which a change in pressure triggers a reaction | |
// Input pins | |
const int powerSwitchPin = 7; // digital input pin for momentary switch that controls power to circuits | |
const int calibrateSwitchPin = 12; // digital input pin for momentary switch that triggers calibration mode | |
// Variables | |
int lampState; // variable to store ON/OFF state of lamp; set by lampOn() and lampOff() | |
int powerButtonState; // variable to store state of the momentary switch that is the power button | |
int calibrateButtonState; // variable to store state of the momentary switch that is the calibration button | |
int calibratedMaxLoad = 0; // 0 if never calibrated, 1 if calibrated at least once | |
int lastKnownHueValue = hueMin; // variable to store the last known hue of the lamp light | |
// Variables related to the total pressure sensed by the system through all sensors | |
int pressureMin = pressureSensorCount * pressureSensorMin; // variable to hold minimum total pressure value (set at calibration) | |
int pressureMax = pressureSensorCount * pressureSensorMax; // variable to hold maximum pressure value | |
int pressureAve = 0; // variable to hold the average of the summed sensor values from the last <queueSize> readings | |
QueueArray <int> queue; // variable to hold the last <queueSize> summed values of sensor readings (for average smoothing purposes) | |
void setup() { | |
// Initialize serial communication | |
Serial.begin(19200); | |
Serial.println("Initializing"); | |
// Initialize the power switch. | |
pinMode(powerSwitchPin, INPUT); | |
// Initialize the calibration switch. | |
pinMode(calibrateSwitchPin, INPUT); | |
// Store the intial powerButtonState based on the powerpowerSwitchPin. | |
powerButtonState = digitalRead(powerSwitchPin); | |
// Store the initial calibrateButtonState based on the calibrate switch | |
calibrateButtonState = digitalRead(calibrateSwitchPin); | |
// Initialize all BlinkM modules on the I2C bus | |
Serial.println("beginWithPower"); | |
BlinkM_beginWithPower(); | |
Serial.println("stopScript"); | |
BlinkM_stopScript(blinkmAddr); // turn off startup script | |
Serial.println("lampOff"); | |
lampOff(); // The lamp is initially OFF. | |
// Set the printer of the queue | |
queue.setPrinter(Serial); | |
Serial.println("Ready"); | |
} | |
void loop() { | |
// Check if the momentary switch for power is being closed, | |
// and turn the lamp on or off accordingly. | |
int powerSwitchState = digitalRead(powerSwitchPin); | |
if (powerSwitchState != powerButtonState) { // The state has changed. | |
if (powerSwitchState == HIGH) { // If switch is closed | |
if (lampState == 0) { // and if lamp is OFF, | |
lampOn(); // then turn lamp on. | |
} | |
else { // Else lamp is ON | |
lampOff(); // then turn lamp off. | |
} | |
} | |
} | |
powerButtonState = powerSwitchState; // Remember the current switch state. | |
// Check if the momentary switch for calibration is being closed | |
// and, if it is, calibrate the sensor. | |
int calibrateSwitchState = digitalRead(calibrateSwitchPin); | |
if (calibrateSwitchState != calibrateButtonState) { // The state has changed. | |
if (calibrateSwitchState == HIGH) { // If switch is closed, | |
calibrateMaxLoad(); // calibrate the system. | |
} | |
} | |
// Remember the current calibrate switch state. | |
calibrateButtonState = calibrateSwitchState; | |
// If the lamp is ON, monitor for changes in weight | |
// and update the lamp hue accordingly. | |
if (lampState == 1) { | |
// Read the pressure sensors. Values range from 0-1023. | |
// Add each reading to sumSensorValues. | |
int sumSensorValues = 0; // variable to hold the sum the values from all the pressure sensors | |
for (int i = 0; i < pressureSensorCount; i++) { | |
sumSensorValues += analogRead(pressureSensorPin[i]); | |
} | |
// If the system has not been calibrated yet, monitor the load and | |
// shift the lamp hue from blue to red to indicate when enough weight has been | |
// added to the platter to exceed the pressureChangeThreshold. | |
// If the system has already been calibrated, monitor the load and | |
// shift the lamp hue from red to blue to indicate | |
// when the load has dropped to the triggerPressure, | |
// which is some percentage of the total load set at calibration. | |
if (calibratedMaxLoad) { | |
monitorEating(sumSensorValues); | |
} else { | |
monitorServing(sumSensorValues); | |
} | |
} | |
} | |
// We expect the load to increase as food is added to the platter. | |
// Switch the lamp hue from blue to red once the load increases from its minimum | |
// to an amount greater than pressureMin + 2*pressureChangeThreshold. | |
// Argument sumSensorValues is the sum of current sensor readings. | |
void monitorServing(int sumSensorValues) { | |
queue.pop(); // pop the oldest reading from the queue | |
queue.push(sumSensorValues); // push this reading into the queue | |
pressureAve = calculateAveragePressure(); // Store the new average pressure | |
if (pressureAve > pressureMax) { | |
setPressureMax(pressureAve); | |
} | |
Serial.print("pressure (ave, min, max): "); | |
Serial.print(pressureAve); | |
Serial.print(", "); | |
Serial.print(pressureMin); | |
Serial.print(", "); | |
Serial.println(pressureMax); | |
// If the average pressure has increased by more than twice the threshold amount, update the hue. | |
if (abs(pressureMax - pressureAve) > pressureChangeThreshold * 2) { | |
// Blink red to indicate calibration can take place. | |
BlinkM_fadeToHSB( blinkmAddr, hueMin, saturationMax, brightnessMin); | |
delay(750); | |
BlinkM_fadeToHSB( blinkmAddr, hueMin, saturationMax, brightnessMax); | |
delay(750); | |
} else { | |
BlinkM_fadeToHSB( blinkmAddr, initialHue, saturationMax, brightnessMax); | |
} | |
} | |
// We expect the load to decrease as food is eaten. | |
// Shift the lamp hue from red to blue as pressureAve decreases to approach the trigger pressure. | |
// Argument sumSensorValues is the sum of current sensor readings. | |
void monitorEating(int sumSensorValues) { | |
// If the pressure has changed from last time by | |
// more than the threshold amount, update the hue. | |
if (abs(sumSensorValues - pressureAve) > pressureChangeThreshold) { | |
// Update the average pressure based on the current reading | |
queue.pop(); // pop the oldest reading from the queue | |
queue.push(sumSensorValues); // push this reading into the queue | |
pressureAve = calculateAveragePressure(); // Store the new average pressure | |
Serial.print("pressure (ave, min, max, trigger): "); | |
Serial.print(pressureAve); | |
Serial.print(", "); | |
Serial.print(pressureMin); | |
Serial.print(", "); | |
Serial.println(pressureMax); | |
// Map so that the lamp hue is red (0) | |
// when the sensor reading is at its minimum (load is at its maximum) | |
// and shifts to blue (160) as the sensor reading increases (load decreases). | |
setLampHue(map(pressureAve, pressureMin, pressureMax, hueMin, hueMax)); | |
} | |
} | |
// Store the new hue value and update lamp hue | |
void setLampHue(int value) { | |
int hueValue = constrain(value, hueMin, hueMax); // Don't allow a negative number or a number greater than 255. | |
// Store the new hue value. | |
lastKnownHueValue = hueValue; | |
// Set blinkms with hue; saturation and brightness are at maximum value. | |
BlinkM_fadeToHSB( blinkmAddr, hueValue, saturationMax, brightnessMax); | |
delay(50); // wait a bit because we don't need to go fast | |
Serial.print("Set hue to "); | |
Serial.println(hueValue); | |
} | |
// Turn the lamp on | |
void lampOn() { | |
// Set the pressureMax based on the initial state (platform is assumed to be empty) | |
int pressure = calibrateMinLoad(); | |
// Ensure the queue is empty first. | |
while (!queue.isEmpty()) { | |
queue.pop(); | |
} | |
// Then push the current pressure value into the queue. | |
for (int i = 0; i < queueSize; i++) { | |
queue.push(pressure); | |
} | |
// Turn on the lights | |
BlinkM_fadeToHSB( blinkmAddr, initialHue, saturationMax, brightnessMax); | |
lampState = 1; | |
// Reset the state of calibration for the maximum load | |
calibratedMaxLoad = 0; | |
Serial.println("Lamp is ON"); | |
} | |
// Turn the lamp off | |
void lampOff() { | |
// Pop all the sensor readings from the queue. | |
while (!queue.isEmpty ()) { | |
queue.pop(); | |
} | |
BlinkM_fadeToHSB( blinkmAddr, lastKnownHueValue, saturationMin, brightnessMin ); | |
lampState = 0; | |
Serial.println("Lamp is OFF"); | |
} | |
void setPressureMin(int value) { | |
pressureMin = constrain(value, pressureSensorCount * pressureSensorMin, pressureSensorCount * pressureSensorMax); | |
Serial.print("Setting floor resistance to "); | |
Serial.println(pressureMin); | |
} | |
void setPressureMax(int value) { | |
pressureMax = constrain(value, pressureSensorCount * pressureSensorMin, pressureSensorCount * pressureSensorMax); | |
Serial.print("Setting ceiling resistance to "); | |
Serial.println(pressureMax); | |
} | |
// Set the pressureMax based on the initial state (platform is assumed to be empty) | |
// and return the sum of sensor values. | |
int calibrateMinLoad() { | |
Serial.println("Calibrating for minimum load..."); | |
int sumSensorValues = 0; // variable to hold the sum the values from all the pressure sensors | |
for (int i = 0; i < pressureSensorCount; i++) { | |
int reading = analogRead(pressureSensorPin[i]); | |
Serial.print("pin "); | |
Serial.print(i); | |
Serial.print(": "); | |
Serial.println(reading); | |
sumSensorValues += reading; | |
} | |
// Set the summed sensor reading as the minimum pressure (meaning the load to monitor will be added afterward). | |
setPressureMax(sumSensorValues); | |
return sumSensorValues; | |
} | |
void calibrateMaxLoad() { | |
Serial.println("Calibrating for maximum load..."); | |
int sumSensorValues = 0; // variable to hold the sum the values from all the pressure sensors | |
// Sum the current sensor readings. | |
// Set this value as the minimum pressure, | |
// thus assuming the sum of sensor readings will increase over time (meaning the load will decrease over time). | |
for (int i = 0; i < pressureSensorCount; i++) { | |
sumSensorValues += analogRead(pressureSensorPin[i]); | |
} | |
setPressureMin(sumSensorValues); | |
Serial.print("pressureMin = "); | |
Serial.println(pressureMin); | |
Serial.print("pressureMax = "); | |
Serial.println(pressureMax); | |
// Map so that the lamp hue is red (0) | |
// when the sensor reading is at its minimum (load is at its maximum) | |
// and shifts to blue (160) as the sensor reading increases (load decreases). | |
setLampHue(map(sumSensorValues, pressureMin, pressureMax, hueMin, hueMax)); | |
calibratedMaxLoad = 1; | |
} | |
// Calculate the average of the summed sensor values in the queue | |
int calculateAveragePressure() { | |
/*Serial.println("Calculating average pressure..."); | |
Serial.print("queue items: "); | |
Serial.println(queue.count());*/ | |
if (queue.count() > 0) { | |
int sum = 0; | |
int size = 0; | |
QueueArray<int> tempQueue; | |
tempQueue.setPrinter(Serial); | |
// Sum the values currently stored in queue. | |
while (!queue.isEmpty()) { | |
int value = queue.pop(); | |
sum += value; | |
size++; | |
tempQueue.push(value); | |
/*Serial.println(value); | |
Serial.print("queue items: "); | |
Serial.println(queue.count());*/ | |
} | |
while(!tempQueue.isEmpty()) { | |
queue.push(tempQueue.pop()); | |
} | |
//Serial.print("Average pressure = "); | |
//Serial.println(sum/size); | |
return sum/size; // We lose fractional amounts in this operation since the arguments are ints, but who cares? This ain't rocket science. | |
} | |
else { | |
//Serial.print("Average pressure = 0"); | |
return 0; // If there are no values to be averaged, return 0. | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment