Final industrial end node software

Dependencies:   mbed

main.cpp

Committer:
jmckneel
Date:
2018-05-15
Revision:
0:3845f08fcd02
Child:
1:13d222b50e8e

File content as of revision 0:3845f08fcd02:

/* -----------------------------------------------------------------------------
    Authors: Jared McKneely, Kyle Gong
    Title: Industrial End Node Software v. 2.0.0
    Date: April 23rd, 2018
    
    Description:
    Controls a Skittle-sorting machine. Gathers status of Skittle receptacles
    from gateway, and updates the count upon Skittle detection. Ceases movement
    if a Skittle receptacle is found to be full.
    
----------------------------------------------------------------------------- */

// Libraries -------------------------------------------------------------------
#include "mbed.h"

// Macros ----------------------------------------------------------------------
#define PC_BAUD         (115200) // Baud rate for PC debug
#define XB_BAUD         (57600)  // Baud rate for XBee PRO 900HP communications
#define SCAN_TIME_MS    (10)     // Wait time for skittle scanning
#define TX              (PA_9)   // XBee transmit pin
#define RX              (PA_10)  // XBee receive pin
#define NODE_ID         (99)     // Industrial node ID
#define BUFFER_SIZE     (32)     // Size of XBee buffer

#define R_DISPENSE      (PB_2)   // Red skittle solenoid
#define O_DISPENSE      (PB_12)  // Orange skittle solenoid
#define Y_DISPENSE      (PA_4)   // Yellow skittle solenoid
#define G_DISPENSE      (PB_0)   // Green skittle solenoid
#define V_DISPENSE      (PC_1)   // Violet skittle solenoid

#define GENEVA_STEP     (D9)     // Geneva wheel stepper motor PWM pin (STEP)
#define GENEVA_PERIOD   (10)     // Period of PWM for geneva wheel stepper motor (ms)
#define GENEVA_PULSE    (1000)   // Pulse width of geneva wheel PWM for rotation (us)
#define GENEVA_STOP     (0)      // Stop pulse width

#define SLIDE_PERIOD    (20)     // Period of PWM for slide servo (ms)
#define SLIDE_PIN       (D3)     // Slide servo PWM pin

#define AMP_PIN         (PC_4)   // Ammeter pin
#define KITTY_CAT_EN    (D7)     // Kitty cat enable/power up pin
#define KITTY_CAT_PWM   (PA_13)  // Kitty cat PWM pin

// Hardware Parameters ---------------------------------------------------------
I2C i2c(I2C_SDA, I2C_SCL);               // Pins for I2C communication (SDA, SCL)
Serial pc(SERIAL_TX, SERIAL_RX);         // Used for debugging
Serial xb(TX, RX);                       // XBee PRO 900HP for communicating with gateway
PwmOut geneva_pwm(GENEVA_STEP);          // PWM for controlling the geneva wheel
PwmOut slide_pwm(SLIDE_PIN);             // PWM for controlling the slide servo
int sensor_addr = 41 << 1;               // RGB sensor I2C address
AnalogIn analog_value(AMP_PIN);          // Current measuring input

DigitalOut r_solenoid(R_DISPENSE);       // Red solenoid
DigitalOut o_solenoid(O_DISPENSE);       // Orange solenoid
DigitalOut y_solenoid(Y_DISPENSE);       // Yellow solenoid
DigitalOut g_solenoid(G_DISPENSE);       // Green solenoid
DigitalOut v_solenoid(V_DISPENSE);       // Violet solenoid

DigitalOut kitty_cat_en(KITTY_CAT_EN);   // Enabling pin for the unclogging motor
DigitalOut kitty_cat_pwm(KITTY_CAT_PWM); // Software PWM pin for the unclogging motor
Ticker kitty_cat_timer;                  // Creates even intervals

// Globals ---------------------------------------------------------------------
int C = 0;                    // Current level of clear
int R = 0;                    // Current level of red
int G = 0;                    // Current level of green
int B = 0;                    // Current level of blue

bool r_full = true;           // Is the red receptacle full?
bool o_full = true;           // Is the orange receptacle full?
bool y_full = true;           // Is the yellow receptacle full?
bool g_full = true;           // Is the green receptacle full?
bool v_full = true;           // Is the violet receptacle full?

