How would I add this to my code?

regpye

New Member
I have written some simple code to operate a small pump to flood my seedlings in a hydroponic grow tube.
The code works well, but there is a long time between flooding and I would like to add some code to the pump off cycle that will flash a LED on C.4 of a 08M2 chip and the flashing to decrease in time the closer to the end of the cycle. The idea is to be able to visualize how much time is left by seeing the flashes, short quick flashes would mean the time is nearly up, long slow flashes the cycle has just started .
I think it maybe easy to do using the timing cycle in my code, but I have no idea of how to implement that feature.

I am using an LDR to test for night and day because there are longer off times at night time, so the code will need to work in both instances of day and night.

My existing code:
Code:
; 08M2+ chip
; Seedling pump cycle

main:
low c.0            ;pump is on green led off
low c.2            ;day time yellow led off
low c.4            ;red led off to be used to indicate time remaining for off time.


readadc C.1,b0        ; read LDR for night or day into variable b0
if b0 > 100 then day    ; if b0 > 100 then do day
if b0 < 100 then night    ; if b0 < 100 then do night
day:
high c.0            ;pump is on green led on
low c.2            ; day time yellow led off
for b0 = 1 to 1         ;set number of minutes depends on filling time.   
    wait 50        ;wait 50 seconds
if b0 < 100 then night    ; if b0 < 100 then do night       
next b0
off_time_day:           
low c.0             ; pump off
for b1 = 1 to 60         ;set number of minutes time for draining and roots breathing air.
if b0 < 100 then night    ; if b0 < 100 then do night
    wait 60        ;wait 60 seconds
    next b1

goto main

night:
high c.2            ; night time yellow led on
for b2 = 1 to 1         ;set number of minutes depends on filling time.   
    wait 50        ;wait 50 seconds
if b0 > 100 then day    ; if b0 > 100 then do day       
next b2
off_time_night:           
low c.0             ; pump off
high c.2            ; day time yellow led on
for b3 = 1 to 120     ;set number of minutes time for draining and roots breathing air.
if b0 > 100 then day    ; if b0 > 100 then do day
    wait 60        ;wait 60 seconds
    next b3

goto main
 
Last edited:

oracacle

Senior Member
You need to x change the curly bracket on the code call to a square bracket.

There problem you have with the current version of your choice is the wait commands, while they are being executed the micro controller can not do anything else.
So "wait 60" means nothing happens until 60 servings had elapsed.

While juggling flags and time counters can make things more complex it increases the flexibility the code had to execute code while waiting for time to elapse or an even to happen.
 

regpye

New Member
You need to x change the curly bracket on the code call to a square bracket.

There problem you have with the current version of your choice is the wait commands, while they are being executed the micro controller can not do anything else.
So "wait 60" means nothing happens until 60 servings had elapsed.
Fixed the curly bracket, easy to miss that one. Thanks for pointing that out.

Okay I understand what you mean, but I don't know enough to code this myself yet.
What would you suggest I do instead of using wait or pause commands, is there a better way to do it so that I can implement the additional code that I have no idea of how to do that yet either?
 

oracacle

Senior Member
OK, this shoukld do what you need to.
Had to work around some of the restriction of the M2 chips.
I had to use one pause with a variable length

this post has more info about mapping if you want to take it a bit further

Code:
; 08M2+ chip
; Seedling pump cycle

Symbol day_on_time    = 50        ;number if second pump on during the day
symbol day_off_time     = 3600    ;number of seconds pump is off during the day
symbol night_on_time    = 50
symbol night_off_time    = 7200
symbol min_in         = 0        ;lowest possible reading from timer
;the 2 below at least 1/1000 of lowest possible on/off time
;they are in milliseconds,while the on/off time is in seconds
;above is a minimum of 50s, or 50,000ms, that divided by 1000 gives 50
;this mean that the lowest time for the LED flash we can use is 50ms
;higher than that causes underflow in the map equation
symbol max_out         = 1050    ;highest time period in ms for LED - shorter is better for execution time
symbol min_out         = 50        ;lowest possible time poeriod for LED

symbol day_flag        = bit0    ;is day or night
symbol pump_flag        = bit1    ;did we set the pump to on or off
symbol ldr_reading    = b4
symbol time_counter    = w3        ;count the time
symbol in_val         = w4        ;for holding calculated in value for later mapping
symbol out_val         = w5        ;for holding calculated out value for later mapping
symbol map_val         = w6        ;the mapped value    
symbol pause_time        = w7        ;the duration of the only pause in the code - try and keep below 1s


symbol true            = 1
symbol false        = 0

init:
    out_val = max_out-min_in    ;pre calulate this value from what put in above
    time_counter = 8000        ;mkae higher than highest pumpon/off time to force inot if statement on first run

main:

readadc C.1,ldr_reading            ; read LDR for night or day into variable b0
    ;first determine time of day
    if ldr_reading > 100 then    ;is it day?
        day_flag = true        ;set to true
        
        if pump_flag = true and time_counter > day_off_time then
            ;if the pump is on, and time period exceeded turn it off
            low c.0
            pump_flag = false            'set pump flag
            in_val = day_on_time-min_in    'calculate this for mapping
            time_counter = 0            'reset time counter
        end if
        
        if pump_flag = false and time_counter > day_on_time then
            ;if the pump is off, and time period exceeded turn it on
            high c.0
            low c.2
            pump_flag = true
            in_val = day_off_time-min_in
            time_counter = 0
        end if
    
    else
        day_flag = false        ;otherwise assume its night
        
        if pump_flag = true and time_counter > night_off_time then
            ;if the pump is on, and time period exceeded turn it off
            low c.0
            pump_flag = false
            in_val = night_on_time-min_in
            time_counter = 0
        end if
        
        if pump_flag = false and time_counter > night_on_time then
            ;if the pump is off, and time period exceeded turn it on
            high c.0
            low c.2
            pump_flag = true
            in_val = night_off_time-min_in
            time_counter = 0
        end if
    
    end if

    if time_counter <> time then            ;keep track of time without concern of overflow
        time_counter = time_counter + 1
    end if
    gosub map
    pause pause_time
    toggle c.4

goto main

map:
    ;this procedure maps the time value to the pause duration and will alter it as time decreases.
    map_val = time_counter - min_in
    map_val = map_val * out_val / in_val + min_out
    pause_time = max_out - map_val
    return
 

hippy

Technical Support
Staff member
The idea is to be able to visualize how much time is left by seeing the flashes, short quick flashes would mean the time is nearly up, long slow flashes the cycle has just started .
It will be basically be a three-step process. The first step is to replace any loops and waits with a routine which can wait for however long you want to wait. Something like this ...
Code:
Symbol PAUSE_MS = 10

Symbol minutes = w1
Symbol ms      = w2

minutes = 5
Gosub WaitAndFlash
End

WaitAndFlash:
  ms = 60000 ; One minute's worth of milliseconds
  Do
    Pause PAUSE_MS
    If ms > PAUSE_MS Then
      ms = ms - PAUSE_MS
    Else
      ; One minute has elapsed
      If minutes = 0 Then
        ; No more minutes to wait so return
        Return
      End If
      minutes = minutes - 1
      ms = ms - PAUSE_MS + 60000
    End If
  Loop
Then you will need to determine what the flash rate of the LED will be. That could be determined by how many minutes you have left to go or could, with a little more effort, be based upon the proportion or percentage of the time elapsed or to go.

You will also need to add something within the 'WaitAndFlash' routine which actually turns the LED on and off at the rate desired.

That could be achieved by having an 'UpdateLed' routine similar to below and adding a "Gosub UpdateLed" before the 'Loop' in the above -
Code:
Symbol rateTime    = w3
Symbol elapsedTime = w4

UpdateLed:
  elapsedTime = elapsedTime + PAUSE_MS
  If elapsedTime >= rateTime Then
    elapsedTime = elapsedTime - rateTime
    Toggle LED
  End If
  Return
Then all that remains is to set the 'rateTime' desired to make it work. The simplest scheme would be a 500ms time until the last minute when it is shorter, causing faster flashing -
Code:
SetLedFlashRate:
  Select Case minutes
    Case > 1 : rateTime = 500
    Else     : rateTime = 125
  End Select
  return
 

regpye

New Member
SetLedFlashRate: Select Case minutes Case > 1 : rateTime = 500 Else : rateTime = 125 End Select return
Thanks Hippy.
I am getting an error in the code that you have provided; I don't know what it means.

Toggle LED
^
Syntax error on line 45 at/before position 11

Error: This command requires the pin number in format PORT.PIN not PINx!
 

hippy

Technical Support
Staff member
Sorry about that. You will need to add a "Symbol LED = C.4" to the code.
 

regpye

New Member
OK, this shoukld do what you need to.
Had to work around some of the restriction of the M2 chips.
I had to use one pause with a variable length
Thanks for all that work, it appears to work in the simulation, I will program a chip tomorrow and test it out in practice (It is very late here now in Australia) Also thanks for the learning experience, I will have to study it more fully so I can understand how it is working.
 

regpye

New Member
Sorry about that. You will need to add a "Symbol LED = C.4" to the code.
That seems to have fixed it, testing in simulation now and will program a chip tomorrow to test in practice.
Thanks very much for your quick responses and teaching, very much appreciated.
 

Aries

New Member
Fixed the curly bracket, easy to miss that one. Thanks for pointing that out.
It's always worth doing two things when posting:
(1) Use the Preview button to see what it looks like
(2) Look at what you've written after you've posted it, and use the Edit option to correct if necessary
 

regpye

New Member
OK, this shoukld do what you need to.
Had to work around some of the restriction of the M2 chips.
I had to use one pause with a variable length
I tried using your code oracacle, but unfortunately it didn't work so well in practice.
Spent the last two days and nights working it out my own way which is probably not very elegant and probably not as efficient as it could be, but I understand what I have done and most of all it works. I used the pause command to my advantage as it also provides the needed timing for the code duration as well as for the time for the flashes. I also changed the idea slightly, by making most of the flashing a constant flash and then the last 12 minutes or so the increasing flash rate. I also added it to both night and day sections, just in case I needed that.
The final code which I have working now;

Code:
symbol flash_rate = 2000
symbol pump_pause = 500 ; 10x number of secongs needed, find out how many seconds to fill grow pipe for the auto syphon to work.

main:
readadc C.1,b0        ; read LDR for night or day into variable b0
if b0 > 100 then day    ; if b0 > 100 then do day
if b0 < 100 then night    ; if b0 < 100 then do night
;--------------------------------------------------------
day:
high c.0            ; pump is on, green LED is on
low c.2            ; yellow led off, day time
low c.4            ;red led off
for b1 = 1 to 100     
pause pump_pause        ;wait 50 seconds
readadc C.1,b0        ; read LDR for night or day into variable b0        
if b0 < 100 then night    ; if b0 < 100 then do night        
next b1
off_time_day:            
low c.0    ; pump off
gosub daypause            ; 
readadc C.1,b0        ; read LDR for night or day into variable b0    
if b0 < 100 then night    ; if b0 < 100 then do night
goto main
;--------------------------------------------------------
night:
high c.0            ; pump is on, green LED is on
 high c.2    low c.4        ; yellow led on, red LED off
for b3 = 1 to 100         
pause pump_pause        ;wait 60 seconds or seconds if less than 1 minute
readadc C.1,b0        ; read LDR for night or day into variable b0        
if b0 > 100 then day    ; if b0 > 100 then do day        
next b3
off_time_night:            ;
low c.0            ; pump off, green led off
for b3 = 1 to 3000        
high c.4
pause 200
low c.4
pause flash_rate
readadc C.1,b0        ; read LDR for night or day into variable b0    
if b0 > 100 then day    ; if b0 > 100 then do day
next b3
 let w0 = flash_rate
for b9 = 1 to 190
  high C.4
  pause 200
  low C.4
  pause w0
  w0 = w0 - 10
next b9
goto main
;--------------------------------------------------------
daypause:
for b3 = 1 to 1800         
high c.4
pause 200
low c.4
pause flash_rate
readadc C.1,b0        ; read LDR for night or day into variable b0        
if b0 < 100 then night    ; if b0 < 100 then do night
next b3
let w0 = flash_rate
for b9 = 1 to 190
  high C.4
  pause 200
  low C.4
  pause w0
  w0 = w0 - 10
next b9
return
 

regpye

New Member
It will be basically be a three-step process. The first step is to replace any loops and waits with a routine which can wait for however long you want to wait. Something like this .
I tried it on the screen and it seemed to work at first, but hard to tell until put in practice. In practice it didn't work for me, so I carried on with my own version, most probably not the best way to do it, but it works and does all I need. You can see my code in #13 that I ended up using.
thanks for your assistance though, I did learn a thing or two from you which I appreciate.
 

oracacle

Senior Member
you can put the LED cod eflash within an if statement
Code:
if pump_flag = true then
  ;do led flash stuff here
end if
this means the LED will only operate whenever the pump is running.

The change in LED flash rate is very subtle over the given time period and may not be noticable until you get towards the end. This a function of how the map functuion works.
As an idea the below needs the loop counter to get to 30 before the map_val variable as moved 3. In terms of the code i wrote previously, that could be 30 seconds to change the LED duration by just 3ms
here is some test code, it seem to work in simulator and should work on chip too
Code:
symbol min_out     = 50
symbol max_out    = 1050
symbol max_in    = 10000
symbol min_in    = 0
symbol out_val    = max_out-min_out
symbol in_val    = max_in-min_in
symbol map_val    = w0
symbol counter    = w1
symbol inv_map    = w2

init:
    counter = 0
