· 7 years ago · Jan 15, 2019, 01:08 PM
1Project End Goals (What I ultimately want to achieve):
2
31) Measure temperature and light in the grow space, my room and outside, mostly because the data should be interesting and defines the environment in and around the grow space. Values to be saved in a database (MySQL in this case) and be graphed through MS Excel through an odbc connection. The light and temperature will also be used as control to ensure the light mentioned in point 2 below, does actually switch on.
42) Switch a 220volt light system on and off on a configurable timetable.
53) Measure soil moisture. I'm going to measure the moisture content in two pots and may even look into setting up checks for possible water overflow or leaks, as a control measure.
64) Switch a 220volt water pump on for a period of time to water the plants, when moisture levels drop below a certain threshold.
75) Control the temperature with the use of a fan/extractor fan.
86) Video Cam to remotely monitor. Website for remote access and control.
9
10//
11
12Step 1: Component List
13
14Software used:
151) Arduino IDE.
162) Microsoft Visual C# 2008 Express Edition.
173) MySQL. Many sources. Try http://sourceforge.net/projects/phpdev5/
184) MySQL/Net 5.2. This provides you with these definitions on the forms code:
19using MySql.Data;
20using MySql.Data.MySqlClient;
215) Microsoft Excel.
226) MySQL Connector/ODBC 5.1. For MS Excel to connect to MySQL through ODBC.
237) SendEmail
248) Fritzing
259) HeidiSQL
26
27Hardware used:
28
291) Arduino Duemilanove
302) 1 x TIP-120 NPN Transistor
313) HFS41
324) 3 x DS18B20
335) 1 x 1N4001 Diode or others
346) 4 x Galvanized Nails
357) Some Cable (I like to use UTP Cat5 cable)
368) 1 x Light Dependent Resistor
379) A breadboard
3810) PC or laptop
3911) A grow light of sorts
4012) Some Jumper Wire
4113) Pots and plants.
4214) 1 x 1.5Kohm resisor
4315) 4 x 4.7Kohm resistor (Does not have to be exact)
44
45
46//
47
48Step 2: Light Control
49
50Objective: Switch the plant grow light on and off at set times of the day.
51
52Component considerations:
53
54- Lights:
55Plants need the right kind of light to grow. Human visible light does not necessarily mean that it has the right light wavelengths required for plant growth. Choices can be:
56MH - Pros: Very strong light with much of the light spectrum necessary for plant growth. Cons: This kind of light gives off a lot of heat, so you will have to invest heavily in cooling your environment. Heat can cause fire. Setup can be expensive.
57
58HPS - pretty much the same as above in my opinion. The Light spectrum differs from MH.
59CFL's - This is my current choice of light. Pros: They can be bought colour tempered, meaning they can either emit warm (red) or cold (blue) light. Much cooler and therefore you will have less trouble with controlling heat. The light can be very close to your plant canopy without burning your plants and the plants will get much more light. Cons: Not as well tested as the above two. Might not have enough light to take plants through a flowering stage. I opted for two 125w .5 amp CFL's. One red and the other blue.
60
61LED's - Pros: Good light spectrum for plant growth. Very cool. Cons: Untested. You will need a lot of led's to match the wattage of other forms of light. This will drive up costs and due to the amount of LEDs, you might even end up having heat issues, purely because of the number of LED's necessary. (I will do some tests in the future.)
62
63- Relay:
64I used a zero crossing HFS41 solid state relay, rated 5amp output at 220-240volt. Depending on the light solution you choose, you might need a bigger and better relay to switch your lights. Be sure what you're doing here. Lights are conductive and the wrong relays can either reduce the lifespan of your devices or at worst, a fire. Ask someone that knows if you don't.
65
66- Transistor:
67For those that don't know how a standard relay works- The relay has 2 inputs and 2 output legs (other combinations exist). Some relays are closed and some open when there is no power running over the input legs. I suggest closed, since it is safer, but you might want it to be open (on) even if your arduino is not switched on. The arduino can only push 40ma out of one digital pin. The HFS41 needs between 3 and 15volt at at least 40ma to switch on. From this you might think you can simply connect the relay input pins directly to one of the arduino pins. I've asked around on forums and everyone I spoke to suggested it would be safer to do the switching with a transistor. So the pin will switch on the transistor. I also want to mention that if you're going to use relays and motors etc. its best to have the arduino power supply connected to the arduino because it will give the arduino more ma's to work with. The USB is only good to provide power to smaller setups. I have both connected, since I need the USB for communication to the computer.
68
69- Cable:
70UTP cat 5. Long enough to reach your grow space.
71
72
73//
74
75Step 3: Light Sensor
76
77Objective: This will be used to tell me if the light has really switched on or not. The kids could have switched it off at the plug. So if the light is switched on, the light sensor should pick it up. If not, it must sms or email me.
78
79- Light Dependant resistor.
80- 1 x 4.7Kohm resistor.
81
82//
83
84Step 4: Temperature Sensors
85
86Objective: I want to record the temperature at many places. I guess you could get away with only one temp sensor in your grow area, but the sensor I use makes taking the temperature at multiple locations easy and fun. The Temperature sensor will also ultimately be used to switch a fan/ extractor fan on to control the temperature. Depending on what you want to grow, you'll need to regulate the temperature. Without any temperature control, my grow space never reached above 30'C with my two 125watt CFL's. So temp control was not high on my list.
87
88- DS18B20: This temp sensor is amazing. The DS18B20 digital thermometer provides 9-bit to 12-bit Celsius temperature measurements and has an alarm function with nonvolatile user-programmable upper and lower trigger points. The DS18B20 communicates over a 1-Wire bus that by definition requires only one data line (and ground) for communication with a central microprocessor. It has an operating temperature range of -55°C to +125°C and is accurate to ±0.5°C over the range of -10°C to +85°C. In addition, the DS18B20 can derive power directly from the data line ("parasite power"), eliminating the need for an external power supply. Each DS18B20 has a unique 64-bit serial code, which allows multiple DS18B20s to function on the same 1-Wire bus. Thus, it is simple to use one microprocessor to control many DS18B20s distributed over a large area. One thing to note is that it takes a bit of time to read this sensor and this will be the only place I use delays in my sketch. I hate those and tried my best to avoid delays wherever I could. More on delays later.
89
90- 3 x 4.7Kohm.
91
92//
93
94Step 5: Moisture Sensors
95
96Objective: Measure the moisture levels in the soil to trigger an event that will water the plants.
97
98- 2 x Galvanized Nails: Basically they're stuck into two ends of your pot and we measure the current across them. The wetter the soil, the better the current flow. This sounds very basic and it is. One has to calibrate your readings which could be challenging. The lower limit in dry soil for me is below 300 and in wet soil above 800. The analogue input of the arduino can read the voltage on a scale of 0 for 0 volts to 1023 for 5 volts.
99
100- 2 x 4.7Kohm resistor.
101
102//
103
104Step 6: Electrical Layout
105
106(photo)
107
108//
109
110Step 7: Arduino Sketch
111
112Objective: All attempts are made to keep this as simple as possible. The arduino should really not do anything, but wait for an instruction and then execute the corresponding code. Therefore a computer is permanently necessary for this project. When I'm done developing and everything is working well, I'll connect it up to a spare old Laptop I have and place it closer to my grow area, as it's currently connected to my desktop computer.
113
114(code template)
115
116//
117
118Step 8: C# Program & MySQL
119
120Objective: The program must control the arduino via the USB serial port. The code to address the port was derived from http://csharp.simpleserial.com/
121
122Todo:
123- Create the MySQL database and tables. Code listed below C# code or see Garden2.zip.
124- You must set your MySQL database username and password in the code.
125- Also set you email details in the emailStatus function.
126
127//
128
129C# Code
130
131// ---------------------------------------------------------------------------------------------------------------------------------
132
133using System;
134using System.Collections.Generic;
135using System.ComponentModel;
136using System.Data;
137using System.Drawing;
138using System.Text;
139using System.Windows.Forms;
140using MySql.Data; // For MySQL
141using MySql.Data.MySqlClient; // For MySQL
142using System.Diagnostics; // To do emailStatus()
143
144
145namespace Garden
146{
147 public partial class Form1 : Form
148 {
149 string RxString, testS, tempS, light, Temp1, Temp2, Temp3, moist1, moist2 = "";
150 double workD, tmp1, tmp2, tmp3, m1, m2 = 0;
151 char call = '0';
152 char[] buff = new char[1];
153 char[] conv = new char[70];
154 int l1, c2, lightOnH, lightOnM, lightOffH, lightOffM = 0;
155 int count = 0;
156 bool state = false;
157
158 public Form1()
159 {
160 InitializeComponent();
161 // Hide some buttons and textboxes. I wanted to create a separate form to set variables
162 // ie. light on time. I couldn't figure out how to pass variables between forms in c#.
163 // This will be on my wish list for later versions. For now I hide things to create more
164 // space on the form.
165 label3.Visible = false;
166 label4.Visible = false;
167 label5.Visible = false;
168 label6.Visible = false;
169 lonhBOX.Visible = false;
170 lonmBOX.Visible = false;
171 loffhBOX.Visible = false;
172 loffmBOX.Visible = false;
173 saveBUT.Visible = false;
174 CancelBUT.Visible = false;
175 }
176
177
178 private void buttonStart_Click(object sender, EventArgs e) // Lets start things.
179 { // All intervals needs to be save in "settings" table. Todo.
180 timer1.Interval = 10000; // Timer for 10 seconds. I display values on the from every
181 // 10 seconds.
182 timer1.Enabled = true; // Start the timer.
183 timer1.Tick += new EventHandler(timer1_Tick); // Setup the event handler for the timer.
184 timer2.Interval = 500; // This happens every .5 second to update the date/time
185 // field on the form.
186 timer2.Enabled = true;
187 timer2.Tick += new EventHandler(timer2_Tick);
188 timer3.Interval = 3600000; // Interval at which to email status to owner.
189 timer3.Enabled = true;
190 timer3.Tick += new EventHandler(timer3_Tick);
191 serialPort1.PortName = "COM3"; // USB serial port number.
192 // Need to make this changeable through setting settings.
193 // To save in the database in the "settings" table.
194 serialPort1.BaudRate = 9600; // Serial port speed.
195 // Sometimes I need to reset the arduino via button on the board
196 // to get things going at start-up. Not sure why the serial
197 // communication does not always work straight away. Once reset and
198 // working it keeps working, so it doesn't bug me too much. FYI.
199 serialPort1.Open(); // Open serial port for communication.
200 if (serialPort1.IsOpen)
201 {
202 buttonStart.Enabled = false;
203 buttonStop.Enabled = true;
204 }
205 ReadMyData(); // Get settings from "settings" table.
206 CallA(); // Send char "a" to the arduino to get values.
207 }
208
209 private void emailStatus()
210 {
211 Process myProc;
212 // Start the process.
213 // Get SendEmail here: http://caspian.dotconf.net/menu/Software/SendEmail/
214 myProc = Process.Start("c:\\email\\sendEmail.exe", "-f your@email.com -t your@email.com -u Status -m " + l1 + "," + tmp1 + "," + tmp2 + "," + tmp3 + "," + m1 + "," + m2 + "; -s your.smtp.com -xu username -xp password");
215 // Stop the process.
216 myProc.CloseMainWindow();
217 }
218
219
220 private void buttonStop_Click(object sender, EventArgs e) // Stop everything.
221 {
222 if (serialPort1.IsOpen)
223 {
224 timer1.Enabled = false; // Stop the timers.
225 timer2.Enabled = false;
226 timer3.Enabled = false;
227 serialPort1.Close(); // Close the serial port.
228 buttonStart.Enabled = true;
229 buttonStop.Enabled = false;
230 }
231
232 }
233
234 private void Form1_FormClosing(object sender, FormClosingEventArgs e)
235 {
236
237 if (serialPort1.IsOpen) serialPort1.Close(); // Close to port if we leave.
238 }
239
240 private void DisplayText(object sender, EventArgs e) // Display form values. You can look here for the
241 // forms text names.
242 {
243
244 lightBox.Text = light; // Display values in textboxes.
245 m1Box.Text = moist1;
246 m2Box.Text = moist2;
247 workD = Convert.ToDouble(Temp1) / 10000; // Get our decimal places back.
248 tmp1 = workD; // Save for Database save.
249 tempS = workD.ToString("N2");
250 tempBox1.Text = tempS;
251 workD = Convert.ToDouble(Temp2) / 10000;
252 tmp2 = workD;
253 tempS = workD.ToString("N2");
254 tempBox2.Text = tempS;
255 workD = Convert.ToDouble(Temp3) / 10000;
256 tmp3 = workD;
257 tempS = workD.ToString("N2");
258 tempBox3.Text = tempS;
259
260 if (state)
261 {
262 stateTextBox.Text = "On";
263 }
264 else
265 {
266 stateTextBox.Text = "Off";
267 }
268 c2 = c2 + 1;
269
270 if (c2 == 6) // Every 6 10 seconds save to database. This is quite allot of data points.
271 {
272 l1 = Convert.ToInt16(light); // Convert values to save in the databse.
273 m1 = Convert.ToInt16(moist1);
274 m2 = Convert.ToInt16(moist2);
275 c2 = 0;
276 // Setup connection string.
277 MySqlConnection MyCon = new MySqlConnection("SERVER=localhost;" + "DATABASE=garden2;" + "UID=root;" + "PASSWORD=password;");
278 // Open connection.
279 MyCon.Open();
280 // Setup the SQL string.
281 MySqlCommand command = new MySqlCommand("insert into readings values (null, null," + l1 + "," + tmp1 + "," + tmp2 + "," + tmp3 + "," + m1 + "," + m2 + ")", MyCon);
282 // Execute the SQL string on the database.
283 command.ExecuteNonQuery();
284 // Close Database connection.
285 MyCon.Close();
286
287 // Do some logic to determine whether to switch the light on or off. This could probably be done in a separate timer event.
288 if (DateTime.Now.Hour >= lightOnH)
289 {
290 if (DateTime.Now.Hour >= lightOffH)
291 {
292 if (DateTime.Now.Minute >= lightOffM)
293 {
294 CallC(); // Light off.
295 }
296 }
297 else
298 {
299 if (DateTime.Now.Minute >= lightOnM)
300 {
301 CallB(); //Light on.
302 }
303 }
304
305 }
306 else
307 {
308 CallC(); // Light off.
309 }
310 }
311 }
312
313 private void Sort(object sender, EventArgs e) // Dissect the string we got from the arduino.
314 {
315 testS = "~"; // The value break char.
316 light = ""; // Clear all values.
317 Temp1 = "";
318 Temp2 = "";
319 Temp3 = "";
320 moist1 = "";
321 moist2 = "";
322 count = 0;
323 while (RxString[count] != testS[0]) // Check first char for break value(~). Repeat till found.
324 {
325 light = light + RxString[count]; // If not add the char to new string(light).
326 count += 1; // Goto next char.
327 }
328 count += 1; // Lets go on to the next char via (count).
329 while (RxString[count] != testS[0])
330 {
331 Temp1 = Temp1 + RxString[count];
332 count += 1;
333 }
334 count += 1;
335 while (RxString[count] != testS[0])
336 {
337 Temp2 = Temp2 + RxString[count];
338 count += 1;
339 }
340 count += 1;
341 while (RxString[count] != testS[0])
342 {
343 Temp3 = Temp3 + RxString[count];
344 count += 1;
345 }
346 count += 1;
347 while (RxString[count] != testS[0])
348 {
349 moist1 = moist1 + RxString[count];
350 count += 1;
351 }
352 count += 1;
353 while (RxString[count] != testS[0])
354 {
355 moist2 = moist2 + RxString[count];
356 count += 1;
357 }
358 // Add more while statements for extra values from arduino.
359 }
360
361 public void ReadMyData() // Read "settings" table.
362 {
363 // SQL query string.
364 string mySelectQuery = "SELECT LOnH, LOnM, LOffH, LOffM FROM settings";
365 // Database connection string.
366 MySqlConnection myConnection = new MySqlConnection("SERVER=localhost;" + "DATABASE=garden2;" + "UID=root;" + "PASSWORD=password;");
367 MySqlCommand myCommand = new MySqlCommand(mySelectQuery, myConnection);
368 // Open database connection.
369 myConnection.Open();
370 MySqlDataReader myReader;
371 // Execute query string.
372 myReader = myCommand.ExecuteReader();
373 // Always call Read before accessing data.
374 while (myReader.Read())
375 {
376 lightOnH = myReader.GetInt16(0);
377 lightOnM = myReader.GetInt16(1);
378 lightOffH = myReader.GetInt16(2);
379 lightOffM = myReader.GetInt16(3);
380 }
381 // Always call Close when done reading.
382 myReader.Close();
383 // Close the connection when done with it.
384 myConnection.Close();
385 }
386
387 private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) // We have data at the port.
388 {
389 System.Threading.Thread.Sleep(50); // A delay to make sure all the data is received at the port.
390 switch (call)
391 {
392 case 'a': // We called a "a".
393 RxString = ""; // Clear the recieve string.
394 RxString = serialPort1.ReadExisting(); // Get the data from the port.
395 this.Invoke(new EventHandler(Sort)); // Dissect the string into individual values.
396 this.Invoke(new EventHandler(DisplayText)); // Display the values on the form.
397 break;
398 case 'c': // We called a "c". Future use.
399 break;
400 case 'd': // We called a "d". Future use.
401 break;
402 case 'e': // We called a "e". Future use.
403 break;
404 }
405 }
406
407 private void CallA() // Send char "a" to serial port.
408 {
409 if (!serialPort1.IsOpen) return;
410 buff[0] = 'a';
411 call = 'a'; // Used to see what call we made to the arduino.
412 serialPort1.Write(buff, 0, 1);
413 }
414
415 private void CallB() // Send char "b" to serial port.
416 {
417 if (!state)
418 {
419 if (!serialPort1.IsOpen) return;
420 buff[0] = 'b';
421 call = 'b';
422 serialPort1.Write(buff, 0, 1);
423 state = true;
424 }
425 }
426
427 private void CallC() // Send char "c" to serial port.
428 {
429 if (state) {
430 if (!serialPort1.IsOpen) return;
431 buff[0] = 'c';
432 call = 'c';
433 serialPort1.Write(buff, 0, 1);
434 state = false;
435 }
436 }
437
438 private void wtime() // Writes date/time to forms textbox.
439 {
440 DateBox.Text = Convert.ToString(DateTime.Now);
441 }
442
443 private void timer1_Tick(object sender, System.EventArgs e) // Timer event.
444 {
445 CallA(); // Send "a". Get values.
446 }
447
448 private void timer2_Tick(object sender, System.EventArgs e)
449 {
450 wtime(); // Call wtime function.
451 }
452
453 private void timer3_Tick(object sender, System.EventArgs e)
454 {
455 emailStatus(); // Call emailStatus function.
456 }
457
458 private void lightOn_Click(object sender, EventArgs e) // Send light on commend to arduino. Char "b".
459 {
460 if (!serialPort1.IsOpen) return;
461 buff[0] = 'b';
462 call = 'b';
463 serialPort1.Write(buff, 0, 1);
464 state = true;
465 }
466
467 private void lightOff_Click(object sender, EventArgs e) // Send light off commend to arduino. Char "c".
468 {
469 if (!serialPort1.IsOpen) return;
470 buff[0] = 'c';
471 call = 'c';
472 serialPort1.Write(buff, 0, 1);
473 state = false;
474 }
475
476 private void settingsBUT_Click(object sender, EventArgs e) // Let's enter new settings.
477 {
478 ReadMyData(); // Get the data from the "settings" table.
479 stateTextBox.Visible = false;
480 lightOn.Visible = false;
481 lightOff.Visible = false;
482 label3.Visible = true;
483 label4.Visible = true;
484 label5.Visible = true;
485 label6.Visible = true;
486 lonhBOX.Visible = true;
487 lonmBOX.Visible = true;
488 loffhBOX.Visible = true;
489 loffmBOX.Visible = true;
490 saveBUT.Visible = true;
491 CancelBUT.Visible = true;
492 lonhBOX.Text = Convert.ToString(lightOnH);
493 lonmBOX.Text = Convert.ToString(lightOnM);
494 loffhBOX.Text = Convert.ToString(lightOffH);
495 loffmBOX.Text = Convert.ToString(lightOffM);
496 }
497
498 private void CancelBUT_Click(object sender, EventArgs e) // Cancel. No change saved to table.
499 {
500 label3.Visible = false;
501 label4.Visible = false;
502 label5.Visible = false;
503 label6.Visible = false;
504 lonhBOX.Visible = false;
505 lonmBOX.Visible = false;
506 loffhBOX.Visible = false;
507 loffmBOX.Visible = false;
508 saveBUT.Visible = false;
509 CancelBUT.Visible = false;
510 stateTextBox.Visible = true;
511 lightOn.Visible = true;
512 lightOff.Visible = true;
513 }
514
515 private void saveBUT_Click(object sender, EventArgs e) // Save settings changes to "settings" table.
516 {
517 lightOnH = Convert.ToInt16(lonhBOX.Text); // Convert textbox strings to integers.
518 lightOnM = Convert.ToInt16(lonmBOX.Text);
519 lightOffH = Convert.ToInt16(loffhBOX.Text);
520 lightOffM = Convert.ToInt16(loffmBOX.Text);
521 // Open Database connection.
522 MySqlConnection MyCon = new MySqlConnection("SERVER=localhost;" + "DATABASE=garden2;" + "UID=root;" + "PASSWORD=password;");
523 MyCon.Open();
524 // Update table SQL string.
525 MySqlCommand command = new MySqlCommand("update settings set LOnH=" + lightOnH + ", LOnM =" + lightOnM + ", LOffH=" + lightOffH + ", LOffM=" + lightOffM, MyCon);
526 command.ExecuteNonQuery();
527 // Close Database connection.
528 MyCon.Close();
529 // Fix the form.
530 label3.Visible = false;
531 label4.Visible = false;
532 label5.Visible = false;
533 label6.Visible = false;
534 lonhBOX.Visible = false;
535 lonmBOX.Visible = false;
536 loffhBOX.Visible = false;
537 loffmBOX.Visible = false;
538 saveBUT.Visible = false;
539 CancelBUT.Visible = false;
540 stateTextBox.Visible = true;
541 lightOn.Visible = true;
542 lightOff.Visible = true;
543 }
544 }
545}
546
547// ---------------------------------------------------------------------------------------------------------------------------------
548
549# HeidiSQL Dump
550#
551# --------------------------------------------------------
552# Host: 127.0.0.1
553# Database: garden2
554# Server version: 5.1.39-community
555# Server OS: Win32
556# Target compatibility: ANSI SQL
557# HeidiSQL version: 4.0
558# Date/time: 2009-11-14 15:57:31
559# --------------------------------------------------------
560
561/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ANSI,NO_BACKSLASH_ESCAPES';*/
562/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;*/
563
564
565#
566# Database structure for database 'garden2'
567#
568
569CREATE DATABASE /*!32312 IF NOT EXISTS*/ "garden2" /*!40100 DEFAULT CHARACTER SET utf8 */;
570
571USE "garden2";
572
573
574#
575# Table structure for table 'readings'
576#
577
578CREATE TABLE /*!32312 IF NOT EXISTS*/ "readings" (
579 "Id" bigint(20) unsigned NOT NULL AUTO_INCREMENT,
580 "dtime" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
581 "light" int(10) DEFAULT NULL,
582 "temp1" double DEFAULT NULL,
583 "temp2" double DEFAULT NULL,
584 "temp3" double DEFAULT NULL,
585 "m1" int(10) DEFAULT NULL,
586 "m2" int(10) DEFAULT NULL,
587 PRIMARY KEY ("Id"),
588 UNIQUE KEY "Id" ("Id"),
589 KEY "Id_2" ("Id")
590);
591
592
593
594#
595# Table structure for table 'settings'
596#
597
598CREATE TABLE /*!32312 IF NOT EXISTS*/ "settings" (
599 "LOnH" tinyint(2) unsigned NOT NULL,
600 "LOnM" tinyint(2) unsigned NOT NULL,
601 "LOffH" tinyint(2) unsigned NOT NULL,
602 "LOffM" tinyint(2) unsigned NOT NULL
603);
604
605/*!40101 SET SQL_MODE=@OLD_SQL_MODE;*/
606/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;*/