int r_dispense = 0;           // Number of red skittles needing dispensing
int o_dispense = 0;           // Number of orange skittles needing dispensing
int y_dispense = 0;           // Number of yellow skittles needing dispensing
int g_dispense = 0;           // Number of green skittles needing dispensing
int v_dispense = 0;           // Number of violet skittles needing dispensing

bool rx_flag = false;         // Receive flag
bool geneva_spinning = false; // Geneva wheel flag
char xb_buffer[BUFFER_SIZE];  // Contains the XBee's receive buffer
int xb_index = 0;             // Position in the XBee buffer

// Optimal Skittle Color Parameters --------------------------------------------
uint16_t red_R = 3104;
uint16_t red_G = 1376;
uint16_t red_B = 1299;
uint16_t red_C = 5441;

uint16_t orange_R = 6586;
uint16_t orange_G = 2810;
uint16_t orange_B = 2014;
uint16_t orange_C = 11056;

uint16_t yellow_R = 7994;
uint16_t yellow_G = 6247;
uint16_t yellow_B = 2836;
uint16_t yellow_C = 16767;

uint16_t green_R = 2702;
uint16_t green_G = 4098;
uint16_t green_B = 2029;
uint16_t green_C = 8623;

uint16_t violet_R = 1331;
uint16_t violet_G = 1024;
uint16_t violet_B = 922;
uint16_t violet_C = 3148;

uint16_t empty_R = 648;
uint16_t empty_G = 652;
uint16_t empty_B = 572;
uint16_t empty_C = 1831;

uint16_t empty_dist = 0;
uint16_t red_dist = 0;
uint16_t orange_dist = 0;
uint16_t yellow_dist = 0;
uint16_t green_dist = 0;
uint16_t violet_dist = 0;

// Get Number (Helper) ---------------------------------------------------------
/* Returns the number corresponding to an ASCII character. */
int get_number(char c){
    if (c == '1'){
        return 1;    
    }
    if (c == '2'){
        return 2;    
    }
    if (c == '3'){
        return 3;    
    }
    if (c == '4'){
        return 4;    
    }
    if (c == '5'){
        return 5;    
    }
    if (c == '6'){
        return 6;    
    }
    if (c == '7'){
        return 7;    
    }
    if (c == '8'){
        return 8;    
    }
    if (c == '9'){
        return 9;    
    }
    return 0;
}

// Initialize RGB Sensor -------------------------------------------------------
/* Initializes the I2C comm registers on the Adafruit TCS34725. */
void init_RGB(void){
    // 1.) Connect to the color sensor and verify
    i2c.frequency(100000);
    char id_regval[1] = {146};
    char data[1] = {0};
    i2c.write(sensor_addr,id_regval,1, true);
    i2c.read(sensor_addr,data,1,false);
    
    // 2.) Initialize color sensor
    char timing_register[2] = {129,0};
    i2c.write(sensor_addr,timing_register,2,false);
    char control_register[2] = {143,0};
    i2c.write(sensor_addr,control_register,2,false);
    char enable_register[2] = {128,3};
    i2c.write(sensor_addr,enable_register,2,false);
}

// Read RGB --------------------------------------------------------------------
/* Reads the current color values from the RGB sensor, stores the values in the
   global variables clear, red, green, and blue. */
void read_RGB(void){
    // 1.) Read clear
    char clear_reg[1] = {148};
    char clear_data[2] = {0,0};
    i2c.write(sensor_addr,clear_reg,1, true);
    i2c.read(sensor_addr,clear_data,2, false);
    C = ((int)clear_data[1] << 8) | clear_data[0];
    
    // 2.) Read red
    char red_reg[1] = {150};
    char red_data[2] = {0,0};
    i2c.write(sensor_addr,red_reg,1, true);
    i2c.read(sensor_addr,red_data,2, false);
    R = ((int)red_data[1] << 8) | red_data[0];
    
    // 3.) Read green
    char green_reg[1] = {152};
    char green_data[2] = {0,0};
    i2c.write(sensor_addr,green_reg,1, true);
    i2c.read(sensor_addr,green_data,2, false);
    G = ((int)green_data[1] << 8) | green_data[0];
    
    // 4.) Read blue
    char blue_reg[1] = {154};
    char blue_data[2] = {0,0};
    i2c.write(sensor_addr,blue_reg,1, true);
    i2c.read(sensor_addr,blue_data,2, false);
    B = ((int)blue_data[1] << 8) | blue_data[0];
}

