FinalReport

Download as pdf or txt
Download as pdf or txt
You are on page 1of 117

Aeroponic Control System for

Efficient Growth

ECE 480 - Spring 2016


Design Team #8

Justin Fecteau
Patrick Pomaville
Samuel Metevia
Patricia Huang
Jacob Jones
Saleh Alghamdi

Sponsor: Great Lakes Controls and Engineering


Facilitator: Professor Robert McGough
Executive Summary:

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:

Chapter 1 - Introduction and Background…………………………………5


1.1 – Introduction……………………………………………………...…5
1.2 – Background………………………………………………………...5

Chapter 2 - Solution Space and Selected Approach……………………..6


2.1 - Objectives and Critical Customer Requirements……………….6
2.2 - FAST Diagram……………………………………………………...7
2.3 - Conceptual Designs……………………………………………….8
2.4 - Selected Solution…………………………………………………10
2.5 - Original Budget……………………………………………………12
2.6 - Original Schedule……...............................................................13

Chapter 3 - Technical Description…………………………………………..15


3.1 - Frame……………………………………………………………….15
3.2 - Plumbing…………………………………………………………...16
3.3 - Enclosure…………………………………………………………..23
3.4 - Relay Board and PCB…………………………………………….27
3.5 - Sensors……………………………………………………………..29
3.5.1 - Temperature Sensor……………………………………29
3.5.2 - Humidity Sensor………………………………………....30
3.5.3 - PH Sensor…………………………………………….….31
3.6 - Lights and Fans…………………………………………………....34
3.7 - Microcontroller……………………………………………………...37
3.8 - HMI…………………………………………………………………..43
3.9 - WiFi Module and Recipe Website………………………………..47
3.10 - Safety Considerations……………………………..…………….49

Chapter 4 - Verification of Functionality……………………………………52


4.1 - Light and Fan Data………………………………………………...52

3
4.2 - Overall System with Sensors…………………………………..…54
4.3 - Plant Growth………………………………………………………..58

Chapter 5 - Design Issues…………………………………………………..…62


5.1 - Design for Accessibility……........................................................62
5.2 - Design for Simplicity……………………………………………….63

Chapter 6 - Final Summary……………………………………………………64


6.1 - Final Schedule and Budget……………………………………….64
6.2 - Summary……………………………………………………………65
6.3 - Suggestions for Future Work……………………………………...65

Appendix 1 - Individual Technical Roles and Work………………………67


Appendix 2 - References………………………………………………………72
Appendix 3 - Mechanical and Electrical Schematics……………………..73
Appendix 4 - Computer Code…………………………………………………84
Appendix 5 - Bill of Materials…………………………………………………115

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

2.1: Objectives and Critical Customer Requirements


Great Lakes Controls and Engineering provided the overall objective for the
project in significant detail before any design process had begun. The main objective of
this project was to develop an automatically controlled closed loop system for growing
leafy greens in a small space. Due to time constraints, the objective of the project from
the beginning was centered on actually building a functional electromechanical controls
system, with far less emphasis on the actual growth of vegetation.
In an aeroponic system there is no growth medium, as the plant’s roots are
suspended in air and sprayed by a series of fine nozzles at regular intervals. The
sponsor required that the water be housed in a reservoir on the floor and that a water
pump be used to boost water to a high pressure to push the water out through the
nozzles as 5-50 micron droplets at around 80 PSI. This pressure needed be monitored
to ensure sufficient pressure for efficient growth. It was required that a shelf hold the
vegetation and that it be monitored for ambient air temperature and relative humidity.
Additionally, the sponsor required that the system contain LEDs and cooling fans to
regulate the temperature of the system.
It was instructed that the system be controlled from the user through an HMI
(touchscreen input) and that the HMI allow the user to set the recipe they want to use
for the vegetation they will be growing. Also, it was required that the system be
accessible through Wi-Fi in order to change the recipe of the vegetation cycle.
According to the project sponsor, the final product needed to satisfy the following
criteria:
1. System must run off of 120 VAC wall outlet
2. System must be controlled through an HMI that is simple to use
3. All control components must be housed in an enclosure
4. Users must control the growing environment through the HMI, including:
a. When/how often to turn the water spray on/off
b. Pressure of the Water in PSI
c. When/how long to turn on/off the UV lights

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.

