/*Pier Piling Pivot Arduino control program
by Earl Stirling for the explOratorium
installed on Fort Mason Piers
*/
/*
version 0.813
07/jan/2009
*/
/*This is a state machine.
This program counts on tight(ish) loops with no stalling.
I recommend using anything that stalls, the functions delay() or delayMicroseconds(),
they’re unnecessary, and you might miss a timeout
*/
/*
*/
//These are the states that the machine can be in.
//Entries in this enum should match the state_table pointer.
enum
{
WTG_FOR_BUTTONPRESS = 0,
ASCENDING,
UP,
DESCENDING,
DOWN,
ERROR,
curr_state_LIMIT
}
curr_state, prev_state = WTG_FOR_BUTTONPRESS;
//This table contains a pointer to the function to call in each state.
//These must match the enums for curr_state
void (*state_table[curr_state_LIMIT])() =
{
WtgForButtonpress,
Ascending,
Up,
Descending,
Down,
Error
};
//these constant defines are the correct place to change timeout values, pin values, etc.
#define KEEPUPTIME 120 //should be 120, change before deployment // timeout in seconds to keep the piling up
#define HOLDDOWNTIME 4 // time in seconds to keep the piling down before next ascent
#define MOVEMENTTIME 60 // time in seconds for maximum time to move the piling
#define BUTTONPINNUMBER 2 //the pin that the button is attached to
#define UPPERLIMITPINNUMBER 3 //the pin that the upper limit switch is attached to
#define LOWERLIMITPINNUMBER 4 //the pin that the lower limit switch is attached to
#define INSECTORPINNUMBER 5 //the pin for detecting that the mechanism is in normal operating range
#define RAISEMOTORPINNUMBER 6 //the pin for the relay to raise the piling
#define LOWERMOTORPINNUMBER 7 //the pin for the relay to lower the piling
//#define MOTORBRAKEPINNUMBER 8 //the pin for the relay to release the motor brake
#define ledPin 13 //the pin attached to the LED
long StartMovementTime;
int error_code, spinner = 0, thistime = 0;
//Limit switch values
enum {
MADE,
NOTMADE}
UpperLimitSwitch,
LowerLimitSwitch,
InSectorSensor;
//UI button value
enum {
PRESSED, NOTPRESSED}
Button;
//MOTOR CONTACTS!
//which motor contacts you’re calling
typedef enum {
RAISE, LOWER}
Motor_Contacts;
#define RAISE RAISEMOTORPINNUMBER
#define LOWER LOWERMOTORPINNUMBER
//#define brake MOTORBRAKEPINNUMBER
//motor actions you’re calling for
#define OFF 0
#define ON 1
//TIMERS!
//Timer values (should make this an array of type timertypes)
//int UpCountdownTimerValue, HOLDDOWNTIMERValue, ReversalCounterValue;
//TimerState UpCountdownTimer, HoldDownTimer;
//Actions you can call for the Timers
typedef enum {
START, RESET, EXPIRE, CHECK}
Timer_Actions;
#define START 0
#define RESET 1
#define EXPIRE 2
#define CHECK 3
//enumeration of the types of timers
typedef enum {
HOLDDOWN, KEEPUP, MOVEMENT, TIMERS_LIMIT}
Timers_new;
#define HOLDDOWN 0
#define KEEPUP 1
#define MOVEMENT 2
//states in which the timers can exist
typedef enum {
EXPIRED, CURRENT}
TimerState;
#define EXPIRED 0
#define CURRENT 1
void setup()
{
Serial.begin(9600); //setup serial output port
delay(3000);
//Send a string of CTRL-R’s to reset the LCD to 9600 baud
// for (int i=5000; i != 0 ; i–) {Serial.print(0×12,BYTE); };
// Initialize counters, flash LED’s, turn off outputs
//Initialize LCD display
//first set to 20 characters wide
Serial.print(0×7C,BYTE);
Serial.print(0×03,BYTE);
delay(128);
//then set to 4 lines long
Serial.print(0×7C,BYTE);
Serial.print(0×05,BYTE);
delay(128);
//clear display
Serial.print(0xFE,BYTE);
Serial.print(0×01,BYTE);
delay(128);
//turn display on
Serial.print(0xFE,BYTE);
Serial.print(0×0C,BYTE);
delay(128);
/*
//Set up splash screen for LCD
lcd_cursor(1,1);
Serial.print(”Pier Piling Pivot”);
Serial.print(”version 0.81″);
//delay(128);
//now save the current screen as the splash to the LCD’s NVRAM
Serial.print(0×7C,BYTE);
Serial.print(0×10,BYTE);
//delay(128);
*/
lcd_cursor(1,1);
Serial.print(”Starting setup “);
pinMode(BUTTONPINNUMBER, INPUT);
pinMode(UPPERLIMITPINNUMBER, INPUT);
pinMode(LOWERLIMITPINNUMBER, INPUT);
pinMode(INSECTORPINNUMBER, INPUT);
pinMode(RAISEMOTORPINNUMBER, OUTPUT);
pinMode(LOWERMOTORPINNUMBER, OUTPUT);
//pinMode(MOTORBRAKEPINNUMBER, OUTPUT);
pinMode(ledPin, OUTPUT);
MotorContact (LOWER, OFF);
MotorContact (RAISE, OFF);
lcd_cursor(1,1);
Serial.print(”Done with setup “);
};
// * * Begin State Functions * *
void Down()
{
/* Piling is down, wait for the hold down timeout, then
transition to waiting for someone to press the button.*/
poll();
for (int i=(HOLDDOWNTIME - 1); i != 0 ; i–)
{
//delay(128);
//move to 0,0
lcd_cursor(2,1);
Serial.print(”Hold Down: “);
Serial.print(i);
Serial.print(” “);
}
curr_state = WTG_FOR_BUTTONPRESS;
}
void Error()
{
//digitalWrite (brake,LOW);
lcd_cursor(2,1);
Serial.print(”Error “);Serial.print(error_code);Serial.print(” “);
switch (error_code)
{
case 2 : lcd_cursor(4,1);Serial.print(”Movement Sector “); break;
case 1 : lcd_cursor(4,1);Serial.print(”Movement Timeout “); break;
};
MotorContact (LOWER, OFF);
MotorContact (RAISE, OFF);
for (int i=error_code; i != 0 ; i–)
{
digitalWrite(ledPin, HIGH); // sets the LED on
delay(1000); // waits for a second
digitalWrite(ledPin, LOW); // sets the LED off
delay(1000); // waits for a second
};
}
void WtgForButtonpress()
{
/* Hang around waiting for some curious kid to press the button. */
//Serial.print(”WtgForButtonpress”);
poll();
if ((InSectorSensor == MADE) and (Button == PRESSED))
{
MotorContact (RAISE,ON);
StartMovementTime = millis();
//lcd_cursor(2,1);Serial.print(”Start Movement Time (sec)”);Serial.print(StartMovementTime/1000);
curr_state = ASCENDING;
}
}
void Ascending()
{
// When the piling reaches the top, stop raising it.
//Serial.print(”Ascending”);
poll();
MovementErrorCheck();
if ((curr_state != ERROR) and (UpperLimitSwitch == MADE))
{
MotorContact(RAISE,OFF);
curr_state = UP;
}
}
void Descending()
{
// When the piling reaches the bottom, stop lowering it.
//Serial.print(”Descending”);
poll();
MovementErrorCheck();
if ((curr_state != ERROR) and (LowerLimitSwitch == MADE))
{
MotorContact(LOWER,OFF);
curr_state = DOWN;
}
}
void Up()
{
/* If the timer expires or the button is pressed, lower
the piling (check that lower switch isn’t hit) .*/
//Serial.print(”Up”);
poll();
for (int i=(KEEPUPTIME - 1); i != 0 ; i–)
{
//delay(128);
lcd_cursor(2,1);Serial.print(”Keep Up: “);
Serial.print(i);
}
if (InSectorSensor == MADE)
{
MotorContact (LOWER,ON);
StartMovementTime = millis();
//lcd_cursor(2,1);Serial.print(”Start Movement Time (sec)”);Serial.print(StartMovementTime/1000);
curr_state = DESCENDING;
}
}
// * * End of State Functions * *
void poll()
{
thistime = (millis()/1000) % 4;
if (spinner != thistime)
{
lcd_cursor(1,20);
spinner = thistime;
//Serial.println(spinner);
switch (spinner)
{
case 0:
//print
Serial.print(”>”);
break;
case 1:
//print an Up arrow
Serial.print(”v”);
break;
case 2:
Serial.print(”<”);
break;
case 3:
Serial.print(”^”);
break;
}
};
if (prev_state != curr_state)
{
lcd_cursor(1,1);
Serial.print(” “);
lcd_cursor(1,1);
Serial.print(curr_state);
prev_state = curr_state;
}
if (digitalRead(BUTTONPINNUMBER) == HIGH)
{
if (Button != PRESSED) {lcd_cursor(3,1);Serial.print(”Button is PRESSED “);}
Button = PRESSED;
}
else
{
if (Button != NOTPRESSED) {lcd_cursor(3,1);Serial.print(”Button is NOT-PRESSED “);}
Button = NOTPRESSED;
//Serial.print(”Button is NOTPRESSED”);
}
if (digitalRead(UPPERLIMITPINNUMBER) == HIGH)
{
if (UpperLimitSwitch != MADE) {lcd_cursor(3,1);Serial.print(”Upper Limit MADE “);}
UpperLimitSwitch = MADE;
}
else {
if (UpperLimitSwitch != NOTMADE) {lcd_cursor(3,1);Serial.print(”Upper Limit NOT-MADE “);}
UpperLimitSwitch = NOTMADE;
//Serial.print(”UpperLimitSwitch is NOTMADE”);
}
if (digitalRead(LOWERLIMITPINNUMBER) == HIGH)
{
if (LowerLimitSwitch != MADE) {lcd_cursor(3,1);Serial.print(”Lower Limit MADE “);}
LowerLimitSwitch = MADE;
}
else {
if (LowerLimitSwitch != NOTMADE) {lcd_cursor(3,1);Serial.print(”Lower Limit NOT-MADE “);}
LowerLimitSwitch = NOTMADE;
//Serial.print(”LowerLimitSwitch is NOTMADE”);
}
//Serial.print(”insectorpinnumber is “);Serial.print(digitalRead(INSECTORPINNUMBER));
if (digitalRead(INSECTORPINNUMBER) == HIGH)
{
if (InSectorSensor != MADE) {lcd_cursor(3,1);Serial.print(”In Sector MADE “);}
InSectorSensor = MADE;
}
else
{
if (InSectorSensor != NOTMADE) {lcd_cursor(3,1);Serial.print(”In Sector NOTMADE “);}
InSectorSensor = NOTMADE;
}
}
//** Begin Service Functions & Procedures**
//function call to twiddle the contacts
void MotorContact (int contact, int action)
{
//Serial.print(”Setting Motor Contact “); Serial.print(contact);
if (action == ON)
{//digitalWrite (brake,HIGH);
digitalWrite (contact,HIGH);
//Serial.print(” HIGH”);
}
else
{//digitalWrite (brake,LOW);
digitalWrite (contact,LOW);
//Serial.print(” LOW”);
}
};
void MovementErrorCheck()
/*function call to check for errors while moving
will change state to ERROR if there is a movement error,
and throw a code to serial output */
{
//Serial.print((millis()-StartMovementTime)/1000);
if (((millis()-StartMovementTime)/1000) > MOVEMENTTIME)
{
curr_state = ERROR;
error_code = 1;
lcd_cursor(2,1);Serial.print(”Err Movement Timeout”);
};
if (InSectorSensor == NOTMADE)
{
curr_state = ERROR;
error_code = 2;
lcd_cursor(2,1);Serial.print(”Err Out of Range “);
}
};
void lcd_cursor(int row, int column)
{
int cursor_position, offset;
switch (row)
{
case 1:
offset = 0;
break;
case 2:
offset = 64;
break;
case 3:
offset = 20;
break;
case 4:
offset = 84;
break;
}
cursor_position=offset+column+127;
Serial.print(254,BYTE);
Serial.print(cursor_position,BYTE);
delay(128);
};
// * * Main Loop (”Booooorrrrring!!!!”) * *
void loop()
{
//lcd_cursor(1,1);Serial.print(”Starting loop “);
/* The heart of the state machine is this one loop. The function
corresponding to the current state is called once per iteration. */
while (1)
{
state_table[curr_state]();
}
};