// Identify Color --------------------------------------------------------------
int identify_color(void){
    // 1.) Compute distances from each color using the clear computation
    empty_dist = abs((empty_R) - (R)) + abs((empty_G) - (G)) + abs((empty_B) - (B)) + abs((empty_C) - (C));
    red_dist = abs((red_R) - (R)) + abs((red_G) - (G)) + abs((red_B) - (B)) + abs((red_C) - (C));
    orange_dist = abs((orange_R) - (R)) + abs((orange_G) - (G)) + abs((orange_B) - (B)) + abs((orange_C) - (C));
    yellow_dist = abs((yellow_R) - (R)) + abs((yellow_G) - (G)) + abs((yellow_B) - (B)) + abs((yellow_C) - (C));
    green_dist = abs((green_R) - (R)) + abs((green_G) - (G)) + abs((green_B) - (B)) + abs((green_C) - (C));
    violet_dist = abs((violet_R) - (R)) + abs((violet_G) - (G)) + abs((violet_B) - (B)) + abs((violet_C) - (C));;
    int min_dist = 65535;
    uint8_t min_dist_index = 0;
    
    // 2.) Preliminary distance check
    if (empty_dist < min_dist) {
        min_dist = empty_dist;
        min_dist_index = 0;
    }
    if (red_dist < min_dist) {
        min_dist = red_dist;
        min_dist_index = 1;
    }
    if (orange_dist < min_dist) {
        min_dist = orange_dist;
        min_dist_index = 2;
    }
    if (yellow_dist < min_dist) {
        min_dist = yellow_dist;
        min_dist_index = 3;
    }
    if (green_dist < min_dist) {
        min_dist = green_dist;
        min_dist_index = 4;
    }
    if (violet_dist < min_dist) {
        min_dist = violet_dist;
        min_dist_index = 5;
    }
    
    // 3.) Return minimum distance index
    return min_dist_index;
}

// Set Slide Servo -------------------------------------------------------------
/* Given a character corresponding to a color, repositions the slide to the position
   corresponding to that color's skittle receptacle. */
void set_slide_servo(uint8_t color){
    if (color == 1){
        slide_pwm.pulsewidth_us(1200);
    }
    else if (color == 2){
        slide_pwm.pulsewidth_us(1475);
    }
    else if (color == 3){
        slide_pwm.pulsewidth_us(1750);
    }
    else if (color == 4){
        slide_pwm.pulsewidth_us(2025);
    }
    else if (color == 5){
        slide_pwm.pulsewidth_us(2300);
    }
}

// Buffer Handler --------------------------------------------------------------
/* Extracts information from a message received by the gateway and placed into
   the XBee buffer. */
void parse_buffer(void){
    // 1.) Check red data
    if (xb_buffer[9] == 'n'){
        r_full = false;
    }
    else if (xb_buffer[9] == 'i'){
        r_full = true;
    }
    else{
        r_dispense = r_dispense + get_number(xb_buffer[9]);
    }
    
    // 2.) Check orange receptacle
    if (xb_buffer[14] == 'n'){
        o_full = false;
    }
    else if (xb_buffer[14] == 'i'){
        o_full = true;
    }
    else{
        o_dispense = o_dispense + get_number(xb_buffer[14]);
    }
    
    // 3.) Check yellow receptacle
    if (xb_buffer[19] == 'n'){
        y_full = false;
    }
    else if (xb_buffer[19] == 'i'){
        y_full = true;
    }
    else{
        y_dispense = y_dispense + get_number(xb_buffer[19]);
    }
    
    // 4.) Check green receptacle
    if (xb_buffer[24] == 'n'){
        g_full = false;
    }
    else if (xb_buffer[24] == 'i'){
        g_full = true;
    }
    else{
        g_dispense = g_dispense + get_number(xb_buffer[24]);
    }
    
    // 5.) Check violet receptacle
    if (xb_buffer[29] == 'n'){
        v_full = false;
    }
    else if (xb_buffer[29] == 'i'){
        v_full = true;
    }
    else{
        v_dispense = v_dispense + get_number(xb_buffer[29]);
    }
    
    // 6.) Clear buffer
    memset(xb_buffer, '\0', BUFFER_SIZE);
    rx_flag = false;
    if (geneva_spinning){
        geneva_pwm.pulsewidth_us(GENEVA_PULSE);
    }
}

// Is the Receptacle Full? -----------------------------------------------------
/* Returns the current value of the full receptacle boolean. */
bool is_full(uint8_t color){
    if (color == 1){
        return r_full;
    }
    else if (color == 2){
        return o_full;
    }
    else if (color == 3){
        return y_full;
    }
    else if (color == 4){
        return g_full;
    }
    else if (color == 5){
        return v_full;
    }
    return false;
}

// Increment Count -------------------------------------------------------------
/* Sends a message to the gateway indicating a change in the count of the
   skittle color corresponding to the character sent. */
void incr_count(uint8_t color, int count){
    if (color == 1){
        xb.printf("ni:%d,re:%d,or:0,ye:0,gr:0,pu:0\n", NODE_ID, count);
    }
    else if (color == 2){
        xb.printf("ni:%d,re:0,or:%d,ye:0,gr:0,pu:0\n", NODE_ID, count);
    }
    else if (color == 3){
        xb.printf("ni:%d,re:0,or:0,ye:%d,gr:0,pu:0\n", NODE_ID, count);
    }
    else if (color == 4){
        xb.printf("ni:%d,re:0,or:0,ye:0,gr:%d,pu:0\n", NODE_ID, count);
    }
    else if (color == 5){
        xb.printf("ni:%d,re:0,or:0,ye:0,gr:0,pu:%d\n", NODE_ID, count);
    }
}

// Dispense Skittles -----------------------------------------------------------
/* Controls the solenoid placed at the bottom of each skittle receptacle in order
   to dispense a number of skittles equal to the number currently in its count. */
void dispense_skittles(void){
    if (r_dispense > 0){
        while (r_dispense > 0){
            printf("Red skittle requested. Dispensing.\r\n");
            r_solenoid = 1;
            wait(0.25);
            r_solenoid = 0;
            r_dispense = r_dispense - 1;
        }
        r_full = false;
    }
    if (o_dispense > 0){
        while (o_dispense > 0){
            printf("Orange skittle requested. Dispensing.\r\n");
            o_solenoid = 1;
            wait(0.25);
            o_solenoid = 0;
            o_dispense = o_dispense - 1;
        }
        o_full = false;
    }
    if (y_dispense > 0){
        while (y_dispense > 0){
            printf("Yellow skittle requested. Dispensing.\r\n");
            y_solenoid = 1;
            wait(0.25);
            y_solenoid = 0;
            y_dispense = y_dispense - 1;
        }
        y_full = false;
    }
    if (g_dispense > 0){
        while (g_dispense > 0){
            printf("Green skittle requested. Dispensing.\r\n");
            g_solenoid = 1;
            wait(0.25);
            g_solenoid = 0;
            g_dispense = g_dispense - 1;
        }
        g_full = false;
    }
    if (v_dispense > 0){
        while (v_dispense > 0){
            printf("Violet skittle requested. Dispensing.\r\n");
            v_solenoid = 1;
            wait(0.25);
            v_solenoid = 0;
            v_dispense = v_dispense - 1;
        }
        v_full = false;
    }
}

// Is Jammed -------------------------------------------------------------------
/* Reads the Adafruit INA169 for high current and returns a boolean that
   indicates whether or not the Geneva drive is jammed. */
bool is_jammed() {
    float meas;
    meas = analog_value.read(); //Reads the voltage from ammeter (representatitive of curerent 1:1)
    meas = meas * 5000;         // Change the value to be in the 0 to 5000 range
    //printf("measure = %.2f mA\r\n", meas);
    
    if (meas > 9000) {           // If the value is greater than 600mA oscillate
        printf("Is jammed, attempting to fix...");
        geneva_pwm.pulsewidth_us(GENEVA_STOP);
        wait(10);
        geneva_pwm.pulsewidth_us(GENEVA_PULSE);
        wait(.1);
        return true;
    }
    else {
        return false;
    }
}

