Last active
April 12, 2023 17:34
-
-
Save rchrd2/81c371053fdf74798661 to your computer and use it in GitHub Desktop.
Toaster_AI_v2.pd
This file contains hidden or 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
// Toaster AI v2 | |
// by Richard Caceres | |
// 11/12/09 | |
// | |
// Safety Precautions that could be put in place | |
// Overheating | |
// Description: Precautions to prevent the toaster from being on too long. | |
// Status: A solution is put in place where the toaster can only be on N seconds at a time. | |
// There is also a cool down period. | |
// | |
// Motor position: | |
// Description: Precautions to prevent the motor from over-pull or over-pusing | |
// Status: There is a max motor time "motorMaxOnTime". If this time is reached, the motor | |
// and relay are disabled. This is a worst case precaution. | |
// When the Arduino starts up, the motor is moved to the top switch. | |
// | |
// Strange sensor behavior: | |
// Description: Precautions to account for strange sensor behavior or misreadings. | |
// Status: There are no measures put in place. This falls back to the overheating precaution. | |
// | |
// Electric Shock or electricution: | |
// Description: Accidental short circuits or unintentional flow of electricity. | |
// Status: The toaster was measured with a multimeter. Insulation should be considered. | |
// | |
// PIN DEFINITIONS | |
////////////////////////////////////////////////////// | |
// Motor pins defined as: +motorPin1 -motoPin2 pulls lever down | |
const int motorPin1 = 18; | |
const int motorPin2 = 19; | |
// Lever Switches | |
const int leverSwitchTopPin = 2; | |
const int leverSwitchBottomPin = 3; | |
// Range Finder Rx Tx pins | |
const int rangeFinderPingPin1 = 8; | |
const int rangeFinderInputPin1 = 9; | |
const int rangeFinderPingPin2 = 11; | |
const int rangeFinderInputPin2 = 10; | |
// Relay pin | |
const int relayPin = 6; | |
// PROGRAM VARIABLES | |
////////////////////////////////////////////////////// | |
// init variables | |
boolean hasBeenInit = false; | |
// program variables | |
boolean selfDestruct = false; | |
// debug variables | |
boolean enableDebug = true; | |
long debugRate = 200; | |
long lastDebug = 0; | |
// Motor variables | |
int motorDirection; // 0 = off, 1 = pull lever down, 2 = push lever back up | |
unsigned long motorPullTimeStart; | |
unsigned long motorPullTimePassed; | |
unsigned long motorPushTimeStart; | |
unsigned long motorPushTimePassed; | |
// motor beliefs? | |
// max motor on time. | |
// note that this should probably be more sophisticated. | |
long motorMaxOnTime = 12000; // 12 seconds | |
// Switch Variables | |
boolean leverSwitchTop = false; | |
boolean leverSwitchTopJustPressed = false; | |
boolean leverSwitchTopJustReleased = false; | |
long leverSwitchTopPressTime; | |
long leverSwitchTopTimePressed; | |
boolean leverSwitchBottom = false; | |
boolean leverSwitchBottomJustPressed = false; | |
boolean leverSwitchBottomJustReleased = false; | |
long leverSwitchBottomPressTime; | |
long leverSwitchBottomTimePressed; | |
// Rangefinder Variables | |
long rangeFinder1Dur; | |
long rangeFinder1DistanceInches; | |
long rangeFinder1Pressed; | |
long rangeFinder1JustPressed; | |
unsigned long rangeFinder1JustPressedTime = 0; | |
long rangeFinder1JustReleased; | |
long rangeFinder2Dur; | |
long rangeFinder2DistanceInches; | |
long rangeFinder2Pressed; | |
long rangeFinder2JustPressed; | |
unsigned long rangeFinder2JustPressedTime = 0; | |
long rangeFinder2JustReleased; | |
long rangeFinderThresholdInches = 5; | |
unsigned long rangeFinderMaxOnTime = 1200000 ; // 20 minutes. THIS IS NOT USED | |
// Relay Variables | |
long relayOn = false; | |
unsigned long relayOnTimeStart; // this is not used | |
unsigned long relayOnTimePassed; // this is not used | |
unsigned long relayMaxOnTime = 20000; // this is not used | |
unsigned long relayHeatValue = 0; | |
unsigned long relayMaxHeatValue = 120000; // ~ 120 seconds is the max time the toaster should be on | |
double relayHeatDecreaseRate = 2.0; // this is a multiplier for how fast this heat value drops | |
unsigned long relayMaxReachedTime = 0; | |
unsigned long relayMaxReachedWaitTime = 20000; // 20 seconds COOL DOWN TIME / EJECT TIME | |
boolean relayMaxWasReached = false; | |
unsigned long relayLastUpdate = 0; | |
// SETUP | |
////////////////////////////////////////////////////// | |
void setup() | |
{ | |
if (enableDebug) Serial.begin(9600); | |
// Setup Pin Modes | |
pinMode (motorPin1, OUTPUT); | |
pinMode (motorPin2, OUTPUT); | |
pinMode (leverSwitchTopPin, INPUT); | |
pinMode (leverSwitchBottomPin, INPUT); | |
pinMode (rangeFinderPingPin1, OUTPUT); | |
pinMode (rangeFinderInputPin1, OUTPUT); | |
pinMode (rangeFinderPingPin2, INPUT); | |
pinMode (rangeFinderInputPin2, INPUT); | |
pinMode (relayPin, OUTPUT); | |
} | |
// RANGEFINDER FUNCTIONS | |
////////////////////////////////////////////////////// | |
long readDistanceDuration(int pingPin, int inputPin) | |
{ | |
// This function reads the ultrasonic rangefinder. | |
// It needs the pingPin and the inputPin (ie. TX, RX); | |
// The PING))) is triggered by a HIGH pulse of 2 or more microseconds. | |
// Give a short LOW pulse beforehand to ensure a clean HIGH pulse: | |
pinMode(pingPin, OUTPUT); | |
digitalWrite(pingPin, LOW); | |
delayMicroseconds(2); | |
digitalWrite(pingPin, HIGH); | |
delayMicroseconds(5); | |
digitalWrite(pingPin, LOW); | |
// read the sensor | |
pinMode(inputPin, INPUT); | |
return pulseIn(inputPin, HIGH); | |
} | |
void printDurationInches(long duration) | |
{ | |
// convert the time into a distance | |
long inches = microsecondsToInches(duration); | |
long cm = microsecondsToCentimeters(duration); | |
Serial.print(inches); | |
Serial.print(" in, "); | |
Serial.print(cm); | |
Serial.print(" cm"); | |
Serial.println(); | |
} | |
long microsecondsToInches(long microseconds) | |
{ | |
// According to Parallax's datasheet for the PING))), there are | |
// 73.746 microseconds per inch (i.e. sound travels at 1130 feet per | |
// second). This gives the distance travelled by the ping, outbound | |
// and return, so we divide by 2 to get the distance of the obstacle. | |
// See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf | |
return microseconds / 74 / 2; | |
} | |
long microsecondsToCentimeters(long microseconds) | |
{ | |
// The speed of sound is 340 m/s or 29 microseconds per centimeter. | |
// The ping travels out and back, so to find the distance of the | |
// object we take half of the distance travelled. | |
return microseconds / 29 / 2; | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
void initToaster() | |
{ | |
// This function makes sure the motor is at the top. | |
delay(1000); | |
long startTime = millis(); | |
boolean exitInit = false; | |
while ( exitInit == false ) | |
{ | |
Serial.println("initing"); | |
// Check if the top switch has been pressed | |
if ( digitalRead(leverSwitchTopPin) == HIGH ) | |
{ | |
exitInit = true; | |
} | |
// Check if the motor has been moving up too long. Immediately self destruct if it is not reached | |
else if ( millis() - startTime > motorMaxOnTime ) | |
{ | |
selfDestruct = true; | |
exitInit = true; | |
} | |
// set the motor to move up | |
setMotorDirection(2); | |
} | |
// turn off the relay | |
relayOn = false; | |
setRelayState(); | |
// stop the motor | |
setMotorDirection(0); | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
void updateSensorValues() | |
{ | |
// UPDATE SWITCHES | |
boolean prevLeverSwitchTop = leverSwitchTop; | |
leverSwitchTop = digitalRead(leverSwitchTopPin); | |
if (leverSwitchTop == true && prevLeverSwitchTop == false) | |
{ | |
leverSwitchTopJustPressed = true; | |
leverSwitchTopPressTime = millis(); | |
} | |
leverSwitchTopTimePressed = millis() - leverSwitchTopPressTime; | |
boolean prevLeverSwitchBottom = leverSwitchBottom; | |
leverSwitchBottom = digitalRead(leverSwitchBottomPin); | |
if (leverSwitchBottom == true && prevLeverSwitchTop == false) | |
{ | |
leverSwitchBottomJustPressed = false; | |
leverSwitchBottomPressTime = millis(); | |
} | |
leverSwitchBottomTimePressed = millis() - leverSwitchBottomPressTime; | |
// add justReleased here if you need it | |
// UPDATE RANGEFINDERS | |
// Range Finder 1 | |
// first reset the rangefinder variables, but keep track of previous state | |
long prevRangeFinder1Pressed = rangeFinder1Pressed; | |
rangeFinder1Pressed = false; | |
rangeFinder1JustPressed = false; | |
rangeFinder1JustReleased = false; | |
// read the new values | |
rangeFinder1Dur = readDistanceDuration(rangeFinderPingPin1, rangeFinderInputPin1); | |
rangeFinder1DistanceInches = microsecondsToInches(rangeFinder1Dur); | |
if (rangeFinder1DistanceInches < rangeFinderThresholdInches) | |
{ | |
rangeFinder1Pressed = true; | |
} | |
if (rangeFinder1Pressed == true && prevRangeFinder1Pressed == false) | |
{ | |
rangeFinder1JustPressed = true; | |
rangeFinder1JustPressedTime = millis(); | |
} | |
if (rangeFinder1Pressed == false && prevRangeFinder1Pressed == true) | |
{ | |
rangeFinder1JustReleased = true; | |
} | |
// Range Finder 2 | |
// first reset the rangefinder variables, but keep track of previous state | |
long prevRangeFinder2Pressed = rangeFinder2Pressed; | |
rangeFinder2Pressed = false; | |
rangeFinder2JustPressed = false; | |
rangeFinder2JustReleased = false; | |
// read the new values | |
rangeFinder2Dur = readDistanceDuration(rangeFinderPingPin2, rangeFinderInputPin2); | |
rangeFinder2DistanceInches = microsecondsToInches(rangeFinder2Dur); | |
if (rangeFinder2DistanceInches < rangeFinderThresholdInches) | |
{ | |
rangeFinder2Pressed = true; | |
} | |
if (rangeFinder2Pressed == true && prevRangeFinder2Pressed == false) | |
{ | |
rangeFinder2JustPressed = true; | |
rangeFinder1JustPressedTime = millis(); | |
} | |
if (rangeFinder2Pressed == false && prevRangeFinder2Pressed == true) | |
{ | |
rangeFinder2JustReleased = true; | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
void printDebugInformation() | |
{ | |
if (millis() - lastDebug > debugRate) | |
{ | |
lastDebug = millis(); | |
} | |
else | |
{ | |
return; | |
} | |
// new lines | |
Serial.println(); | |
Serial.println(); | |
Serial.println(); | |
Serial.println(); | |
Serial.println(); | |
// Rangefinder variables | |
Serial.print("rf: "); | |
Serial.print(rangeFinder1Pressed); | |
Serial.print(":"); | |
Serial.println(rangeFinder2Pressed); | |
Serial.print("motor:"); | |
Serial.print(motorDirection); | |
Serial.print(":PushTime="); | |
Serial.print(motorPushTimePassed); | |
Serial.print(":PullTime="); | |
Serial.print(motorPullTimePassed); | |
Serial.print(":MAX="); | |
Serial.println(motorMaxOnTime); | |
Serial.print("Relay: "); | |
Serial.print(relayOn); | |
Serial.print(":"); | |
Serial.print(relayHeatValue); | |
Serial.print(":"); | |
//Serial.print(relayOnTimePassed); | |
//Serial.print(":"); | |
Serial.print(relayMaxHeatValue); | |
Serial.print(":"); | |
if (relayMaxWasReached == true) Serial.println("MaxIsReached"); | |
else Serial.println("MaxNotReachedYet"); | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
void setMotorDirection(int motorDirection) | |
{ | |
boolean mPinVal1 = false; | |
boolean mPinVal2 = false; | |
switch (motorDirection) | |
{ | |
case 1: | |
mPinVal1 = true; | |
break; | |
case 2: | |
mPinVal2 = true; | |
break; | |
} | |
digitalWrite(motorPin1, mPinVal1); | |
digitalWrite(motorPin2, mPinVal2); | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
void updateMotorDirection() | |
{ | |
// This function uses the sensor data to determine if the motor should move in | |
// in a certain direction. | |
int prevMotorDirection = motorDirection; | |
motorDirection = 0; // 0 = off, 1 = pull lever down, 2 = push lever back up | |
// Large conditional statement to determine motor direction | |
if ( | |
rangeFinder1Pressed == true && rangeFinder2Pressed == true // both sensors are on | |
&& leverSwitchBottom == false // the motor has not reached the bottom | |
&& motorPullTimePassed < motorMaxOnTime // the motor has not been on too long | |
&& relayMaxWasReached == false // if we are cooling down, don't pull the lever anymore | |
) | |
{ | |
// Pull the lever down | |
motorDirection = 1; | |
} | |
// check for the realy max is true | |
else if | |
( | |
leverSwitchTop == false | |
&& motorPushTimePassed < motorMaxOnTime | |
&& relayMaxWasReached == true | |
) | |
{ | |
// Push the lever up | |
motorDirection = 2; | |
} | |
// else check if both hands are on the sensors | |
else if | |
( | |
leverSwitchTop == false | |
&& motorPushTimePassed < motorMaxOnTime | |
&& (rangeFinder1Pressed != true || rangeFinder2Pressed != true) | |
) | |
{ | |
// Push the lever up | |
motorDirection = 2; | |
} | |
// set timer data about motor | |
if (motorDirection != prevMotorDirection) | |
{ | |
motorPullTimePassed = 0; | |
motorPushTimePassed = 0; | |
motorPullTimeStart = millis(); | |
motorPushTimeStart = millis(); | |
switch(motorDirection) | |
{ | |
case 1 : | |
break; | |
case 2 : | |
break; | |
} | |
} | |
else | |
{ | |
switch(motorDirection) | |
{ | |
case 1 : | |
motorPullTimePassed = millis() - motorPullTimeStart; | |
break; | |
case 2 : | |
motorPushTimePassed = millis() - motorPushTimeStart; | |
break; | |
} | |
} | |
// check for self destruct | |
if (motorDirection == 1 && motorPullTimePassed > motorMaxOnTime) | |
{ | |
selfDestruct = true; | |
} | |
if (motorDirection == 2 && motorPushTimePassed > motorMaxOnTime) | |
{ | |
selfDestruct = true; | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
void updateRelay() | |
{ | |
boolean prevRelayOn = relayOn; | |
relayOn = false; | |
// check to see if we are in a cool off time. | |
if (relayMaxWasReached == true && (millis() - relayMaxReachedTime) > relayMaxReachedWaitTime) | |
{ | |
relayMaxWasReached = false; | |
} | |
if ( | |
leverSwitchBottom == true | |
&& relayHeatValue < relayMaxHeatValue | |
//&& relayOnTimePassed < relayMaxOnTime | |
&& relayMaxWasReached != true | |
) | |
{ | |
relayOn = true; | |
} | |
// update relay timer variables (these are not used right now) | |
if (relayOn == true && prevRelayOn == false) | |
{ | |
relayOnTimeStart = millis(); | |
} | |
else if (relayOn == false && prevRelayOn == true) | |
{ | |
relayOnTimePassed = 0; | |
} | |
// update the relay "heat" value | |
double relayDelta = (double)millis() / (double)relayLastUpdate; | |
// check to see if the max was reached | |
if (relayHeatValue > relayMaxHeatValue) | |
{ | |
relayMaxWasReached = true; | |
relayMaxReachedTime = millis(); | |
} | |
if (relayOn == true) | |
{ | |
relayHeatValue += (millis() - relayLastUpdate); | |
} | |
else if (relayHeatValue > 0) | |
{ | |
double decrement = ((millis() - relayLastUpdate) * relayHeatDecreaseRate); | |
if (relayHeatValue < decrement) | |
{ | |
relayHeatValue = 0; | |
} | |
else | |
{ | |
relayHeatValue -= decrement; | |
} | |
} | |
relayOnTimePassed = millis() - relayOnTimeStart; | |
relayLastUpdate = millis(); | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
void setRelayState() | |
{ | |
digitalWrite(relayPin, relayOn); | |
} | |
// MAIN PROGRAM | |
////////////////////////////////////////////////////// | |
void loop() | |
{ | |
if (hasBeenInit == false) | |
{ | |
initToaster(); | |
hasBeenInit = true; | |
} | |
if (selfDestruct == false) | |
{ | |
updateSensorValues(); | |
updateMotorDirection(); | |
setMotorDirection(motorDirection); // set the motor direction | |
updateRelay(); | |
setRelayState(); | |
} | |
else | |
{ | |
// commit suicide | |
setMotorDirection(0); | |
relayOn = false; | |
setRelayState(); | |
} | |
if (enableDebug) printDebugInformation(); | |
//delay(10); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment