Ultimate Chicken House - Phase II - software design - clock and alarms
Having an 18 pin PIC in my possession I decided to limit myself to this microchip and to build the entire project around no more than 18 pins. Initial experiments with the internal PIC oscillator were unsuccessful as it loses or gains too many seconds each day in an unpredictable way. With 2 pins used up for an external oscillator, 2 for power and 5 for the hbridge that left 9 pins for sensors and display. I wanted to use a 7 segment LED for cost, space and simplicity and so ended up with 7 pins for the LED, 1 pin for the feeder door sensor and 1 pin for an input button. I wondered if I might charlieplex the display somehow to get more display from the same number of pins but this seemed beyond me.
Was it feasable to construct a fully programmable 24 hour clock, with programmable alarms (feed and door) plus adjustable feed units with a single digit (7 segment) display and a single input buton? It certainly seemed like it would be easier to use a PIC with more pins or an LCD display but this became a challenge I had to overcome.
The idea of the project was to build as cheaply and simply as possible - this meant using free software. It would have been possible to program for free in assembler but despite what many people say it's a steep learning curve and I wasn't prepared to invest the time in something I wasn't going to use that frequently. I discovered mikroBasic, a BASIC PIC compiler from mikroElektonika. It seems sometimes there is such a thing as a free lunch! The free version of the program is fully functional with the limitation of 2K programming words. This is more than ample for most small projects and what's more there are free online books and a very helpful support forum. I have to say I am inclined to buy their products now on the basis they have provided a great product for free.
The program design required 1) an accurate clock to run the events 2) activation of the feeding and door routines as alarms 3) display of text messages and 24 hour clock on a single digit display and 4) programming via a single button.
Accurate Clock
I based the clock on the rather clever Roman Black zero error timer. This uses timer 0 on an interrupt every 4th clock cycle which decrements 256 from a counter. Once the value falls below 256 a procedure is called to increment the time. Using an external 8 MHz crystal oscillator the interrupt occurs at 8 / 4 = 2 MHz i.e. 2 million times per second. I don't need to time events that accurately so I decided to incremement the time in tenths of a second. If there are 2 million interrupts in a second then there are 200,000 interrupts in a tenth of a second. Therefore if we start timer 0 as 200,256 then it will reach 256 every tenth of a second which is when we increment our tenth of a second clock. I actually found my clock was gaining around 17 seconds per day i.e. about 0.2% so I adjusted the timer 0 value around 200,040 to compensate. Roman Black explains it better than I can but although there is some inaccuracy over the very short term (jitter) the clock has zero error over the long term.
In the time_increment procedure it was a simple matter of checking when the tenths counter hit ten, it increments the seconds counter. When the second counter hits 60 it increments the minute counter, when the minute counter hits 60 it increments the hour counter, when the hour counter hits 24 it restarts at 0. I also added in some actions to occur at certain times.
sub procedure time_increment
'this increments the time every tenth of a second using the 24 hr clock
inc(tenths)
inc(button_timer)
inc(display_timer)
If tenths=10 then
inc(sec)
dec(diy_timer)
If sec = 60 then
inc(mins)
If mins=60 then
inc(hr)
If hr =24 then
hr = 0
' reset the door flag here
end if
mins=0
' write time to EEPROM here
end if
sec=0
end if
tenths = 0
end if
nix_time = hr * 60 + mins ' updates the functions timer NB: implicit conversion
end sub
sub procedure interrupt ' this calls the time_increment procedure every tenth of a second
count = count - 256
if count < 256 then
count=count + count_sec_val
time_increment
end if
INTCON = $20 ' clear interupt flag
end sub
Alams
This was straight forward. If the time is between midnight and door opening time the door down procedure is called and if it's between door opening time and midnight the door opening procedure is called. A flag records the last action so as to avoid repeating this action. A similar event occurs with the feeder and the feeding_done flag is reset at midnight.
'--------------------Door operating checks--------------------------------------
if door_down = True then
if nix_time >= door_nix_time then ' If it's after door open time open
do_door_down(False)
end if
end if
if door_down = False then
if nix_time < door_nix_time then ' Essentially sets door as down after dark
do_door_down(True) ' NB: in areas of very high latitude this needs changing
end if
end if
' --------------------Feeder checks---------------------------------------------
if feeding_done = False then ' If it's feeding time or up to 5 mins later then feed
if nix_time >= feed_1_nix_time then
if nix_time <= feed_1_nix_time + 5 then
do_feed(feed_units)
end if
end if
end if
Both the door opening and feeding procedures require the relay to be opened (via the transistor on pin b.7) to provide power to the hbridge. After a few milliseconds, the appropriate port a pins are brought high and low so that the hbridge applies a potential difference across the output pins in the appropriate direction. In the case of the door, this drives the latching relay to switch on or off. After the few milliseconds required all pins are taken low and the power to the hbridge is cut. In the case of the feeder, the program continue to drive the other half of the hbridge for the desired time to allow the motor to let out the line for the feeder hatch to open. Once the desired time for the feed is up (depending on how many feed units have been programmed), the hbridge is reversed and the motor winds the line in. This occurs until the hatch sensor is closed (= door shut) or in the case of a failed sensor will shut the motor off after seven seconds avoiding potential damage to the hatch and running the motor forever!
sub procedure do_door_down(dim down as Boolean) ' breaks or closes the VSB door operation circuit
portb.7 = 1 ' hbridge on and warm up
delay_ms(5)
if down = True then ' we want door to go down
porta.0 = 0
porta.1 = 1
door_down = True
end if
if down = False then
porta.0 = 1
porta.1 = 0
door_down = False
end if ' give sufficient time for relay to work
delay_ms(6)
portb.7 = 0 ' reset all
porta.0 = 0
porta.1 = 0
end sub
sub procedure do_feed(Dim units as byte) ' performs a single feeding operation dispensing x units
portb.7 = 1 ' hbridge on and warm up
delay_ms(3)
porta.2 = 1 ' we want hopper to open
porta.3 = 0
delay_ms(850) ' Standard opening time then stop
porta.2 = 0
porta.3 = 0
if units >= 1 then
Vdelay_ms(units * 250) ' Set delay to dispense remaining units
end if
if porta.4 = 1 then ' close the hopper until the hatch contact is made
diy_timer = 7
do
porta.2=0 ' reverse motor
porta.3=1
if porta.4 = 0 then
delay_ms(5)
if porta.4 = 0 then break
end if
end if
loop until diy_timer = 0
end if
'NB: We need a timer on interupt here in case hopper never closes
porta.2 = 0
porta.3 = 0
portb.7 = 0
feeding_done = True
end sub
<--- (electronic circuit design) previous --- | --- next (software design - one button programmer) --->
