Hi folks,
The project required a menu driven LCD display to control 4 motors, so I used Hunt The Wumpus as a code base. The project is working well, so doesn't require debugging, but if there was a better way of doing things, please let me know!
To give a little background on the project: it's used to control 4x 12V/30A 2000LB ATV ($45 each, super cheap!) winches that are mounted above my garage door to lift my kayaks up and down for storage. I needed to add additional logic to sense wether the garage door was up or down. I didn't want someone playing with it and putting down a kayak while the garage door was up.
This is my first time using C++ or an Arduino and was hoping to get a better understanding of what this particular void is doing; the sketch does not compile without it.
void (*state)() = NULL;
void (*last_state)() = NULL;
Complete Sketch:
// -------------------------------------------------------------------------------
// An Arduino sketch that controls 4 hoists connected to the Arduino Digital PINs:
// Each hoist has a H bridge motor controller attached to it with an ENABLE PIN
// as well as UP and DOWN PINs. All hoists share the UP and DOWN PINs and are
// selected by enabling a specific hoist.
//
//
//
// PIN 6 - Garage Door 2
// PIN 7 - Garage Door 1
// PIN 8 - Hoist 1 Enable
// PIN 9 - Hoist 2 Enable
// PIN 10 - Hoist 3 Enable
// PIN 11 - Hoist 4 Enable
// PIN 12 - UP
// PIN 13 - DOWN
// --------------------------------------------------------------------------------
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>
const int sleep_time = 20000;
const int hoist_up = 13;
const int hoist_down = 12;
const int hoist[] = {8, 9, 10, 11};
const int door[] = {7, 6};
void (*state)() = NULL;
void (*last_state)() = NULL;
unsigned long last_state_change_time;
unsigned long time;
const uint8_t menu_col[4][2] = { {0, 3},
{4, 7},
{8, 11},
{12, 15} };
uint8_t selected_menu_idx;
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
enum BackLightColor { OFF=0x0, RED=0x1, YELLOW=0x3, GREEN=0x2, TEAL=0x6, BLUE=0x4, VIOLET=0x5, WHITE=0x7 };
uint8_t clicked_buttons;
boolean locked;
void setup() {
Serial.begin(9600);
delay(2000);
pinMode(hoist_up, OUTPUT);
pinMode(hoist_down, OUTPUT);
pinMode(hoist[0], OUTPUT);
pinMode(hoist[1], OUTPUT);
pinMode(hoist[2], OUTPUT);
pinMode(hoist[3], OUTPUT);
pinMode(door[0], INPUT);
pinMode(door[1], INPUT);
lcd.begin(16, 2);
state = begin_welcome;
selected_menu_idx = 0;
locked=false;
}
void loop() {
time = millis();
if (last_state != state) {
last_state = state;
last_state_change_time = time;
}
check_sleep();
check_door();
read_button_clicks();
state();
delay(10);
}
void check_sleep() {
static unsigned long last_button_time;
static boolean asleep = false;
if ( clicked_buttons ) {
last_button_time = time;
}
if ( time - last_button_time >= sleep_time ) {
lcd.clear();
lcd.setBacklight(OFF);
asleep = true;
digitalWrite(hoist[0], LOW);
digitalWrite(hoist[1], LOW);
digitalWrite(hoist[2], LOW);
digitalWrite(hoist[3], LOW);
digitalWrite(hoist_up, LOW);
digitalWrite(hoist_down, LOW);
state = check_sleep;
} else if ( (time - last_button_time < sleep_time ) & (asleep == true) ) {
asleep = false;
state = begin_welcome;
}
}
void check_door() {
if ( ( digitalRead(door[0]) == LOW) & (locked == true) ) {
state = blink_close;
} else if ( ( digitalRead(door[0]) == LOW) & (locked == false) ) {
digitalWrite(hoist[0], LOW);
digitalWrite(hoist[1], LOW);
digitalWrite(hoist[2], LOW);
digitalWrite(hoist[3], LOW);
digitalWrite(hoist_up, LOW);
digitalWrite(hoist_down, LOW);
lcd.clear();
lcd.setBacklight(RED);
lcd.setCursor(0, 0);
lcd.print(F(" Garage Door Up"));
locked = true;
state = blink_close;
} else if ( ( digitalRead(door[0]) == HIGH) & (locked == true) ) {
locked = false;
state = begin_welcome;
}
}
void blink_close() {
static boolean blink = true;
static unsigned long last_blink_time;
if (time - last_blink_time >= 1000) {
lcd.setCursor(1, 1);
if (blink) {
lcd.write(0x7E);
lcd.print(F(" CLOSE DOOR "));
lcd.write(0x7F);
} else {
lcd.print(F(" "));
}
blink = !blink;
last_blink_time = time;
}
}
void read_button_clicks() {
static uint8_t last_buttons = 0;
uint8_t buttons = lcd.readButtons();
clicked_buttons = (last_buttons ^ buttons) & (~buttons);
last_buttons = buttons;
}
void clear_current_menu() {
lcd.setCursor(menu_col[selected_menu_idx][0], 1);
lcd.print(' ');
lcd.setCursor(menu_col[selected_menu_idx][1], 1);
lcd.print(' ');
}
void highlight_current_menu() {
lcd.setCursor(menu_col[selected_menu_idx][0], 1);
lcd.write(0x7E);
lcd.setCursor(menu_col[selected_menu_idx][1], 1);
lcd.write(0x7F);
}
//! Check for left and right button clicks and update the menu index as needed.
void handle_menu() {
if (clicked_buttons & BUTTON_LEFT) {
selected_menu_idx = (selected_menu_idx > 0) ? selected_menu_idx - 1 : 3;
} else if (clicked_buttons & BUTTON_RIGHT) {
selected_menu_idx = (selected_menu_idx < 3) ? selected_menu_idx + 1 : 0;
}
}
void begin_welcome() {
lcd.clear();
lcd.setBacklight(TEAL);
lcd.print(F("Hoist Controller"));
state = blink_select;
}
void blink_select() {
static boolean blink = true;
static unsigned long last_blink_time;
if (time - last_blink_time >= 1000) {
lcd.setCursor(0, 1);
if (blink) {
lcd.write(0x7E);
lcd.print(F(" PRESS SELECT "));
lcd.write(0x7F);
} else {
lcd.print(F(" "));
}
blink = !blink;
last_blink_time = time;
}
if (clicked_buttons & BUTTON_SELECT) {
state = start_menu;
}
}
void start_menu() {
digitalWrite(hoist[0], LOW);
digitalWrite(hoist[1], LOW);
digitalWrite(hoist[2], LOW);
digitalWrite(hoist[3], LOW);
lcd.clear();
lcd.setBacklight(TEAL);
lcd.setCursor(0, 0);
lcd.print(F("Select Hoist #"));
lcd.setCursor(0, 1);
lcd.print(F(" 01 02 03 04"));
// selected_menu_idx = 0;
lcd.setCursor(menu_col[selected_menu_idx][0], 1);
lcd.write(0x7E);
// lcd.print('[');
lcd.setCursor(menu_col[selected_menu_idx][1], 1);
// lcd.print(']');
lcd.write(0x7F);
state = begin_input_move;
}
void begin_input_move() {
lcd.setCursor(0, 0);
lcd.print(F("Select Hoist # "));
lcd.print(' ');
state = input_move;
}
void input_move() {
if (clicked_buttons) {
clear_current_menu();
if (clicked_buttons & BUTTON_SELECT) {
Serial.println("Goint to move_hoist from input_move");
state = begin_move_hoist;
} else {
handle_menu();
}
highlight_current_menu();
}
}
void begin_move_hoist() {
// Enable Selected Hoist
digitalWrite(hoist[selected_menu_idx], HIGH);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(F("Press UP or DOWN"));
lcd.setCursor(0, 1);
lcd.print(F("To Move Hoist"));
state = move_hoist;
}
void move_hoist() {
uint8_t buttons = lcd.readButtons();
if ( clicked_buttons ) {
if ( clicked_buttons & BUTTON_SELECT) {
state = start_menu;
} else
digitalWrite(hoist_up, LOW);
digitalWrite(hoist_down, LOW);
}
if (buttons & BUTTON_UP) {
digitalWrite(hoist_down, LOW);
digitalWrite(hoist_up, HIGH);
} else if ( buttons & BUTTON_DOWN) {
digitalWrite(hoist_up, LOW);
digitalWrite(hoist_down, HIGH);
}
}