// Scan Delay ------------------------------------------------------------------
/* Used to halt skittle counting until a different color reading is confirmed as
   the Geneva drive rotates in between skittles. */
void scan_delay(uint8_t color){
    uint8_t c;
    while (true){
        read_RGB();
        c = identify_color();
        if ((c == 0) || (c != color)){
            return;
        }
        printf("scanning delay..........\r\n");
        c = 0;
        //is_jammed();
    }
}

// Stir ------------------------------------------------------------------------
/* Calls on the kitty cat to scratch around and stir the skittle reservoir,
   which unclogs the reservoir and vaguely resembles a feline-like action. */
void stir(void){    
    // 1.) Enable the kitty cat
    kitty_cat_en = 1;
    
    // 2.) Create software PWM for 10 cycles
    kitty_cat_pwm = 1;
    wait(0.05);
    kitty_cat_pwm = 0;
    wait(0.05);
    kitty_cat_pwm = 1;
    wait(0.05);
    kitty_cat_pwm = 0;
    wait(0.05);
    kitty_cat_pwm = 1;
    wait(0.05);
    kitty_cat_pwm = 0;
    wait(0.05);
    kitty_cat_pwm = 1;
    wait(0.05);
    kitty_cat_pwm = 0;
    wait(0.05);
    kitty_cat_pwm = 1;
    wait(0.05);
    kitty_cat_pwm = 0;
    wait(0.05);
    
    // 3.) Disable the kitty cat
    kitty_cat_en = 0;
}

// RX Handler ------------------------------------------------------------------
/* Handles receives from the XBee. Copies XBee characters over to xb_buffer. */
void rx_handler(void){
    char c = xb.getc();
    if (xb_index > 31){
        xb_index = 0;
    }
    if (c == '\n'){
        xb_index = 0;
        if ((xb_buffer[3] == '9') && (xb_buffer[4] == '9')){
            rx_flag = true;
        }
    }
    else{
        xb_buffer[xb_index] = c;
        xb_index = xb_index + 1;
    }
}

// Kitty Cat Flip --------------------------------------------------------------
void kitty_cat_flip(void){
    kitty_cat_pwm = !kitty_cat_pwm;
}

