FinalReport
FinalReport
FinalReport
Efficient Growth
Justin Fecteau
Patrick Pomaville
Samuel Metevia
Patricia Huang
Jacob Jones
Saleh Alghamdi
This report has been prepared by Design Team 8 as part of the Electrical and
Computer Engineering Senior Design course at Michigan State University during the
spring 2016 semester. The team was sponsored by Great Lakes Controls and
Engineering and was tasked with developing an aeroponic control system for efficient
growth of leafy greens. The developed system allows for the automatic growth and
monitoring of leafy greens suspended in air.
This report includes an introduction to the project, background information on
aeroponics, an exploration of possible solutions, a technical description of the work
performed, verification of functionality, a description of design issues, a discussion of
the final cost and schedule, and suggestions for future work. Additionally, several
appendices include descriptions of the technical roles and work of each team member,
references, electrical schematics, computer code, and a complete bill of materials.
1
Acknowledgements:
Design Team 8 of ECE 480 at Michigan State University would like to thank
everyone involved in the development of the project:
● Team Sponsors Justin Walz and Ryan Palmer for overseeing the project and
for providing several parts to make the project affordable
● Team Facilitator Professor Robert McGough for his guidance and support
throughout the duration of the project
● ECE Lab Technician Mr. Gregg Mulder for providing various small
components and tools for building the project
● ECE Shipping/Receiving Specialist Ms. Roxanne Peacock for assisting with
easily ordering and receiving parts
● ECE 480 Professor Timothy Grotjohn and Lalita Udpa for overseeing the
course and for providing information throughout the semester
● Technician Support Katie Rose for various technical assistance including
PCB fabrication, wrapping, and wiring
2
Table of Contents:
3
4.2 - Overall System with Sensors…………………………………..…54
4.3 - Plant Growth………………………………………………………..58
4
Chapter 1: Introduction and Background
1.1: Introduction
Great Lakes Controls and Engineering is interested in developing an
automatically controlled aeroponic system for growing leafy greens suspended in air
without soil. They tasked the ECE 480 Design Team 8 with developing a prototype. The
intention is to eventually mass produce this system and make it available to all, so the
team and sponsor hope that this prototype will lay the foundation for future work on
further developing such a system for commercial use.
The team has created a prototype of this closed loop system. Users are able to
monitor several features of the growth cycle while also setting recipes for automatic
growth. This is done through various sensors, a Human Machine Interface (HMI), and a
microcontroller. The team was primarily responsible for the selection of these
components, for building and programming the subsystems, and for testing the overall
system.
1.2: Background
Aeroponics is the process of growing a plant with the roots hanging in the air
rather than in soil or another medium. The roots are then sprayed, or misted, with a
nutrient rich solution in order to feed the plant. Aeroponics was first used in the 1920s
by botanists in an attempt to better study root structures. The growing method wasn’t
known to many until NASA funded an aeroponics operation in the 1990s. The main
benefit of an aeroponic system is increased growth due to a greater surface area of the
roots receiving oxygen and nutrients. Another benefit of having roots suspended in air is
being able to fit more plants in a given area. The recent trends in indoor growing have
been the use of hydroponics and aquaponics. Aeroponics has mostly been available in
large scale industrial setups, leaving consumers to resort to hydroponics or aquaponics
for their indoor growing needs. Growing with aeroponics uses significantly less water
than growing in hydroponics. This project will bring to fruition an automated indoor
growing system similarly priced to consumer hydroponic systems on the market, while
allowing consumers to reap the benefits that aeroponics provide.
5
Chapter 2: Solution Space and Selected Approach
6
5. The user display should include
d. Ambient air temperature in degrees Fahrenheit with accuracy of ± 1%
e. Relative humidity as a percentage
f. pH level
g. Electrical Conductivity
h. Nutrient Levels
i. Pressure in PSI
Additionally, the sponsor required that the team provide electrical schematics, a
full bill of materials, and an operator’s manual.
Ultimately, the task of the team was to determine the best components, build the
system to meet the above critical requirements, and test the system to ensure
functionality, all while staying within the very limited budget.
7
The overall need of this product was to create a user friendly, cost-effective
aeroponic system capable of growing assorted vegetables with as little user input as
possible. The diagram shows that this was broken down into two categories: basic,
necessary functions of the system and supporting functions of the system, which
needed to be addressed but were not essential to the completion of the system. It was
decided that the two major functions of designing this aeroponic growing system would
be the watering system that provides nutrients to the roots, and the control system that
monitors the condition of the plants and devices. Within the watering system, the user
would need to mix water with nutrients which would then be pressurized and released
onto the roots of the plants. It was requested by the sponsor that this process, along
with the temperature, humidity, conductivity, and pH, be monitored through a
microcontroller and displayed on a HMI. Other design challenges to supplement the two
essential functions of this product were to keep the relative cost as low as possible, and
to ensure that the interface be very user friendly, all the while providing a final product
that would be well built and easily maintained.
8
Table 2.1: Conceptual Design Matrix
Design #1 featured a major low cost emphasis. If this design was chosen, a
handmade polycarbonate enclosure would have been used. Used parts would be
implemented in order to keep a lower cost. The polycarbonate enclosure would be able
to rest on any table or flat surface with access to a 120VAC source. There would also
need to be available space for a plastic water storage tank. The user interface would be
a used, low cost HMI that would not have great resolution and would possibly not be
accessible through Wi-Fi. The use of pH and electrical conductivity sensors would not
be necessary, as long as the consumer was well informed to mix the right ratio of
aeroponic nutrients and water. The best feature of this design would have been the
ability to customize the enclosure to any functionality necessary to grow the best
possible produce. However, it was decided that this design would not be of sufficient
quality and would not be easily reproduced, as several parts would be used.
For Design #2, in order to ensure ease of use of the system, a stand would have
been built out of lumber, which would provide adequate space to house a water storage
tank and pump underneath. The vegetation would be housed in a fish tank. This would
be perfect for an aeroponic growth system, as it would be easily sealable in order to
preserve proper temperature and humidity. A fish tank would also be easily accessible
from the top for cleaning and would provide numerous lighting options. In order to
reduce the possibility of programming error, the temperature, pH, humidity, and
9
electrical conductivity sensors would all be independent systems and monitors to
eliminate the need of a microcontroller. This solution also would eliminate the need of
an HMI as all readings would be available on each sensor’s monitor at the expense of
losing programmable outputs. This would cause the end user to have to manually turn
on and off the watering system periodically during the plant’s life. Despite the very low
potential cost, it was determined that this design would not be automatic enough and
that quality would again be lacking.
For Design #3, a custom enclosure would have been used, which would ensure
quality and dependability. Additionally, a microcontroller/HMI combination system would
be used. This would have allowed for the elimination of a separate microcontroller,
which would save a significant amount of time. However, this would increase the price
of the system, as microcontroller/HMI systems were found to be very expensive. Top of
the line sensors, pumps, fittings, nozzles, and other electrical hardware would provide
long lasting dependability and extremely manageable troubleshooting in the future,
should something break or malfunction. This design would have allowed the mechanical
and electrical installation of this project to be very simple and ergonomic. However, it
was determined that although this design would be of the utmost quality, it would far
exceed the project budget.
After the evaluation of all four designs, it was decided that the best option would
be to use Design #4, which will be described below.
10
Figure 2.2: Final Design Flowchart
The building of this prototype was based on the idea of being able to monitor and
control all system features through the HMI. The HMI screen is designed to show live
readings of all sensors and features options to control various system functions. The
entire system is housed within an aluminum frame, which can be seen in Figure 2.3.
11
Mounted to the side of the frame is a panel which contains the enclosure. This
enclosure, which plugs into a wall outlet, houses the HMI, microcontroller, a relay
system, power supplies, and cabling. An ESP8266 Wi-Fi module is also mounted on the
enclosure which allows for Wi-Fi access through a recipe website developed by the
team. This panel also contains a water pump with pressure switch, a pressure gauge, a
controllable solenoid valve, and an expansion tank. The middle box seen in the above
figure is made of plexiglass and houses a system of nozzles for misting the vegetation,
which receive water from a water basin that sits on the floor. The vegetation sits on the
grated shelf above this box.
The system also contains a temperature sensor, a humidity sensor, and a pH
sensor, which are all constantly monitored on the HMI. The very top shelf, which is
adjustable, contains strips of red and blue LED lights to provide the vegetation with the
proper light spectrum. Additionally, two cooling fans are mounted within the system to
help regulate temperature.
The team believes that this efficient and easy to use design will lay the
foundation for future extensive work and improvements on automatic aeroponic
systems, ultimately resulting in commercialization of such a product. Every aspect of
this design will be described in significant technical detail in Chapter 3 and schematics
of all components can be found in Appendix 3.
12
Table 2.2: Cost to Team Only
13
Table 2.3: Gantt Chart Part A
14
Chapter 3: Technical Description
3.1: Frame
As mentioned above, the entire system is housed within an aluminum frame.
Aluminum was chosen because it is light and is easy to work with. The frame pieces
were provided by the sponsor and assembled by members of the team in the lab. The
original drawing of the frame can be seen in Figure 3.1.
The frame is 6’x4’x2’ and features two grated shelves. The top shelf is designed
to hold the lights and is adjustable. The middle shelf is designed for holding vegetation
within net pots. The roots of the vegetation reach down into the housing below. This
housing is made of plexiglass and is designed to contain the misting nozzles. The
plexiglass prevents water from spraying in all directions and also protects the roots. The
plexiglass housing features a door on one of the long sides of the frame, which allows
for modifications and cleaning of the housing.
In order to keep all equipment in a centralized place, a steel mounting plate was
designed, fabricated, and mounted onto the side of the aluminum frame. This steel
mounting plate was designed to not only keep all electrical equipment in a centralized
15
place for ease of use, but it was also designed to be aesthetically pleasing. This
mounting plate also allowed for conservation of cabling, rather than having cables and
tubing going to each piece of equipment in different places. The enclosure is mounted
by 4 ¼” x 20 nuts and bolts, the WellxTrol expansion tank is mounted by a 5/16” x 24
threaded U-Bolt, lock washers, and 5/16” x 24 nuts, the pump and solenoid valve are
each mounted by 4 10/32” bolts, and the pressure gauge and pressure switch are
mounted each by a zip tie. The mounting plate itself is mounted to the frame by 6 bolts
and T-nuts that slide into the rails on the frame’s aluminum extrusion. The steel
mounting plate and mounted equipment can be seen in Figure 3.2.
3.2: Plumbing
After the initial meeting with the sponsor, it was made clear that the more
expensive plumbing equipment would be provided to the group. This included the pump,
expansion tank, water reservoir, tubing, and filter. When the group received these items,
research was conducted as to what the available options for integrating everything
together was. A plumbing system was designed that is energy efficient and allows for
water to be reused.
16
Some of the assumptions that can be made when dealing with standard
plumbing systems is that when there are threaded materials they should always be NPT
(National Pipe Thread), and the proper way to seal threaded material is to use teflon
tape, pipe dope, or both depending on the stubbornness of the material. Thread Seal
Tape, most commonly referred to as teflon tape, is not only used to seal threads by
acting as a deformable filler, but it also provides lubrication for prevention of seizing
during the removal of the connection. The proper way to install teflon tape is to apply 3-
5 full rotations onto the threaded material in the direction of the tightening threads.
There are various industry standards as to which teflon tape can be used on which
threads, and because the maximum thread size on the aeroponic growing system is
3/4”, the proper color of teflon tape to use it white. One of the hazards of working with
teflon tape is the over application of it which will prevent proper thread seating and
reduce the shear point of each thread. Another way to properly seal threads is the use
of pipe dope. The main difference between this and teflon tape is that pipe dope is not a
solid rather, a paste. It is not recommended to use both teflon tape and pipe dope, but
under the right circumstances using both will ensure a pressure tight fit.
Quick connect fittings were used in this project as a way to provide a water and
pressure tight seal that is cost effective and user friendly. Quick connect fittings are
made out of various materials including nylon and brass. Compared to sweat joint
copper fittings, quick connect fittings are not cost effective, as copper fittings are
generally 10-50% cheaper. One way to offset this price difference is to show that copper
piping is much more expensive than quick connect PVC tubing. Not only is PVC quick
connect tubing easier to work with as it is flexible and can be cut with scissors, the
average homeowner does not have copper cutters, flux, sand cloth, solder, and a
blowtorch at their disposal to use copper sweat fittings and piping. But, compared to
other types of fittings such as NPT and compression, as they are made of brass, quick
connect fittings are generally 100%-300% cheaper than their brass counterparts. The
way quick connect fittings work is by utilizing a series of collets and O-rings in a
sequence to mimic a Chinese finger trap. When a perpendicularly cut piece of the
correct diameter piping is applied to the fitting, the pipe will slide into place and an
overcoming force is felt once the tube is properly seated. At the front of the connection
17
lies a rubber O-ring which provides a watertight seal against the tube, while the collet
holds the tube in place. The only way to remove the tube from the fitting without
breaking anything is to put force on the collet in the opposite direction of the outgoing
pipe before pulling the tube out. Because this system requires very few tools, it is the
perfect method for the average consumer. However over time, the rubber O-rings will
begin to leak water, as they will eventually lose their lubrication, crack, and lose the
watertight connection.
There are a sequence of water reservoirs and tanks integrated into the plumbing
circuit for various reasons. The first of which is a 18 gallon blue tote acting as the
reservoir for all water holding. A hole was cut into the water reservoir to provide ample
space for the mounting hardware used by the water supply and pH probe. From the
water reservoir, a ⅜” piece of tubing was cut and secured to the frame which will
prevent anything from getting caught on it. The water reservoir, mounting hardware, and
pH cable can be seen in Figure 3.3.
The first ⅜” piece of tubing acts as the main supply for the entire system which
connects to the pump located on the bottom right corner of the steel mounting plate.
The pump, the Aquatec series 8800 24VAC pump, is a permanent, totally
enclosed, non-ventilated diaphragm pump. This pump includes 3 diaphragms, is self-
priming, and capable of being run dry. This pump is capable of pressurizing up to 125
18
PSI safely, well within the design specifications of the sponsor. This pump also features
⅜” quick connections which allows for easy installation. In line with the pump, using ⅜”
tubing and quick connect connections, is an 80 PSI factory set pressure switch. This
PSWX80 pressure switch is capable of adjustment from 70 PSI - 110 PSI. This pressure
switch has a 20 PSI pressure swing in that if the nominal pressure is 90 PSI, the switch
will close at 70 PSI allowing the pump to turn on and the switch will open when the
nominal pressure is reached. The Aquatec 8800 pump and pressure switch can be seen
in Figure 3.4.
After the in line pump and pressure switch is the WellxTrol expansion tank,
connected by a series of tubing and fittings. From the pressure switch, a ⅜” tube is
connected to a ⅜” x ⅜” x ⅜” quick connect tee. This tee has 3 connections, one from
the pump supply, one to the expansion tank, and one to the rest of the plumbing circuit.
This expansion tank, the WellxTrol WX101 is connected by a ⅜” tube connected to a ⅜”
QC x ½” NPT reducer, to a ¾” NPT x ½” NPT galvanized reducer using teflon tape and
pipe dope as sealant. This expansion tank, known for its dependability and durability
was the perfect choice for this application. The WellxTrol 2 gallon tank provides plenty
of volume to support continuous misting cycles reducing strain on the pump. The idea
behind this is if there was no expansion tank, the pump would have to pressurize the
19
system from 0 PSI to 80 PSI every time water is needed, which may be once every
hour. Putting this kind of strain on a small RO pump is very inefficient, so by adding this
expansion tank, the pump pressurizes the system by pumping water into this expansion
tank. The expansion tank does this by containing pressurized air supplied from the
pump. Water is not compressible, so this expansion tank has a rubber diaphragm which
separates air and water. The pump pressurizes the system by pumping water into the
expansion tank and pushing the rubber diaphragm into the tank, reducing the size of the
air pocket inside. This provides the pressure for the system. The expansion tank is held
onto the steel mounting plate by a custom 5/16”x24 threaded rod U-Bolt and supported
by 5/16”x24 lock washers and nuts. The expansion tank and mounting hardware can be
seen in Figure 3.5.
With two of the three tee connections accounted for, that leaves the last
connection going to the rest of the plumbing circuit. From the last connection of the ⅜” x
⅜” x ⅜” quick connect tee is a ⅜” QC x ¼” QC reducer, which is the OD of the rest of
the material used in the plumbing circuit. In line with the circuit is a 1-100 PSI air filled
pressure gauge and a 12VDC controllable solenoid valve, which is opened or closed
using the HMI. It is crucial to know the correct pressure in the system because
otherwise the plant roots will not absorb the nutrients correctly, so an in line pressure
20
gauge was installed. This very cost effective pressure gauge, which from the factory has
built in ¼” QC x ¼” QC fittings, is extremely easy to install. After the in line pressure
gauge is a 12VDC panel mountable solenoid valve supplied by ElectricSolenoidValves.
Not only is this solenoid valve cost effective, as it is 300%-500% less expensive than its
other ¼” counterparts, it also comes with screw holes allowing for ease of installation.
This solenoid valve also has ¼” QC x ¼” QC water connections and 2 spade terminal
electrical connections for easy wiring and plumbing. This solenoid valve is capable of
operating and functioning at pressures from 3 PSI - 115 PSI and requires 5W of total
power at 12VDC. To ensure that water is conserved to its maximum potential, the valve
can open in less than one second, allowing for water to not be wasted at lower
pressures in the time it takes for the valve to open. The pressure gauge and electric
solenoid valve can be seen in Figure 3.6 and Figure 3.7 respectively.
Figure 3.6: Air Filled Pressure Gauge Figure 3.7: 12VDC Solenoid Valve
From the ¼” QC connection on the 12VDC solenoid valve, a ¼” tube goes into
the root enclosure to a series of six misting nozzles, which are activated by turning the
solenoid valve on. The misting nozzle section is supported by aluminum brackets zip
tied to the mesh enclosure top as shown in Figure 3.8.
21
Figure 3.8: Misting Nozzles on Aluminum Brackets
The series of misting nozzles contains 5 misting tees and 1 misting elbow. The
misting tees are ¼” QC x ¼” QC x 10/24” NPT and the misting elbow is ¼” QC x 10/24”
NPT. These outside diameters are used because not only are 10/24” NPT threads
industry standard for misting nozzle threads, but ¼” OD is the same diameter tubing as
the solenoid valve. The sponsor requested that the pressure be 80 PSI and that the
water misting droplet size be 50 microns. The proper misting nozzle orifice size given
these design specifications is 0.02”. For one nozzle, at 80 PSI with a 0.02” orifice, the
nozzle will output 2.1 GPH, or 7.95 LPH of water. The nozzle is screwed into the
threaded side of each misting tee and a rubber O-ring provides a watertight seal
between the nozzle and the misting tee as shown in Figure 3.9.
22
After the series of misting nozzles spray the roots of the plants in a set interval,
water is directed to a centralized location to be collected for recycling using landscape
fabric. Not only does this fabric allow oxygen to pass through, which is beneficial for the
roots, but it also helps shield the roots from light, and is still dense enough to drain
water to a designated location. After the water passes through the localized point on the
landscape fabric, which also acts as a filter, it is collected in a metal funnel with a built in
strainer which directs the water back into the reservoir tank using a ¾” hose.
3.3: Enclosure
A 14’’x12’’x6’’ steel Hoffman A1614CH enclosure was used in order to provide a
watertight location for all electronics to be housed. This enclosure is mounted on the
steel plate and houses all electrical components, the main power switch, the HMI and
cutout, and cord grips for cable exiting and entering. To mount the HMI onto the
enclosure, a CNC program was developed using AutoCAD Electrical and inputted into a
5-axis machining mill which cut a seamless cutout. The overall enclosure can be seen in
Figure 3.10 and Figure 3.11. Major components within this enclosure will be discussed
in later sections.
23
Within the enclosure is a metal backplane for all electronics and hardware to be
mounted, which is 14.75” tall x 12.88” wide. On the lower left of this backplane are two
4” utility boxes, one that holds the main power switch, and one that holds a 120VAC
dual outlet. This outlet is used to power the pump’s power supply, a 12VDC power
supply, and a 5VDC power supply which are connected via a three way splitter. Also, in
order to maintain wire organization and cleanliness, the use of cable trunking with
dimensions of 1” x 2.25” and various lengths are mounted on the backplane. All
appropriate wiring and cabling is contained within this trunking. To further enhance the
cleanliness of cable racing, industry standard DIN rails are used with mountable
terminal blocks, end caps, jumpers, and terminal block separators. The use of terminal
blocks and jumpers efficiently splits voltage sources into multiple points as aesthetically
pleasing as possible as seen in Figure 3.12 and Figure 3.13 respectively.
Figure 3.12: 120VAC and GND TB’s Figure 3.13: 5VDC and 12VDC TB’s
To power these terminal block strips, a 14 AWG computer cable is used to power
the 120VAC terminal strip, and in parallel, 12VDC and 5VDC power supplies are used
to power their respective terminal blocks. Cord grips, which are made by a steel
housing, rubber compression ring, steel compression ring, and a tensioning nut, are
used to maintain the watertight seal of the enclosure. These are also aesthetically
24
pleasing and prevent any damage that may be caused by cable exiting and entering the
enclosure. On this particular enclosure, there are four cord grips, one for the power
cable, and three for controls cabling and equipment power. In total, there are two cables
for power, and nine for controls, which are used for their corresponding inputs, outputs
or power. These cord grips and cabling entering and exiting the enclosure can be seen
in Figure 3.14.
Rather than having exposed 120VAC sources and power supplies being
connected by wires and cables, this system employs the use of a dual 120VAC
standard wall outlet. The wall outlet is powered by the 14 AWG computer cable and is in
parallel with the other 120VAC sources. The outlet, power supplies, and three way
splitter can be seen in Figure 3.15.
25
Figure 3.15: Dual 120VAC Outlet with Power Supplies
As an added feature, the system incorporates the use of an in line main power
switch being held by one of the 4” utility boxes. The switch provides an easy method to
power the system on and off. To add the switch, a hole was cut using a pneumatic
cutting wheel and the 4” utility box on the backplane was offset in order for the switch
plate to sit flush on the enclosure housing. The main power switch can be seen in
Figure 3.16.
26
3.4: Relay Board and Printed Circuit Board
A SainSmart 8-channel 5VDC relay was used to switch the system’s lights,
cooling fans and solenoid valve. The relay board can be seen in Figure 3.17
Each relay on the SainSmart board requires 15mA of current to activate. The
microcontroller used in the project, an Arduino MEGA 2560, is able to provide as much
as 40mA per pin. This rating however, is the absolute maximum that the controller can
provide. It is recommended that each pin does not provide more than 20mA to prevent
damage to the device. While the pins of the microcontroller could theoretically drive the
relay board, there is only a very small margin for error. To prevent possible issues, a
PCB circuit was designed to drive the relays using transistors. This PCB circuit can be
seen in Figure 3.18.
27
Figure 3.18: PCB Circuit
28
As the SainSmart relay only requires 15mA to be activated, this design provides
more than enough current to activate the relay while protecting the microcontroller
output pins from a possible overcurrent condition.
As the WiFi module requires 3.3VDC power, a separate power supply circuit was
created. This circuit was also soldered onto the PCB board. It can be seen in the lower
left corner of Figure 3.18. To create a 3.3VDC source, an LM3940 dropout regulator
was used. The WiFi module requires approximately 200mA, which is well below the
LM3940’s rated output of 1A. A 5V input to the LM3940 is provided from the same 5V
power supply as the sensors and the SainSmart relay board. In order to maintain
stability in the regulator’s control loop, a 4.7µF and 47µF are placed between ground
and the input and output, respectively. These capacitors meet or exceed the capacitor
ratings recommended by the manufacturer.
3.5: Sensors
3.5.1: Temperature Sensor
The temperature sensor selected for this system is the DS18B20 digital
temperature sensor. This sensor was selected for several reasons with the primary
reason being that source code was available to easily interface it with the
microcontroller. Additionally, this sensor was selected because it is inexpensive,
waterproof, and came prewired. The sensor communicates over a 1-Wire bus, so only
one data line was required for communication with the microcontroller. Interfacing this
sensor with the microcontroller will be discussed in a later section.
The temperature sensor is used to ensure that the vegetation is always at the
ideal temperature, so the system is designed to automatically activate lights or cooling
fans if needed. The sensor has an operating range of -67 to 257 degrees Fahrenheit
with an accuracy of ±0.5 degrees. This easily met the sponsor requirements. The
sensor tip is mounted on the center surface of the vegetation shelf in order to provide
the temperature within the leaves of any vegetation grown. The sensor can be seen in
Figure 3.19.
29
Figure 3.19: DS18B20 Digital Temperature Sensor
The sensor cable has three wires and is wired back to the enclosure. The black
wire is connected to 5VDC common ground, the red wire is connected to the 5VDC
source, and the blue wire is connected to the microcontroller for communication.
Additionally, a 4.7kΩ pull-up resistor was added between the power and data wires in
order to pull up the voltage and obtain correct readings. The sensor value is
continuously displayed on the HMI.
30
sprayed with the misting nozzles to prevent excessively high readings or a possible
short circuit in the equipment. This setup can be seen below in Figure 3.20 and Figure
3.21.
Figure 3.20: Box for Humidity Sensor Figure 3.21: DHT11 Humidity Sensor
The sensor has three pins and is wired back to the enclosure. The VDD pin is
connected to 5VDC, the GND pin is connected to 5VDC common ground, and the DQ
pin is connected to the microcontroller. This sensor value is also continuously monitored
on the HMI.
3.5.3: PH Sensor
The pH sensor components selected for this project are the Atlas Scientific pH
EZO, Single Circuit Carrier Board, and the American Marine Pinpoint pH Probe.
Compared to other pH sensors available, the pH EZO circuit was chosen because of its
relatively low cost and microcontroller compatibility. The probe measures the pH via the
Pinpoint Probe and is sent to the sensor via BNC connector. The sensor then
communicates with the microcontroller using serial communication. All three of these
components are integrated together to provide a fully functioning pH reading system.
This sensor is capable of full range pH readings from .001 to 14.00 and is
accurate down to two thousandths (+/-0.02). This sensor is capable of operating with
UART and I2C protocols and can be run in continuous or single reading modes. This
sensor is very sensitive and can become dysfunctional should the sensor surface be
31
touched or handled harshly. The Single Circuit Carrier Board, which holds the pH EZO
circuit, is mounted on a standoff in the electrical enclosure on the left side of the
mounting plate as seen in Figure 3.22.
The sensor is powered by the 5VDC source and has a full load amperage of
18.3mA, which is negligible in calculating the full load amperage of the system. The
Single Circuit Carrier Board, which can be seen in Figure 3.22, holds the pH EZO
sensor. The sensor communicates with the microcontroller with the Tx and Rx pins on
the carrier board. These pins connect to the Rx3 and Tx3 pins on the microcontroller.
32
The American Marine Pinpoint Probe, as seen in Figure 3.23, is an 8” long
platinum tipped, fast response, and durable pH probe. Unlike pH testing strips which
can be used once and discarded, the Pinpoint Probe can be used indefinitely. The
probe tip must be in water at all times to ensure correct functionality, otherwise the
probe tip will dry, crack, and break. The probe features a 10’ long cable which allows
the pH sensor to be housed inside the enclosure.
To ensure that the probe will remain in water and be supported in the water
reservoir at all times, an aluminum mounting bracket was fabricated and bolted to the
water reservoir with a rubber clamp holding the probe. The water supply is also zip tied
to this bracket. The mounting bracket, mounted pH probe, and water supply tube can be
seen in Figure 3.24.
33
3.6: Lights and Fans
Lights and fans were used in this system to provide sufficient lumen, wavelength,
and airflow. The lights, which are run off of the 120VAC source, are the CBconcept
SMD5050 red and blue models. These lights, which are shipped in individual strips of
16.4’, are capable of being cut into sections. Two light strips were purchased, one red
and one blue. The light strips provide plenty of coverage, as each strip was cut into two
pieces and mounted onto the frame.
To mount these light strips for uniform distribution, a red-blue-red-blue sequence
was utilized. Each strip, measuring approximately 8’, was zip tied to the frame and
shaped into a “U” pattern to maximize coverage, making sure that the LEDs were
pointing down. Once the light strips had been mounted, careful installation of the cables
was then performed. The rubber shielding had to be carefully removed from the jacket
without damaging the internal wire, and then an 18AWG two conductor cable was
soldered to each internal light lead, taking into consideration the polarity of the LEDs, as
current only flows one way. After soldering the cable to the lights, one end of the cable
was connected to ground, and one was connected the NC side of its respective relay.
The COM side of the relay was then connected to a source of 120VAC, completing the
circuit. The same was done for each of the four light strips.
As each foot of LEDs uses 4.2W, it was calculated that the full load amperage of
the light system is 134.4W at 120VAC. The HMI is able to control these lights by
sending a signal to the microcontroller. Once the microcontroller processes the signal, it
sends an output signal to the relay, closing its respective contact and allowing current to
flow to the specified light strip. The lights are set up in a way that allows individual strips
to be turned on at a time, which therefore simulates a dimmer and provides numerous
lighting options. The light strips and their layout can be seen in Figure 3.25.
34
Figure 3.25: Individual Light Strip Layout
The fans selected for this project are the Arctic F12 PWM PST Low Noise Fans.
These fans were selected because of their relatively low cost and operation of 12VDC.
Two fans were purchased to provide sufficient airflow coverage. Each fan, which uses
0.25A at 12VDC, is capable of moving 74 cubic feet per minute (CFM) and will maintain
a consistent temperature should the vegetation overheat. The two fans are mounted
about 16” apart centered on the width of the frame as shown in Figure 3.26.
35
To mount these fans to the frame, two pieces of aluminum were cut for each and
bolted to the side of the frame. The bottom of each fan provides support to the bracket
and the clear plexiglass was drilled through to provide a point to secure the bracket. The
fan bracket and mounting system can be seen in Figure 3.27.
Once both fans were mounted, each fan cable was spliced together with an 18
AWG two conductor cable to run back to the enclosure. Similar to the lights, one wire of
the cable was connected to the 12VDC common with the other wire going to the NC
side of a relay contact. The COM side of the same contact then has a wire run to a point
of 12VDC. These fans are not only operated through the HMI, but also automatically
turn on or off once the temperature sensor reaches a certain threshold.
36
3.7: Microcontroller
An Arduino MEGA 2560 microcontroller is used in this project to control the
system. This microcontroller can be seen below in Figure 3.28.
The team opted to use an Arduino family microcontroller because of its simplicity.
Unlike the Raspberry Pi, another processor popular with small projects such as this, the
Arduino does not have an operating system. This means that there is less overhead and
executing simple code such as turning lights on and off or reading data from a sensor is
easier. The microcontroller used for this project is being used for communicating with an
HMI, reading data from a sensor, and switching lights, fans, and a valve on and off.
None of these tasks are complex enough to justify the additional features and
complexity of the Raspberry Pi.
Another important consideration was the wide variety of sensors that already
have code libraries available specifically. These libraries made integrating the sensors
into the system much easier, as the code to interpret the sensor data was already
written. Only code to process this data and display the information on the HMI needed
to be written.
Within the Arduino family, there are several different models with different
processors, output and input pins, and connection types. For this project, a particularly
37
important feature was the number of serial connections. The HMI, the ESP8266 WiFi
module, and the pH sensor all communicate with the microcontroller via a 5V serial
connection. Therefore, a microcontroller with multiple serial connections was necessary
for this system.
Out of all Arduino models, the $45.95 MEGA 2560 is the only one that has four
standard serial ports. Other Arduino models, such as the Arduino UNO, which cost
anywhere from $10 to $21 less than the MEGA 2560, can theoretically create extra
serial ports using the SoftwareSerial library. There is a major limitation, however, in that
only one of these ports created with the SoftwareSerial library can receive data at a
time. Since this project is composed of several independent parts that may theoretically
need to communicate simultaneously or near-simultaneously via serial, this is not an
acceptable design choice. With these considerations in mind, the MEGA 2560 was the
clear choice for the project.
An Arduino MEGA 2560 requires an input power source of between 6V and 20V.
Power can be delivered via a barrel connector or a standard USB cable. In this project,
power is delivered via the microcontroller’s barrel connector. As the project already had
a 12V source for the fans and solenoid valve, the microcontroller was also powered with
the 12V supply.
Every Arduino sketch, or program file, contains at least two routines, or functions:
setup() and loop(). The code in setup() is run when the Arduino is first powered on or
the onboard reset button is pressed. The code loop() then runs on indefinitely until the
microcontroller is shut off.
While it is possible to create an entire Arduino sketch using just the default
setup() and loop() methods, this is impractical for a more complex project such as this
one. In any software project, it is important to write code that is logical and readable.
The use of functions is a key method to achieve these goals, as well as to make code
reusable, abstract, and modular. Rather than writing every feature of the program into
one giant block, the function only does a certain task. Therefore, the main body of the
program become much easier to read, as a new reader only sees the function calls
rather than the inner workings of each function. This also means that future users can
38
use the features of these functions with just one line of descriptive code, rather than
copying and pasting dozens of lines each time.
The setup() function is used to initialize variables, and ready the entire system for
operation. All of the output pins corresponding to the relay board are set to “LOW” so
that every component starts out turned off. The setup() function also sets the boolean
state variable corresponding to each component to false, resets all of the timer
variables, and sets some system options such as how often the system should read the
humidity, pH, and temperature, and whether the temperature displayed should be in
degrees Celsius or Fahrenheit. An attempt is made to get recipe values from the WiFi
Module. In the event that the WiFi Module is unable to connect to the internet, a recipe
using default values for the light and valve timers, humidity, temperature, and pH
ranges. These values are obtained from the baby lettuce recipe provided by the team’s
sponsor. Finally, the function calls on the function setLights() to turn on the lights and
set their timers for the appropriate time called for by the recipe.
The loop() function serves as the heart of the entire program. Each time a
function is run, it first checks for any incoming messages from the HMI. If a message is
available, it reacts using the appropriate function. After handling any messages from the
HMI, the timer timeElapsed is compared to the time stored in the variable dataInterval.
By default, this interval is 60 seconds. If 60 seconds have elapsed since the last
reading, temperature, humidity, and pH readings are taken and displayed on the HMI.
The last task done by the loop() function is to check the timers for the lights and the
valve. If the proper amount of time has elapsed since these components were last
toggled, then they are toggled again by calling the switchObject() function.
In this project, the remainder of the functions written can be divided into three
categories based on the three primary tasks of the microcontroller: read and process
data from the sensors, control the lights, fans, and pump, and communicate with the
HMI. The complete Arduino sketch file used in this project, aeroponics.ino, can be found
in the Appendix. A brief overview of how it achieves each of its three primary tasks
follows.
Reading and processing data from the sensors is accomplished with three
functions named after their corresponding sensor and three helper functions to display
39
the data on the HMI: pH(), temp(), humid(), displayPH(), displayTemp(), and
displayHumid(). Each of the main sensor functions are based off of code written by the
manufacturers specifically for Arduino microcontrollers. The specific process for each
sensor varies, but their basic mode of operation is to take voltages in from the sensors
and convert them to decimal or floating point values. The temperature function temp()
also includes code to convert the read temperature from Celsius to Fahrenheit.
The display functions are used to send data to the HMI to display the latest
sensor readings. While this task could theoretically be performed inside of the main
sensor functions, the display functions are useful because they allow the reading to be
displayed without needing to be updated. As will be discussed later, it is necessary to
redisplay the sensor values on the HMI screen every time the display page is loaded.
The next task covered by the helper functions is controlling the system’s lights,
fan, and solenoid valve. This is accomplished with the switchObject() function. The
switchObject() function takes two integer arguments, corresponding to the object being
switched and the current page that the HMI is displaying. Objects can be toggled either
by the user pressing a button on the HMI or by the system automatically when a timer
reaches its set point. Therefore, it is important to know the current page that the HMI is
displaying so that the appropriate updates can be made to the display.
The body of the switchObject() function is merely a large switch statement based
on the object being switched. A switch statement is used rather than a block of if-else
statements so that the processor jumps straight to the case corresponding to the object
being switched rather than parsing through an entire block of if-else statements each
time the function is run. Within the case, the object’s Boolean state variable is switched
to the opposite state, its output pin on the microcontroller is switched from LOW to
HIGH or vice-versa, the togglePic() function is called to make any needed updates to
the HMI screen, and the timers corresponding to the object are reset.
The final set of functions are used to communicate with the HMI. These functions
include readHMI(), sendMessage(), buttonPressed(), togglePic(), and loadPage(). The
function readHMI() is used to process incoming messages from the HMI. Messages are
sent via Serial using the sendMessage() function, while the remaining three functions
40
are used to interpret incoming messages from the HMI and do the necessary tasks
associated with them.
Each time the main loop() function is run, readHMI() is called on to receive any
messages that the HMI may have sent. When a message is received, the readHMI()
function stores it in a String variable until the character “0xff” is seen three times in a
row. This character sequence is used by the HMI to signal the end of a unique
message. Therefore, once this sequence is read, the message is complete. The
complete message string is returned by the readHMI() function for further processing.
The sendMessage() function takes in a string input corresponding to the
message that is to be sent to the HMI, writes it to the Serial buffer, and then writes the
character “0xff” three times in succession. This last step is necessary as the character
“0xff” is used by the HMI to signal the end of messages that it sends as well as those
that it receives.
It would theoretically be possible to send Messages to the HMI without the use of
a helper function. The programmer could simply write their messages to the correct
serial buffer and remember to add the proper character sequence at the end of each
message. The addition of a helper function provides two advantages. First, if the
programmer changes which serial port that the HMI is connected to, the serial write
commands only need to be changed inside of the helper function rather than
everywhere that a message is sent. Secondly, writing the end sequence every time a
message is sent makes the program longer and has a much higher potential for error,
as if the programmer forgets to add this sequence or adds it incorrectly even once,
certain messages will not be sent properly. This can lead to frustrating debugging
sessions.
Debugging a large software project that interacts with several different outside
components is already difficult. A helper function reduces the possible points of failure
in a software project and saves the programmer valuable time when troubleshooting a
design. Though the sendMessage() function appears to accomplish a simple task, it is
actually a textbook case of why helper functions are necessary on any large software
project.
41
The last three HMI functions process messages from the HMI and update the
display for the user. The togglePic() function is called whenever something in the
happens in the system that necessitates changing a picture on the HMI display. In this
design, this happens whenever one of the lights, the fans, or the solenoid valve
changes state. The togglePic() function takes two integer inputs and boolean that
correspond to the object that has changed, the page that the HMI is displaying, and the
new state of the object, respectively. The function uses this information to send a
message to the HMI telling it to update the affected picture and refresh the display.
The loadPage() function is used to display the proper information on the HMI
when the user changes the page. By default, the HMI will load the prestored display
every time that the user switches the page. In order to display the most recent data, it is
necessary to send a command to set all of the images to their proper state and all of the
text to the proper value.
The loadPage() takes just one integer argument that corresponds to the page
that is being loaded. Depending on which page needs to be loaded, the function sends
messages to the HMI to display certain images and text values. This function is called
every time that the user presses a button that changes the displayed page on the HMI.
Finally, the buttonPressed() function is used to react to any buttons that the user
presses. It takes two integer arguments that correspond to the button that was pressed,
and the page that it was pressed on. Two switch statements are used to perform the
action corresponding to the button that was pushed. Possible actions include toggling
lights, fans, or the valve, loading a new display page on the HMI, requesting an update
to the recipe via WiFi and changing the timers for the lights or solenoid valve.
At its core, the microcontroller used in this project is just a somewhat complicated
switch. Its primary functions are to automatically switch components on and off and to
send and receive messages from sensors and an HMI display. Despite these seemingly
simple tasks, designing efficient and reusable software to achieve them comes with a
certain set of challenges.
42
3.8: HMI
To meet the project requirements for a 6’’ touchscreen HMI (Human Machine
Interface), a Makerfire Nextion NX8048T070 7.0” TFT Display was purchased. This HMI
was selected primarily for its relatively low cost. There are very few HMIs available for
under $100 that meet the project requirements of a 6’’ screen. At $82.98, this HMI was
the best option available that met the project requirements without being a
disproportionate share of the budget. The HMI is mounted to the door of the enclosure,
which is shown in Figure 3.29.
The Nextion NX8048T070 HMI requires 5VDC power for operation and draws
510mA of current at full brightness. This represents over 50% of the 1A rating of this
project’s 5V supply, but it is not a problem as the other components using 5V power
draw no more than 0.03mA total. The total current drawn by the 5V supply is therefore
502mA, which is just 50% of the 5V supply’s rating.
In order to communicate with outside components such as the microcontroller, it
was also crucial that the HMI and any other components share the same ground
43
reference. Without a shared 0V ground reference, the HMI would not be able to properly
interpret messages sent and received by the microcontroller.
The HMI communicates with the microcontroller over a serial connection. In this
project, the HMI’s serial pins are connected to the Serial3 pins of the Arduino
microcontroller. The HMI uses this connection to send information to the microcontroller
and to receive commands to display data and images. As a control sequence to
differentiate multiple commands sent in succession, the Nextion HMI uses the character
“0xff” printed three times in a row. This means that each time “0xff” is printed three
times, the HMI acknowledges that a command is complete. The Arduino functions
“readHMI()” and “sendMessage()” use this to interpret and transmit information by
looking for this control sequence or adding to the end of transmissions respectively.
A unique challenge encountered while working with the Nextion HMI is that when
reassigning a value on the HMI, such as changing displayed text or an image, the HMI
will not recognize commands that use a space anywhere in the assignment. If the user
wants to change textbox t0 to say “test”, they must send the exact command
“t0.txt=”test”” without any whitespace characters. At this time, there are no tutorials or
instructions with the product that mention this, and it stands in contrast to programming
languages such as C and C++ where whitespace in assignment statements is not only
permissible but encouraged in coding standards. Understanding this syntax was crucial
to properly communicating with the HMI.
Another challenge encountered when working with the HMI is that it is not
capable of storing changes to the display sent by the user. When a new page is loaded,
any changes that were made are erased. If the user wishes to return to a page, the
microcontroller must send commands to set the images to the proper state and the text
to the proper numbers. Displaying the correct information necessitated writing a new
function in the microcontroller named loadPage() as was discussed in Section 3.7.
In this project, there are four different pages accessible on the HMI. The first
page, displayed at start up, is the home page. From this page, users can press one of
the labelled buttons to access either the status panel or the recipe page. The recipe
page allows the user to view the currently stored recipe. A recipe is retrieved from the
team database if WiFi access is available. If the system is not WiFi accessible, the
44
default values stored on the Arduino will be displayed as the recipe. Under normal
operation, the recipe will automatically update once an hour. If the user needs or
desires a more immediate update, they can press the green “Get Recipe” button. This
will prompt the WiFi module to get data from the team database immediately and
update the page. In the lower right corner of the screen, users can press the green
button with a picture of a house to return to the home page. The home page and recipe
page are shown below in Figure 3.30 and Figure 3.31.
Figure 3.30: HMI Home Screen Figure 3.31: HMI Recipe Page
On the status panel, users can view the status of the lights, fans, and solenoid
valve via images on the left side of the screen. These images toggle between an on and
an off state when the respective components are turned on and off. On the right side of
the screen, users can see the current temperature, humidity, and pH of the system.
When the temperature is too high or the pH is out of specification, the area will turn red
to let the user know that something is wrong. In the lower right corner of the screen,
users can navigate the other pages. Pressing the green button with a picture of a house
will take them back to the home page, while pressing the orange button with a clock will
take them to the control panel. On the control panel, users can manually switch the
lights, fans, and solenoid valve on and off. They can also adjust how many hours the
lights are turned on each day, how often the solenoid valve opens, and the time that the
valve stays open. Changing the values on this screen does not change the default
45
values displayed on the HMI’s recipe page. The user can reset the system back to the
recipe values by pressing the “Default” button on the right side of the screen. In the
lower right corner of the screen, users can press the green button with a house to return
to the home page. The status panel and control panel are shown below in Figure 3.32
and Figure 3.33.
Figure 3.32: HMI Status Panel Figure 3.33: HMI Control Panel
46
3.9: WiFi Module and Recipe Website
As required, this aeroponic control system is WiFi accessible. The WiFi module
used in this project is an ESP8266 Thing Development board. This module can be seen
in Figure 3.34.
The ESP8266 is a WiFi module that is meant to interact with any standard
microcontroller, such as an Arduino MEGA 2560. There are currently several variations
of the ESP8266 available on the market. The Thing Development Board was chosen
primarily because unlike many other models of the ESP8266, the Thing allows the user
to upload code via a standard Micro-USB cable. Unlike any other system component,
the WiFi Module requires 3.3V power. A discussion of how a 3.3V source was created
for this project can be found in Section 3.4.
In this system, the purpose of the WiFi module is to allow the user to access
recipe data from an online database. These recipes contain system parameters, such
as the amount of time the lights should be turned on each day and the maximum
ambient temperature of the system that the user has selected to grow a particular plant.
At an interval of one hour, or when requested by the user via the HMI, the ESP8266 will
attempt to connect to a database written by the team.
47
An interval of one hour was chosen in order to minimize power consumption by
the WiFi module. When sending or receiving data, the ESP8266 draws an average of
210mA. At 3.3V, running this represents 693mW of power. The ESP8266 uses
programs similar to the Arduino microcontroller, which means that its program runs on
an infinite loop. As the user is unlikely to be constantly updating the recipe, there is no
practical reason for the WiFi module to be constantly connecting to the internet and
drawing close to 700mW of power. If the user does wish to update the recipe more
frequently, this is possible by pressing a button on the HMI as discussed earlier.
Upon receiving data from the team web database, the ESP8266 transmits this
data to the microcontroller, which processes it and makes modifications as necessary.
While the ESP8266 has 500 kilobytes of program storage space compared to the
Arduino MEGA 2560’s 256 kilobytes, the support code necessary for the ESP8266’s
firmware and operating system, as well as the libraries necessary for enabling a WiFi
connection take up much more storage space than a standard Arduino sketch.
Therefore, it is more practical to put the data processing code on the MEGA 2560
microcontroller. The complete sketch file used in this project, WiFi.ino, can be found in
the Appendix.
As mentioned, the team developed a website for sending recipes for growth to
the system. On the recipe home page, there are panels for new users and for existing
users. A new user is required to register a new account. After creating a new account,
the page is directed to create a new recipe page, where the new user can input growth
variable values and other comments. After clicking the submit button on the bottom of
page, a new recipe is pushed to the recipe database. An existing user is required to log
in to review a submitted recipe. After logging in to an account, the page is directed to a
review recipe page. The upper left corner of the page shows the last submitted time,
and the user is able to update the recipe and submit again. Currently, the recipe
database only allows each user to have one recipe at a time. The system will process
the top recipe of the database query and will not process other recipes until the top one
is done. A screenshot from the recipe website can be seen below in Figure 3.35.
48
Figure 3.35: Recipe Website Screenshot
49
plate, and all other electrical hardware. In the case of an electrical short, the shorted
electrical current will have a safe path to travel, rather than through one of the end
users. Additionally, a 15A fuse was installed. By using this 15A fuse in series with the
main source of 120VAC, the main power circuit is safe from any shorts. That is, in the
case that an electrical component becomes shorted to ground, rather than tripping the
circuit breaker in the main 120VAC circuit and turning off everything else on that circuit,
the only thing affected is the fuse. This 15A fuse can be seen in Figure 3.36.
Another way in which safety was taken into consideration in this project was the
use of a main power switch. This switch located on the bottom left side of the electrical
enclosure provides an easy means of turning the system on and off should
troubleshooting every need to occur. This switch also provides a way to eliminate any
intermittent electrical connection when plugging the system into the wall. Additionally,
the decision was made to always terminate loose wire ends and to ensure that there
were no exposed wires. This significantly reduces the possibility of wire and equipment
malfunction.
The next safety aspect factored into this system was the necessity of keeping the
electrical enclosure as watertight as possible. Because almost all electrical and water
components are in a centralized location, it is essential that all electrical and water
components are segregated as much as possible. To achieve this desired result, cord
grips were utilized for the application of exiting and entering cable as seen in Figure
3.14. Cord grips not only prevent wires from being pulled out of their terminals, but a
50
series of rubber grips, steel washers, tensioning nuts, and casings prevent water or dust
from exiting or entering the enclosure.
The last safety measure taken in this system was to ensure that there are two fail
safes on pressurizing the system. That is, the system must not become so pressurized
as to become a makeshift explosive should something unexpected happen. The
mechanical pressure switch turns the power to the pump off automatically once the
calibrated pressure is made. Additionally, should the pressure switch ever fail, the on
board maximum pressure set screw on the pump can be adjusted so that the pump
never pressurizes the system past its design specifications.
51
Chapter 4: Verification of Functionality
Figure 4.1: Blue LED Spectrum Figure 4.2: Red LED Spectrum
This data, along with readily available photosynthetically active radiation (PAR)
charts, was used to determine the Photosynthetic Photon Flux/Lumen (PPF) of the
52
lights. The PPFD values were determined from the PPF values by dividing by the grow
area under the lights and accounting for a 20% loss due to the edges of the grow
chamber being uncovered. The DLI values for various hours of lights-on were then
determined from this estimated PPFD value.
The team elected to test the system with lettuce seeds. Lettuce needs a DLI
minimum of 6 mol/m^2/day and tends to develop tip burn from calcium deficiency when
the DLI exceeds 17 mol/m^2/day. Table 4.1 shows the estimated DLI values that the
lighting system outputs for various light-hours/day.
Light-Hours/Day DLI(Mol/m^2/day)
16 7.52
17 7.99
18 8.46
19 8.93
20 9.4
21 9.87
22 10.34
23 10.81
24 11.28
The purpose of the cooling fans in this control system is to regulate the ambient
temperature of the vegetation growing environment. A maximum temperature is one of
the recipe values specified by the user on the team’s web database. When the ambient
temperature exceeds the user-specified maximum temperature, the fans are
automatically turned on. The fans turn off when the temperature drops four degrees
Fahrenheit below the specified maximum.
53
While testing the system, the team used a recipe with a maximum ambient
temperature of 76 degrees Fahrenheit. Over the course of the testing period, the
surrounding temperature varied between 72 and 82 degrees Fahrenheit. As planned,
the microcontroller did switch on the fans anytime the temperature exceeded 76
degrees, and it kept them on until temperature dropped below 72 degrees. It was
discovered during testing, however, that the fans are not always powerful enough to
dramatically change the temperature in a warm environment. This not a critical issue
because it is expected that users will be placing the system in a climate controlled
environment. Larger fans are recommended for future versions of the design
54
Figure 4.3: Misting Nozzle Spraying
On the data side, the HMI displayed needed to show the user air temperature in
degrees Fahrenheit (with an accuracy of ±1%), relative humidity as a percentage, pH
level, electrical conductivity, nutrient levels, and pressure in PSI. The temperature,
humidity, and pH requirements were met in the final design. As shown in Figure 4.4,
these values are viewable on the Status Panel page of the HMI. The temperature
sensor chosen for this project was specifically selected to meet the requirement of ±1%
accuracy.
55
The electrical conductivity and nutrient level requirements were not met in the
final design. After performing market research, sensors to monitor electrical conductivity
and nutrient levels were not economical to purchase considering the project’s $500
budget. The project’s sponsor was understanding with regards to this constraint, and he
assured the team that it would be acceptable for the final product to not include these
sensors.
The pressure requirement is not met on the HMI as a pressure sensor was also
not economical to purchase for this project. The lack of pressure display on the HMI is
mitigated, however, by the presence of a pressure gauge situated to the right of the HMI
enclosure. This pressure gauge, reading the required system pressure of about 80-100
PSI, can be seen in Figure 4.5. While the user cannot see the pressure directly on the
HMI, they can view it nearby. The spirit, if not the letter, of this requirement was met.
Additionally, the system needed to be WiFi accessible. This criterion was not only
met, but expanded upon through the creation of the recipe website specifically for this
system. This website is able to effectively send recipes to the WiFi module through the
steel enclosure without any problems.
56
One of the overall goals of this project was to develop a system that is efficient.
This goal was accomplished by always taking the conservation and recycling of
resources into consideration. One of these steps to preserve resources was to select
electrical components that did not draw extreme amounts of power. As calculated using
Full Load Amperage (FLA) data from each electrical components data sheet, it is shown
that the max current to ever be present in the system is 5.5548A at various voltage
levels, as seen in Table 4.2.
57
Table 4.3: Sponsor Criteria Status
58
Figure 4.6: Vegetation Shelf Prepped Figure 4.7: Adjusted Lights
A growth medium had to be selected, as the plants in this system are grown
hanging in air. The medium is simply used as a structure and support for the plant. The
team considered rock wool or coconut coir as a medium. After doing some research on
rock wool, it was found that using rock wool on campus is prohibited because it contains
asbestos, so coconut coir was selected. Additionally, House and Garden Aqua Nutrient
Flakes A&B were obtained to assist with growing. Finally, the water reservoir was filled
with distilled water to avoid impurities, per recommendation of the sponsor.
Before planting, the coconut coir needed to be kept in a closed tank filled with
water to saturate the material in order for it to be suitable for plant root growth. After two
days, it expanded and was ready to relocate into the net pots. The team started
germinating without using the system in order to figure out the time needed for the
seeds to grow. The team used baby leaf lettuce seeds as recommended by the sponsor
and discovered that they took one day to start to grow. Seeds were then placed in the
system using the baby leaf lettuce recipe, which can be seen in Figure 4.8.
59
Figure 4.8: Tested Recipe
Several seeds were placed in each coconut coir filled net pot ¼” deep. For this
stage of the plants, it was decided to mist for five minutes every eight hours, since
seedlings do not require much water until roots are developed. The system maintained
a humidity reading of 90% in the root chamber during this period. After one day the
seeds broke the surface of the coconut coir. On the second day the cotyledons were
displayed. Initial growth can be seen in Figure 4.9 and Figure 4.10.
Figure 4.9: Plant Growth - 1 Day Figure 4.10: Plant Growth - 3 Days
60
On the fourth day, the team replaced two of the lettuce net pots with transplanted
wheatgrass for variety in testing. This allowed for a test of how the system functions
with plants more advanced in their growth stage. At the six day mark, some of the
seedlings were starting to shrivel due to excessive temperature caused by the weather.
This can be seen in Figure 4.11.
The fans could not cool the seedlings off fast enough to withstand the
temperature change. Additionally the excess heat caused the humidity in the root
chamber to drop, making the coco coir dry out faster and the plants to require more
water for transpiration. At this point the team changed the misting interval to be on for
one minute every two hours due to the heat and the increase in root mass, which saved
the remaining plants.
Ultimately, the team was successful in starting growth cycles. The team believes
that the system is capable of handling full growth cycles given more time, as actual
plant growth was not the focus or an original requirement of this project. A more ideal
testing environment would also be favorable, as growth conditions were never ideal due
to the only possible testing location being the active and bright design lab.
61
Chapter 5: Design Issues
62
5.2: Design for Simplicity
Another design issue to take into consideration when designing any product is
designing for simplicity. In this case, designing for simplicity means developing the
aeroponic system in such a way that someone with little to no skills could easily replace
broken parts or troubleshoot the system. Obviously, due to the fact that this is just a
prototype and that the timeline and budget were very strictly limited, the simplicity of the
design could be improved. If this prototype were to ever be developed into a commercial
product several improvements could be made in this regard.
One major way to improve the simplicity of this system would be to modify the
enclosure circuitry. In a real production setting, the circuitry within the enclosure could
be combined into one or several printed circuit boards. These boards could be sold
individually to allow for replacements if needed. Another simplification would be to use
components that can be easily disconnected and replaced if broken. The current system
would require some effort to replace a malfunctioning sensor, fan, or light strip. Ideally,
these components could easily snap off and be replaced without having to do any
rewiring.
Currently the HMI is simple to use, but the system could also be made simpler
through additional HMI features. Troubleshooting could be made easy through the use
of a system monitoring screen that shows the current functionality status of each
component. A notification system could be developed that automatically sends a
message to the HMI when a certain component is malfunctioning. Additionally,
automatic software updates could be implemented in a real production setting. This
would allow for constant modifications and features to be added to the system with little
to no effort by the user.
63
Chapter 6: Final Summary
64
6.2: Summary
Overall, the development of the aeroponic control system was a success. The
system meets all critical sponsor requirements and was completed on schedule. As
required, the system allows for the automatic growth and monitoring of leafy greens
suspended in air. The final system is efficient due to the fact that it does not draw a
significant amount of power and that it reuses water. The team has collaborated with
faculty, and experts in manufacturing, aeroponics, and automation to design a fully
functional closed loop control system that will help revolutionize the aeroponic industry.
Although a fully functioning design has been fabricated, there is still much room for
improvement. Future design teams will further implement improvements onto this
system with the hope of eventually providing the system to consumers.
● Black or spray painted root enclosure, as the roots cannot be exposed to light
● Sloped collection tray for water return
● Reduce the height of the root enclosure by at least half, as the current enclosure
is unnecessarily large
● Design a printed circuit board to have the relay system and all power supplies on
one board, as this would allow a significant reduction in enclosure size
● Higher quality HMI
● Black lexan net pot holder would allow for further plant customization, as
opposed to the current steel mesh
● Use 120VAC fans and solenoid valve instead of 12VDC to decrease the size of
the 12VDC power supply and reduce strain on the circuit
● Digital pressure readings to monitor on HMI
● More powerful lights for more growing variety
65
● Attach water reservoir to frame and add wheels to frame in order to easily move
system around
● Clean up mounting panel, make it and enclosure smaller, and expose as little
cabling as possible
● Water agitator, as the water will eventually smell if it sits out too long
● Adding more nozzles would allow for more net pots, meaning more vegetation
● A conductivity or some sort of nutrient sensor if an economical solution becomes
available
● A parts per million (PPM) sensor to monitor nutrition levels
● Code to adjust misting frequency as temperature varies
66
Appendix 1: Individual Technical Roles and Work
67
microcontroller to avoid short-circuiting its power supply, placing the system main power
switch on the left side of the enclosure to improve accessibility, and connecting the 5V
and 12V common to ground so that the microcontroller and HMI shared a common
reference point and were able to communicate.
68
designed the layout and mounted everything inside. He also assembled the frame with
the help of Justin and Jacob. He decided to mount everything in a centralized location
by using steel sheet metal stock, and designed and fabricated the layout of the
mounting plate. He also developed a program on a milling CNC machine to seamlessly
cut out the HMI hole on the enclosure as well as the hole for the main power switch. He
also developed the entire controls circuits and layouts and designed them in AutoCAD
Electrical. He designed and fabricated the entire plumbing circuit, and fabricated the
mounting hardware for the nozzles, fans, pH probe, water source and installed the light
strips onto the frame with the help of Justin and Jacob. In addition to this, he wired the
entire system and troubleshooted the HMI/Arduino communication circuit, fans,
plumbing system, lights, solenoid valve, and all power circuits.
He was also responsible for getting any other hardware necessary for the project,
from landscape fabric to outlet covers. He primed and prepped the water pump with the
help of Justin and Sam, and troubleshooted the plumbing circuit, from leaking threads to
pump calibration. He wrapped the frame in black visqueen with the help of Justin, and
installed the layers of landscape fabric and visqueen to the top of the plant shelf
installed the net pots, and wrapped the root section with landscape fabric to preserve
humidity. He also installed the water return system.
69
and Jacob. This was an extensive process that required a great deal of troubleshooting.
He also helped fabricate the mounting hardware for the humidity sensor and fans.
Justin consistently worked with Patrick to reconfigure the system frame as new
components were added throughout the course of the project and to troubleshoot these
various components. He helped with checking and troubleshooting the wiring during the
initial power up of the system. Additionally, he came up with the method of using four
separate light strips that could be activated individually, and worked on creating these
strips and mounting them. Justin also assisted with preparing the water system and with
coming up with a method for returning water to the water reservoir. Finally, he worked
with Patrick on preparing the system for plant growth by covering the plant shelf with
landscape fabric, installing the net pots, and by wrapping the plexiglass with visqueen.
70
Saleh Alghamdi - Webmaster #2
As a webmaster, Saleh worked on coordinating the team’s website. This
responsibility not only included the design, creation and layout of the website, but it also
meant ensuring that all the team’s documents and media were completely available on
the website. As a webmaster, he had to learn to use HTML and CSS language in order
to create and lay out the website. His role also included adding appropriate pictures that
fit with the project in order to make the page more appealing and informative. He also
worked to update the website based on new changes during the project. This was an
intensive role that required a good familiarity of using HTML and CSS language.
Saleh did research regarding choosing the sensors including humidity,
temperature and pH sensors. He also figured out where to put the sensors in order to
get accurate results. Additionally, he assisted Patrick, Justin and Jacob in building the
basis of the aluminum frame
His other technical contributions include taking care of all the planting and
planting supplies. He was in charge of researching which supplies the team needed
based on calculations from the lights and fans. There was confusion between choosing
rock wool or coconut coir as a medium to germinate seeds. However, his research
indicated that rock wool contains asbestos, which causes a lung cancer, so he chose to
use coconut coir. He also picked the nutrients that the plants needed grow. Finally, he
chose the seeds and was responsible for providing distilled water that was used for
growing the seeds.
71
Figure A1.1: Design Team 8 (from left to right) - Patricia Huang, Saleh Alghamdi,
Samuel Metevia, Patrick Pomaville, Justin Fecteau, Jacob Jones
Appendix 2: References
72
Appendix 3: Mechanical and Electrical Schematics
73
Figure A3.2: Plumbing Circuit
74
Figure A3.3: Main Panel Layout
75
Figure A3.4: Power Distribution #1
76
Figure A3.5: Microcontroller
77
Figure A3.6: HMI, WiFi, and Inputs
78
Figure A3.7: Relay Circuit
79
Figure A3.8: Outputs
80
Figure A3.9: WiFi Power Supply Circuit
81
Figure A3.10: HMI Cutout Layout
82
Figure A3.11: Main Enclosure Layout
83
Appendix 4: Computer Code
Arduino Code:
#include "DHT.h"
#include "OneWire.h"
#include <elapsedMillis.h>
#define DHTPIN 8
#define DHTTYPE DHT11
//Global variables
elapsedMillis timer_recipe;
84
String sensorstring, inputstring, username, recipename;
void setup(){
Serial.begin(9600);
Serial1.begin(115200); //Communication with HMI
Serial2.begin(9600); //Communication with pH sensor
Serial3.begin(115200); //Communication with WiFi
pinMode(31, OUTPUT);
pinMode(32, OUTPUT);
pinMode(33, OUTPUT);
pinMode(34, OUTPUT);
pinMode(35, OUTPUT);
pinMode(36, OUTPUT);
digitalWrite(35, LOW);
digitalWrite(36, LOW);
red_share = 0.53; //% of the light that should be red. Max is 1.0 (100%)
valve_freq_default = valve_freq;
valve_dur_default = valve_dur; //To start, make the defaults the same as
the actual
85
pH_max = 8.0;
temp_min = 60;
temp_max = 81;
humid_min = 75.0;
humid_max = 99.9;
username = "";
recipename = "";
ph = 0.0;
temp_current = 0.0;
humid_current = 0.0;
dht.begin();
setHMI();
setLights();
lights_init = false;
recipe_init = true; //Possible debugging variable
}
void setHMI(){
sendMessage("thsp=300"); //Have the HMI go to sleep after 300 seconds (5
min) with no touch
sendMessage("thup=1"); //Have the HMI exit sleep mode if the user
touches a button
}
//Sets the light timers based on red/blue ratio and hours vs days
void setLights(){
float temp, temp_blu;
temp = red_share *2.0; //Weighting for red vs blue
temp_blu = 2.0 - temp;
int_red1_on = long(float(lights_master*3600000)*temp);
int_red2_on = int_red1_on;
86
if(int_red2_on <0){ //This and the next block are the same checks as
done for the blu timer
int_red2_on = 0;
int_red1_on = 0;
}
if(int_red2_on >86400000){
int_red2_on = 86400000;
int_red1_on = 86400000;
}
if(lights_init){
//Start all lights on
digitalWrite(31, HIGH);
digitalWrite(32, HIGH);
digitalWrite(33, HIGH);
digitalWrite(34, HIGH);
blu1 = true;
blu2 = true;
red1 = true;
red2 = true;
timer_blu1_on = 0;
timer_blu1_off = 0;
timer_blu2_on = 0;
timer_blu2_off = 0;
timer_red1_on = 0;
timer_red1_off = 0;
timer_red2_on = 0;
timer_red2_off = 0;
}
}
void humid(){
humid_current = dht.readHumidity(false);
displayHumid();
87
// and the valve isn't running automatically) shut it off
if((humid_current > (humid_min + 2.5) or humid_current > 96) and
(valve_open and !user_valve)){
switchObject(5,currentPage);
if(currentPage == 1){
sendMessage("t0.bco=65535");
sendMessage("ref t0");
}
}
}
void displayHumid(){
if(currentPage == 1){
String temp = "t1.txt=\"";
temp += String(humid_current);
temp += " %\"";
sendMessage(temp);
sendMessage("ref t1");
}
}
ds.reset_search();
if ( !ds.search(addr)) {
Serial.print("No more addresses.\n");
ds.reset_search();
return;
}
//Serial.print("R=");
//for( i = 0; i < 8; i++) {
// Serial.print(addr[i], HEX);
// Serial.print(" ");
// }
// if ( addr[0] == 0x10) {
// Serial.print("Device is a DS18S20 family device.\n");
// }
// else if ( addr[0] == 0x28) {
// Serial.print("Device is a DS18B20 family device.\n");
// }
// else {
// Serial.print("Device family is not recognized: 0x");
88
// Serial.println(addr[0],HEX);
// return;
// }
ds.reset();
ds.select(addr);
ds.write(0x44,1); // start conversion, with parasite power on at
the end
present = ds.reset();
ds.select(addr);
ds.write(0xBE); // Read Scratchpad
Serial.print("P=");
Serial.print(present,HEX);
Serial.print(" ");
for ( i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds.read();
}
//Serial.print(" CRC=");
//Serial.print( OneWire::crc8( data, 8), HEX);
//Serial.println();
Whole = Tc_100 / 100; // separate off the whole and fractional portions
Fract = Tc_100 % 100;
String message;
message = "";
//message = "t0.txt=\"";
if(fahrenheit)
89
message += String(Whole_f);
else
message += String(Whole);
message += ".";
if (Fract < 10)
{
message += "0";
}
if(fahrenheit){
message += String(Fract_f);
}
else{
message += String(Fract);
}
displayTemp();
void displayTemp(){
if(currentPage == 1){
String temp = "t0.txt=\"";
temp += String(temp_current);
if(fahrenheit) temp += " F";
else temp += " C";
temp += "\"";
sendMessage(temp);
if(temp_current < temp_max){ //Get rid of red box if temp is OK
sendMessage("t0.bco=65535");
}
sendMessage("ref t0");
}
}
90
//Event on Serial2 means that the pH sensor is sending information, so
read it
void serialEvent2(){
if(timeElapsed > 45000){ //Don't waste too much time constantly reading
the pH.
//Only get a reading when it's close to time to read, or after
sensorstring = Serial2.readStringUntil(13);
sensor_stringcomplete = true;
}
}
void pH(){
//Checks for input.
if (input_stringcomplete == true) {
Serial2.print(inputstring);
Serial2.print('\r');
inputstring= "";
input_stringcomplete = false;
}
//Get entire message from pH and do something with it
if (sensor_stringcomplete == true) {
//Serial.println(sensorstring);
if (isdigit(sensorstring[0])) {
ph = sensorstring.toFloat();
}
}
sensorstring = "";
sensor_stringcomplete = false;
displayPH();
void displayPH(){
if(currentPage == 1){
String temp;
temp = "t2.txt=\"";
temp += String(ph);
temp += "\"";
sendMessage(temp);
sendMessage("ref t2");
91
}
if(ph < pH_min or ph > pH_max){
//Change the screen if pH is out of spec
if(currentPage == 1){
sendMessage("t2.bco=57600");
sendMessage("t3.pco=57600");
if(ph < pH_min)
sendMessage("t3.txt=\"TOO LOW\"");
else if(ph > pH_max)
sendMessage("t3.txt=\"TOO HIGH\"");
sendMessage("ref t2");
sendMessage("ref t3");
}
}
else{ //If it's ok, display that it's ok
if(currentPage == 1){
sendMessage("t2.bco=65535");
sendMessage("t3.txt=\"OK\"");
sendMessage("ref t2");
sendMessage("ref t3");
}
}
}
//HMI event
void serialEvent1(){
String message;
int object;
//Serial.println("HMI READ");
message = readHMI();
Serial.println(String(message[0],DEC));
if(message.length()>0){
switch(message[0]){
//Touch event
case 'e':{
currentPage = String(message[1],DEC).toInt(); //Update page
object = String(message[2],DEC).toInt(); //Determine which
button is being pressed
92
loadPage(currentPage); //Load the page we are on. HMI will just
put default values on there without this step.
}
default:
break;
}
}
}
//Constant process
void loop() {
String message;
int object;
//For each light, check if it's either On and it's On time is up, or
vice versa
if( (blu1 and (timer_blu1_on >= int_blu1_on)) or (!blu1 and
(timer_blu1_off >= int_blu1_off)) ){
switchObject(1,currentPage);
}
if( (blu2 and (timer_blu2_on >= int_blu2_on)) or (!blu2 and
(timer_blu2_off >= int_blu2_off)) ){
switchObject(2,currentPage);
}
if( (red1 and (timer_red1_on >= int_red1_on)) or (!red1 and
(timer_red1_off >= int_red1_off)) ){
switchObject(3,currentPage);
}
if( (red2 and (timer_red2_on >= int_red2_on)) or (!red2 and
(timer_red2_off >= int_red2_off)) ){
switchObject(4,currentPage);
}
93
if(recipe_init){
getRecipe();
recipe_init = false;
}
void getRecipe(){
Serial3.print("GGG");
delay(200); //Give the Module time to react, but don't wait too long or
the response time will be too delayed.
//Serial3 data is only read when the programs gets back to the "loop()"
function
}
//Serial 3 is connected to the WiFi Module. When we have data coming in,
update the recipe if the auto_flag is set
void serialEvent3(){
if(auto_flag)
updateRecipe(Serial3.readString());
}
if(cnt >1) //Don't look for digits when we are loading the username
and recipe name
while(!isDigit(data[idx])) idx++; //Loop until we find a digit;
94
cnt = data.indexOf("</td>", idx+1); //use cnt as a way to find
where the user name ends
if(cnt>0 and cnt < data.length()){
username = data.substring(idx, cnt);
}
idx = cnt;
cnt = 0;
temp = "";
//Loop to trim possible problems from name
while(cnt <username.length()){
//add whitespace for anything that's not alphanumeric or a
single ' ' character
cnt++;
}
username = temp;
cnt = 0; //reset cnt
temp = "";
break;
}
case 1:{
cnt = data.indexOf("</td>", idx+1); //use cnt as a way to find
where the user name ends
if(cnt>0 and cnt < data.length()){
recipename = data.substring(idx, cnt);
}
idx = cnt;
cnt = 0;
temp = "";
//Loop to trim possible problems from name (carriage return, non-
alphanumeric characters etc.)
while(cnt <recipename.length()){
//add whitespace for anything that's not alphanumeric
if(!isDigit(recipename[cnt]) and !isAlpha(recipename[cnt])) temp
+= " ";
else temp += recipename[cnt];
cnt++;
}
recipename = temp;
cnt = 1; //reset cnt
temp = "";
break;
}
case 2:{
if(idx < (data.length() -1) and isDigit(data[idx+1])){
temp = data[idx];
temp += data[idx+1];
lights_default = temp.toInt();
temp = "";
95
}
else if(idx < (data.length()) and isDigit(data[idx])){
temp = data[idx];
lights_default = temp.toInt();
temp = "";
}
break;
}
case 3:{
if(idx < (data.length() -1) and isDigit(data[idx+1])){
temp = data[idx];
temp += data[idx+1];
hold = temp.toInt();
//Shows percentage as whole number. 99.99999 instead of
100.000001 because of rounding
red_share = float(hold / 99.99999999999);
temp = "";
hold = 0;
}
else if(idx < (data.length()) and isDigit(data[idx])){
temp = data[idx];
hold = temp.toInt();
red_share = float(hold / 99.9999999999999);
temp = "";
hold = 0;
}
break;
}
case 4:{
if(idx < (data.length() -1) and isDigit(data[idx+1])){
temp = data[idx];
temp += data[idx+1];
temp_min = temp.toInt();
temp = "";
}
else if(idx < (data.length()) and isDigit(data[idx])){
temp = data[idx];
temp_min = temp.toInt();
temp = "";
}
break;
}
case 5:{
if(idx < (data.length() -1) and isDigit(data[idx+1])){
temp = data[idx];
temp += data[idx+1];
temp_max = temp.toInt();
temp = "";
}
else if(idx < (data.length()) and isDigit(data[idx])){
temp = data[idx];
temp_max = temp.toInt();
temp = "";
}
96
break;
}
case 6:{
if(idx < (data.length() -1) and isDigit(data[idx+1])){
temp = data[idx];
temp += data[idx+1];
humid_min = temp.toInt();
temp = "";
}
else if(idx < (data.length()) and isDigit(data[idx])){
temp = data[idx];
humid_min = temp.toInt();
temp = "";
}
break;
}
case 7:{
if(idx < (data.length() -1) and isDigit(data[idx+1])){
temp = data[idx];
temp += data[idx+1];
humid_max = temp.toInt();
temp = "";
}
else if(idx < (data.length()) and isDigit(data[idx])){
temp = data[idx];
humid_max = temp.toInt();
temp = "";
}
break;
}
case 8:{
if(idx < (data.length() -1) and isDigit(data[idx+1])){
temp = data[idx];
temp += data[idx+1];
valve_dur_default = temp.toInt();
temp = "";
}
else if(idx < (data.length()) and isDigit(data[idx])){
temp = data[idx];
valve_dur_default = temp.toInt();
temp = "";
}
break;
}
case 9:{
if(idx < (data.length() -1) and isDigit(data[idx+1])){
temp = data[idx];
temp += data[idx+1];
valve_freq_default = temp.toInt();
temp = "";
}
else if(idx < (data.length()) and isDigit(data[idx])){
temp = data[idx];
valve_freq_default = temp.toInt();
97
temp = "";
}
break;
}
default:
break;
}
if(cnt == 9) break;
cnt++;
String readHMI(){
char bite;
int readEnd = 0;
String mes;
delay(10);
while(Serial1.available()>0){ //Read all bytes on HMI buffer
bite = Serial1.read();
//Serial.println(bite);
mes += bite;
if(bite == 0xff) //When 3 of these come in a row, the message is over
readEnd++;
if(readEnd == 3)
break;
}
return mes;
}
Serial1.print(mes.c_str());
98
}
//Currently not used. May be useful in future applications
int getCurrentPage(){
/*while(Serial1.available()>0)
Serial1.read();*/
sendMessage("sendme");
delay(250);
String value = readHMI();
if(value.length() > 1 && value[0] == 'f')
return String(value[1],DEC).toInt();
return 0;
}
case 1:{
switch(num){
case 1:{
loadPage(0);
break;
}
case 2:{
loadPage(2);
break;
}
default:
break;
}
break;
}
99
//On page 2, the first 6 buttons are used to switch objects on and
off. Do switchObject on the appropriate object
case 2:{
switch(num){
case 1:
case 2:
case 3:
case 4:{
switchObject(num, 2);
break;
}
case 5:{
//Update state variable to show that change was manual
user_valve = !user_valve;
switchObject(num, 2);
break;
}
case 6:{
//Update state variable to show that change was manuals
user_fans = !user_fans;
switchObject(num, 2);
break;
}
case 7:{
loadPage(1);
break;
}
case 8:{
loadPage(0);
break;
}
//19 through 24 are the up/down arrows. Update the affected value
case 19:
{
auto_flag = false;
if(lights_master <24) lights_master++; //Don't go above 24 hours
for the lights
temp = "n1.val=";
temp += String(lights_master);
sendMessage(temp);
sendMessage("ref n1");
temp="";
setLights();
break;
}
case 20:
{
auto_flag = false;
if(lights_master>0) lights_master--;
temp = "n1.val=";
temp += String(lights_master);
sendMessage(temp);
sendMessage("ref n1");
temp="";
100
setLights();
break;
}
case 21:
{
auto_flag = false;
valve_freq++;
temp = "n2.val=";
temp += String(valve_freq);
sendMessage(temp);
sendMessage("ref n2");
temp="";
break;
}
case 22:
{
auto_flag = false;
if(valve_freq >1) valve_freq--; //Freq of 0 will cause problems
temp = "n2.val=";
temp += String(valve_freq);
sendMessage(temp);
sendMessage("ref n2");
temp="";
break;
}
case 23:
{
auto_flag = false;
if((valve_dur) < (valve_freq*30)) valve_dur++; //Don't have the
valve on for more than half the time. Might change this later
temp = "n3.val=";
temp += String(valve_dur);
sendMessage(temp);
sendMessage("ref n3");
temp="";
break;
}
case 24:
{
auto_flag = false;
if(valve_dur >1) valve_dur--; //Duration of 0 means the pump
will never run
temp = "n3.val=";
temp += String(valve_dur);
sendMessage(temp);
sendMessage("ref n3");
temp="";
break;
}
//Reset defaults button
case 26:{
auto_flag = true;
lights_master = lights_default;
valve_dur = valve_dur_default;
101
valve_freq = valve_freq_default;
temp = "n1.val=";
temp += String(lights_master);
sendMessage(temp);
sendMessage("ref n1");
temp = "n2.val=";
temp += String(valve_freq);
sendMessage(temp);
sendMessage("ref n2");
temp = "n3.val=";
temp += String(valve_dur);
sendMessage(temp);
sendMessage("ref n3");
temp="";
setLights();
}
default:
break;
}
break;
}
//On three, the only buttons are to go to home page and to get data
from WiFi right now
case 3:{
switch(num){
case 1:{
auto_flag = true; //If we get a new recipe, assume we want
updates
getRecipe(); //Get the recipe if we pressed this button. Note
that user must refresh the page to see it
}
case 2:{
loadPage(0);
}
}
}
default:
break;
}
}
102
if(blu1){ digitalWrite(31, HIGH);
togglePic(1, true, page);
}
else{ digitalWrite(31, LOW);
togglePic(1, false, page);
}
break;
}
case 2:{
blu2 = !blu2;
timer_blu2_off = 0;
timer_blu2_on = 0;
if(blu2){ digitalWrite(32, HIGH);
togglePic(2, true, page);
}
else{ digitalWrite(32, LOW);
togglePic(2, false, page);
}
break;
}
case 3:{
red1 = !red1;
timer_red1_off = 0;
timer_red1_on = 0;
if(red1){ digitalWrite(33, HIGH);
togglePic(3, true, page);
}
else{ digitalWrite(33, LOW);
togglePic(3, false, page);
}
break;
}
case 4:{
red2 = !red2;
timer_red2_off = 0;
timer_red2_on = 0;
if(red2){ digitalWrite(34, HIGH);
togglePic(4, true, page);
}
else{ digitalWrite(34, LOW);
togglePic(4, false, page);
}
break;
}
case 5:{
valve_open = !valve_open;
timer_valve_off = 0;
timer_valve_on = 0;
if(valve_open){ digitalWrite(35, HIGH);
togglePic(5, true, page);
}
else{ digitalWrite(35, LOW);
togglePic(5, false, page);
}
103
break;
}
case 6:{
fans = !fans;
if(fans){ digitalWrite(36, HIGH);
togglePic(6, true, page);
}
else{ digitalWrite(36, LOW);
togglePic(6, false, page);
}
break;
}
default:
break;
}
}
//Swtich the state of the pictures of our HMI screen. Different protocol
for page 1 vs page 2
void togglePic(int obj, bool onn, int page){
switch(page){
case 1:{
switch(obj){
case 1:{
if(onn){sendMessage("q0.picc=1");
}
else{sendMessage("q0.picc=2");
}
sendMessage("ref q0");
break;
}
case 2:{
if(onn){sendMessage("q1.picc=1");
}
else{sendMessage("q1.picc=2");
}
sendMessage("ref q1");
break;
}
case 3:{
if(onn){sendMessage("q2.picc=1");
}
else{sendMessage("q2.picc=2");
}
sendMessage("ref q2");
break;
}
case 4:{
if(onn){sendMessage("q3.picc=1");
104
}
else{sendMessage("q3.picc=2");
}
sendMessage("ref q3");
break;
}
case 5:{
if(onn){sendMessage("q4.picc=1");
}
else{sendMessage("q4.picc=2");
}
sendMessage("ref q4");
break;
}
case 6:{
if(onn){sendMessage("q5.picc=1");
}
else{sendMessage("q5.picc=2");
}
sendMessage("ref q5");
break;
}
default:
break;
}
break;
}
case 2:{
switch(obj){
case 1:{
if(onn){sendMessage("b0.picc=4");
sendMessage("b0.picc2=3");}
else{sendMessage("b0.picc=3");
sendMessage("b0.picc2=4");}
sendMessage("ref b0");
break;
}
case 2:{
if(onn){sendMessage("b1.picc=4");
sendMessage("b1.picc2=3");}
else{sendMessage("b1.picc=3");
sendMessage("b1.picc2=4");}
sendMessage("ref b1");
break;
}
105
case 3:{
if(onn){sendMessage("b2.picc=4");
sendMessage("b2.picc2=3");}
else{sendMessage("b2.picc=3");
sendMessage("b2.picc2=4");}
sendMessage("ref b2");
break;
}
case 4:{
if(onn){sendMessage("b3.picc=4");
sendMessage("b3.picc2=3");}
else{sendMessage("b3.picc=3");
sendMessage("b3.picc2=4");}
sendMessage("ref b3");
break;
}
case 5:{
if(onn){sendMessage("b4.picc=4");
sendMessage("b4.picc2=3");}
else{sendMessage("b4.picc=3");
sendMessage("b4.picc2=4");}
sendMessage("ref b4");
break;
}
case 6:{
if(onn){sendMessage("b5.picc=4");
sendMessage("b5.picc2=3");}
else{sendMessage("b5.picc=3");
sendMessage("b5.picc2=4");}
sendMessage("ref b5");
break;
}
default:
break;
}
break;
}
default:
break;
}
}
//Used to load a new page on the HMI. Sends necessary data so it shows the
current states
void loadPage(int pageNum){
String temp;
106
int i;
float f;
temp = "";
currentPage = pageNum; //Make sure arduino knows what page we are now on
switch(pageNum){
case 0:
break;
//One page 1, we need to know the state of the outside components and
use that state to show the correct picture
case 1:{
switch(blu1){
case true:{
sendMessage("q0.picc=1");
//sendMessage("b0.picc2=2");
sendMessage("ref q0");
break;
}
case false:{
sendMessage("q0.picc=2");
//sendMessage("b0.picc2=1");
sendMessage("ref q0");
break;
}
default:
break;
}
switch(blu2){
case true:{
sendMessage("q1.picc=1");
//sendMessage("b1.picc2=2");
sendMessage("ref q1");
break;
}
case false:{
sendMessage("q1.picc=2");
//sendMessage("b1.picc2=1");
sendMessage("ref q1");
break;
}
default:
break;
}
switch(red1){
case true:{
sendMessage("q2.picc=1");
//sendMessage("b2.picc2=2");
sendMessage("ref q2");
break;
}
case false:{
sendMessage("q2.picc=2");
//sendMessage("b2.picc2=1");
sendMessage("ref q2");
break;
107
}
default:
break;
}
switch(red2){
case true:{
sendMessage("q3.picc=1");
//sendMessage("b3.picc2=2");
sendMessage("ref b3");
break;
}
case false:{
sendMessage("q3.picc=2");
//sendMessage("b3.picc2=1");
sendMessage("ref q3");
break;
}
default:
break;
}
switch(valve_open){
case true:{
sendMessage("q4.picc=1");
//sendMessage("b4.picc2=2");
sendMessage("ref q4");
break;
}
case false:{
sendMessage("q4.picc=2");
//sendMessage("b4.picc2=1");
sendMessage("ref q4");
break;
}
default:
break;
}
switch(fans){
case true:{
sendMessage("q5.picc=1");
//sendMessage("b5.picc2=2");
sendMessage("ref q5");
break;
}
case false:{
sendMessage("q5.picc=2");
//sendMessage("b5.picc2=1");
sendMessage("ref q5");
break;
}
default:
break;
}
displayTemp();
displayHumid();
108
displayPH();
break;
}
//Page 2 needs to display the right pictures and also display the
values for lights timer, valve timer, valve frequency
case 2:{
switch(blu1){
case true:{
sendMessage("b0.picc=4");
sendMessage("b0.picc2=3");
sendMessage("ref b0");
break;
}
case false:{
sendMessage("b0.picc=3");
sendMessage("b0.picc2=4");
sendMessage("ref b0");
break;
}
default:
break;
}
switch(blu2){
case true:{
sendMessage("b1.picc=4");
sendMessage("b1.picc2=3");
sendMessage("ref b1");
break;
}
case false:{
sendMessage("b1.picc=3");
sendMessage("b1.picc2=4");
sendMessage("ref b1");
break;
}
default:
break;
}
switch(red1){
case true:{
sendMessage("b2.picc=4");
sendMessage("b2.picc2=3");
sendMessage("ref b2");
break;
}
case false:{
sendMessage("b2.picc=3");
sendMessage("b2.picc2=4");
sendMessage("ref b2");
break;
}
default:
break;
}
109
switch(red2){
case true:{
sendMessage("b3.picc=4");
sendMessage("b3.picc2=3");
sendMessage("ref b3");
break;
}
case false:{
sendMessage("b3.picc=3");
sendMessage("b3.picc2=4");
sendMessage("ref b3");
break;
}
default:
break;
}
switch(valve_open){
case true:{
sendMessage("b4.picc=4");
sendMessage("b4.picc2=3");
sendMessage("ref b4");
break;
}
case false:{
sendMessage("b4.picc=3");
sendMessage("b4.picc2=4");
sendMessage("ref b4");
break;
}
default:
break;
}
switch(fans){
case true:{
sendMessage("b5.picc=4");
sendMessage("b5.picc2=3");
sendMessage("ref b5");
break;
}
case false:{
sendMessage("b5.picc=3");
sendMessage("b5.picc2=4");
sendMessage("ref b5");
break;
}
default:
break;
}
temp = "n1.val=";
temp += String(lights_master);
sendMessage(temp);
temp = "n2.val=";
temp += String(valve_freq);
sendMessage(temp);
110
temp = "n3.val=";
temp += String(valve_dur);
sendMessage(temp);
sendMessage("ref n1");
sendMessage("ref n2");
sendMessage("ref n3");
break;
} //case 2 ending
temp = "t3.txt=\"";
temp += recipename;
temp += "\"";
sendMessage(temp);
sendMessage("ref t3");
temp = "n0.val=";
temp += String(lights_default);
sendMessage(temp);
sendMessage("ref n0");
temp = "n1.val=";
f = 100.000001 * red_share;
temp += String(int(f));
sendMessage(temp);
sendMessage("ref n1");
temp = "n2.val=";
temp += String(temp_min);
sendMessage(temp);
sendMessage("ref n2");
temp = "n3.val=";
temp += String(temp_max);
sendMessage(temp);
sendMessage("ref n3");
temp = "n4.val=";
f = humid_min * 1.00000000001;
temp += String(int(f));
sendMessage(temp);
sendMessage("ref n4");
temp = "n5.val=";
f = humid_max * 1.000000001;
111
temp += String(int(f));
sendMessage(temp);
sendMessage("ref n5");
temp = "n6.val=";
temp += String(valve_dur_default);
sendMessage(temp);
sendMessage("ref n6");
temp = "n7.val=";
temp += String(valve_freq_default);
sendMessage(temp);
sendMessage("ref n7");
}
default:
break;
} //pageNum ending
}
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <elapsedMillis.h>
#include <ESP8266HTTPClient.h>
ESP8266WiFiMulti WiFiMulti;
bool read_flag;
int read_time;
elapsedMillis timer;
void setup() {
Serial.begin(115200);
delay(10);
read_flag = true;
read_time = 3600000; //read every hour
112
WiFiMulti.addAP("EGR Wi-Fi WPA2", "engineering");
WiFiMulti.addAP("EGR Wi-Fi", "");
//wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
Serial.println("Connecting Wifi...");
delay(5000); //Give the Arduino time to boot.
//Serial events are only triggered during the "loop()" function, so
setup needs to be done before we transmit data
if(WiFiMulti.run() == WL_CONNECTED) {
//Code below is for debugging purposes
//Other commeneted "Serial.print" or "Serial.println" are for the
same purpose
//Serial.println("");
//Serial.println("WiFi connected");
//Serial.println("IP address: ");
//Serial.println(WiFi.localIP());
}
if(cnt == 3) {
//Proper message was sent. User wants to take a reading
read_flag = true;
break;
}
}
//Continuous loop
void loop() {
//Check it's time or the user requested a reading
if(read_flag or timer >= read_time){
if((WiFiMulti.run() == WL_CONNECTED)) {
if(read_flag) read_flag = false;
if(timer > read_time) timer = 0;
HTTPClient http;
113
//USE_SERIAL.print("[HTTP] begin...\n");
// configure traged server and url
//http.begin("192.168.1.12", 443, "/test.html", true, "7a 9c f4 db
40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS
//http.begin("192.168.1.12", 80, "/test.html"); //HTTP
http.begin("http://webdev.cse.msu.edu/~huangbe1/ece480/PhpProject1/esp8266
read.php");
//USE_SERIAL.print("[HTTP] GET...\n");
// start connection and send HTTP header
int httpCode = http.GET();
if(httpCode) {
// HTTP header has been send and Server response header has
been handled
//USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode);
}
else {
//USE_SERIAL.print("[HTTP] GET... failed, no connection or no
HTTP server\n");
}
}
else {
//Serial.println("Not connected"); delay(2000);
}
}
}
114
Appendix 5: Bill of Materials
115
Table A5.2: Mechanical Bill of Materials
116