Skip to content

Instantly share code, notes, and snippets.

@corinnas
Created December 7, 2010 01:47
Show Gist options
  • Save corinnas/731342 to your computer and use it in GitHub Desktop.
Save corinnas/731342 to your computer and use it in GitHub Desktop.
/* 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