2.2: FAST Diagram


The criteria of this project were broken down into subsystems in order to better
organize potential design solutions. This was done using a FAST diagram, which can be
seen below in Figure 2.1.

Figure 2.1: FAST Diagram

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.

2.3: Conceptual Designs


Using the provided sponsor objectives and the organization of the FAST
diagram, the team was able to start developing a design plan for the aeroponic system.
It was clear from the beginning that this project would be expensive, so several possible
designs were considered with the overall concern being balancing cost and quality. A
conceptual design matrix is shown below in Table 2.1, with explanations of each design
following.

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.

2.4: Selected Solution


This selected solution was only made possible through the decision to eliminate
some minor sponsor requirements. Due to the number and price of components, it was
discussed and agreed upon with the sponsor to eliminate the originally planned
electrical conductivity sensor. Additionally, the decision was made to not use any sensor
to monitor nutrient levels, which meant that nutrient levels would not be monitored on
the HMI. These two decisions, which were made early on in the design process, allowed
for essential components to be purchased of higher quality.
A flowchart of the overall final solution, which the team believed provided the
best balance of quality and affordability, can be seen in Figure 2.2.

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.

Figure 2.3: Overall View of Frame

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.

2.5: Original Budget


Several parts were provided by Great Lakes Controls and Engineering in order to
stay within the ECE 480 budget of $500. Additionally, several smaller components were
found in various shops free of charge. Table 2.2 shows the components purchased by
Design Team 8 only. Chapter 6 contains a full budget as if no parts were provided and
Appendix 5 contains a full Bill of Materials.

12
Table 2.2: Cost to Team Only

2.6: Original Schedule


The original team schedule can be seen in Table 2.3 and Table 2.4 in the form of
a Gantt chart. This Gantt chart was developed as a way of ensuring constant progress
before any technical work had begun.

13
Table 2.3: Gantt Chart Part A

Table 2.4: Gantt Chart Part B

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.

Figure 3.1: Multiple Views of Frame

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.

Figure 3.2: Equipment Mounted on the Steel Mounting Plate

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.

Figure 3.3: Water Reservoir and pH Mounting Hardware

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.

Figure 3.4: Aquatec 8800 Pump and Pressure Switch

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.

Figure 3.5: WellxTrol WX101 Expansion Tank and Mounting Hardware

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.

Figure 3.9: Misting Nozzle and Misting Tee

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.

Figure 3.10: Enclosure Exterior Figure 3.11: Enclosure Interior

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.

Figure 3.14: Cord Grips and Controls Cabling

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.

Figure 3.16: Main Power Switch

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

Figure 3.17: SainSmart Relay Board

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

To minimize the current drawn by the microcontroller, each relay input is


connected to the collector of a TIP31A transistor. The transistor’s emitter is connected
to ground because the relay board is active low. This means that it is activated when the
input is shorted to ground. The transistor’s base is connected to a digital output pin of
the microcontroller through a 1.8kΩ resistor.
When the microcontroller outputs 5V, the transistor is turned on and the relay coil
has a clear path to ground through the transistor. This activates the circuit and turns on
the component that is attached to a particular relay. When the output is low, the
transistor is cutoff, the relay coil is deactivated, and the component is switched off.
With 5V corresponding to a high output on the microcontroller, a Base-Emitter
voltage of 0.7V, and the emitter of each transistor being connected to ground, this
creates a current draw of approximately 2.4mA on the output pin of the microcontroller.
(5 − 0.7) 𝑉 / 1800 𝛺 = 2.4 mA
A transistor beta of 25 means that the relay is driven by a current of 52mA
.25 ∗ 2.4 𝑚𝐴 = 52 𝑚𝐴

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.

3.5.2: Humidity Sensor


