Hardware components | ||||||
| × | 1 | ||||
Software apps and online services | ||||||
|
This project is designed to water plants. Once a day, a pulse width modulated servo opens a spigot on the top reservoir to dispense water into the bottom reservoir. The bottom reservoir is on springs with a push button switch at the bottom. When the bottom reservoir fills to the point that the switch gets pressed, the spigot on the top reservoir is closed and the spigot on the bottom reservoir is opened. Water is dispensed for 1 minute (the water finishes dispensing before 1 minute has elapsed). After 1 minute is done, the bottom spigot is closed using a servo. The apparatus remains dormant until 24 hours have elapsed and the process repeats.
Check out the video to see it in action!
/******************************************************************************
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;
}
}
Comments