My father asked me to make an automatic aquarium feeder. He didn’t want to go home every day in summer in order to feed the fish. At first I went with him to Chinese shops, there you can buy such a thing for $ 10, but he didn’t choose anything and had to do the feeding itself. It was assumed that the fish need to be fed twice a day - at 7 and 16 hours. Once you need to know the time, you need to make a clock, in the end I applied the RTC DS1302 module. Food needs to somehow pour in certain doses, then I chose a unipolar stepping motor and Darlington assembly ULN2003 to twist it. To control had to connect the display and keyboard. Later, I wanted to implement a thermostat, for which the DS18B20 sensor was purchased. To save pins, I slapped another shift register 74HC595N through which I connected the LCD display.
I was a little misleading, having used the words “without arduino” in the title of the topic, in fact, I collected a mock-up on it. ')
Thus, I tested the basic functionality of the program.
// pins of a stepper motor const int Step1Pin = 9; const int Step2Pin = 10; const int Step3Pin = 11; const int Step4Pin = 12;
Stepper motor (100, Step1Pin, Step2Pin, Step3Pin, Step4Pin);
int key = 0; int show_time = 1; int CurS = 0; // current tunable parameter (hour / min / sec) long timestamp; long timestamp_eeprom; char k; int ArrT [3]; // time int ArrD [3]; // date Time t; int counter1 = 100; int counter2 = 1; int counter3 = 300; int counter4 = 30; // backlight off counter int temp1 = 0; int ttemp = 0; byte thermostat = 0; byte i; byte data [12]; byte present = 0; float celsius = 0;
// menu MenuSystem ms; Menu mm ("------ Menu ------"); MenuItem mm_mi1 ("Feed Now"); MenuItem mm_mi2 ("Time Setup"); MenuItem mm_mi3 ("Date Setup"); MenuItem mm_mi4 ("Thermostat Setup");
// Run the clock rtc.halt (false); rtc.writeProtect (false);
// read the temperature settings from the EEPROM int TEMP_EEPROM = 0; TEMP_EEPROM = EEPROM_int_read (4); if ((TEMP_EEPROM> = 0) && (TEMP_EEPROM <= 100)) { temp1 = TEMP_EEPROM; }
// Launch the screen with the number of characters and lines lcd.setLED2Pin (HIGH); lcd.begin (16, 2); lcd.clear ();
void loop () { // read the temperature byte addr [8]; if (ds.search (addr)) { ds.reset (); ds.select (addr); ds.write (0x44, 1); // start conversion, with parasite power on at the end delay (1); // 1000 present = ds.reset (); ds.select (addr); ds.write (0xBE); // Read Scratchpad
for (i = 0; i <9; i ++) {// we need 9 bytes data [i] = ds.read (); } int16_t raw = (data [1] << 8) | data [0]; byte cfg = (data [4] & 0x60); // at the lower bits if (cfg == 0x00) raw = raw & ~ 7; // 9 bit resolution, 93.75 ms else if (cfg == 0x20) raw = raw & ~ 3; // 10 bit res, 187.5 ms else if (cfg == 0x40) raw = raw & ~ 1; // 11 bit res, 375 ms // default is 12 bit resolution, 750 ms conversion time celsius = (float) raw / 16.0; }
if (show_time == 1) { //lcd.clear (); counter2--; if (counter2 == 0) { if (counter4> 0) { counter4--; lcd.setLED2Pin (HIGH); } else { lcd.setLED2Pin (LOW); }
counter2 = 10; lcd.setCursor (0, 0); // Set the cursor to print the time in the top row lcd.print (rtc.getTimeStr ()); // Print time lcd.setCursor (9.0);
// timestamp = getEpochTime (rtc.getTime ()); //lcd.print (timestamp); lcd.print (rtc.getDateStr ()); // Print the date } }
// check once per ... seconds counter1--; if (counter1 == 0) { counter1 = 100;
// thermostat if (temp1> 0 && celsius> 0) {// if the settings are not 0 and the sensor outputs more than 0 if (thermostat == 0) { if (celsius <temp1-1) { thermostat = 1; lcd.setLED1Pin (LOW); // on } } else { if (celsius> temp1 + 1) { thermostat = 0; lcd.setLED1Pin (HIGH); // off } } }
// feeder t = rtc.getTime (); int H = t.hour; if ((H == 0) || (H == 8) || (H == 16)) {
if (k == 'u') {incrD ();} if (k == 'd') {decrD ();}
if (k == 'r') { if (CurS <2) { CurS ++; } else { CurS = 0; } delay (300); } if (k == 'l') { if (CurS> 0) { CurS--; } else { CurS = 2; } delay (300); } if (k == 'e') { rtc.setDate (ArrD [0], ArrD [1], ArrD [2]); // Date. k = 'c'; //output } } }
// Time setup void time_setup_selected (MenuItem * p_menu_item) {
lcd.clear (); lcd.setCursor (0,0); lcd.print ("Time setup"); delay (300); int Flash = 0; // or draw numbers or spaces, blinking the selected item int FC = 0; // counter for flash
// function displays hh or mm or cc void flashPrint (int Flash, int CT) { if ((CurS == CT) && (Flash == 0)) { lcd.print (""); } else { if (ArrT [CT] <10) { lcd.print ('0'); } lcd.print (ArrT [CT]); } } // function displays day, month or year void flashPrintDate (int Flash, int CT) { if ((CurS == CT) && (Flash == 0)) { if (CurS == 2) { lcd.print (""); } else { lcd.print (""); } } else { if (ArrD [CT] <10) { lcd.print ('0'); } lcd.print (ArrD [CT]); } }
// function increments selected item by 1 void incr () { int limit = 59; if (CurS == 0) { limit = 23; }
if (ArrT [CurS] <limit) { ArrT [CurS] ++; } else { ArrT [CurS] = 0; } delay (200); } // function reduces the selected item by 1 for the date void decr () { int limit = 59; if (CurS == 0) { limit = 23; } if (ArrT [CurS]> 0) { ArrT [CurS] -; } else { ArrT [CurS] = limit; } delay (200); }
// function increments selected item by 1 for date void incrD () { int limit = 31; if (CurS == 1) { limit = 12; } if (CurS == 2) { limit = 5079; }
if (ArrD [CurS] <limit) { ArrD [CurS] ++; } else { ArrD [CurS] = 1; } delay (200); } // function reduces the selected item by 1 for the date void decrD () { int limit = 31; if (CurS == 1) { limit = 12; } if (CurS == 2) { limit = 5079; } if (ArrD [CurS]> 1) { ArrD [CurS] -; } else { ArrD [CurS] = limit; } delay (200); }
// timestamp uint32_t getEpochTime (Time tm) { int i; uint32_t seconds; int year = tm.year - 1970;
// seconds from 1970 till 1 jan 00:00:00 of the given year seconds = year * (SECS_PER_DAY * 365); for (i = 0; i <year; i ++) { if (LEAP_YEAR (i)) { seconds + = SECS_PER_DAY; // add extra days for leap years } }
// add days for this year, months start from 1 for (i = 1; i <tm.mon; i ++) { if ((i == 2) && LEAP_YEAR (year)) { seconds + = SECS_PER_DAY * 29; } else { seconds + = SECS_PER_DAY * monthDays [i-1]; // monthDay array starts from 0 } } seconds + = (tm.date-1) * SECS_PER_DAY; seconds + = tm.hour * SECS_PER_HOUR; seconds + = tm.min * SECS_PER_MIN; seconds + = tm.sec; return seconds; }
// eeprom long int void EEPROMWritelong (int address, long value) { // Decomposition from a long to 4 bytes by using bitshift. // One = Most significant -> Four = Least significant byte byte four = (value & 0xFF); byte three = ((value >> 8) & 0xFF); byte two = ((value >> 16) & 0xFF); byte one = ((value >> 24) & 0xFF);
// Write the 4 bytes into the eeprom memory. EEPROM.write (address, four); EEPROM.write (address + 1, three); EEPROM.write (address + 2, two); EEPROM.write (address + 3, one); }
long EEPROMReadlong (long address) { // Read the 4 bytes from the eeprom memory. long four = EEPROM.read (address); long three = EEPROM.read (address + 1); long two = EEPROM.read (address + 2); long one = EEPROM.read (address + 3);
// Return the recomposed long by using bitshift. return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF); }
I am not a programmer, so don’t blame the program very much, I know myself that there is a jamb on the joint.
But Arduino UNO itself is a big fee, besides its a pity. It was decided to make a feeder on a clean Atmega328. As a result, I developed a board in Sprint-Layout:
In this version of the board, the DS1302 watch is already divorced right on the board, as well as minor errors have been fixed.
Printed circuit board manufactured by LUT method and tinned with Rose alloy
The keyboard board was also made, it can be seen on the photo of the layout.
Then he soldered all the details:
I have no interface for programming, so I upload all changes to the Arduino, and then simply rearrange the controller in the socket on the board.
The body painted in OpenSCAD and printed on a 3D printer
On the housing cover there are two ears, in case someone wants to fix it directly on the glass of the aquarium.
I also painted and printed out the hull for the trough itself.
Inside this thing should spin such a cylinder with slots:
Keyboard and watch module fixed with hot melt:
And he collected everything to the heap:
Here you can see the result of the work:
I tried, everything works. I gave the device to my father for testing. Surely there will be shortcomings when we test - accomplish your goal. about results and corrections.
After everything was done, I drew a diagram:
Do not kick the very first time using the Eagle
R1, R2 - 10 kOhm, R3 - 3 kOhm, resistors on the buttons of 2.2 kOhm, C1, C2 - 20 pF, the electrolyte C3 is 5 μF, the electrolyte also costs about 1000 microfarads. Quartz for the microcontroller 16 MHz, any npn transistor.
According to the results, you can estimate the estimate:
1. Atmega328 microcontroller - $ 1.8; 2. 16x2 LCD screen - $ 2.3; 3. Thermal sensor DS18B20 - $ 3; 4. The RTC DS1302 clock module is $ 3.5; 5. ABS plastic for 3d printing 84g. - $ 2.5 6. Stepper motor, quartz, resistors, Conder, textolite, etc. - $ 1, as if such a good enough. Total: ~ $ 14 .
What I would like to do in the future
1. Russify the interface. To do this, you need to deal with the LiquidCrystal595Rus.h library; 2. Make a setting for adjusting the dose of feed; 3. Make the setting for selecting the feeding time; 4. You can control the light and the compressor, for this there are free pins and legs ULN2003; 5. If the motor is switched to analog pins, then there will be a place to connect the Wi-Fi module, thus it can be linked to OpenHAB to see temperature graphs and always know if the device is working, even when abroad.
In conclusion, I want to add that all the source code is uploaded to github . There are also boards in layout, and openscad, stl, source program files.