The humidity sensor used in this system is the Phantom YoYo DHT11 humidity
sensor. Similar to the reasons for selecting the temperature sensor, this sensor was
selected for its low cost and because it is known to be easily compatible with the
selected microcontroller. The sensor uses a digital signal acquisition technique that
ensures long term stability and high reliability. Interfacing this sensor with the
microcontroller will also be discussed in a later section.
The sensor has a measurement range for relative humidity of 20-90% with an
accuracy of ±5%. The humidity sensor’s primary purpose is to ensure that the plants’
roots are receiving enough water and nutrients. In order to best simulate the
environment of the plant’s root system, the humidity sensor is mounted in a metal box
with a removable cover among the net pots that hold the plant’s roots within the
plexiglass housing of the frame. The metal box protects the sensor from being directly

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.

Figure 3.21: pH EZO Circuit and Single Circuit Carrier Board

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.

Figure 3.22: Atlas Scientific Single Circuit Carrier Board

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.

Figure 3.23: American Marine Pinpoint pH Probe

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.

Figure 3.24: Probe Mounting Bracket, pH Probe, and Water Supply

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.

Figure 3.26: Fan Layout

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.

Figure 3.27: Fan Mounting

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.

Figure 3.28: Arduino MEGA 2560

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.

Figure 3.29: HMI Mounted to Enclosure

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.

Figure 3.34: ESP8266 WiFi Module

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

3.10: Safety Considerations


In industry, there are always hazards when using electricity, especially when
using alternating current up to 120V. Such hazards include personnel electrocution,
shorting nodes together causing a short circuit, burning expensive electrical
components, and bad wiring practices which can lead to incorrect measurement
readings or electric shock. To circumvent these issues, multiple steps were taken in the
design section of this project to ensure not only ease of use, but personnel and
equipment safety. The first of these steps to ensure safety was the grounding of all
electrical and applicable mechanical equipment. This included the frame, steel mounting

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.

Figure 3.36: 15A Fuse

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

4.1: Light and Fan Data


During the building process, it was necessary to verify that the chosen light strips
would be sufficient. The daily light integral (DLI) of the lights was calculated in order to
ensure that the lights chosen provide enough light output at the required wavelengths
for plant growth. To determine the DLI of the lights two factors needed to be known: the
length of time that the lights will be on per day and the photosynthetic photon flux
density (PPFD). In order to determine the PPFD of a given light, an integrating sphere is
needed, which the team did not have access to. To estimate the PPFD of the lights, an
Oriel Spectrometer System was utilized to first determine the wavelength spectrum that
each light emitted. The wavelength spectrum of each color of lights is shown in Figure
4.1 and Figure 4.2.

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.

Table 4.1: Estimated DLI Values

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

4.2: Overall System


The sponsor of this project gave several main criteria for a success. With few
exceptions caused by budgetary constraints, the final product meets each of these
criteria. A specific discussion of each criterion follows.
The sponsor’s first criterion was that the system run off of a 120VAC wall outlet.
This criterion was achieved. The system also needed to have all of its electrical
components housed in an enclosure. As shown in Figure 3.10 and Figure 3.11, this
requirement was also met. Electrical schematics of all circuitry within the enclosure
were also required and provided.
The system also needed to be controlled by the user through an HMI that is
simple to use. Though the term simple is qualitative, it is reasonable to say that the final
product meets this requirement because of the intuitive HMI layout. The sponsor also
requested that an operator’s manual be provided with the final product, which will
improve the ease of use for the end user.
In order to provide the user with accurate data and robust control, the HMI
needed to be able to allow the user to control how often the spray nozzles turned on
and off, the pressure of the water, and how long the lights should be turned on for. The
final product allows the user to control how often the spray nozzles are turned on and
how long they stay on for, as well as how many hours per day the lights are turned on.
An image of a nozzle successfully spraying can be seen below in Figure 4.3.

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.

Figure 4.4: HMI Status Panel Reading Sensor Values

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.

Figure 4.5: Pressure Gauge Reading Required System Pressure

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.

Table 4.2: Full Load Amperage Calculations

