![]() Figure 1 Stainless steel moisture probe in potted plant
|
The basis of operation for this system is the following:
Stainless steel probes to measure the relative soil moisture content by measuring resistance,
A microcontroller with built-in analog to digital converter as the heart of the system, and high-current power MOSFETs to drive pumps, electrically operated water valves, or other devices.
Correct moisture content in soil is maintained by constantly monitoring the relative moisture content in each plant's pot with the stainless forked probe, and operating the pumps to raise moisture level.
Each plant is constantly measured, and it's moisture level is converted to a number between 0 and 1023 (Base ten.) Stored in the DATA EEPROM on the microcontroler are Min. and Max. values for each plant.
When a plant's moisture level drops below the min. value, that plant's pump is turned on. When the moisture goes above the Max. setting for that plant, the pump turns off.
Also stored in DATA EEPROM on the microcontroler are Min. Time between water and Max. time between water. These settings allow the system to be optimized for exotic plants which require special watering routines and these parameters can be set anywhere from 0 to 4095 hours, which is 0 to 170.625 days.
A plant will not be watered until the Minimum number of hours has passed since it was last watered. Note that the min. time can be set to 0, in which case it does not effect the systems operation. The maximum watering time specifies a time after which a plant will be watered, even if the moisture level is above the Minimum moisture level setting for that plant. Note that it will never get watered if the moisture level is above the maximum. (Example of timed watering: For a cactus type plant, you could set the max. moisture to 500, the min. to 0, the min. time to 1 week, and the max. time to 4 weeks. The plant would get watered about once a month, but the soil would get dry between waterings, as may be found in soils where cactus are native)
This system can be programmed via it's "Learn" button, or a Personal Computer connected to the RS232 interface. The firmware provides to the RS232 interface a well-defined protocol which allows reading and writing of all SRAM and DATA EEPROM, in both Byte (8 bit) modes and Word (16 bit). This allows the settings of ALL operational parameters, and allows the reading and writing of the eight 16-byte strings in DATA EEPROM.(More on that later.) The RS232 interface is not intended for direct end user use with a serial terminal (although it can be used that way,) but rather to support Software which could be written to provide many other features.
To program using the "Learn" button, one simply inserts the probes into their plants, turns the RUN/STOP switch to STOP, and goes about watering their plants as normal, but they press the "Learn" button before and after watering. Soon, the system will have learned (And stored to non-volatile DATA EEPROM) the min. and max. moisture levels that you normally keep your plants at. Once it's learned this, just switch the switch to RUN, and it waters the plants whenever you would have, based on moisture level.
![]() Figure 2 Probe, PCB, and RS232 Plug |
One toggle switch, two push buttons: "Run/Stop", "Reset", and "Learn":
Also, by holding the Learn button for 5 seconds (or until the Yellow LED lights), the system will erase all learned settings and be ready to start learning again. Furthermore, resetting while holding the Learn button causes the system to reinstate the "default settings."
There are eight 16-character strings in DATA EEPROM that can easily be written to or read from over the RS232 interface, which are intended to store plants names, for use with software which might run on a Personal Computer.
![]() Figure 3 Block Diagram of Hardware |
Details: Power Conditioning: This section is simply a reverse voltage protection diode, filter capacitors, and an AN7805, a positive 5 volt, 1 Amp solid state voltage regulator Integrated Circuit. See bottom left portion of Schematic Diagram.
5 Channel Analog To Digital Inputs: This section consists of a 2 by 5 pin (Ten pins), gold plated 0.1 inch spaced Pin Header, where Sensors can be plugged on, and 5 10k, 1%, 0.6W Metal Film resisters, connected as conditional pullups on all 5 ADC inputs. By "conditional", the author describes how the pullup resistors are all fed from an output pin on the microcontroler, such that the pullups are only enabled for that small duration of time when they are needed.
This system measures relative soil moisture-content by measuring soil resistance. This would work, except if one tries to measure the DC resistance of wet soil, the reading will constantly change as the probes and soil begin to charge up, as if they were a wet-cell (rechargeable battery). Indeed, two stainless steel wires poked into damp earth will charge up and then have a tenth of a volt across them for days. To solve this problem, the author designed the system such that voltage is only applied to the probes for about 18 microseconds, ten times a second. This creates a duty cycle which is low enough that the probes never charge up. This gives a constant accurate reading, preserves * probe life, and prevents the plant from being in a constant electrical field. (* Probes would dissolve if left energized.)
User Controls: The user controls consist of 3 LED's: A red, a yellow, and a green, and three switches: One toggle, and two momentary.
The red LED simply indicates that the toggle switch is set to STOP. The green LED flashes to indicate proper operation. Furthermore, the "Learn" button serves two other functions. When the Learn button is depressed while the devices is reset or powered up, the default settings are set into EEPROM. This is needed if you accidentally program it wrongly, and wish to start fresh. Secondly, if you hold the Learn button down for 4 seconds (Yellow LED then comes on to signal to you that 4 seconds has passed), then all MIN./MAX. presets for all channels are "cleared". By "cleared", the author means that the MIN. and MAX. for each of the 5 channels are set to the current reading for each channel. This allows any change in soil moisture to be seen as "Above max." or "Below min.", that is to say, as a "new high", or "a new low," and be learned by subsequent presses of the "Learn" Button.
RS232 interface: The RS232 interface consists of two standard NPN low power bipolar transistors, 4 resistors, 1 small silicon diode, and two large red LED's with built in 1k resistors. The LED's indicate a "mark" (a 'one' or logic high) condition on either Tx or Rx. The transistors and diodes simply function as logic level inverters, and voltage level converters. The RS232 interface happily accepts plus to minus 12 volts, while it outputs 0-5 volts, with MARK=5 volts, SPACE=0 volts. These transistors are needed because the design of the PIC18F252 is such that MARK=0 and SPACE=5 volts (Exactly inverted of RS232, where MARK=+12v and SPACE=-12v), but this is understandable because many popular true RS232 level converter solutions require inverted feed. This RS232 interface tests well at 115.2kbaud, but the firmware for this system is set to communicate at 19.2Kbaud.
Motor drivers: The motor driver block consists of a 10 pin (2x5) gold plated Pin Header, 5 Schottky diodes, five 10k surface mount resistors, and five of International Rectifier's IRL1004, which is a 40 volt, 130 amp (At 25C, With heat sink) N-Channel Power Metal Oxide Field Effect Transistor (MOSFET). The resistors are simply pulldowns, which are needed because the MOSFET has very high input impedance (Max. 100 na), and if some of the motors are running, and the output pins of the microcontroler go into High-Impedance mode, the motors will stay on for some time, due to the gate capacitance on the MOSFET. The 5 diodes protect the FETs from high voltage which may be produces as a result of switching an inductive load.
![]() Figure 4 Flow Chart |
Flow Chart.
This flow chart gives an outline of the systems general operation. "But what about the 5 channels?" you might ask. The main loop runs 10 times a second, each time servicing the next channel. When it's done with the fifth channel, it does the first channel. "ML" in the flowchart refers to the variable into which the 10 bit result of the ADC is read into. ML, in this case, stands for "moisture-level." Each of the five channels have their own set of
"Presets:"
"minimum moisture", "maximum moisture", "minimum time between water", and "maximum time between water."
The ADC returns a value from 0 to 1023, depending on resistance of the soil. All "Presets" can be set with any number from 0 to 4095, although setting a moisture level preset above 1023 is of no use. As for "min./max. time," valid and useful setting range is from 0 to 4095 hours. This allows up to about 170 day periods.
When the learn button is pressed, each channel only updates it's own set of presets.
The "Clear" feature causes the system to read the moisture of each plant's soil, and set each channel's min. and max. presets to equal the current moisture reading. This causes any change in soil conductivity to be seen as a "new high" or a "new low", which can be learned with subsequent presses of the "Learn" button.
![]() Figure 5 Schematic Diagram |
| Parts list | |
| SW1 | Momentary, Reset |
| R18-R19 | 680, 1%, 0.6W, Metal film |
| SW2 | Momentary, Learn |
| D6 | Small silicon diode |
| R1-R2 | 100k, 1.4w, 5% |
| R20 | 100k, 1/4w, 5% |
| R3-R7 | 10k, .6w, 1%, Metal Film |
| D7 | 2.5A, 1000 PIV diode |
| R8-R10 | 275, 1%, 0.6W Metal Film |
| C5 | 22uF, 20v Capacitor |
| LED1 | Small green LED |
| C6-C7 | 100pf, 25v surface mount capacitor |
| LED2 | Small yellow LED |
| C8 | 25v,15uF Capacitor |
| LED3 | Small red LED |
| C9-C12 | 47uF, 10v Capacitor |
| H1 | 5 pin gold plated pin header, .1 inch spacing, ICSP plug |
| IC2 | AN7805 1 amp, 5 volt Solid State Voltage regulator |
| H2-H3 | 10 gold plated pin header, .1 inch spc, 2x5, ADC and Mot. |
| SW3 | SPST Toggle switch |
| IC1 | PIC18F252 flash based microcontroller |
| LED4 | Large red LED with internal 1k resistor |
| XTAL1 | 10Mhz Quartz crystal |
| LED5 | Large red LED with internal 1k resistor |
| C1-C2 | 5pf, 5v surface mount capacitor |
| R11-R15 | 10k surface mount resistors |
| Q1-5 | IRL1004 Power MOSFETs |
| C3-C4 | 100pF, 10v Surface mount capacitors |
| D1-D5 | 1 amp, 50 volt Schottky diodes |
| R17 | 100k, 1/4w, 5% resistor |
| Q6-Q7 | General purpose NPN transistor (C1684 or equiv.) |
Firmware specifications.
RS232 Interface:
Also implemented is a 64-byte buffer, into which incoming bytes are stored (after being pulled from the receive FIFO), until a return/enter character comes (#13,dec). When an "enter" character comes, then the whole command-line-buffer is processed by the command interpreter.
Communications:
Other parts of the firmware which enable good communications between the
system and a host PC over the RS232 interface include a
hexadecimal-to-decimal, a decimal-to-hexadecimal, conversion routines and a
command interpreter.
The hex to decimal to hex conversions are entirely the work of yours truly, and converts any 12 bit number (0-0x0FFF) between binary coded decimal (packed two digits to a byte) number of 0-4095.
The command interpreter accepts a number followed by an equals symbol, or two numbers separated by an equals symbol. Also, a "W" or "B" can be appended to most commands to specify that the function is a Word, or 16 bit operation.
The first number is taken as a memory address. The second word, if supplied, is taken as the data to store at specified address. If no number follows the equals sign, then the data at that memory location is returned, and the location is unchanged.
All readable and writable settings, registers and strings are accessed (read and written to) through a memory mapped scheme.
Here are address ranges and functions: (All addresses and values are in base ten)
0 to 127 is access ram low
128 to 1535 is on-chip static ram
2000 to 2255 is on-chip DATA EEPROM
3000 to 3007 is the eight 16-byte strings, stored at the beginning of the DATA EEPROM.
3968 to 4095 is access ram high (Special Function Registers).
The syntax to read a memory location is:
nnn=[W] where nnn is any decimal number between 0 and 4095, and W, if present, specifies that it's a WORD read, in which case the byte at nnn is taken as the high 8 bits, and the byte at nnn+1 is taken as the low 8 bits, and this 16 bit value is converted to decimal and displayed in the form of "nnnn=dddd",where nnnn is the address just read, and dddd is the decimal data stored there.
Similarly, to write a location, use the syntax: nnn=dddd[W] where nnn is the address to write to, and dddd is the data (0-4095) to write. If the data supplied is more then 3 digits long or the command is ended with a W, then the write is done in 16 bit (word) mode. That is to say that dddd is converted to hex, and the high 8 bits is written to address nnnn, and the low 8 bits are written to address nnnn+1.
To read a string, use the syntax: 300n= where n is a number from 0 to 7 (including 0 and 7). Writing is similar: 3002="String data" would store "String data" to string 2.
Interrupt service routine: The interrupt service routine is triggered by 3 events: TIMER0 overflow, TXREG Empty, and RCREG Full.
TIMER0, running in 16 bit mode off of instruction cycle clock, with 1:64 prescaler, is preloaded with about 0xC2F6 on every overflow, which gives an interrupt rate of 10Hz (10,000,000/64/15625=10; 65,535-15,625=49,910=0xC2F6) The real time clock is a counter consisting of four registers: 1/10th-seconds, seconds, minutes, and hours, and counts hours, minutes, seconds and 1/10 seconds after reset. Every hour this part of the ISR increments the hours counters for each plant. TXREG also trips the ISR when it goes empty. However, when the last byte in FIFO is sent, the interrupt enable for "TXREG Empty" is disabled, until another byte is added to the Tx FIFO.
Whenever a new byte comes into RCREG, it trips the ISR, and the new byte is added to the Rx FIFO, and the RxFIFO-Empty flag is cleared. (When "getchar" reads the last byte from the Rx FIFO, it sets the RxFIFO-Empty flag).
Detailed list of addresses of all settings:
Shown are the address of the first byte of the first channel's setting. All channels are in order, starting with channel 0, and going to channel 4. Thus, to get the address of channel X, just add 1 or 2 (depending on if it's 8 bit or 16 bit value) to the address shown for ch0.
All 16 bit words are HIGH byte first. (For example, 184 is HIGH 8 bits and 185 is low 8 bits)
ALL IN SRAM:
184 High byte of Time since last watering of channel 0. 16 bits.
200 High byte of AD conversion result for channel 0 16 bits
217 RTC Tenths of seconds. 8 bits.
218 RTC Seconds. 8 bits
219 RTC Minutes. 8 bits
220 RTC Hours. 8 bits. (0 to 255 hours. After that, rolls over to 0 hours).
ALL IN EEPROM:
2128 High byte of Minimum Moisture Preset for Channel 0. 16 bits
2130 High byte of Maximum Moisture Preset for Channel 0. 16 bits.
2132 High byte of Minimum Time between watering preset, for channel 0. 16 bits.
2134 High byte of Maximum Time between watering preset, for channel 0. 16 bits.
2136 High byte of Minimum Moisture Preset for Channel 1. 16 bits
2138 High byte of Maximum Moisture Preset for Channel 1. 16 bits.
2140 High byte of Minimum Time between watering preset, for channel 1. 16 bits.
2142 High byte of Maximum Time between watering preset, for channel 1. 16 bits.
And so on. The formula to address any channel is: 2128+(ch#*8)+(Preset-offset*2), where ch# is the desired channel number (0-4) and Preset-offset is a number between 0 and 3, where:
0 = Min. Moisture preset
1 = Max. Moisture preset
2 = Min. Time between watering preset
3 = Max. Time between watering preset.
Status bytes:
Each of the 5 channels has an enable bit, which enables or disables that motor from coming on. (Although a motor is always turned off if the moisture goes above the max. preset.)
2192 = Status byte for channel 0. 8 bits. The Most significant bit is enable. Set=enable.
The lower 4 bits are the string length pointer for String 0.
Strings:
The eight 16-byte strings are stored at the beginning of DATA EEPROM. They are read and set through the address range of 3000 to 3007:
3000 = Virtual address of first string. To set this string, use the syntax:
3000="String data here"
To read, use the syntax:
3000=
and it will reply with
3000="String data here"
ALL in HARDWARE SFR's
All the microprocessor's internal special function registers can be read from and written to, as well. (Although the author doesn't suggest trying to use FSR0 or FSR1, but FSR2 is unused)
3969 = This is the address of the PORTB register. You can manually turn on and off the motors byte writing values to here. (Example:3969=1 would turn on the pump for channel 0.)
Details on interesting parts of the firmware.
Each FIFO has the following:
One 8-bit "input end pointer" (FIFOI)
One 8-bit "output end pointer" (FIFOO)
One 8-bit "status register", who's bits 0 and 1 are used to keep track of "Full" and "Empty" conditions.
Other then that, the only ram used is a whole 256-byte memory bank (bank 4 or 5).
When a new byte is written, the "input end pointer" is incremented once.
When a byte is read out, the "output end pointer" is incremented once.
When reading and writing, the "Empty" and "Full" flags are used to allow full use of all 256 bytes in bank, while preventing the overwriting of still-valid-data, and preventing the reading of old-data (that is to say, prevents reading past end of buffer.)
Being a rotating scheme, there is no overhead in shifting bytes down each time, and due to the use of pointers to indicate ends of valid data, any byte from 0 to 255 can be writing, without causing problems, as apposed to a system which uses a NULL code to indicate end of valid data.
The FIFOs use the indirect addressing feature. FSR0 is reserved for use in the ISR, while FSR1 is used everywhere else. This is to prevent conflict between ISR and non-ISR code using the same FSR at same time.
The FIFOs and the Interrupt Service routine work together to make a very fast buffered "standard input and standard output", with very little overhead of either CPU instructions or wasted SRAM.
The HEX to DEC and DEC to HEX functions only use 6 bytes of ram:
The first four bytes are for input and output: HEXH, HEXL, BCDH, and BCDL. These are how the conversion input and results are handled. The other two bytes used is one temporary "Scratchpad", and the other used is just one bit of one byte -- a bit called the "Overflow flag". This bit is set if an overflow occurs during a conversion.
I found the DAW (Decimal Adjust Wreg) instruction code to be very handy for these functions.
The compare function takes any two 16 bit values, whether they be byte-packed Binary Coded Decimal, two pair ASCII characters, or two 16 bit binary values and compares them, returning one of 3 bits set in a general purpose status register (NOT STATUS. Just a GenPurp file)
it sets the "Greater then", "Equal to", or "Less then" bit, depending on the result. This allows for very easy compares, and program branching. For example, if I wanted to "Skip if x less then or equal to y", I would load x and y into the four input registers for the compare routine, call the compare routine, then do "BTFSC MATHSTAT,GT", which is to say "Skip next instruction if compare was NOT greater then." This works because "Less then or equal to" is the same as saying "Not greater then."
(MATHSTAT is a standard file which I use to return the status of BCD-HEX conversions, Compare, etc., and GT is an EQU to the bit which I have specified as "Greater then".)