PROBLEM STATEMENT:

To design and assemble a mechanism that can sort 3 wooden cubes (4cm x 4cm x 4cm) labeled by 2 magnetic markers starting in random side by side positions by utilizing an Arduino, Hall effect sensors, motors, and an RGB sensor, without the help of external interference.

FUNCTIONAL REQUIREMENTS:

  • The design must be able to move the wooden blocks.
  • The design must be able to recognize which block is which.
  • The design must be able to rearrange the blocks into their final sorted positions.

CONSTRAINTS:

  • The design must be fully automatic. Once the mechanism starts, there cannot be any interactions with it in any way.
  • The design must provide a 12cm x 4 cm area to receive the cubes.
  • The design must fit on a table 5ft. in diameter.
  • The design must be made of only pre-approved material and not be made of wood, metal, or LEGO components unless stated otherwise by the instructors.
  • The design must utilize the Arduino, motors, and the Hall effect sensors.

DEMO VIDEO:

The delays were set to 0 and the motor speeds increased to 12rpm.

CODE:

#include <Stepper.h>
#define GLOBAL_DELAY 50
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS       = Adafruit_MotorShield();

// Connect a stepper motor with 200 steps per revolution (1.8 degree) to motor port #2 (M3 and M4)
Adafruit_StepperMotor *rotor    = AFMS.getStepper(200, 1);
Adafruit_StepperMotor *piston   = AFMS.getStepper(200, 2);

///////////////////////////////////////
// CONSTANTS
namespace State {
    // These Values should be freely changeable, so long as no two values are equal
    enum Value {
        Unknown     = -1,
        Empty       = 0,
        Block1      = 1,
        Block2      = 2,
        Block3      = 3,
    };
}

const int BLOCK_COUNT = 3;          //< The number of slot positions
const int DELAY = 150;
const int SENSOR_DELAY = 100;
const int sensor1 = 13;
const int sensor2 = 12;

const int SORT_123[] = {
    State::Block1,
    State::Block2,
    State::Block3,
};

const int SORT_321[] = {
    State::Block3,
    State::Block2,
    State::Block1,
};

///////////////////////////////////////
// GLOBAL STATE
const int *sortOrder    = SORT_123; //< The sort order we are going to use
int position            = 0;        //< The reading head position (zero-based)
int nextIndex           = 0;        //< The next index to look for (zero-based)
State::Value state[BLOCK_COUNT] = { //< The state of the slots
    State::Unknown,
    State::Unknown,
    State::Unknown,
};

///////////////////////////////////////

void scanState() {
    Serial.println("Scanning");
    delay(DELAY);
    if (state[position] != State::Unknown) {
        // Do nothing; we already know the current state of this slot
        return;
    }

    // TODO: Replace this with the scanner code
    delay(SENSOR_DELAY);
    state[position] = (State::Value)(3 - (int(!digitalRead(sensor1)) + int(!digitalRead(sensor2)))); // check sensors and replace the state position and delete that line
    delay(SENSOR_DELAY);
}

void moveLeft(int count) {
    Serial.println ("Moving Left");
    delay(DELAY);
    // NOTE: Does no error checking; moves the rotor regardless of current position
    const int AMOUNT = 58; // TODO: Find correct AMOUNT
    rotor->step(AMOUNT * count, BACKWARD, SINGLE); // may have to change single

    // Update the position
    position -= count;
}

void moveRight(int count) {
    Serial.println("Moving Right");
    delay(DELAY);
    // NOTE: Does no error checking; moves the rotor regardless of current position
    const int AMOUNT = 58; // TODO: Find correct AMOUNT
    rotor->step(AMOUNT * count, FORWARD, SINGLE);

    // Update the position
    position += count;
}

void activatePiston() {
    Serial.println("Piston");
    delay(DELAY);
    const int AMOUNT = 130; // TODO: Find correct AMOUNT
    piston->step(AMOUNT, FORWARD, SINGLE);
    piston->step(AMOUNT, BACKWARD, SINGLE);

    // Update the state
    state[position] = State::Empty;

    // Update to search for the next block in the list
    ++nextIndex;
}

bool isNext(int position) {
    // TODO: This can be done more elegantly to handle additional cases, but should work fine for our simple case
    // eg: if the blocks are all block 2 versions, this will fail
    return state[position] == sortOrder[nextIndex];
}

// Search to the left of the current position and check if the next block has already been scanned
// Moves to that position, and pistons out the block if found
// @return true if the next block was found, false otherwise
bool searchLeft() {
    // Loop through the stored array to find if the next block has already been scanned
    for (int i = 0; i < position; ++i) {
        if (isNext(i)) {
            moveLeft(position - i);
            activatePiston();
            return true;
        }
    }

    return false;
}

///////////////////////////////////////

void setup() {
    Serial.begin(9600); // Set up Serial library at 9600 bps
    Serial.println("Starting...");

    AFMS.begin(); // Create with the default frequency 1.6KHz
    pinMode(sensor1, INPUT);
    pinMode(sensor2, INPUT);
    rotor->setSpeed(10); // 10 rpm
    piston->setSpeed(10); // 10 rpm

    // TODO: Choose the sort order from an RGB sensor
    sortOrder = SORT_123;
}

void loop() {
    while (nextIndex < BLOCK_COUNT) {
        // Scan the current position if we don't already know what's in it
        scanState();

        if (!isNext(position)) {
            // If this is not what we are looking for, move to the next one
            // NOTE: We are always filling in the array from left to right, so this is always the correct direction
            moveRight(1);
        } else {
            // Push out the current block
            activatePiston();

            // Check if the next block has already been scanned
            while (searchLeft()) {
                // `searchLeft` searches, backs up, and pistons for us
                // So do nothing
            }
        }
    }

    if (nextIndex == BLOCK_COUNT) {
        Serial.println("Complete!");

        // Prevent outputting "Complete!" more than once
        ++nextIndex;
    }
}

TEAM MEMBERS

  • Vlad Samonin
  • Andrew Vandendorpe
  • Brayden Falloon
  • Kenny Le
  • Lyle Cabutaje