Another way that this system recycles resources is by incorporating a water


return system. Not only does having a water return prevent messes, it also allows the
system to not waste any water whatsoever. These two steps in the conservation of
resources will allow the system to reap the many benefits of being efficient in the future.
A summary of the sponsor criteria and whether each was met can be seen below
in Table 4.3.

57
Table 4.3: Sponsor Criteria Status

4.3: Plant Growth


Originally, it was not a requirement of the sponsor that any plants be grown.
Rather, the objective of the project was simply to develop the functioning controls
system. However, as the system was completed early, it was decided and requested by
the sponsor to actually attempt growing.
The system was prepped for growing by covering the grated vegetation shelf with
a layer of landscape fabric and a layer of visqueen. This was done because the plant
roots cannot be exposed to light, a fact which the team discovered very late in the
design process. Twelve holes were cut in these layers and net pots were inserted in
positions that best aligned with the nozzles below. Additionally, the adjustable light
shelf was lowered to a few inches above the vegetation shelf in order to provide the
plants with the proper light. This setup can be seen below in Figure 4.6 and Figure 4.7.

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.

Figure 4.11: Plant Growth - 6 Days

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

5.1: Design for Accessibility


One important design issue in any product is designing for accessibility. That is,
designing so that people with disabilities are able to use the product. Due to serious
time and budget constraints this final product is lacking in accessibility. However,
improvements can be made in the future. Possible improvements will be examined
below for the two specific examples of a visually impaired person and a person unable
to easily stand or walk, possibly wheelchair bound.
Someone who is blind or severely visually impaired would have a difficult time
using the aeroponic system in its current state due to the fact the entire system is
controlled through a visual touch screen. One obvious improvement to accommodate
people in this situation would be to add voice commands and responses to the system.
The system could be improved to accept voice commands to turn on components or to
read off sensor values and then respond as confirmation. For example, a person could
ask for the current temperature reading and the system could respond audibly with a
value, or a person could command the system to activate the cooling fans to which the
system would audibly confirm activation. Other simpler options include adding braille
labels to all components or replacing the touch screen with physical buttons that could
be felt and identified with braille.
Additionally, a person who is unable to easily move around would have a difficult
time using this system. One option for accommodating those in wheelchairs would be
modifying the frame and shelves to have easily adjustable heights. This could allow for
lowering the vegetation section or HMI down to anyone. The system is WiFi accessible
as it is, and the team has developed a recipe website for controlling the system from a
distance. However, this aspect could be improved by adding a live feed video camera to
monitor the plant growth without having to move to the physical system. Also, a system
to automatically add nutrients to the water would allow for even less physical labor when
using the system.

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

6.1: Final Schedule and Budget


The team essentially followed the original schedule and completed everything on
time and as planned. All original customer requirements were met well ahead of
schedule. The team was originally told that attempting to actually grow plants would not
be necessary, but it was later decided that an attempt should be made if time allowed.
This added some extra work at the end of the semester, which caused the team to
move more quickly in the final weeks.
The team was also able to stay within the allotted $500 budget. The actual cost
to the team is shown previously in Table 2.2. However, this was only possible because
a large amount of parts were provided by Great Lakes Controls and Engineering or
found for free. A full list of parts used can be found in Appendix 5. Additionally, Table
6.1 below shows an estimation what the final cost would be if all parts were purchased
by the team.
Table 6.1: Estimated Cost for Entire Project

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.

6.3: Suggestions for Future Work


The team has several suggestions for future work on this aeroponic system.
Several features could have been added with a larger budget and extended timeline.
Also, as this system is a prototype, several improvements to the current design could be
made. The team’s suggestions include:

● 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

The individual roles of each team member are listed below:

Samuel Metevia - Manager


