Josh
Published

Automated Water Dispenser

Using a Texas Instruments MSP430G2553 and a few RC servos, this project dispenses water automatically once per day to water plants.

IntermediateShowcase (no instructions)24 hours1,030
Automated Water Dispenser

Things used in this project

Story

Read more

Code

Automated Water Dispenser Code

C/C++
/******************************************************************************
MSP430G2553 Project Creator

SE 423  - Dan Block
        Spring(2019)

        Written(by) : Steve(Keres)
College of Engineering Control Systems Lab
University of Illinois at Urbana-Champaign
*******************************************************************************/

#include "msp430g2553.h"
#include "UART.h"

void print_every(int rate);

char newprint = 0;
long NumOn = 0;
long NumOff = 0;
int switchstate = 0;
int timecheck = 0;
long milliseconds = 0;
long timeoutcount = 0;
int val = 0;
long dispensecount = 0;
int p26value = 0;
int cnts = 0;

void main(void) {

    WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT

    if (CALBC1_16MHZ ==0xFF || CALDCO_16MHZ == 0xFF) while(1);

    DCOCTL = CALDCO_16MHZ;    // Set uC to run at approximately 16 Mhz
    BCSCTL1 = CALBC1_16MHZ;

    // Initialize Port 1
//    P1SEL &= ~0xf1;  // See page 42 and 43 of the G2553's datasheet, It shows that when both P1SEL and P1SEL2 bits are zero
//    P1SEL2 &= ~0xf1; // the corresponding pin is set as a I/O pin.  Datasheet: http://coecsl.ece.illinois.edu/ge423/datasheets/MSP430Ref_Guides/msp430g2553datasheet.pdf
//    P1REN = 0x0;  // No resistors enabled for Port 1
//    P1DIR |= 0x1; // Set P1.0 to output to drive LED on LaunchPad board.  Make sure shunt jumper is in place at LaunchPad's Red LED
//    P1OUT &= ~0x01;  // Initially set P1.0 to 0
//    P1DIR |= 0xf0; //Set p1.4-p1.7 to outputs


    P2DIR |= 0x12; //Set P2.1 and P2.4 to outputs
    P2DIR &= ~0x40; //Set P2.6 to input
    P2OUT |= 0x40; //Set P2.6 to pull up mode
    P2REN = 0x40; //Enable resistor for P2.6
    P2SEL |= 0x12;  // P2.1 and P2.4 PWM
    P2SEL &= ~0x40; //GPIO P2.6
    P2SEL2 &= ~0x12;
    P2SEL2 &= ~0x40;

//Leftovers from the ADC code. Not all of this is needed.
    ADC10CTL0 = SREF_0 + ADC10SHT_1 + ADC10ON + ADC10IE; //SREF_0 doesn't matter. ADC10SHT sets 8xADC10CLKs. ADC10ON turns on the ADC. ADC10IE Enables interrupt.
    ADC10CTL1 = INCH_3 + SHS_0 + ADC10DIV_0;
    ADC10AE0 = 0x8; //Activate A3 (photoresister)



    // Timer A Config
    TACCTL0 = CCIE;             // Enable Periodic interrupt
    TACCR0 = 16000;                // period = 1 ms
    TACTL = TASSEL_2 + MC_1; // source SMCLK, up mode

    //Timer A1 config
    TA1CCR0 = 40000; //period = 20ms (with ID_3)
    TA1CCTL1 = OUTMOD_7; //TA1CCR1 reset/set
    TA1CCTL2 = OUTMOD_7; //TA1CCR1 reset/set
    TA1CCR1 = 4000; //Initially set duty cycle for motor 1 to 10%
    TA1CTL = TASSEL_2 + MC_1+ID_3; // source SMCLK, up mode, divide by 8
    TA1CCR2 = 2000; //Initially set duty cycle for motor 2 to 5% (always off).

    Init_UART(115200,1);    // Initialize UART for 115200 baud serial communication

    _BIS_SR(GIE);       // Enable global interrupt


    while(1) {  // Low priority Slow computation items go inside this while loop.  Very few (if anyt) items in the HWs will go inside this while loop

// for use if you want to use a method of receiving a string of chars over the UART see USCI0RX_ISR below
//      if(newmsg) {
//          newmsg = 0;
//      }

        // The newprint variable is set to 1 inside the function "print_every(rate)" at the given rate
        if ( (newprint == 1) && (senddone == 1) )  { // senddone is set to 1 after UART transmission is complete

            // only one UART_printf can be called every 15ms
            UART_printf("%ld ms St: %d Pr? %d DC: %ld \n\r", milliseconds, switchstate, p26value, dispensecount); //Prints state, voltage, and reading from ADC
            cnts++;
            newprint = 0;
        }

    }
}


// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
    ADC10CTL0|=ENC+ADC10SC; //Determine the voltage being sent to the ADC.
    p26value = (P2IN >> 6) % 2; //Reads switch state (1 is not pressed)
    timecheck++;
    milliseconds++;
    if (milliseconds == 180000){ //1 Day has elapsed 86400000
        switchstate = 1;
        milliseconds = 0;
    }

    switch(switchstate){
        case 0: //Not dispensing water
            TA1CCR1 = 4900;
            TA1CCR2 = 4900;
            timeoutcount = 0;
            break;
        case 1: //Dispense water from top container until switch is triggered
            timeoutcount++;
            TA1CCR1 = 2000;
            TA1CCR2 = 4900;
            if(timeoutcount == 120000){ //timeout after 2 minutes (dispense from lower container)
               switchstate = 2;
               dispensecount = 0;
               break;
            }

            if (p26value == 0) { //switch is pressed
                switchstate = 2;
                dispensecount = 0;
            }
            break;
        case 2: //Dispense water from lower container for set amount of time
            TA1CCR1 = 4900;
            TA1CCR2 = 2000;
            dispensecount++;
            if(dispensecount == 60000){ //dispense for 1 minute
                dispensecount = 0;
                switchstate = 0;
            }
            break;
        default:
            TA1CCR1 = 4900;
            TA1CCR2 = 4900;
            timeoutcount = 0;
            break;


    }

//    if (timecheck == 2000) { //Changes every second
//        timecheck = 0;
//        TA1CCR1 = 2000; // 10% duty cycle
//        TA1CCR2 = 2000; // 5% duty cycle
//    }
//    if (timecheck == 1000) {
//        TA1CCR1 = 6000; //5% duty cycle //4000 2000
//        TA1CCR2 = 6000; // 15% duty cycle
//    }
}



// ADC 10 ISR - Called when a sequence of conversions (A7-A0) have completed
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void) {

    print_every(500); //Print every 500 ms
    }



// USCI Transmit ISR - Called when TXBUF is empty (ready to accept another character)
#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCI0TX_ISR(void) {

    if(IFG2&UCA0TXIFG) {        // USCI_A0 requested TX interrupt
        if(printf_flag) {
            if (currentindex == txcount) {
                senddone = 1;
                printf_flag = 0;
                IFG2 &= ~UCA0TXIFG;
            } else {
                UCA0TXBUF = printbuff[currentindex];
                currentindex++;
            }
        } else if(UART_flag) {
            if(!donesending) {
                UCA0TXBUF = txbuff[txindex];
                if(txbuff[txindex] == 255) {
                    donesending = 1;
                    txindex = 0;
                }
                else txindex++;
            }
        } else {  // interrupt after sendchar call so just set senddone flag since only one char is sent
            senddone = 1;
        }

        IFG2 &= ~UCA0TXIFG;
    }

    if(IFG2&UCB0TXIFG) {    // USCI_B0 requested TX interrupt (UCB0TXBUF is empty)

        IFG2 &= ~UCB0TXIFG;   // clear IFG
    }
}


// USCI Receive ISR - Called when shift register has been transferred to RXBUF
// Indicates completion of TX/RX operation
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void) {

    if(IFG2&UCB0RXIFG) {  // USCI_B0 requested RX interrupt (UCB0RXBUF is full)

        IFG2 &= ~UCB0RXIFG;   // clear IFG
    }

    if(IFG2&UCA0RXIFG) {  // USCI_A0 requested RX interrupt (UCA0RXBUF is full)

//    Uncomment this block of code if you would like to use this COM protocol that uses 253 as STARTCHAR and 255 as STOPCHAR
/*      if(!started) {  // Haven't started a message yet
            if(UCA0RXBUF == 253) {
                started = 1;
                newmsg = 0;
            }
        }
        else {  // In process of receiving a message
            if((UCA0RXBUF != 255) && (msgindex < (MAX_NUM_FLOATS*5))) {
                rxbuff[msgindex] = UCA0RXBUF;

                msgindex++;
            } else {    // Stop char received or too much data received
                if(UCA0RXBUF == 255) {  // Message completed
                    newmsg = 1;
                    rxbuff[msgindex] = 255; // "Null"-terminate the array
                }
                started = 0;
                msgindex = 0;
            }
        }
*/

        IFG2 &= ~UCA0RXIFG;
    }

}

// This function takes care of all the timing for printing to UART
// Rate determined by how often the function is called in Timer ISR
int print_timecheck = 0;
void print_every(int rate) {
    if (rate < 15) {
        rate = 15;
    }
    if (rate > 10000) {
        rate = 10000;
    }
    print_timecheck++;
    if (print_timecheck == rate) {
        print_timecheck = 0;
        newprint = 1;
    }

}

Credits

Josh

Josh

1 project • 2 followers

Comments