// Main ------------------------------------------------------------------------
int main(void) {
// INITIALIZATION --------------------------------------------------------------
    // 1.) Intialize baud rates
    pc.baud(PC_BAUD);
    xb.baud(XB_BAUD);
    printf("IoT Project Industrial Node: Skittle Sorter v. 2.0.0\r\n");
    printf("Authors: Jared McKneely, Kyle Gong\r\n");
    printf("Date of revision: January 12th, 2018\r\n\n");
    printf("Initialized comm baud rates: %d baud debug, %d baud XBee PRO 900HP.\r\n", PC_BAUD, XB_BAUD);
    wait(1);
    
    // 2.) Initialize external hardware
    printf("Initializing external hardware parameters.\r\n");
    slide_pwm.period_ms(SLIDE_PERIOD);
    geneva_pwm.period_ms(GENEVA_PERIOD);
    init_RGB();
    xb.attach(&rx_handler, Serial::RxIrq);
    r_solenoid = 0;
    o_solenoid = 0;
    y_solenoid = 0;
    g_solenoid = 0;
    v_solenoid = 0;
    kitty_cat_en = 0;
    kitty_cat_pwm = 0;
    
    // 3.) Check the receptacles with the gateway
    printf("Contacting gateway, checking receptacles.\r\n");
    while(!rx_flag){
        xb.printf("ni:%d,st:st\n", NODE_ID);
        wait(1);
    }
    parse_buffer();
    rx_flag = false;
    
    // 4.) Create color buffer, booleans identifying color objects
    printf("Initializing software parameters.\r\n");
    uint8_t c_buff[10];
    bool skittle = false;
    int jamCount = 0;  // Stops the motor if checks and is jammed twice consecutive
    int stirCount = 0; // When it reaches 3, the machine stirs the reservoir
    
    // 5.) Test the slide PWM
    slide_pwm.pulsewidth_us(1200);
    wait(1);
    slide_pwm.pulsewidth_us(1475);
    wait(1);
    slide_pwm.pulsewidth_us(1750);
    wait(1);
    slide_pwm.pulsewidth_us(2025);
    wait(1);
    slide_pwm.pulsewidth_us(2300);
    wait(1);
    slide_pwm.pulsewidth_us(1750);

// PROCESS CONTROL -------------------------------------------------------------
    // 5.) Begin scanning cycle
    printf("Beginning scanning routine.\r\n");
    geneva_pwm.pulsewidth_us(GENEVA_PULSE);
    geneva_spinning = true;
    while (true){
        // a.) Check for a jam (ammeter control)
        if (is_jammed()){
            jamCount = jamCount + 1;
        }
        else {
            jamCount = 0;
        }
        if (jamCount == 2){
            geneva_pwm.pulsewidth_us(GENEVA_STOP);
            wait(10);
            geneva_pwm.pulsewidth_us(GENEVA_PULSE);
        }
        
        // b.) Scan for Skittle
        skittle = false;                      // Reset the skittle bool
        while (skittle == false){

            // i.) Scan 10 times for a skittle
            c_buff[0] = identify_color();
            read_RGB();
            c_buff[1] = identify_color();
            read_RGB();
            c_buff[2] = identify_color();
            read_RGB();
            c_buff[3] = identify_color();
            read_RGB();
            c_buff[4] = identify_color();
            read_RGB();
            c_buff[5] = identify_color();
            read_RGB();
            c_buff[6] = identify_color();
            read_RGB();
            c_buff[7] = identify_color();
            read_RGB();
            c_buff[8] = identify_color();
            read_RGB();
            c_buff[9] = identify_color();
            
            // ii.) Determine if all five measurements were the same color, and if it was a skittle
            if ((c_buff[0] == c_buff[1]) && (c_buff[1] == c_buff[2]) && (c_buff[2] == c_buff[3]) && (c_buff[3] == c_buff[4]) && (c_buff[4] == c_buff[5]) && (c_buff[5] == c_buff[6]) && (c_buff[6] == c_buff[7]) && (c_buff[7] == c_buff[8]) && (c_buff[8] == c_buff[9]) && (c_buff[0] != 0)) {
                skittle = true;
                set_slide_servo(c_buff[0]);
                stirCount = 0;
            }
            
            // iii.) Check the XBee buffer for parsing
            if (rx_flag){              // If a block of information was received
                parse_buffer();        // Extract the information
            }
            
            // iv.) Handle stirring!
            stirCount = stirCount + 1; // Add to the stir count
            printf("Stir count is %d\r\n", stirCount);
            if ((stirCount >= 50) && (geneva_spinning == true)){ // If the stir count is too high
                stir();                // Stir the 'V' three times
                stir();
                stir();
                stirCount = 0;         // Reset the stir count
            }
            else if(stirCount >= 50){
                stirCount = 0;
            }
            dispense_skittles();       // Dispense any skittles
        }
        
        // d.) Increment/full receptacle control
        if (is_full(c_buff[0])){                    // If the receptacle is full, halt the machine temporarily
            printf("%c is full.\r\n", c_buff[0]);     // Print the full receptacle
            geneva_pwm.pulsewidth_us(GENEVA_STOP);  // Stop the geneva wheel
            geneva_spinning = false;                // Set the flag to false
            kitty_cat_en = 0;
        }
        else{                                       // Otherwise, it's not full!
            set_slide_servo(c_buff[0]);             // Set the slide servo for the colored receptacle
            incr_count(c_buff[0], 1);               // Increment the count of that skittle color
            geneva_pwm.pulsewidth_us(GENEVA_PULSE); // Start the geneva wheel
            geneva_spinning = true;                 // Set the flag to true
            kitty_cat_en = 1;
        }
        
        // e.) Dispense any skittles with flags marked, delay until this Skittle is removed from the scanning bay
        if (geneva_spinning){
            scan_delay(c_buff[0]);                    // Delay scan until open air is detected
        }
    }
}