As team manager, Sam created the team’s schedule and ensured that the team’s
milestones were reached on time. He was also responsible for organizing meetings with
the team’s facilitator and their sponsor. Sam also followed up with team members
regularly to check their progress and assisted with other aspects of the project as
needed.
On the technical side, he was primarily responsible for programming the Arduino
microcontroller and the ESP8266 WiFi Module to ensure the seamless operation of the
entire system. As the microcontroller interacts with all of the other system components,
it was necessary for him to determine the proper electrical connection for each
component and to find or write code to send and receive data from each component.
Sam also worked with Patricia to design the user interface for the HMI. As the
HMI is a critical system component, he had to learn how the HMI communicated and
find ways to use its capabilities to fulfill the project requirements. The Nextion HMI uses
a unique instruction set, which meant that Sam had to learn the exact commands and
command syntax that the HMI uses to display data and respond to a user’s touch.
Sam designed the circuits on the printed circuit board that controlled the
SainSmart relay board and provided power to the WiFi Module. As the team member
responsible for the microcontroller aspect of this project, he suggested that a transistor
circuit be used to control the relay board rather than connecting the board directly to the
microcontroller and risking damage. Applying basic circuit principles, he designed a
circuit that used TIP31A transistor with its base connected to the microcontroller output
pins to control the relay board, reducing the current drawn from the microcontroller.
Finally, he also assisted Patrick with priming the water pump, confirming that the
electrical schematics matched the project design, and troubleshooting the installed
system. Sam also made modifications to the final installation to ensure proper operation
of the system. These modifications included adjusting the mounting of the

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.

Patricia Huang - Webmaster #1


As webmaster, Patricia designed and created the team website and ensured
everything on the team website was up to date. She also created the recipe website to
collect customer inputs, which are then sent to the microcontroller. Patricia also assisted
testing and debugging the system, which including reporting system errors.
Patricia’s technical role was primarily managing the team website. She searched
and collected stylesheets from the web that were used for the team webpage
framework. She reused some of the code from stylesheets and redesigned the entire
team website. She was also responsible for collecting latest team activities and keeping
the team website up to date.
Patricia also worked with Sam to design the user interface for the HMI. She
designed the original HMI screens with Nextion IDE, which means she had to learn the
layout design and interaction between each screen pages. She designed the
component icons and page background images which involved online image editor and
Microsoft Word Art. Patrica also designed the user recipe pages to collect data from
potential customers. She also created a page to transmit that data to a readable
sequence which was used for the WiFi receiver.

Patrick Pomaville - Lab Coordinator


As per the responsibilities of lab coordinator, Patrick ordered all parts that were
not supplied by Great Lakes Controls and Engineering and made sure that the Senior
Capstone Lab was always in order, ready for the next user. He also researched and
picked out all parts used in the plumbing circuit as well as the power supplies, pH probe,
single circuit carrier board, relay board, outlets, switches, and all other hardware.
His technical contribution extends from the beginning of design to the completion
of the product. He fabricated the printed circuit board located in the enclosure and

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.

Justin Fecteau - Document Coordinator


As document coordinator, Justin ensured that all work was properly documented
over the course of the semester and that all assignments were completed in a high
quality fashion. He led the way on organizing and completing project reports, and on
deciding what information needed to be included.
Justin kept notes at team meetings and organized which parts would be provided
by Great Lakes Controls and Engineering and which parts would need to be ordered.
He was heavily engaged in discussions to design the overall system and in the
decisions of which parts to order. He researched and selected the temperature sensor
and humidity sensor. Justin also assisted Patrick with fabricating the printed circuit
board within the enclosure and with wiring the various components in the system. He
assembled the aluminum frame and helped to mount the main panel on it with Patrick

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.

Jacob Jones - Presentation Coordinator


As presentation coordinator, Jacob ensured that all presentations were completed
in a high quality fashion and that the hardware needed for the presentation was
functioning properly. He made sure that the presentation was well formatted, readable,
and that team members did not read word for word off of slides.
Jacob researched the technical details involved in various method of plant growth
in order to compare how aeroponic systems size up against other growing methods. He
researched the nutrient, lighting, temperature, and water requirements for optimal
lettuce growth. Jacob chose which lighting system was implemented in the final design,
as well as how it was installed. He also worked with Patrick, Justin, and Saleh to
assemble the extruded aluminum frame. He was assisted by Justin in the cutting and
mounting of the light strips.
Jacob also calculated the estimated PPFD of the chosen lights to ensure that
they were suitable for lettuce to grow. He germinated the seeds in order to determine
how long the seeds would take to sprout in the system. He assisted Saleh with the pre-
soaking of the coco coir as well as the planting of the lettuce seeds. He documented the
growth of the seedlings and relayed to team members adjustments that needed to take
place in order to ensure proper growth.

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

