Automatic Bird Feeder
Our project was an automatic bird feeder.
It uses an IR sensor to determine how much food is in the dish currently. If there isn't enough food in the dish, it will turn the servo (which would be connected to a hopper door) to allow food to fall from the hopper into the dish. When there is enough food, the IR sensor detects this and closes the hopper. If for any reason the hardware fails and leaves the hopper open or there is no more food to fill the dish, then an error is thrown. The hopper is forced closed and an error LED pattern is shown. In order to fix this issue, a hard reset is required.
The bird feeder can also be used over wifi. On its page you can turn the web control function on or off. When web control is on, you can open or close the hopper manually through your network. When you turn web control off, the automatic control as described above takes over. On the page you can also check the state of the feeder. If there is an error, the web page will tell you so you can go fix it.
There is also a manual override switch available. When the manual mode is switched on, web control and autotomatic control features are disabled. The hopper can be controlled with a switch. When the manual override is switched off, the feeder returns to web control or automatic control.
Parts used: Sharp IR Sensor, Adafruit ESP8266 wifi board, Hitec HS-422 Servo
Sharp IR Sensor
mbed | IR sensor |
---|---|
+5V | red |
GND | black |
p15 | yellow |
Adafruit ESP8266 Board
mbed | Wifi Board |
---|---|
+5V | V+ |
GND | GND |
p28 | RX |
p27 | TX |
p26 | RST |
Hitec HS-422 Servo
mbed | Servo |
---|---|
+5V | red |
GND | black |
p21 | yellow |
Board Picture:
Demo:
Main Code:
main.cpp
// ESP8266 Static page WEB server to control Mbed #include "mbed.h" #include "uLCD_4DGL.h" #include "Servo.h" Serial pc(USBTX, USBRX); Serial esp(p28, p27); // tx, rx DigitalIn hardwareEnableSwitch(p30, PullDown); DigitalIn hardwareValveSwitch(p29, PullDown); //DigitalOut isBrokenPin(p23); Timer timer; volatile float distance = 0; volatile bool isBroken = false; volatile bool hardwareControl = false; volatile bool webControl = false; volatile float webControlServo = 0; AnalogIn distanceSensorIR(p15); uLCD_4DGL uLCD(p9,p10,p8); Servo servo(p21); // Standard Mbed LED definitions DigitalOut led1(LED1); DigitalOut led2(LED2); DigitalOut led3(LED3); DigitalOut led4(LED4); // some test values to show on web page AnalogIn Ain1(p18); AnalogIn Ain2(p19); /* char ssid[32] = "hsd"; // enter WiFi router ssid inside the quotes char pwd [32] = "austin123"; // enter WiFi router password inside the quotes */ float temperature, AdcIn, Ht; float R1=100000, R2=10000; // resistor values to give a 10:1 reduction of measured AnalogIn voltage char Vcc[10]; char Temp[10]; // things for sending/receiving data over serial volatile int tx_in=0; volatile int tx_out=0; volatile int rx_in=0; volatile int rx_out=0; const int buffer_size = 4095; char tx_buffer[buffer_size+1]; char rx_buffer[buffer_size+1]; void Tx_interrupt(); void Rx_interrupt(); void send_line(); void read_line(); int DataRX; int update; int count; char cmdbuff[1024]; char replybuff[4096]; char webdata[4096]; // This may need to be bigger depending on WEB browser used char webbuff[4096]; // Currently using 1986 characters, Increase this if more web page data added char timebuf[30]; void SendCMD(),getreply(),ReadWebData(),startserver(); void gettime(),setRTC(),gettemp(),getbattery(); char rx_line[1024]; int port =80; // set server port int SERVtimeout =5; // set server timeout in seconds in case link breaks. struct tm t; // manual set RTC values int minute =00; // 0-59 int hour =12; // 2-23 int dayofmonth =26; // 1-31 int month =8; // 1-12 int year =15; // last 2 digits int main() { //isBrokenPin = 0; float range=0.0005; float degrees=0.0; servo.calibrate(range, degrees); servo = 0; pc.baud(9600); esp.baud(9600); led1=1,led2=0,led3=0, led4=0; // Setup a serial interrupt function to receive data esp.attach(&Rx_interrupt, Serial::RxIrq); // Setup a serial interrupt function to transmit data esp.attach(&Tx_interrupt, Serial::TxIrq); if (time(NULL) < 1420070400) { setRTC(); } startserver(); DataRX=0; count=0; wait(1.0); while(1) { hardwareControl = (hardwareEnableSwitch == 1); if (hardwareControl) { uLCD.cls(); uLCD.printf("HARDWARECONTROL\n"); timer.stop(); timer.reset(); servo = hardwareValveSwitch; } else if (webControl) { uLCD.cls(); uLCD.printf("WEBCONTROL\n"); timer.stop(); timer.reset(); servo = webControlServo; } else if (!hardwareControl && !webControl) { uLCD.cls(); uLCD.printf("AUTOCONTROL\n"); distance = distanceSensorIR.read(); if (timer.read() > 10.0) { //if feeder has been open too long isBroken = true; } else { if (servo == 0) { //if feeder is closed if (distance < 1.65/3.3) { //if food level > 15 cm away servo = 1; //open feeder uLCD.printf("Open valve"); timer.start(); //start timer } } else { if (distance > 2.3/3.3) { //if food level is < 10cm away servo = 0; //close feeder uLCD.printf("Close valve"); timer.stop(); timer.reset(); } } } } else { isBroken = true; } if (isBroken) { servo = 0; //close feeder //isBrokenPin = 1; led1 = 1; led2 = 0; led3 = 0; led4 = 1; while(isBroken) { led1 = !led1; led2 = !led2; led3 = !led3; led4 = !led4; wait(0.2); } } if(DataRX==1) { ReadWebData(); esp.attach(&Rx_interrupt, Serial::RxIrq); } if(update==1) // update time, hit count, and analog levels in the HUZZAH chip { // get new values gettime(); gettemp(); getbattery(); count++; // send new values sprintf(cmdbuff, "count,time,analog1,analog2=%d,\"%s\",\"%s\",\"%s\"\r\n",count,timebuf,Temp,Vcc); SendCMD(); getreply(); update=0; } } } // Reads and processes GET and POST web data void ReadWebData() { wait_ms(200); esp.attach(NULL,Serial::RxIrq); DataRX=0; memset(webdata, '\0', sizeof(webdata)); strcpy(webdata, rx_buffer); memset(rx_buffer, '\0', sizeof(rx_buffer)); rx_in = 0; rx_out = 0; // check web data for form information if( strstr(webdata, "check=led1v") != NULL ) { led1=!led1; webControlServo = 1; } if( strstr(webdata, "check=led2v") != NULL ) { led2=!led2; webControlServo = 0; } if( strstr(webdata, "check=led3v") != NULL ) { if (!hardwareControl) { webControl = !webControl; } } if( strstr(webdata, "check=led4v") != NULL ) { led4=!led4; } if( strstr(webdata, "POST") != NULL ) { // set update flag if POST request update=1; } if( strstr(webdata, "GET") != NULL && strstr(webdata, "favicon") == NULL ) { // set update flag for GET request but do not want to update for favicon requests update=1; } } // Starts webserver void startserver() { gettime(); gettemp(); getbattery(); pc.printf("++++++++++ Resetting ESP ++++++++++\r\n"); strcpy(cmdbuff,"node.restart()\r\n"); SendCMD(); wait(2); getreply(); pc.printf("\n++++++++++ Starting Server ++++++++++\r\n> "); // initial values sprintf(cmdbuff, "count,time,analog1,analog2=0,\"%s\",\"%s\",\"%s\"\r\n",timebuf,Temp,Vcc); SendCMD(); getreply(); wait(0.5); //create server sprintf(cmdbuff, "srv=net.createServer(net.TCP,%d)\r\n",SERVtimeout); SendCMD(); getreply(); wait(0.5); strcpy(cmdbuff,"srv:listen(80,function(conn)\r\n"); SendCMD(); getreply(); wait(0.3); strcpy(cmdbuff,"conn:on(\"receive\",function(conn,payload) \r\n"); SendCMD(); getreply(); wait(0.3); //print data to mbed strcpy(cmdbuff,"print(payload)\r\n"); SendCMD(); getreply(); wait(0.2); //web page data strcpy(cmdbuff,"conn:send('<!DOCTYPE html><html><body><h1>ECE4180 IoT Auto BirdFeeder</h1>')\r\n"); SendCMD(); getreply(); wait(0.4); if (isBroken) { strcpy(cmdbuff,"conn:send('Current Status: Broken')\r\n"); SendCMD(); getreply(); } else { strcpy(cmdbuff,"conn:send('Current Status: Working')\r\n"); SendCMD(); getreply(); } wait(0.2); strcpy(cmdbuff,"conn:send('Food Level: '..analog1..' cm<br>Analog 2: '..analog2..' V<br><hr>')\r\n"); SendCMD(); getreply(); wait(0.3); strcpy(cmdbuff,"conn:send('<form method=\"POST\"')\r\n"); SendCMD(); getreply(); wait(0.3); strcpy(cmdbuff, "conn:send('<p><input type=\"checkbox\" name=\"check\" value=\"led1v\"> Open Feeder')\r\n"); SendCMD(); getreply(); wait(0.3); strcpy(cmdbuff, "conn:send('<p><input type=\"checkbox\" name=\"check\" value=\"led2v\">Close Feeder')\r\n"); SendCMD(); getreply(); wait(0.3); strcpy(cmdbuff, "conn:send('<p><input type=\"checkbox\" name=\"check\" value=\"led3v\"> Toggle Webcontrol')\r\n"); SendCMD(); getreply(); wait(0.3); strcpy(cmdbuff,"conn:send('<p><input type=\"submit\" value=\"send-refresh\"></form>')\r\n"); SendCMD(); getreply(); wait(0.3); strcpy(cmdbuff, "conn:send('<p><h2>How to use:</h2><ul><li>Select a checkbox to flip on/off</li><li>Click Send-Refresh to send data and refresh values</li></ul></body></html>')\r\n"); SendCMD(); getreply(); wait(0.5); // end web page data strcpy(cmdbuff, "conn:on(\"sent\",function(conn) conn:close() end)\r\n"); // close current connection SendCMD(); getreply(); wait(0.3); strcpy(cmdbuff, "end)\r\n"); SendCMD(); getreply(); wait(0.2); strcpy(cmdbuff, "end)\r\n"); SendCMD(); getreply(); wait(0.2); strcpy(cmdbuff, "tmr.alarm(0, 1000, 1, function()\r\n"); SendCMD(); getreply(); wait(0.2); strcpy(cmdbuff, "if wifi.sta.getip() == nil then\r\n"); SendCMD(); getreply(); wait(0.2); strcpy(cmdbuff, "print(\"Connecting to AP...\\n\")\r\n"); SendCMD(); getreply(); wait(0.2); strcpy(cmdbuff, "else\r\n"); SendCMD(); getreply(); wait(0.2); strcpy(cmdbuff, "ip, nm, gw=wifi.sta.getip()\r\n"); SendCMD(); getreply(); wait(0.2); strcpy(cmdbuff,"print(\"IP Address: \",ip)\r\n"); SendCMD(); getreply(); wait(0.2); strcpy(cmdbuff,"tmr.stop(0)\r\n"); SendCMD(); getreply(); wait(0.2); strcpy(cmdbuff,"end\r\n"); SendCMD(); getreply(); wait(0.2); strcpy(cmdbuff,"end)\r\n"); SendCMD(); getreply(); wait(0.2); pc.printf("\n\n++++++++++ Ready ++++++++++\r\n\n"); } // ESP Command data send void SendCMD() { int i; char temp_char; bool empty; i = 0; // Start Critical Section - don't interrupt while changing global buffer variables NVIC_DisableIRQ(UART1_IRQn); empty = (tx_in == tx_out); while ((i==0) || (cmdbuff[i-1] != '\n')) { // Wait if buffer full if (((tx_in + 1) % buffer_size) == tx_out) { // End Critical Section - need to let interrupt routine empty buffer by sending NVIC_EnableIRQ(UART1_IRQn); while (((tx_in + 1) % buffer_size) == tx_out) { } // Start Critical Section - don't interrupt while changing global buffer variables NVIC_DisableIRQ(UART1_IRQn); } tx_buffer[tx_in] = cmdbuff[i]; i++; tx_in = (tx_in + 1) % buffer_size; } if (esp.writeable() && (empty)) { temp_char = tx_buffer[tx_out]; tx_out = (tx_out + 1) % buffer_size; // Send first character to start tx interrupts, if stopped esp.putc(temp_char); } // End Critical Section NVIC_EnableIRQ(UART1_IRQn); return; } // Get Command and ESP status replies void getreply() { read_line(); sscanf(rx_line,replybuff); } // Read a line from the large rx buffer from rx interrupt routine void read_line() { int i; i = 0; // Start Critical Section - don't interrupt while changing global buffer variables NVIC_DisableIRQ(UART1_IRQn); // Loop reading rx buffer characters until end of line character while ((i==0) || (rx_line[i-1] != '\r')) { // Wait if buffer empty if (rx_in == rx_out) { // End Critical Section - need to allow rx interrupt to get new characters for buffer NVIC_EnableIRQ(UART1_IRQn); while (rx_in == rx_out) { } // Start Critical Section - don't interrupt while changing global buffer variables NVIC_DisableIRQ(UART1_IRQn); } rx_line[i] = rx_buffer[rx_out]; i++; rx_out = (rx_out + 1) % buffer_size; } // End Critical Section NVIC_EnableIRQ(UART1_IRQn); rx_line[i-1] = 0; return; } // Interupt Routine to read in data from serial port void Rx_interrupt() { DataRX=1; //led3=1; // Loop just in case more than one character is in UART's receive FIFO buffer // Stop if buffer full while ((esp.readable()) && (((rx_in + 1) % buffer_size) != rx_out)) { rx_buffer[rx_in] = esp.getc(); // Uncomment to Echo to USB serial to watch data flow pc.putc(rx_buffer[rx_in]); rx_in = (rx_in + 1) % buffer_size; } //led3=0; return; } // Interupt Routine to write out data to serial port void Tx_interrupt() { //led2=1; // Loop to fill more than one character in UART's transmit FIFO buffer // Stop if buffer empty while ((esp.writeable()) && (tx_in != tx_out)) { esp.putc(tx_buffer[tx_out]); tx_out = (tx_out + 1) % buffer_size; } //led2=0; return; } void gettime() { time_t seconds = time(NULL); strftime(timebuf,50,"%H:%M:%S %a %d %b %y", localtime(&seconds)); } void setRTC() { t.tm_sec = (0); // 0-59 t.tm_min = (minute); // 0-59 t.tm_hour = (hour); // 0-23 t.tm_mday = (dayofmonth); // 1-31 t.tm_mon = (month-1); // 0-11 "0" = Jan, -1 added for Mbed RCT clock format t.tm_year = ((year)+100); // year since 1900, current DCF year + 100 + 1900 = correct year set_time(mktime(&t)); // set RTC clock } // Analog in example void getbattery() { AdcIn=Ain1.read(); Ht = (AdcIn*3.3); // set the numeric to the exact MCU analog reference voltage for greater accuracy sprintf(Vcc,"%2.3f",Ht); } // Temperature example void gettemp() { AdcIn=Ain2.read(); Ht = (AdcIn*3.3); // set the numeric to the exact MCU analog reference voltage for greater accuracy sprintf(Temp,"%2.3f",Ht); }
1 comment on Automatic Bird Feeder:
Please log in to post comments.
Great project!!! I would like to try myself, can I kindly ask you to share Servo library :)