main:
    map_val = counter - min_in
    map_val = map_val * out_val / in_val + min_out
    inv_map = max_out - map_val
    sertxd(#counter, "  :  ", #map_val, "  :  ", #inv_map,13,10)
    inc counter
    pause 100
    goto main

Put a
Code:
pump_flag = true
day_flag = true
into the init (before main loop starts) section of the code, this will correct the incorrect flag value on the first loop through.

As for the LDR not working - this is how the code works. it can only change the ouput value once the current timing sequence as passed.
if you want it to be detected and acted upon the instant there is a chngne then you need to take note of the reading at last change and then force a change of sequence. How you do that is down to you.

I am working a little blind here as i don't have the hardware laying around atm. My projects are reaching beyound the capabilities of the Picaxe system so have little need to have them on the shelf.
 

regpye

New Member
this means the LED will only operate whenever the pump is running.
Thank you for your continued support oracacle.
There are 3 LEDs in the project, one to show if it is night time and that part of the code is running, that is on C.2, the second on C.4 is when the pump is off and this is the one that the flashing is coded for, the reason being that it will indicate when the pump is likely to switch back on again to give some warning when messing about with pressurised water, the third LED is when the pump is on, using C.0 and that LED needs no coding as it is connected directly across the output of the mosfet that switches on the pump.

Because of the long delay times for the pump coming on for a very short time of only 50 seconds, it is rather important in the hot Australia summers not to miss a pump cycle, so checking if day or night frequently I thought important. For that reason instead of checking just once I made it to check during each loop. Otherwise there could be a time of up to 4 hours without a pump on, that could be devastating to seedlings in very hot weather.
The system works as a flood and drain, with the pump on for a short time, just enough to fill the system and then an automatic drainage starts to empty it again, most of the time the system is empty of water.

I have to travel to the city for the next few days (several hours away drive) so I will look more closely at your extra code when I get back and give it another try out, I am sure to learn something while doing it
 

regpye

New Member
The change in LED flash rate is very subtle over the given time period and may not be noticable until you get towards the end. This a function of how the map functuion works.
I had a quick look at the code before leaving for the city.
For some reason I got several syntax error messages, so I just commented the lines out to see if any of it would run.
The code ran okay on the screen showing the countdown of the timer, not able to do a practical test until I get back, but there will be something wrong with maybe how I have put your code together I am sure.

I noticed while running the code that C.0 switches on briefly through each cycle while C.4 is flashing. There was no switching on C.0 for 50 seconds before the flashing of C.4 indicating that the pump is now off.

The buffer readout from the screen has the following results from a short test.
It appears a bit strange as it shows that after 62 it all restarts again at 1000, I would have thought that it would continue to count down??

0 : 50 : 1000
1 : 50 : 1000
2 : 50 : 1000
3 : 50 : 1000
4 : 51 : 999
5 : 51 : 999
6 : 51 : 999
7 : 52 : 998
8 : 52 : 998
9 : 52 : 998
10 : 52 : 998
11 : 53 : 997
12 : 53 : 997
13 : 53 : 997
14 : 54 : 996
15 : 54 : 996
16 : 54 : 996
17 : 54 : 996
18 : 55 : 995
19 : 55 : 995
20 : 55 : 995
21 : 56 : 994
22 : 56 : 994
23 : 56 : 994
24 : 57 : 993
25 : 57 : 993
26 : 57 : 993
27 : 57 : 993
28 : 58 : 992
29 : 58 : 992
30 : 58 : 992
31 : 59 : 991
32 : 59 : 991
33 : 59 : 991
34 : 59 : 991
35 : 60 : 990
36 : 60 : 990
37 : 60 : 990
38 : 61 : 989
39 : 61 : 989
40 : 61 : 989
41 : 61 : 989
42 : 62 : 988
43 : 62 : 988
44 : 62 : 988
45 : 63 : 987
46 : 63 : 987
47 : 63 : 987
48 : 64 : 986
49 : 64 : 986
50 : 64 : 986
51 : 64 : 986
52 : 65 : 985
53 : 65 : 985
54 : 65 : 985
55 : 66 : 984
56 : 66 : 984
57 : 66 : 984
58 : 66 : 984
59 : 67 : 983
60 : 67 : 983
61 : 67 : 983
62 : 68 : 982
63 : 50 : 1000
64 : 50 : 1000
65 : 50 : 1000
66 : 51 : 999
67 : 51 : 999
68 : 51 : 999
69 : 51 : 999
70 : 52 : 998
71 : 52 : 998
72 : 52 : 998
73 : 53 : 997
74 : 53 : 997
75 : 53 : 997
76 : 53 : 997
77 : 54 : 996
78 : 54 : 996
79 : 54 : 996
80 : 55 : 995
81 : 55 : 995
82 : 55 : 995
83 : 56 : 994
84 : 56 : 994
85 : 56 : 994
86 : 56 : 994
87 : 57 : 993
88 : 57 : 993
89 : 57 : 993
90 : 58 : 992
91 : 58 : 992
92 : 58 : 992
93 : 58 : 992
94 : 59 : 991
95 : 59 : 991
96 : 59 : 991
97 : 60 : 990
98 : 60 : 990
99 : 60 : 990
100 : 60 : 990
101 : 61 : 989
102 : 61 : 989
103 : 61 : 989


Your code as I have used it for testing;
Code:
; *******************************
; 08M2+ chip
; Seedling pump cycle

Symbol day_on_time    = 50        ;number if second pump on during the day
symbol day_off_time     = 3600    ;number of seconds pump is off during the day
symbol night_on_time    = 50
symbol night_off_time    = 7200
symbol min_in         = 0        ;lowest possible reading from timer
;the 2 below at least 1/1000 of lowest possible on/off time
;they are in milliseconds,while the on/off time is in seconds
;above is a minimum of 50s, or 50,000ms, that divided by 1000 gives 50
;this mean that the lowest time for the LED flash we can use is 50ms
;higher than that causes underflow in the map equation
symbol max_out         = 1050    ;highest time period in ms for LED - shorter is better for execution time
symbol min_out         = 50        ;lowest possible time poeriod for LED

symbol day_flag        = bit0    ;is day or night
symbol pump_flag        = bit1    ;did we set the pump to on or off
symbol ldr_reading    = b4
symbol time_counter    = w3        ;count the time
symbol in_val         = w4        ;for holding calculated in value for later mapping
symbol out_val         = w5        ;for holding calculated out value for later mapping
symbol map_val         = w6        ;the mapped value    
symbol pause_time        = w7        ;the duration of the only pause in the code - try and keep below 1s
                     ;symbol min_out    = 50
                     ;symbol max_out    = 1050
symbol max_in    = 10000
                      ;symbol min_in    = 0
                      ;symbol out_val    = max_out-min_out
                      ;symbol in_val    = max_in-min_in
                      ;symbol map_val    = w0
symbol counter    = w1
symbol inv_map    = w2


symbol true            = 1
symbol false        = 0

init:
    out_val = max_out-min_in    ;pre calulate this value from what put in above
    time_counter = 8000        ;mkae higher than highest pumpon/off time to force inot if statement on first run
    counter = 0
main:

readadc C.1,ldr_reading            ; read LDR for night or day into variable b0
    ;first determine time of day
    if ldr_reading > 100 then    ;is it day?
        day_flag = true        ;set to true
        
        if pump_flag = true and time_counter > day_off_time then
            ;if the pump is on, and time period exceeded turn it off
            low c.0
            pump_flag = false            'set pump flag
            in_val = day_on_time-min_in    'calculate this for mapping
            time_counter = 0            'reset time counter
        end if
        
        if pump_flag = false and time_counter > day_on_time then
            ;if the pump is off, and time period exceeded turn it on
            high c.0
            low c.2
            pump_flag = true
            in_val = day_off_time-min_in
            time_counter = 0
        end if
    
    else
        day_flag = false        ;otherwise assume its night
        
        if pump_flag = true and time_counter > night_off_time then
            ;if the pump is on, and time period exceeded turn it off
            low c.0
            pump_flag = false
            in_val = night_on_time-min_in
            time_counter = 0
        end if
        
        if pump_flag = false and time_counter > night_on_time then
            ;if the pump is off, and time period exceeded turn it on
            high c.0
            low c.2
            pump_flag = true
            in_val = night_off_time-min_in
            time_counter = 0
        end if
    
    end if

    if time_counter <> time then            ;keep track of time without concern of overflow
        time_counter = time_counter + 1
    end if
    gosub map
    pause pause_time
    toggle c.4
    
    ;--------------------------------
    map_val = counter - min_in
    map_val = map_val * out_val / in_val + min_out
    inv_map = max_out - map_val
    sertxd(#counter, "  :  ", #map_val, "  :  ", #inv_map,13,10)
    inc counter
    pause 100
    goto main
    ;--------------------------------

goto main

map:
    ;this procedure maps the time value to the pause duration and will alter it as time decreases.
    map_val = time_counter - min_in
    map_val = map_val * out_val / in_val + min_out
    pause_time = max_out - map_val
    return
 

hippy

Technical Support
Staff member
What is the functionality of the unit you are designing ? As far as I can tell you want a pump to repeatedly cycle on and off at one rate during the day and cycle on and off at another during night time.

My tip would be to refactor the code so it's more condense as to what it's doing, modularise so the functionality is separate to the control.

My attempt is below. The main loop is a finite state machine which cycles between pump on and off, and moves between day or night. The rest are support routines to handle what that main program needs to do.

Code:
#Picaxe 08M2
#Terminal 4800
#No_Data

;          .-----__-----.
;         -| V+      0V |-
;         -| SI      SO |-
;  LED <---| C.4    C.1 |<--- LDR
;         -| C.3    C.2 |---> Pump
;          `------------'

Symbol LDR        = C.1 ; LDR pin
Symbol LDR_DAY    = 100 ; Greater or equal this it's day
Symbol LDR_NIGHT  = 100 ; Lower or equal this it's night

Symbol PUMP       = C.2 ; Pump control

Symbol LED        = C.4  ; LED indicator

Symbol reserveW0  = w0  ; b1:b0
Symbol dayOrNight = w1  ; Set to IS_DAY or IS_NIGHT
Symbol minutes    = w2  ; Number of minutes to wait
Symbol ms         = w3  ; Millisecond timer
Symbol enableLed  = w4  ; Toggle LED when delaying, 1=Yes, 0=No
Symbol ledRate    = w5  ; Led toggling rate
Symbol ledElapsed = w6  ; Led timer

Symbol LOOP_MS    = 20  ; Basic loop time in milliseconds

Symbol IS_DAY     = 0   ; Set 'dayOrNight' to 0 to use 'On dayOrNight Goto Day, Night'
Symbol IS_NIGHT   = 1   ; Set 'dayOrNight' to 1 to use 'On dayOrNight Goto Day, Night'

; Determine the pump time periods in minutes

Symbol DAY_PUMP_ON_TIME    = 40
Symbol DAY_PUMP_OFF_TIME   = 20

Symbol NIGHT_PUMP_ON_TIME  = 15
Symbol NIGHT_PUMP_OFF_TIME = 10

; Main program code

MainProgram:
  Gosub DetermineDayOrNight
  On dayOrNight Goto DayPumpOn, NightPumpOn

; Daytime pumping

DayPumpOn:
  Gosub TurnPumpOn : minutes = DAY_PUMP_ON_TIME : Gosub WaitAndFlashLed
  Gosub DetermineDayOrNight
  On dayOrNight Goto DayPumpOff, NightPumpOff

DayPumpOff:
  Gosub TurnPumpOff : minutes = DAY_PUMP_OFF_TIME : Gosub WaitAndFlashLed
  Gosub DetermineDayOrNight
  On dayOrNight Goto DayPumpOn, NightPumpOn

; Night time pumping

NightPumpOn:
  Gosub TurnPumpOn : minutes = NIGHT_PUMP_ON_TIME : Gosub WaitAndFlashLed
  Gosub DetermineDayOrNight
  On dayOrNight Goto DayPumpOff, NightPumpOff

NightPumpOff:
  Gosub TurnPumpOff : minutes = NIGHT_PUMP_OFF_TIME : Gosub WaitAndFlashLed
  Gosub DetermineDayOrNight
  On dayOrNight Goto DayPumpOn, NightPumpOn

; Determine if day or night

DetermineDayOrNight:
  ReadAdc LDR, b0        ; read LDR and set 'dayOrNight'
  If b0 > LDR_DAY Then
    dayOrNight = IS_DAY
  End If
  If b0 < LDR_NIGHT Then
    dayOrNight = IS_NIGHT
  End If
  Return

; Control the pump

TurnPumpOn:
  SerTxd("Pump On", CR, LF)
  High PUMP
  Return

TurnPumpOff:
  SerTxd("Pump Off", CR, LF)
  Low PUMP
  Return

; Delay routines

WaitAndFlashLed:
  enableLed = 1
  Goto WaitMinutes

WaitWithoutFlashingLed:
  enableLed = 0
  Goto WaitMinutes

WaitMinutes:
  Low LED
  ledElapsed = 0
  Do
    SerTxd("  ", #minutes, CR, LF)
    Gosub SetLedRate
    Gosub WaitOneMinute
    minutes = minutes - 1
  Loop Until minutes = 0
  Low LED
  Return

WaitOneMinute:
  ms = 60000         ; Delay for a minute, 60 seconds, 60 x 1000 milliseconds
  #IfDef SIMULATING
    ms = 0           ; Run as fast as possible when simulating
  #endif
  Do
    Pause LOOP_MS
    ms = ms Min LOOP_MS - LOOP_MS
    If enableLed = 1 Then
      Gosub UpdateLed
    End If
  Loop Until ms = 0
  Return

; Led handling

SetLedRate:
  Select Case minutes
    Case > 4 : ledRate = 1000 ; Slow flash
    Case > 2 : ledRate = 500
    Case > 1 : ledRate = 250
    Else     : ledRate = 125  ; Fast flash
  End Select
  Return

UpdateLed:
  ledElapsed = ledElapsed + LOOP_MS
  If ledElapsed >= ledRate Then
    Toggle LED
    ledElapsed = 0
  End If
  Return
 

regpye

New Member
What is the functionality of the unit you are designing ? As far as I can tell you want a pump to repeatedly cycle on and off at one rate during the day and cycle on and off at another during night time. Correct
Thanks for your code design Hippy, I have tried it out and it works very well, but doesn't suit my circuit boards that have already been made and populated.
I will describe the needed functions, most of which you have done, just a few small changes or additions.
25779

I like the way that you have made it easy to change the times for the pump on and off, that is great. The flashing sequences are also good.
The pump on times are a bit critical and really need to be in seconds as the current receiver tank and pump it takes 50 seconds to reach the correct level, the water needs to come in fairly fast so that the auto syphon will activate. If too hard to change this code it can be achieved by restricting the water flow a little by trial and error or increasing the size of the receiver tank, but that also has a problem because that tank is made from a 2 metre length of PVC pipe cut from a 6M length, so an increase in length size would mean a loss of material.
The reason for adding the flashing at an increasing rate is to give some indication of when the pump is likely to start up again, I have been caught a few times already not having any idea where the timing is at that time. A quick flashing would indicate to wait a while before messing around with pumps and stuff. It is not really practical to just turn off the pump.

The boards have three LEDs, a mosfet and an LDR

A green LED is wired with the mosfet so that when the pump is on the green LED is activated as well, no coding needed for that LED

The LDR is as you have coded. However on a practical test this morning using my first version that has no flashing, but has the same LDR code I noticed as day was breaking that the pump was fluctuating on and off very fast and could not deliver the water properly. Later in the day when in full daylight that was not a problem.

When in the night cycle the flashing LED would be on C.2 Yellow LED, and when the day cycle the flashing on C.4 Red LED

I realize that you didn't put the pump on C.0 because that is serial out and would possibly effect the sertxd on that pin, but after testing that the code works, which it does, that pin could be used for the mosfet I think??

In all I am very impressed with how you have designed this project, I have/will learn a lot from it when I have digested all its workings.
 

Attachments

Last edited:

hippy

Technical Support
Staff member
Thanks for the update. It should be possible to change the code to do what you want without altering the hardware or set-up. I have posted revised code at the end with a few changes -

Timing is now in seconds, maximum single wait allows 65,536 seconds; about 1,000 minutes, 18 hours so should be usable on a cyclic day pump. I used the values from oracacle's post #6; pumps every hour during the day, every two hours at night.

Pump moved to C.0. SerTxd is only executed when simulating so won't mess with the pump on a real chip.

I have added some LDR hysteresis which should prevent it switching between day and night too frequently. You may have to tweak LDR_DAY and LDR_NIGHT values.

Added the extra LED and updated the code to decide which or none to flash or light. I have added all the possible wait and flash or not routines so to change LED functionality you should only need to alter the four wait options in the main loops.

You may need to specify exactly what the LED output you want to see. Currently I have it -

Day Pump On - Day LED always lit when pumping
Day Pump Off - Day LED slow flashing until two minutes from end when speeds up

Night Pump On - Night LED always lit when pumping
Night Pump Off - Night LED slow flashing until two minutes from end when speeds up

Code:
#Picaxe 08M2
#Terminal 4800
#No_Data

;              .-----__-----.
;             -| V+      0V |-
;             -| SI     C.0 |---> Pump
;  Day LED <---| C.4    C.1 |<--- LDR
;             -| C.3    C.2 |---> Night LED
;              `------------'


Symbol PUMP       = C.0 ; Pump control

Symbol LDR        = C.1 ; LDR pin
Symbol LDR_DAY    = 110 ; Greater or equal this it's day
Symbol LDR_NIGHT  =  90 ; Lower or equal this it's night

Symbol NIGHT_LED  = C.2  ; Night LED indicator
Symbol DAY_LED    = C.4  ; Day LED indicator

Symbol reserveW0  = w0  ; b1:b0
Symbol dayOrNight = w1  ; Set to IS_DAY or IS_NIGHT
Symbol seconds    = w2  ; Number of seconds to wait
Symbol ms         = w3  ; Millisecond timer
Symbol whichLed   = w4  ; Which LED to toggle when delaying
Symbol ledRate    = w5  ; Led toggling rate
Symbol ledElapsed = w6  ; Led timer

Symbol LOOP_MS    = 20  ; Basic loop time in milliseconds

Symbol IS_DAY     = 0   ; Set 'dayOrNight' to 0 to use 'On dayOrNight Goto Day, Night'
Symbol IS_NIGHT   = 1   ; Set 'dayOrNight' to 1 to use 'On dayOrNight Goto Day, Night'

Symbol NO_LED     = $FF ; Fake pin number when no LED flashing

; Determine the pump times in seconds

; Note : Numbers from oracacle's post #6

Symbol DAY_PUMP_ON_TIME    = 50
Symbol DAY_PUMP_OFF_TIME   = 3600

Symbol NIGHT_PUMP_ON_TIME  = 50
Symbol NIGHT_PUMP_OFF_TIME = 7200

; Main program code

MainProgram:
  Gosub DetermineDayOrNight
  On dayOrNight Goto DayPumpOn, NightPumpOn

; Daytime pumping

DayPumpOn:
  Gosub TurnPumpOn : seconds = DAY_PUMP_ON_TIME : Gosub WaitWithDayLedOn
  Gosub DetermineDayOrNight
  On dayOrNight Goto DayPumpOff, NightPumpOff

DayPumpOff:
  Gosub TurnPumpOff : seconds = DAY_PUMP_OFF_TIME : Gosub WaitFlashingDayLed
  Gosub DetermineDayOrNight
  On dayOrNight Goto DayPumpOn, NightPumpOn

; Nighttime pumping

NightPumpOn:
  Gosub TurnPumpOn : seconds = NIGHT_PUMP_ON_TIME : Gosub WaitWithNightLedOn
  Gosub DetermineDayOrNight
  On dayOrNight Goto DayPumpOff, NightPumpOff

NightPumpOff:
  Gosub TurnPumpOff : seconds = NIGHT_PUMP_OFF_TIME : Gosub WaitFlashingNightLed
  Gosub DetermineDayOrNight
  On dayOrNight Goto DayPumpOn, NightPumpOn

; Determine if day or night

DetermineDayOrNight:
  ReadAdc LDR, b0        ; Read LDR and set 'dayOrNight'
  If b0 > LDR_DAY Then
    dayOrNight = IS_DAY
  End If
  If b0 < LDR_NIGHT Then
    dayOrNight = IS_NIGHT
  End If
  Return

; Control the pump

TurnPumpOn:
  #IfDef SIMULATING
    If dayOrNight = IS_DAY Then
      SerTxd("Day")
    Else
      SerTxd("Night")
    EndIf
    SerTxd(" Pump On", CR, LF)
  #EndIf
  High PUMP
  Return

TurnPumpOff:
  #IfDef SIMULATING
    If dayOrNight = IS_DAY Then
      SerTxd("Day")
    Else
      SerTxd("Night")
    EndIf
    SerTxd(" Pump Off", CR, LF)
  #EndIf
  Low PUMP
  Return

; Day delay routines

WaitWithDayLedOn:
  High DAY_LED
  Low  NIGHT_LED
  whichLed = NO_LED
  Goto WaitSeconds

WaitFlashingDayLed:
  Low  DAY_LED
  Low  NIGHT_LED
  whichLed = DAY_LED
  Goto WaitSeconds

; Night delay routines

WaitWithNightLedOn:
  High NIGHT_LED
  Low  DAY_LED
  whichLed = NO_LED
  Goto WaitSeconds

WaitFlashingNightLed:
  Low NIGHT_LED
  Low DAY_LED
  whichLed = NIGHT_LED
  Goto WaitSeconds

; Delay routines

WaitWithoutFlashingLed:
  Low DAY_LED, NIGHT_LED
  whichLed = NO_LED
  Goto WaitSeconds

WaitSeconds:
  ledElapsed = 0
  Do
    #IfDef SIMULATING
       SerTxd("  ", #seconds, CR, LF)
    #EndIf
    Gosub SetLedRate
    Gosub WaitOneSecond
    seconds = seconds - 1
  Loop Until seconds = 0
  Low DAY_LED, NIGHT_LED
  Return

WaitOneSecond:
  ms = 1000          ; Delay for one second, 1000 milliseconds
  #IfDef SIMULATING
    ms = 0           ; Run as fast as possible when simulating
  #endif
  Do
    Pause LOOP_MS
    ms = ms Min LOOP_MS - LOOP_MS
    Gosub UpdateLed
  Loop Until ms = 0
  Return

; Led handling

SetLedRate:
  Select Case seconds
    Case >  60 : ledRate = 1000  ; Slow flash
    Case >  30 : ledRate = 500
    Case >  10 : ledRate = 250
    Else       : ledRate = 125   ; Fast flash
  End Select
  Return

UpdateLed:
  If whichLed <> NO_LED Then
    ledElapsed = ledElapsed + LOOP_MS
    If ledElapsed >= ledRate Then
      Toggle whichLed
      ledElapsed = 0
    End If
  End If
  Return
 

regpye

New Member
I have posted revised code at the end with a few changes -
Wow that was quick work, and it is working perfectly, I am really impressed by your coding skills, so many thanks to you Hippy and Oracacle for both your efforts.
It is about 3:30am here in Australia and I was so excited I had to program a chip and test it out, all appears to be working perfectly on the night run.
It will take me some time to nut out how it all works, but a simple task turned out to be something more complicated than first conceived, and it shows what can be done when you have the coding skills like you guys have.
 

regpye

New Member
I have added some LDR hysteresis which should prevent it switching between day and night too frequently. You may have to tweak LDR_DAY and LDR_NIGHT values.
Okay, I have done a full test now and I started at about 3 am this morning. The night cycle completed and started again still on night cycle. Daylight came but the code was still on the night cycle well into daylight, so something needs to be changed to check more often for a change in light conditions. I switched off, waited a few moments and switched on again and the code started off with the day cycle as it should, so both conditions are working, just needs to check more, because while in a long flashing cycle, if it just starts before day break, it means that that cycle will continue until it has fully finished.
 

regpye

New Member
Have you checked the LDR setting? I'm talking about in real life.
Good luck...........
Bear
Yes I have and two systems are working now, both have the same problem. One is flood and drain system which is the important one, the other is deep water culture which doesn't matter so much. The deep water culture I just checked a couple of minutes ago and it is not daylight just yet and it just started a pump on using the night cycle. That one will not complete a that cycle for another 4 hours as it is a long cycle. The next cycle should start on day cycle which will start about 10:40am. No problem with the hysteresis, that is working fine now. set it to 95 -105
Flood and drain can be without any water for a long period if the night cycle starts just on day break, as the whole system is completely drained. Not much of a problem at this time of year in Australia as it is winter, but in the summer time when it is very hot it could be a problem.
 

regpye

New Member
Timing is now in seconds, maximum single wait allows 65,536 seconds; about 1,000 minutes, 18 hours so should be usable on a cyclic day pump. I used the values from oracacle's post #6; pumps every hour during the day, every two hours at night. Great, I can change that to suit different situations.

Pump moved to C.0. SerTxd is only executed when simulating so won't mess with the pump on a real chip. perfect

I have added some LDR hysteresis which should prevent it switching between day and night too frequently. You may have to tweak LDR_DAY and LDR_NIGHT values. I changed it to 95 - 105 and working well.

Added the extra LED and updated the code to decide which or none to flash or light. I have added all the possible wait and flash or not routines so to change LED functionality you should only need to alter the four wait options in the main loops. Really good, thanks

You may need to specify exactly what the LED output you want to see. Currently I have it -

Day Pump On - Day LED always lit when pumping Good
Day Pump Off - Day LED slow flashing until two minutes from end when speeds up Could be a little longer, maybe 15 minutes or thereabouts

Night Pump On - Night LED always lit when pumping Good
Night Pump Off - Night LED slow flashing until two minutes from end when speeds up Could be a little longer, maybe 15 minutes or thereabouts
Hippy, would it be possible to add a check for day or night about halfway through the night flashing sequence? That way the long off time can be halved if daylight breaks when the flashing cycle started just before daybreak.
I am still working my way through the code design and may have some questions on how things work in the future. I love it.
 

oracacle

Senior Member
OK, had 20 mins to ahve a look at this.

put this in the main loop
Code:
    if day_flag = false and ldr_reading > 100 then
        reset
    end if

    if day_flag = true and ldr_reading < 100 then
        reset
this will force the entire system to reset when the change occours

as for the map issue, it is overflowing with those vaklues, my overflow happened @ 66, whicgh makes sense as that exceeds 64553 that can be held in a word varibale.
 

regpye

New Member
OK, had 20 mins to ahve a look at this.

put this in the main loop
Code:
    if day_flag = false and ldr_reading > 100 then
        reset
    end if

    if day_flag = true and ldr_reading < 100 then
        reset
this will force the entire system to reset when the change occours

as for the map issue, it is overflowing with those vaklues, my overflow happened @ 66, whicgh makes sense as that exceeds 64553 that can be held in a word varibale.
Thanks Oracacle,
I am getting an error message, maybe I have put it in the wrong area of the code, the error I am getting is;

if day_flag = false and ldr_reading > 100 then
^
Syntax error on line 99 at/before position 14

Error: Unknown symbol - false
The carrot points to false

This is where I placed your extra code;

Code:
DetermineDayOrNight:
  ReadAdc LDR, b0        ; Read LDR and set 'dayOrNight'
  If b0 > LDR_DAY Then
    dayOrNight = IS_DAY
  End If
  If b0 < LDR_NIGHT Then
    dayOrNight = IS_NIGHT
  End If
;----------------------------------
if day_flag = false and ldr_reading > 100 then
        reset
    end if

    if day_flag = true and ldr_reading < 100 then
        reset
;----------------------------------      
  Return
 

oracacle

Senior Member
That was intended to go with the code I posted in post #6 as a result the flags and symbols used are different.


also take a look through this and see if you can implement it within my previous code
Code:
symbol min_out     = 50 / 10
symbol max_out    = 1050 / 10
symbol max_in    = 10000 / 10
symbol min_in    = 0
symbol out_val    = max_out-min_out
symbol in_val    = max_in-min_in
symbol map_val    = w0
symbol counter    = w1
symbol inv_map    = w2

init:
    counter = 0
main:
    map_val = counter - min_in
    map_val = map_val * out_val / in_val + min_out * 10
    inv_map = max_out * 10 - map_val
    sertxd(#counter, "  :  ", #map_val, "  :  ", #inv_map,13,10)
    inc counter
    pause 100
    goto main
it will run by itself, it just needs working into the control code.
 

regpye

New Member
That was intended to go with the code I posted in post #6 as a result the flags and symbols used are different.


also take a look through this and see if you can implement it within my previous code
I am lost, really confused now.
 

inglewoodpete

Senior Member
Thanks Oracacle,
I am getting an error message, maybe I have put it in the wrong area of the code, the error I am getting is;

if day_flag = false and ldr_reading > 100 then
^
Syntax error on line 99 at/before position 14


Error: Unknown symbol - false
The carrot points to false
If you haven't already resolved this error...

Unlike some languages, True and False are not system defined constants. You need to declare them.

I use the following block in my declarations:
Rich (BB code):
   'Generic Constants
   Symbol False         = 0
   Symbol True          = 1
   Symbol Lo            = 0
   Symbol Hi            = 1
 

regpye

New Member
Okay, I have done a full test now and I started at about 3 am this morning. The night cycle completed and started again still on night cycle. Daylight came but the code was still on the night cycle well into daylight, so something needs to be changed to check more often for a change in light conditions. I switched off, waited a few moments and switched on again and the code started off with the day cycle as it should, so both conditions are working, just needs to check more, because while in a long flashing cycle, if it just starts before day break, it means that that cycle will continue until it has fully finished.
I have tried to modify hippy's code a little, but I am not getting the desired results.
What I am trying to do is if the night off cycle is running and it has become daylight, the program will reset and start again to test for day or night.
My code changes don't make any difference so I must be doing something wrong.

Code:
; Determine if day or night
DetermineDayOrNight:
  ReadAdc LDR, b0    ; Read LDR and set 'dayOrNight'
  If b0 > LDR_DAY Then
    dayOrNight = IS_DAY
  End If
  If b0 < LDR_NIGHT Then
    dayOrNight = IS_NIGHT
  End If
  
  ; Check for light conditions change from night to day
  If dayOrNight = IS_DAY And ledElapsed = 0 Then
    ledElapsed = 1
  End If
  If dayOrNight = IS_NIGHT And ledElapsed = 1 Then
    Reset
  End If
 
  Return
 

hippy

Technical Support
Staff member
My code changes don't make any difference so I must be doing something wrong.
The 'ledElapsed' cycling period is too short to be able to be used as a time elapsed counter, but you are sort of on the right track.

This is untested but I believe it should do what you need. First we need to add a new variable to count how long the elapsed time in a period has been, which needs to be zeroed whenever a delay starts, and increments as we progress. Finally we need to add a check for an 'early abort' when we go from night to day ...
Rich (BB code):
Symbol elapsed    = w7  ; Elapsed Time

WaitSeconds:
  ledElapsed = 0
  elapsed = 0
  Do
    #IfDef SIMULATING
       SerTxd("  ", #seconds, CR, LF)
    #EndIf
    Gosub SetLedRate
    Gosub WaitOneSecond
    seconds = seconds - 1
    elapsed = elapsed + 1
    Gosub CheckForEarlyAbort
  Loop Until seconds = 0
  Low DAY_LED, NIGHT_LED
  Return
Then of course we need that 'early abort' routine. This is a bit tricky but the logic is sound.

If elapsed time ever becomes longer than the DAY_PUMP_OFF_TIME we might be in a situation where 'we think it's night, but it's actually day, so may need to abort'. Note that the night on elapsed time can never get that high, nor can the day on or day off time, so we will only have that situation at night during the pump off period.

So we've been off for a long time and it may have become day, so let's check if it is night or has become day. If it's still night, 'same as it ever was', and we can simply return, will repeat the process after the next second.

If it's day we have to abort early so we do that by forcing the seconds remaining to be zero, which aborts the night off state. After the off state we always go to an on state, and we'll go to a day on state because it's now day.

Code:
CheckForEarlyAbort:
  If elapsed > DAY_PUMP_OFF_TIME Then
    Gosub DetermineDayOrNight
    If dayOrNight = IS_DAY Then
      seconds = 0
    End If
  End If
  Return
 
Top