[1] Josh Clark "How Aeroponics Works" 22 December 2008. HowStuffWorks.com.


<http://home.howstuffworks.com/lawn-garden/professional-landscaping/alternative-
methods/aeroponics.htm> 15 February 2016
[2] “DS18B20 Programmable Resolution 1-Wire Digital Thermometer Datasheet”.
<http://www.micropik.com/PDF/dht11.pdf>
[3] “DHT11 Humidity and Temperature Sensor Datasheet”.
<http://www.micropik.com/PDF/dht11.pdf>
[4] “Atlas Scientific pH EZO Sensor Data Sheet”.
<https://www.atlas-scientific.com/_files/_datasheets/_circuit/pH_EZO_datasheet.pdf>
[5] “Aquatec 8800 24VAC RO Pump Data Sheet”
<http://www.aquatec.com/sg_userfiles/8800_RO_Booster_Pump_24Vac.pdf>

72
Appendix 3: Mechanical and Electrical Schematics

Figure A3.1: Schematic Table of Contents

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

Team Website URL:


http://www.egr.msu.edu/classes/ece480/capstone/spring16/group08/index.html

Recipe Website URL:


http://webdev.cse.msu.edu/~huangbe1/ece480/PhpProject1/Sign-In.html

Arduino Code:

#include "DHT.h"
#include "OneWire.h"
#include <elapsedMillis.h>

#define DHTPIN 8
#define DHTTYPE DHT11

DHT dht(DHTPIN, DHTTYPE); //Set up pin 8 for humidity sensor


OneWire ds(9); //Temperature sensor on pin 9

//Global variables

long dataInterval, int_blu1_on, int_blu2_on, int_red1_on, int_red2_on;

long int_blu1_off, int_blu2_off, int_red1_off, int_red2_off;

int temp_min, temp_max, currentPage, lights_master, valve_freq, valve_dur;

int lights_default, valve_freq_default, valve_dur_default;

bool blu1, blu2, red1, red2, valve_open, fans, fahrenheit,


sensor_stringcomplete, input_stringcomplete;

bool lights_init, auto_flag, recipe_init, user_valve, user_fans;

elapsedMillis timeElapsed, timer_valve_off, timer_blu1_on, timer_blu2_on;

elapsedMillis timer_red1_on, timer_red2_on, timer_fan, timer_valve_on;

elapsedMillis timer_blu1_off, timer_blu2_off, timer_red1_off,


timer_red2_off;

elapsedMillis timer_recipe;

float pH_min, pH_max, humid_min, humid_max, red_share, ph, temp_current,


humid_current;

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);

//Make sure that pins are low initially

digitalWrite(35, LOW);
digitalWrite(36, LOW);

//Reset all timers


timer_valve_off = 0;
timer_valve_on = 0;
timer_fan = 0;
timer_recipe = 0;
timeElapsed = 0;
user_valve = false; //checks to see if the valve is regularly scheduled
or activated by the user. Prevents humidity sensor from interfering
user_fans = false; //Does the same thing for the fans

//valve and fans should be deactivated


valve_open = false;
fans = false;
fahrenheit = true; //Use F for temperature
lights_init = true; //Tell set lights that it's boot time
auto_flag = true; //Tell the WiFi module to automatically update

lights_master = 15; //Hours lights are on as stated in recipe


lights_default = lights_master; //Will be changed once recipe is read

red_share = 0.53; //% of the light that should be red. Max is 1.0 (100%)

dataInterval = 60000; //Check sensors every 60 seconds


currentPage = 0; //HMI always starts on the home page

valve_freq = 1; //Number of hours when the valve needs to turn on


