Skip to content

Instantly share code, notes, and snippets.

@rchrd2
Last active April 12, 2023 17:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rchrd2/81c371053fdf74798661 to your computer and use it in GitHub Desktop.
Save rchrd2/81c371053fdf74798661 to your computer and use it in GitHub Desktop.
Toaster_AI_v2.pd
// 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