Pictures
Our project is an automated pet feeder that is controlled by a wireless infra-red remote control.
As pet lovers, we understand that the responsibilities of life sometimes inhibit pet owners from properly caring for their pets. Pet care should be fun, not burdensome, and so our goal with this project was to assist owners with pet care by providing a system that automates diet management.
Our pet feeder consists of two components. The first component is a remote control that allows pet owners to design the diet plan for their pet. The second component is a feeder that receives instruction from the remote control and refills the pet bowl (to feed the pet) when appropriate. Wireless communication is achieved via IR transmission. The overall program flow is illustrated in Figure 0.
Figure 0: Logic flowchart for our project. The remote component automates the feeder through IR transmission.
The user interface on the remote consists of a keypad and LCD. The LCD prompts the options the user can select and the keypad allows the user to make a selection and input data. The feeder consists of a motor-controlled food dispenser (where a high torque DC motor is used to turn a wheel in the cereal dispenser) and three weight sensors to monitor the weight of food in the pet bowl. The user interface and the feeder communicate via a pair of IR transceivers and IR endecs(encoder and decoder).
There were two aspects of our project where hardware/software tradeoffs were considered. The first involved the transmission of IR data between the remote and feeder components. Data is transmitted using the IrDA standard, so IR endecs were used to handle the translation from RS-232 to IrDA. Although the signals could be translated in software, handling the translation in hardware allowed us more time to focus on other issues in our project. The second aspect involved low-passing the signals provided by the weight sensors measuring the weight in the pet bowl. Voltage spikes caused by adding to or removing food from the bowl needed to be filtered, and instead of building a low pass filter in hardware, an averaging function was implemented in software. Since surface stability is essential to accurate weight measurements, the reduction in hardware allowed us to construct a more accurate weight sensor circuit.
RS-232 and IrDA (Infrared Data Association) standards were used in this project. RS-232 was used to communicate between the Mega32 chip and the IR endecs. IrDA was used to communicate between the IR transceivers and between the transceivers and the endecs.
There were no patents or copyrights associated with this project.
Two Mega32’s were used to run the two components of this project. The Mega32 on the remote component was responsible for handling the user interface and informing the feeder component when to refill the pet bowl. The Mega32 on the feeder component upon receiving instruction to refill the bowl would activate the motor-controlled food dispenser until the bowl was filled up.
Remote Component
Three functions were programmed into the remote. The user can change the remote’s current time, input a new feeding schedule and prompt the feeder to refill the pet bowl. The state diagram used to handle these functions is shown in Figure 1. The method for handling keypad debouncing was reused from Lab 2.
Figure 1: State diagrams for the LCD/keypad user interface. The three available options are change time of day, input feeding schedule and refill bowl. The third option is also executed if the current time matches a feeding schedule time.
The setup used to interpret the keypad input is a modified version of the sample code given in Lab 2 tailored for the scavenged keypad we used. Pins corresponding to the rows and columns were separately read in order to determine which key was pressed, and the numeric value of the key pressed was stored in a variable for use in other functions. Figure 2 displays how we wired up the keypad.
Connection on bottom
Pin 1 -- Column 1: 1 4 7 *
Pin 2 -- Column 2: 2 5 8 0
Pin 3 -- Column 3: 3 6 9 #
Pin 4 -- Row 1: 1 2 3
Pin 5 -- Row 2: 4 5 6
Pin 6 -- Row 3: 7 8 9
Pin 7 -- Row 4: * 0 #
(a) Each switch shorts one row to one column.
(b) Each pin should be connected to one bit of an i/o port.
(c) The i/o port pins will be used both as inputs and outputs.
When they are inputs, they have internal pullup resistors turned on.
Figure 2: A pin layout of the keypad used and a short description of how the keypad input was interpreted.
The connection of the LCD to PORTC is detailed below. A trimpot was used to adjust contrast.
[LCD] [Mega32 pin]
1 GND - GND
2 +5V - VCC
3 VLC 10k trimpot wiper (trimpot ends go to +5 and gnd)
4 RS - PC0
5 RD - PC1
6 EN - PC2
11 D4 - PC4
12 D5 - PC5
13 D6 - PC6
14 D7 - PC7
Accurate timing is critical to automating the feeder to dispense food according to the inputted feeding schedule. Timer0 was set to a 62.5kHz PWM signal for IR transmission, so to implement an accurate 1ms timing scheme a counting variable that alternated between 62 and 63 (average 62.5) was used. Additional timing variables to count seconds, minutes and hours were directly modified in the timer0 overflow ISR.
Another important design consideration was to have the remote control capable of automating the feeder upon a reset. To accomplish this, the remote’s current time and the current feeding schedule are stored in EEPROM and copied back into volatile memory upon reset. However, the timing will not be updated when the remote is turned off. Implementing a device that will track timing even when the remote is turned off is beyond the scope of our current project.
The design for IR communication was based off of a previous 476 project Wireless Electromyograph. A string of ones is transmitted for one minute to indicate that the bowl should be refilled. This signal is outputted as a RS-232 UART through port D.0. A MCP2120 IR encoder converts the RS 232 signal to the IrDA standard. The signal is then transmitted via IR transmission by a transceiver, and is received by another transceiver on the receiving end. A MCP2120 IR decoder then converts the IrDA standard signal back to a RS 232 signal. This is illustrated in the following Figure 3.
Figure 3: Diagram illustrating the encoding and decoding of signals using the IrDA standard.
To ensure reliable communication, the baud rates of the Mega32 UART and the MCP2120 must match. A 62.5kHz square wave produced by the Mega32’s timer0 was used to drive the MCP2120. By hardwiring the BAUD2:BAUD0 pins of the MCP2120 to 100, the clock divider of the MCP2120 is set to 64 and the effective baud rate for the endec was:
To have the MCU match this baud rate, UBBR was set to:
More specifically, UBBRL was set to 0xff and UBBRH was set to 0x03.
The IrDA compatible signal was transmitted using a ZHX1810 IrDA transceiver. The device receives 0-5V CMOS compatible signals and contains an IR diode with rise time and optical transmission spectrum that adheres to the IrDA standard. The standard also requires reliable transmission of at least 1 meter, which our circuit meets. The IR transmit circuit is shown in Figure 4.
Figure 4: IR transmit circuit. A RS-232 UART signal is outputted out of port D.0, converted to IrDA standard in the MCP2120 and transmitted with the ZHX1810 (shown by purple line). The MCP2120 is driven by a 62.5kHz square wave (shown by green line).
Feeder Component
Three concurrent tasks are run on the feeder component MCU: monitor the weight of the pet bowl, receive instruction from the remote control, and activate motor controlling the dispenser under correct conditions (reception of instruction to feed and an empty pet bowl).
The weight of the pet bowl is measured using two IESP12’s, push button force sensors. (A third sensor was included for testing purposes but was not used in the computation of the weight of food). The sensors act as variable resistors that are sensitive to the weight or pressure on top of the push button. To translate this into a voltage that the MCU can read, the voltage divider circuit in figure 5 was used.
Figure 5: Voltage divider circuit used to determine the weight of the food bowl.
The selection of 1MΩ resistors was to maximize the change in voltage with respect to a change in resistance for the variable force sensor resistor. Figure 6 displays the sensor resistance as a function of weight (gf = force created by a mass of 1 gram).
When no weight is present, the sensor resistance will be greater than 100MΩ and the voltage divider will read:
We anticipated an applied force of 500gf when the bowl is full, which implies the sensor resistance is around 10kΩ and the voltage divider output equal to:
Thus, with this selection of resistors we were able to utilize the full 0-5V range.
Figure 6: Pressure vs. Resistance graph for the IESP12 taken from the IESP12 datasheet courtesy of CUI Inc. We anticipated a maximum weight of 500gf which corresponds to a resistance of 10kΩ.
The following snippet of code was used to calculate the weight of the food bowl.
if (ADCSR.6 == 0)
begin
if ((Aindex%2)==0) ADMUX = 0b11000000;
if ((Aindex%2)==1) ADMUX = 0b11000001;
Ain = ADCL;
Ain += ADCH*256;
sum+=Ain;
Aindex++;
if (Aindex==200)
begin
Aindex=0;
weight = (int)(sum/200);
sum=0;
The output of the three voltage dividers were fed into port A.0, and A.1, which corresponded to the ADC input 0 and 1 respectively. To maximize the accuracy of the A/D conversion, ADCSR was set to 0b11000111 so that the ADC clock frequency was minimized and all 10 bits of the data register utilized. Each ADC channel was sampled sequentially and the weight was calculated to be the average of 200 samples, 100 per channel. The averaging reduces the effect of any voltage spikes caused by weight changes in the food bowl.
When the weight is calculated, the average samples ADC value is compared to an upper and lower weight threshold. The upper threshold of the average sampled ADC value was chosen to be 999 and the lower threshold was chosen to be 850. The use of two thresholds instead of one threshold acted as a Schmidt Trigger implemented in software and reduced unwanted spikes in the determination of whether the weight of food in the bowl was sufficient. Due to the wiring of the force sensor in series with a 1Mohm resistor, the greater the weight of food, the smaller the resistance of the force sensor, and thus the smaller the voltage drop across the force sensor, and the smaller the ADC sampled value at Port A.0 and A,1.
The ADC sampled values thus decrease as the weight of food increases. A refill bowl instruction is executed if the average ADC sampled value is above the upper threshold (i.e. insufficient weight of food), and is ignored if the weight is below the lower threshold. In addition, a green LED is lighted if the bowl can be refilled (i.e. insufficient weight of food), and a red LED lighted if the bowl should not be refilled.
The IR receive circuit, as shown in figure 7, is essentially the same as the IR transmit circuit in the feeder component. An important difference, as noted in theWireless Electromyograph webpage, is that the Tx pin of the MCP2120 must be tied high when receiving IR signals, whereas the Rx pin can be left floating when the device is transmitting IR signals. This is to inhibit the transmit function when receiving signals since the MCP2120 gives priority to transmission and a floating Tx pin may accidentally induce a transmit instruction, when it is supposed to be receiving signals.
Figure 7: IR receive circuit. An IR signal is received by the ZHX1810, converted to a RS-232 UART signal in the MCP2120 and inputted into port D.1 (shown by purple line). The MCP2120 is driven by a 62.5kHz square wave (shown by green line).
A refill bowl instruction is executed when five 0xff bytes are read in succession. At this point, a PWM signal is fed into PORTC.0 of figure 8 below. Through testing, the current frequency and duty cycle setting of the PWM signal was found to minimize the chances of jamming in the food dispenser. The motor is turned off when the weight of the bowl crosses the lower threshold, signified by the red LED turning on. The optoisolator isolating circuit for the DC motor shown in figure 8 was adapted from Lab 5.
Figure 8: Optoisolator circuit used to drive the motor. A 9V battery was used on the motor end.
Food Dispenser
A cereal dispenser was used to contain the pet food. The wheel in the cereal dispenser was connected to a high torque DC motor via a thick rubber band so that when the motor was turned on, the wheel was turned and the food would be dispensed. Initially, we had a lot of difficulty getting the wheel to turn as there was too much friction and the pet food was too heavy. The torque provided by the DC motor was insufficient in turning the wheel. Friction was minimized by cutting down the flaps of the wheel which reduced the contact between the wheel and the sides of the cereal dispenser. The cereal dispenser was also tilted at an angle to reduce the weight of the pet food on the wheel. The motor and cereal dispenser was then taped down into position using duct tape and a box to hold the cereal dispenser in position. With this setup we were able to dispense food with minimal jamming.
Force Sensor
The system to measure the weight of the pet food comprised of two force sensors that were placed under the food bowl. The sensors were soldered onto tiny solder boards that were then held together using duct tape. Readings were taken from both force sensors and averaged, as the weight of the food bowl was distributed across the two force sensors.
The final result of our project design accomplished the main goal of designing an automated pet feeder. The remote component was able to acquire and implement a feeding schedule and the feeder component was able to refill the pet bowl at appropriate times and to the appropriate amount.
Speed of Execution
Since feeding times were accurate to the minute, speed of execution was not an issue since the process of refilling the pet bowl at a specified time could be completed within a minute. More specifically, the IR transmission of the refill bowl instruction and the subsequent refilling of the pet bowl took less than minute. In addition, the weight of the bowl was updated at a much greater frequency than one reading per minute.
Accuracy
The final design was able to meet the accuracy requirements as set in our project expectations. By updating timing variables in the ISR, timing was accurate to the minute. Combined with our method of coding a refill bowl instruction as a string of ones sent over a minute, this setup ensured that the pet bowl was refilled at the correct time. In addition, our design of the force sensor circuit and calibration of the MCU’s ADC allowed us to efficiently determine whether the pet bowl was full and ensure that the dispenser did not add food when the weight of food was already sufficient.
Usability
The usability of our project as an actual pet feeder is inhibited by the limitations of our mechanical design. Realistically, the feeding component of our device would be susceptible to a rowdy pet who tried tampering with it. However, with the current set-up using duct tape and cardboard boxes, the structure of the pet feeder is not ideal and could be made more stable with better materials.
However, from a user point of view our project is simple to use. The user interface on the remote is instinctive and easy to navigate. Storing schedule and timing information into EEPROM also makes the design very robust. All that is lacking is a timing device that is active even when the remote is turned off.
Safety and Interference
Safety was not a concern in our project since all components excluding the MCU and motor ran on 5V. Also, there were no heavy objects in our design or direct connections to the human or pet body. Interference was also not an issue since our IR signals were transmitted over a very small distance. Also, not many other groups were using IR.
Our project succeeded in meeting the goals that defined our automated pet feeder. By entering a food schedule and time of day into the remote, the device was able to automate the feeding of the pet by instructing the feeding component to refill the pet bowl at the scheduled times. The feeding component also ensured that the contents of the bowl did not overflow, especially if the bowl was already full during a scheduled feeding time.
While our main goal was accomplished, there are features that could be added to this project. Originally we wanted the feeder component to be able to operate independently of the remote. This required that when a feeding schedule or time of day was inputted into the remote, this data would be transmitted to the feeder component via infra red transmission. In addition we hoped to let the user determine the amount per feeding for each feeding time. Both features could not be included because our IR transmission was not accurate enough. We contacted Arthur Gariety, one of the creators of the Wireless Electromyograph project and have come to the conclusion that the source of the noise experienced in our transmission most likely derives from the clock circuit used to drive the IR endecs. That group was able to scavenge a clock oscillator circuit of a much higher frequency and thus was able to use a much higher baud rate for data transmission.
Also, given more time (and perhaps better expertise), we hope to design a better dispensing system that is less prone to jamming and physically more sturdy. The use of a more powerful motor or solenoid could accomplish the former, though acquisition of such a device would send us way over budget.
The design conformed to RS-232 and IrDA standards. All reused code and design was implemented with permission. There were no legal or intellectual property considerations to worry about.
Ethics
We adhered to the IEEE Code of Ethics throughout this project:
1. to accept responsibility in making decisions consistent with the safety, health and welfare of the public, and to disclose promptly factors that might endanger the public or the environment;
We ensured we remained under the power limit of our devices and that none of them got too hot during operation. We also isolated any sharp objects that could potentially hurt others.
2. to avoid real or perceived conflicts of interest whenever possible, and to disclose them to affected parties when they do exist;
We did not hog resources such as workstations or soldering stations.
3. to be honest and realistic in stating claims or estimates based on available data;
We were honest in our report and made realistic claims towards what our project is capable of performing.
4. to reject bribery in all its forms;
The only monetary transactions involved buying ourselves snacks and drinks to cheer ourselves up.
5. to improve the understanding of technology, its appropriate application, and potential consequences;
We committed ourselves to learning how microcontroller design could benefit the pet industry.
6. to maintain and improve our technical competence and to undertake technological tasks for others only if qualified by training or experience, or after full disclosure of pertinent limitations;
We learned a great deal about wireless IR transmission and motor mechanical systems such as stepper motors and servo motors, which we experimented with but did not use in the final design.
7. to seek, accept, and offer honest criticism of technical work, to acknowledge and correct errors, and to credit properly the contributions of others;
We were open to advice provided by our peers, TA’s and Professor Land . We also sought out help from Arthur Gariety, whose work in the WirelessElectromyograph greatly helped with our design.
8. to treat fairly all persons regardless of such factors as race, religion, gender, disability, age, or national origin;
We did not discriminate at all.
9. to avoid injuring others, their property, reputation, or employment by false or malicious action;
We did not commit to vandalism or any violent acts.
10. to assist colleagues and co-workers in their professional development and to support them in following this code of ethics.
We gave assistance when we felt qualified to help.
Remote component code
#include <mega32.h>
#include <stdio.h>
#include <delay.h>
#define t1 30
#define t1delay 1000 //delay for display of lcd messages
#define t2 1000 //delay for display of lcd messages
#define t3 60
//determines how often the motor control function is called to turn on the motor if necessary (1 minute)
#define maxkeys 12
#define terminator 10 //number assigned to terminator key
#define cancel 11 //number assigned to the key to cancel user input
#define LCDwidth 16
#define maxfeed 5 //maximum number of feeding times
#define begin {
#define end }
//the subroutines
void keypad(void);
void userinput(void);
void remoteMenu(void);
void printOptions(void);
void timeSet(void);
void scheduleSet(void);
char hourValidate(void);
char minValidate(void);
void motorControl(void);
void initialize(void);
char testbuffer, receivebuffer;
unsigned char pointfive = 0;
//############### variables for lcd and keypad #####################
//This is for Conn. on bottom keypad only! Order is: 0-9, *, #, other entries are invalid
flash unsigned char keytbl[12] = {0b10111101, 0b11110110,0b11110101,0b11110011,0b11101110, 0b11101101, 0b11101011, 0b11011110, 0b11011101,0b11011011, 0b10111110, 0b10111011};
char keystr[16], printstr[16];
char feedHour[5] = {-1,-1,-1,-1,-1};
char feedMin[5] = {-1,-1,-1,-1,-1};
char feedCount, feedNum;
char remoteParam, printParam, timeParam, schParam;
char count, sec, hrs, min, hrs1, hrs2, min1, min2; //timing variables
char i, keycount, key, butnum, pushflag, maybe, inputflag, keyflag; //keypad variables
int time1, time2, time3, msec, opentime, inputval;
char tempTime;
char motorflag;
eeprom char Ehrs, Emin, EfeedNum;
eeprom char EfeedHour[5];
eeprom char EfeedMin[5];
#asm
.equ __lcd_port=0x15 //LCD currently set to PORTC
#endasm
#include <lcd.h> // LCD driver routines
//timer 0 pwm interrupt
interrupt [TIM0_OVF] void pwm(void)
begin
count--;
if (count==0)
begin
pointfive=pointfive^0x01;
count = 62+pointfive;
//use count to create 1ms timing: 1/62.5kHz * 62.5 = 1ms
msec++;
if (time1>0) --time1;
if (time2>0) --time2;
end
if (msec == 1000)
begin
sec++; //keep track of seconds
msec=0;
if (time3>0) --time3;
end
if (sec == 60)
begin
min++; //keep track of minutes
sec=0;
if (feedNum!=0)
begin
for (i=0;i<feedNum;i++)
begin
if (hrs==feedHour[i] && min==feedMin[i]) motorflag=1; //check to see if it is time to feed the pet and turn on motor
end
end
end
if (min == 60)
begin
hrs++; //keep track of hours
min=0;
end
if (hrs == 24) hrs=0;
end
//UART transmit-empty ISR
interrupt [USART_DRE] void uart_send(void)
begin
UDR=testbuffer; //data string to be transmitted
end
//##############Main Code Here#############
void main(void)
begin
initialize();
while(1)
begin
if (time1==0) remoteMenu(); //run keypad debouncer and interpret results
if (time2==0 && remoteParam == 0) printOptions(); //print out the user options on the lcd
if (time3==0 && motorflag==1)
begin
time3=t3;
motorControl(); //call the function that controls the motor every minute
end
end
end
char hourValidate (void)
begin
if (inputval > 23) //invalid input
begin
lcd_gotoxy(0,0);
lcd_putsf("Hours: 0-23 "); //prompt for valid input
time1 = t1delay;
return 0xff;
end
else return (char)inputval;
end
char minValidate (void)
begin
if (inputval > 59) //invalid input
begin
lcd_gotoxy(0,0);
lcd_putsf("Minutes: 0-59 "); //prompt for valid input
time1 = t1delay;
return 0xff;
end
else return (char)inputval;
end
//Function to determine the user input using the keypad connected to Port C
void keypad(void)
begin
DDRA=0b01111000; //pin 7 connected to Vcc (read as '1')
PORTA=0b10000111;
delay_us(5);
key=PINA; //read upper nibble
DDRA=0b00000111;
PORTA=0b11111000;
delay_us(5);
key = key | PINA; //read lower nibble and combine with upper nibble
if (key != 0xff) //determine decimal value of input if something is pressed
begin
for (butnum=0;butnum<maxkeys;butnum++)
begin
if(keytbl[butnum]==key) break; //if valid, butnum = 0-11
end
if (butnum==maxkeys)
begin
butnum=0xff; //invalid button pressed
end
end
else butnum=0xff; //butnum = 0xff implies an invalid key press or no key press
end
void userinput(void)
begin
keypad(); //acquire user input
switch (pushflag)
begin
case 0: //STATE: RELEASE
if (butnum!=0xff)
begin
maybe=butnum; //butnum must match maybe in the next state to certify a button press
pushflag++; //go to DEBOUNCE
end
break;
case 1: //STATE: DEBOUNCE
if (butnum==maybe) pushflag++; //go to TERMINATOR
else pushflag=0; //return to RELEASE
break;
case 2: //STATE: TERMINATOR
keyflag=1;
if (butnum==cancel)
begin
inputval=0;
pushflag=0;
keycount=0;
for (i=0;i<LCDwidth;i++) keystr[i] = '';
time1=t1delay;
keyflag=0;
end
if (butnum==terminator) //user has keyed the terminating button
begin
if (keycount!=0)
begin
keystr[keycount]=0;
inputflag=1;
for (i=0;i<LCDwidth;i++) keystr[i] = '';
end
pushflag=0;
keycount=0;
keyflag=0;
end
else
begin
if (butnum<10) //input is a value only if buttons 0-9 were pressed
begin
//inputval is the current value of the user input for the current parameter
inputval = inputval * 10; //shift value of inputval from previous user inputs
inputval = inputval + (int)butnum; //update value of what user has just inputted
keystr[keycount++]=butnum+0x30; //tag on current key press (0-9) onto LCD buffer, 0x30 is ASCII offset
end
pushflag++; //go to STILL SAME
end
break;
case 3: //STATE: STILL SAME
if ((butnum+0x30)!=keystr[keycount-1]) pushflag++; //go to DEBOUNCE RELEASE
break;
case 4: //STATE: DEBOUNCE RELEASE
if ((butnum+0x30)==keystr[keycount-1] || butnum == terminator) pushflag--; //return to STILL SAME
else
begin
if (keycount==17)
begin
butnum = terminator; //user has exceeded key limit, return to TERMINATOR. Error is thrown since the value will exceed parameter limits
pushflag = 2;
end
else
begin
lcd_clear();
lcd_gotoxy(0,0);
lcd_puts(keystr);
for (i=keycount;i<LCDwidth;i++) keystr[i] = '';
pushflag=0; //return to RELEASE
end
end
break;
end
end
void printOptions(void) //print out the user options on the lcd
begin
time2 = t2;
if (keystr[0]=='')
begin
lcd_gotoxy(0,0);
switch(printParam)
begin
case 1:
lcd_putsf("Select an option");
break;
case 2:
lcd_putsf("1: Time of day ");
break;
case 3:
lcd_putsf("2: Feed schedule");
break;
case 4:
lcd_putsf("3: Refill bowl ");
break;
case 5:
//Print out current time
hrs1 = hrs/10;
hrs2 = hrs - hrs1*10;
min1 = min/10;
min2 = min - min1*10;
sprintf(printstr, "Time is: %d%d:%d%d", hrs1, hrs2, min1, min2);
lcd_puts(printstr);
break;
case 6:
lcd_putsf("Feed schedule ");
i=0;
break;
case 7:
if (feedNum!=0)
begin
//Print out hour and min info for each feeding
hrs1 = feedHour[i]/10;
hrs2 = feedHour[i] - hrs1*10;
min1 = feedMin[i]/10;
min2 = feedMin[i] - min1*10;
sprintf(printstr, "%d: %d%d:%d%d ", i+1, hrs1, hrs2, min1, min2); //print out feeding times
lcd_puts(printstr);
printParam=6;
i++;
if (i==feedNum)
begin
printParam=0;
i=0;
end
end
else
begin
lcd_putsf("No Feedings ");
printParam=0;
end
break;
end
printParam++;
end
end
void timeSet(void)
begin
lcd_gotoxy(0,0);
switch(timeParam)
begin
case 0:
if (keystr[0]=='')
begin
lcd_putsf("Set hour ");
end
if (inputflag)
begin
//Validate and store hour input
inputflag=0;
tempTime=hourValidate();
inputval=0;
if (tempTime!=0xff)
begin
hrs=tempTime;
Ehrs = hrs; //store feeding time (hours) into eeprom
timeParam++;
end
end
break;
case 1:
if (keystr[0]=='')
begin
lcd_putsf("Set minutes ");
end
if (inputflag)
begin
//Validate and store minute input
inputflag=0;
tempTime=minValidate();
inputval=0;
if (tempTime!=0xff)
begin
min=tempTime; //store feeding time (minutes) into eeprom
Emin = min;
timeParam=0;
remoteParam=0;
end
end
break;
end
end
void scheduleSet(void)
begin
lcd_gotoxy(0,0);
switch (schParam)
begin
case 0:
if (keystr[0]=='')
begin
lcd_putsf("# of feedings ");
end
//Validate and store # of feedings input
if (inputflag)
begin
inputflag=0;
if (inputval > 5 || inputval==0)
begin
lcd_gotoxy(0,0);
lcd_putsf("Feedings: 1-5 "); //prompt for valid data (maximum feedings allowed is 5)
time1 = t1delay;
end
else
begin
feedNum = inputval;
schParam++;
end
inputval=0;
end
break;
case 1:
if (keystr[0]=='')
begin
sprintf(printstr, "Feeding %d: Hour", feedCount+1); //prompt for user to input feeding time (hours)
lcd_puts(printstr);
end
if (inputflag)
begin
//Validate and store hour input
inputflag=0;
tempTime=hourValidate();
if (tempTime!=0xff)
begin
feedHour[feedCount]=tempTime;
schParam=2;
end
inputval=0;
end
break;
case 2:
if (keystr[0]=='')
begin
sprintf(printstr, "Feeding %d: Min ", feedCount+1); //prompt for user to input feeding time (minutes)
lcd_puts(printstr);
end
if (inputflag)
begin
//Validate and store minute input
inputflag=0;
tempTime=minValidate();
if (tempTime!=0xff)
begin
feedMin[feedCount]=tempTime;
schParam=1;
feedCount++;
end
inputval=0;
end
if (feedCount==feedNum)
begin
for (i=feedNum;i<maxfeed;i++)
begin
feedHour[i]=-1;
feedMin[i]=-1;
end
schParam=3;
feedCount=0;
end
break;
case 3:
if (keystr[0]=='')
begin
lcd_putsf("Sure? (Y:1, N:0)"); //check whether user wants to keep or change input
end
if (inputflag)
begin
time1=t1delay;
inputflag=0;
if (inputval==1)
begin
//User has confirmed new settings, store new settings into EEPROM
EfeedNum = feedNum;
for (i=0;i<5;i++)
begin
EfeedHour[i] = feedHour[i];
EfeedMin[i] = feedMin[i];
end
schParam=0;
remoteParam=0;
end
if (inputval==0)
begin
//User has rejected new settings, restore variables to EEPROM values
feedNum = EfeedNum;
for (i=0;i<5;i++)
begin
feedHour[i] = EfeedHour[i];
feedMin[i] = EfeedMin[i];
end
schParam=0;
remoteParam=0;
end
else
begin
lcd_gotoxy(0,0);
lcd_putsf("Enter 1 or 0 ");
time1 = t1delay;
end
inputval=0;
end
break;
end
end
void remoteMenu(void)
begin
time1 = t1;
userinput();
switch(remoteParam)
begin
case 0:
//Option has been selected, call appropriate task
if (inputflag)
begin
inputflag=0;
remoteParam=inputval; //store the option that was selected
inputval=0;
printParam=1;
end
break;
case 1:
//Task 1: Set time of day
timeSet();
break;
case 2:
//Task 2: Input new feeding schedule
scheduleSet();
break;
case 3:
//Task 3: Refill bowl
lcd_gotoxy(0,0);
lcd_putsf("Bowl Refilled ");
time1 = t1delay;
motorflag=1;
remoteParam=0;
break;
default:
lcd_gotoxy(0,0);
lcd_putsf("Input 1-3 "); //prompt for valid input
time1 = t1delay;
remoteParam=0;
break;
end
end
void motorControl(void)
begin
if (testbuffer==0xff)
begin
testbuffer=0x00; //transmit a string of 0’s (via IR transmission)
motorflag=0; //indicates that motor has been turned off
end
else
begin
testbuffer=0xff; //transmit a string of 1’s to turn on motor
end
end
//**********************************************************
//Set it all up
void initialize(void)
begin
OCR0=128;
TIMSK=0x01; //turn on timer 0 ovf-match ISR
TCCR0=0b01101001; //turn on pwm
lcd_init(LCDwidth); //initialize the display
lcd_clear(); //clear the display
//Initialize timing variables
count=62;
pointfive=0;
msec=0;
sec=0;
min=0;
hrs=0;
opentime=0;
time1=t1;
time2=0;
time3=0;
//Initialize variables used in keypad control
pushflag=0;
keycount=0;
inputval=0;
keyflag=0;
inputflag=0;
//Initialize state diagram control variables
remoteParam=0;
printParam=1;
timeParam=0;
schParam=0;
//Initialize variables to store feeding schedule information
feedNum=5;
feedCount=0;
tempTime=0;
hrs = Ehrs;
min = Emin;
feedNum = EfeedNum;
for (i=0;i<5;i++)
begin
feedHour[i] = EfeedHour[i];
feedMin[i] = EfeedMin[i];
end
//Initialize variables for motor control
motorflag=0;
//###initialization for transmitting end of transceiver############
//serial setup for debugging using printf, etc.
UCSRB = 0x18; //enables the interrupts for the UART for the receive and transmit buffer
//62.5kHz/64 = 976.5625baud
//UBRRL = 16MHz/(16*976.5625baud) - 1 = 1023
UBRRL = 0xff;
UBRRH = 0x03;
DDRB.3 = 1; //B.3 is for PWM output
//set up timer 0
// OCR0=249; //1 mSec
OCR0=128;
TIMSK=0x01; //turn on timer 0 cmp-match ISR
// TCCR0=0b00001011; //prescalar to 64 and Clr-on-match
TCCR0=0b01101001; //turn on pwm
//r_ready=0;
//t_ready=1;
UCSRB.5=1; //for transmitting
testbuffer=0;
//crank up the ISRs
#asm
sei
#endasm
end
Feeder component
#include <mega32.h>
#include <stdio.h>
#include <delay.h>
//variables for ADC sampling of force sensor
int Aindex;
int Ain, weight; //raw A to D number
float sum;
unsigned char addFood;
//variables for motor control
unsigned char motorflag, motor;
#define begin {
#define end }
#define t1 1000
#define onTime 50 //time for which motor is on
#define offTime 10 //time for which motor is off
#define t1delay 60000
void initialize(void);
void motorControl(void);
int msec, time1, dirTime;
char count, receivebuffer, dirflag;
unsigned char index, pointfive = 0;
interrupt [TIM0_OVF] void pwm(void)
begin
count--;
if (count==0)
begin
count = 62+pointfive; //use count to create 1ms timing: 1/62.5kHz * 62.5 = 1ms
pointfive = pointfive^0x01; //toggles between 0 and 1 to create an average value of 62.5
msec++; //ms time variable used for all other timing
if (time1>0) --time1;
if (dirTime>0) --dirTime;
end
if (msec == 1000) //called every second
begin
msec=0;
//if (time1>0) --time1;
if(receivebuffer==0xff) index++; //keep track of whether the signal to turn on motor has been received
else index=0;
end
end
//**********************************************************
//UART character-ready ISR
interrupt [USART_RXC] void uart_rec(void)
begin
receivebuffer=UDR; //to store the received signal
end
//**********************************************************
//Entry point and task scheduler loop
void main(void)
begin
initialize();
while(1)
begin
if (ADCSR.6 == 0) //previous ADC conversion is complete
begin
if ((Aindex%2)==0) ADMUX = 0b11000000; //use A.0 as the input for sampling
if ((Aindex%2)==1) ADMUX = 0b11000001; //use A.1 as the input for sampling
Ain = ADCL;
Ain += ADCH*256; //Ain stores sample value
//sum up the sample values and take the average
sum+=Ain;
Aindex++;
if (Aindex==200)
begin
Aindex=0;
weight = (int)(sum/200);
sum=0;
if (weight>999) //insufficient weight in food bowl (the lighter the bowl, the bigger the value stored in “weight”
begin
addFood=1;
PORTB=0x01; //turn on green LED
end
if (weight<850) //sufficient weight in food bowl
begin
addFood=0;
PORTB=0x02; //turn on red LED
end
end
ADCSR.6=1;
end
if (index>5) motorflag=1; //received signal for turning on motor
if (motorflag==1 && time1==0)
begin
time1=t1;
motorControl(); //activate motor until bowl is full
end
PORTC=motor ;
end
end
//**********************************************************
void motorControl(void)
begin
if (addFood==1) //received signal for turning on motor and food weight insufficient
begin //reduce motor speed by feeding it a PWM signal
if (motor==0)
begin
motor=1;
time1=onTime; //high signal: 40ms duration
end
else
begin
motor=0;
time1=offTime; //low signal: 200ms duration
end
end
if (addFood==0) //received signal for turning on motor but food weight is sufficient
begin
motorflag=0; //turn off motor and disable this function
index=0;
motor=0;
time1=t1delay;
end
end
//Set it all up
void initialize(void)
begin
//enable ADC and set prescaler to 1/128*16MHz=125,000
//and clear interupt enable
//and start a conversion
ADCSR = 0b11000111;
//Initialize motor control variables (motor output through PORTC.0)
DDRC.0=1;
PORTC.0=0;
time1=0;
addFood=1;
index=0;
motorflag=0;
motor=0;
Aindex=0;
sum=0;
//serial setup for debugging using printf, etc.
UCSRB = 0x18; //enable interrupts for the UART
//62.5kHz/64 = 976.5625baud
//UBRRL = 16MHz/(16*976.5625baud) - 1 = 1023
UBRRL = 0xff;
UBRRH = 0x03;
DDRB=0xff; //B0 and B1 are LED's, 3 is PWM
//set up timer 0
OCR0=128;
TIMSK=0x01; //turn on timer 0 cmp-match ISR
TCCR0=0b01101001; //turn on pwm
UCSRB.7=1; // RECEIVE , port D bit 0 is input
//crank up the ISRs
#asm
sei
#endasm
end
Figure 4: IR transmit circuit. A RS-232 UART signal is outputted out of port D.0, converted to IrDA standard in the MCP2120 and transmitted with the ZHX1810 (shown by purple line). The MCP2120 is driven by a 62.5kHz square wave (shown by green line).
Figure 5: Voltage divider circuit used to determine the weight of the food bowl.
Figure 7: IR receive circuit. An IR signal is received by the ZHX1810, converted to a RS-232 UART signal in the MCP2120 and inputted into port D.1 (shown by purple line). The MCP2120 is driven by a 62.5kHz square wave (shown by green line).
Figure 8: Optoisolator circuit used to drive the motor. A 9V battery was used on the motor end.
Item | Number Used | Cost |
Mega32 chip | 2 | $16.00 |
Custom PC board | 2 | $10.00 |
Batteries | 3 | $6.00 |
Small solder board | 1 | $1.00 |
Freescale solder boards | 7 | Scavenged |
ZHX1810 IR Transceiver | 2 | $8.10 |
MCP2120 IR Endec | 2 | Sampled |
IESP12 Force Sensor | 3 | Sampled |
LCD | 1 | $8.00 |
DC Motor | 1 | Scavenged |
Keypad | 1 | Scavenged |
Total | $49.10 |
Datasheets
Code and Design References
Background Reference
1 comments:
This is a great post. I like this topic.This site has lots of advantage.I found many interesting things from this site. It helps me in many ways.Thanks for posting this again. dog feeder timer
Post a Comment