valve_dur = 1; //Minutes that the valve stays on automatically

valve_freq_default = valve_freq;
valve_dur_default = valve_dur; //To start, make the defaults the same as
the actual

pH_min = 6.0; //Reasonable parameters if no recipe is uploaded

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();

delay(3000); //Let everything get situated for a couple seconds

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;

//Convert milliseconds to hours


int_blu1_on = long(float(lights_master*3600000)*temp_blu);
int_blu2_on = int_blu1_on;

if(int_blu2_on <0){ //Make sure the calculated time is not 0


int_blu2_on = 0;
int_blu1_on = 0;
}

if(int_blu2_on >86400000){ //Make sure that the calculated time is not


more than 1 day
int_blu2_on = 86400000;
int_blu1_on = 86400000;
}

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;
}

//24 hours minus the on time


int_blu1_off = 86400000 - int_blu1_on;
int_blu2_off = 86400000 - int_blu2_on;
int_red1_off = 86400000 - int_red1_on;
int_red2_off = 86400000 - int_red2_on;

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();

//When humidity is too low, turn the valve on


if((humid_current < humid_min and humid_current < 96) and
!valve_open){switchObject(5,currentPage);
if(currentPage == 1){
sendMessage("t0.bco=57600"); //If humid is too low, make the display
red
sendMessage("ref t0");
}
}
// When the humidity is high enough

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");
}
}

//Code for temperature sensor. Mostly written by manufacturer


void temp(){
byte i;
byte present = 0;
byte data[12];
byte addr[8];

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 ( OneWire::crc8( addr, 7) != addr[7]) {


// Serial.print("CRC is not valid!\n");
// return;
// }

// 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

delay(1000); // maybe 750ms is enough, maybe not


// we might do a ds.depower() here, but the reset will take care of it.

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();

int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract, Tf_100,


Whole_f, Fract_f;
LowByte = data[0];
HighByte = data[1];
TReading = (HighByte << 8) + LowByte;
SignBit = TReading & 0x8000; // test most sig bit
if (SignBit) // negative
{
TReading = (TReading ^ 0xffff) + 1; // 2's comp
}

Tc_100 = (6 * TReading) + TReading / 4; // multiply by (100 * 0.0625)


or 6.25
Tf_100 = 1.8 * Tc_100 + 3200;

Whole = Tc_100 / 100; // separate off the whole and fractional portions
Fract = Tc_100 % 100;

Whole_f = Tf_100 / 100; // separate off the whole and fractional


portions
Fract_f = Tf_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);
}

temp_current = float(Tf_100 / 100.00001);


if (SignBit){ // If its negative
temp_current *= -1.000000000001;
}

displayTemp();

if(Tf_100 > (temp_max*100) and !fans){

switchObject(6, currentPage); //Turn on fans if temp is too hot


if(currentPage == 1){ //Make the text box red
sendMessage("t0.bco=57600");
sendMessage("ref t0");
}
}
//Turn them off once it cools down (with a margin of 4 deg)
//Keep them on if the user is the one that turned them on in the first
place
if(Tf_100 < ((temp_max - 4.0)*100) and (fans and !user_fans))
switchObject(6, currentPage);

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;
}
}

//Used only for calibrating pH sensor


//Serial event should happen when user uses this Arduino Serial monitor to
send calibration info
void serialEvent(){
inputstring = Serial.readStringUntil(13);
input_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

if(String(message[3],DEC).toInt() == 0) //Touch release event


buttonPushed(object, currentPage); //React to what button
was pushed
break;
}
//Event where the HMI sends the current page ID
case 'f':{
currentPage = String(message[1], DEC).toInt();
loadPage(currentPage);
break;
}
//Touch event in sleep mode
case 'h':{
sendMessage("sleep=0"); //Get out of sleep mode

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;

//Check if it's time to read the sensors


if(timeElapsed > dataInterval){
timeElapsed = 0;
humid();
temp();
pH();
}

//Turn valve off it it's done spraying


if((valve_open and user_valve) and timer_valve_on > (60000*valve_dur)){
user_valve = false;
switchObject(5,currentPage);
}

//Turn on the valve if it's time


if(timer_valve_off >= (3600000*valve_freq)){
user_valve = true;
switchObject(5,currentPage);
}

//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);
}

//Get the recipe if it hasn't loaded;

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());
}

//Process data received from the WiFi Module.


void updateRecipe(String data){
int idx, cnt, hold;
cnt = 0;
String temp;
temp = "";
idx = data.indexOf("</tr>");
if(idx>0 and idx<data.length()){
idx = data.indexOf("<td>",idx); //Find first td that we know is in the
information table (not the headings)
idx += 4; //Skip ahead of the <td> part
}

while(idx >0 and idx < data.length()){

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;

else if(cnt == 1){


if(idx < (data.length() -5))
idx = data.indexOf("<td>",idx);
idx += 4; //Skip ahead to recipe name
}
//The data on the team webpage is always in a specific order.
//As the loop progresses, the count variable is updated and a new
parameter is grabbed.
//The current value of cnt is used to tell which paramter is being
grabbed
switch(cnt){
case 0:{

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

if(!isDigit(username[cnt]) and !isAlpha(username[cnt])) temp +=


" ";
else temp += username[cnt];

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++;

idx = data.indexOf("<td", idx+1); //Update index to keep going.


}
if(auto_flag){//If this is set, recipe updates automatically whenever
the Wifi Module sends data
//If statements prevent garbage data from being added
if(lights_default > 0 and lights_default <= 24) lights_master =
lights_default;
if(valve_freq >0) valve_freq = valve_freq_default;
if(valve_dur >0 and valve_dur < (valve_freq*30)) valve_dur =
valve_dur_default;
}

setLights(); //Just in case anything has changed


}

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;
}

void sendMessage(String mes){


while(Serial1.available())
Serial1.read();

Serial1.print(mes.c_str());

Serial1.write(0xff); //Signal to the HMI that the command is over


Serial1.write(0xff);
Serial1.write(0xff);

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;
}

//Do something when a button has been pushed on the HMI


void buttonPushed(int num, int page){
String temp;
//Depending on the page and object number, we know what button it is. Do
what that button does
switch(page){
//Page 0 and 1 just have navigation buttons, so load the corresponding
page
case 0:{
switch(num){
case 1:{
loadPage(1);
break;
}
case 2:{
loadPage(3);
break;
}
default:
break;
}
break;
}

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;
}
}

//Switch outside components (lights, fans, solenoid valve) on/off


//For each object, switch it to the other state, reset the timer, toggle
any pictures on the HMI
void switchObject(int obj, int page){
String temp;
switch(obj){
case 1:{
blu1 = !blu1;
timer_blu1_off = 0;
timer_blu1_on = 0;

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

//Show all the recipe values on page 3


case 3:{
//Code to load all of the recipe values
temp += "t1.txt=\"";
temp += username;
temp += "\"";
sendMessage(temp);
sendMessage("ref t1");

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
}

WiFi Module Code:

#include <Arduino.h>

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <elapsedMillis.h>

#include <ESP8266HTTPClient.h>

#define USE_SERIAL Serial

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

//WiFi network to be connected to. Needs to be updated outside of


Engineering Building

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());
}

//Read incoming data from Serial


void serialEvent(){
String temp;
int in_byte, cnt;
temp = "";
cnt = 0;
while(Serial.available()){
in_byte = Serial.read();
if(in_byte = 107){ //"G". Code used by the microcontroller to request
an update
cnt++;
}
else cnt = 0; //Gs come in a row, so start over if they don't

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

//Recipe website. Will need to be updated in future design

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);

// file found at server


if(httpCode == 200) {
//String payload = http.getString();
//USE_SERIAL.println(payload);

//Send data to microcontroller for processing


http.writeToStream(&USE_SERIAL);
}

}
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

Table A5.1: Electrical Bill of Materials

115
Table A5.2: Mechanical Bill of Materials

Table A5.3: Cabling Bill of Materials

116

You might also like