To poll RTC or not to poll RTC

tommo_NZ

New Member
I am tying to develop "another" timer/scheduler using a DS3231 RTC and a Picaxe 20M2.
There are up to 8 outputs controlled by the timer at various adjustable times and for different periods.
(lights, sprinklers, misters, doping pumps etc..)
On and off times are at 1 minute resolution and are stored in EEPROM as 'minutes since midnight'.
This requires the RTC chip to be read frequently.
Question!, is there a theoretical maximum no of times the RTC can be read, and..
should I let the code just loop without delay reading the time, comparing it with on/off times or....
should the code pause for x seconds and then continue with loop to reduce the number of reads.
Thanks in anticipation,
Regards, Kevin Thompson
Gisborne, New Zealand.
 

RNovember

Well-known member
Question!, is there a theoretical maximum no of times the RTC can be read
Any time you use something electronic, it degrades slightly.

When you read from the chip, its circuitry inside is put to work, wearing out the small silicon components just a little bit.

The less you use it, the longer it should last.
 
Last edited:

tommo_NZ

New Member
Any time you use something electronic, it degrades slightly.
When you read from the chip, its circuitry inside is put to work, wearing out the small silicon components just a little bit.
The less you use it, the longer it should last.
Thanks RNovember for the reply, I guess that's true for all of the components so we can't let that stop us. I was just wondering if the RTC was particularly susceptible to wearing out but oracle's reply quells my concern.
 

tommo_NZ

New Member
I have been polling a ds3231 continuously for 4 years with no signs of issues as yet.
Thanks oracle, that just the reply I was hoping for, I shall boldly forge ahead and poll it with minimal delays. This will increase the accuracy particularly on short on periods. I had an image of the chip frantically accessing the RTC and getting hot or worn out, ridiculous I know but the image is within me now. I once wrote an assembler program to decode morse code but it ran so fast that I had to do 255 null loops for each iteration of main code or it was too fast. This is where I got the image of hardware going mad.
 

Aries

New Member
If you know when your next action is due, you should be able to use the internal timer to work out roughly when that is, and only poll the RTC when you are getting close to that time. You could also poll once an hour or whatever just to keep things in sync.
 

techElder

Well-known member
Any time you use something electronic, it degrades slightly.
When you read from the chip, its circuitry inside is put to work, wearing out the small silicon components just a little bit.
The less you use it, the longer it should last.
There's just a wee bit of misinformation in your post, RNovember. Perhaps a little more explanation on what you meant would be appropriate.
 

hippy

Technical Support
Staff member
Electronic devices should allow infinite and continual reads. And if not there should be a big warning in the datasheet. The most common reason for not allowing continual reading is where a chip does something like take a temperature reading and it needs time to take the reading, determine and process the result, and reading before it's ready can corrupt or reduce the accuracy of the result.

Any damage inflicted upon a chip through reading is pretty much the same as leaving it powered on. You should see its full life expectancy no matter how many you do.

As to whether to poll continually or pause between polling it likely doesn't much matter. But there's probably a few micro-amps to be saved when not polling and it often feels nicer and more satisfying not to poll unnecessarily. It may, as imagined, also save a small amount of heating of the chip.

The amount of heat shouldn't affect the chip's lifetime, but continual or frequent readings may affect results returned from some chips. The ultra-high resolution barometer and altimeter chips seem to be temperature sensitive so it pays to read infrequently and at a regular rate so self-heating is reduced and temperature is largely kept the same. An RTC shouldn't suffer such issues.

What I would probably do, given your timer resolution is in minute units, is pause for just under a minute, then poll repeatedly until a new minute is determined. That gives you the best of both worlds; reduced unnecessary polling, and fast response when a minute change is detected.
 

hippy

Technical Support
Staff member
Any time you use something electronic, it degrades slightly.

When you read from the chip, its circuitry inside is put to work, wearing out the small silicon components just a little bit.

The less you use it, the longer it should last.
You are probably right. But the important thing is how much wearing out it actually causes and how much of a reduction on life expectancy it has.

Most devices have some notional 'many decades' lifetime and any reduction would probably be minuscule in comparison so it usually gets discounted. There's no explicit and guaranteed 'one will get exactly 40 years lifetime' for example, so, if it expires one minute earlier because of continual reading, it's not that significant, and no worse than if one had used a different chip.

It's a bit like having one less baked bean in a can than other cans have. One doesn't really notice and it's not really something to worry over.
 

tommo_NZ

New Member
Electronic devices should allow infinite and continual reads. And if not there should be a big warning in the datasheet. The most common reason for not allowing continual reading is where a chip does something like take a temperature reading and it needs time to take the reading, determine and process the result, and reading before it's ready can corrupt or reduce the accuracy of the result.

Any damage inflicted upon a chip through reading is pretty much the same as leaving it powered on. You should see its full life expectancy no matter how many you do.

As to whether to poll continually or pause between polling it likely doesn't much matter. But there's probably a few micro-amps to be saved when not polling and it often feels nicer and more satisfying not to poll unnecessarily. It may, as imagined, also save a small amount of heating of the chip.
.......
Thank you Hippy for your advice, I guess the chip doesn't know if it is retrieving data, calculating or just looping to kill time, it is still running.
I shall set the delay to around 10 seconds, that is enough to activate the outputs in a timely fashion.
Thanks for taking the time to respond.
Regards, Kevin.
 

tommo_NZ

New Member
If you know when your next action is due, you should be able to use the internal timer to work out roughly when that is, and only poll the RTC when you are getting close to that time. You could also poll once an hour or whatever just to keep things in sync.
Thank you Aries, I will keep that in mind as I develop this further. I don't have much code space left so I need the simplest solution that takes up the least amount of code. I have so little space left that I have to store all the text messages in EEPROM and call a routine that loops through the characters for display. This process has saved about 200 bytes so far.
Regards,
Kevin
 

Aries

New Member
I have so little space left that I have to store all the text messages in EEPROM and call a routine that loops through the characters for display. This process has saved about 200 bytes so far.
Regards,
Kevin
If you aren't already doing so, using TABLE for your text messages will free up EEPROM. Using a NULL character as a terminator means you only need to know the start address of each message (may save you a few bytes of code at the expense of one-byte-longer messages).
 

tommo_NZ

New Member
If you aren't already doing so, using TABLE for your text messages will free up EEPROM. Using a NULL character as a terminator means you only need to know the start address of each message (may save you a few bytes of code at the expense of one-byte-longer messages).
Darn, How dumb am I, I mean I am using EEPROM to store the scheduling data, 10 ports each with up to 10 on/off timings, I am using table to store the text. Using a null char as a terminator adds up. To call the display routine, a word var is loaded with a number so the start position is in the lsb byte and the end position is in the msb. These are the start/end parameters of a for/next loop that moves through the text displaying each char.
I hope you understand this..
Thanks for the reply.
Regards,
Kevin.
 

RNovember

Well-known member
There's just a wee bit of misinformation in your post, RNovember. Perhaps a little more explanation on what you meant would be appropriate.
I meant to add that it should last a long time no matter what you do as long as you don't burn it out.

I thought tommo_NZ was asking if it degraded the chip any amount by reading it.
 
Last edited:

hippy

Technical Support
Staff member
I don't have much code space left so I need the simplest solution that takes up the least amount of code.
If you are happy to post your code I am sure people will take a look at it to see if there are any clever but understandable tricks which can be applied to make it smaller.
 

westaust55

Moderator
If the resolution you seek is 1 minute, have you consider to set the DS3231 to generate an interrupt at 1
Minute intervals?
That would reduce the need to polling and on receipt of an interrupt the PICAXE can check the actual time and determine if an action is to occur.
 

mikeyBoo

Senior Member
hi tommo_nz,
westaust55 is right, there's no point in continuously reading the RTC.
Take a look at proc (aka subroutine) ElapsedTime_Get in BASIC program Picaxe Kayak Control System.bas
https://picaxeforum.co.uk/threads/picaxe-kayak-control-project.28063/
It's used for counting "time on water" but could easily be changed to a real-time clock. This is a very code-efficient way of keeping track of time. It's accurate to within 1 second as long as your main program loop is less than 1 second (usually the case).
The method is:
Read hardware I2C realtime clock on startup (i.e. after reset) & start the 1Hz clock output.
Picaxe will stay as accurate as the 1 second pulse coming from the clock.
If you want, read the realtime clock again when day (or week) rolls over.
When the seconds (1Hz clock) transitions is a good time to increment your software timers & check against their presets.

A clock IRQ is also workable but if it transitions during your timer service routines, it could cause a logic error (e.g. multiple timer “contacts” on a common logic rung). If you are running conditional logic that depends on timer “contacts” you would need to run the conditional logic via the IRQ.
Good luck with your project & don't forget to share.
 

tommo_NZ

New Member
If you are happy to post your code I am sure people will take a look at it to see if there are any clever but understandable tricks which can be applied to make it smaller.
Thank you for the offer Hippy,
following a big rewrite and shuffle of variable assignments, I need to carefully go through and do a review of all the code. There are probably many small errors at the moment but this is my code in Beta.
The application is as follows,
A Picaxe controlled device that can control up to 8 IO ports driving various real world devices. e.g pumps, lights, fans, heaters, irrigation etc. My purpose is for a mini controlled environment to grow unusual plants with specific requirements, but this could be easily adapted to any process.
The user can enable or disable any port, can set multiple on/off times up to 14 per 24 hour period. The clock can be set from within the application.
The device uses the sertxd function and gives feedback via the usb programming cable to a small application on a PC (Termite).
Code is in next 3 messages, it ran over the 10000 char limit.
 
Last edited:

tommo_NZ

New Member
'PART 1
Code:
{' *******************************
'    Filename: Scheduler, Main file
'    Date: ; ; "2018-11-08 12:45:45"
'    File Version: 8.0
'    Written by: tommo_NZ
'    Function: Main file for scheduling timing of irrigation or other processes
'    Last Revision: merging clock set function into main
'    Target PICAXE: Picaxe 20m2
' ***** NOTES *******************
' Scheduling data (On/Off times) are stored as "Minutes since midnight" in EEPROM as a
'  series of integers with up to 14 on/off times for each of the 8 ports.
' Last recorded status is stored at address 0 in EEPROM, being active/inactive flags (in binary).
' On PicAxe 20M2 there are 15 pins available as outputs.
' Pins B.5 And B.7 are For IC2 comms with RTC clock module, Pins C.0, C.1 are for switch inputs.
' Therefore available outputs are B.0, B.1, B.2, B.3, B.4, B.6, C.2, C.3, C.4, C.5, C.7 (11 total).
' *******************************
#picaxe 20M2
#Com 4                        ; Set COM port to COM X, 4800, N,8,1
#No_data    ' no need to use this while testing, data is already there

' EEPROM stores scheduling data in the following fashion:
' The 1st byte stores the last known active status for each of the 8 ports,(left To right, port 8 To port 1)
' ports may be set to active or inactive as user chooses, this means status is kept after power off.
' Then there are 8 rows of 28 bytes which form 14 words representing 7 On/Off cycles.
Eeprom 0,(%01010101,_
104,  1, 114,  1, 0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,_
155,  5, 158,  5, 0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,_
156,  5, 159,  5, 0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,_
157,  5, 160,  5, 0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,_
158,  5, 161,  5, 0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,_
159,  5, 162,  5, 0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,_
160,  5, 163,  5, 0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,_
161,  5, 164,  5, 0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0,   0,  0)

' Text messages are stored in the TABLE area, and retrieved with the readtable command.
Table 0,("CONTROLLER v0.9SCHEDULER RUNNING STOPPEDTIME IS:_MINUTES SINCE MIDNIGHT:_MENU 1234RUNREVIEW/ADJUST CLOCKSAVE_PRESS BLUE TO ACCEPT RED FOR NEXT OPTIONENABLE/DISABLE CHANNELSTO ENTER MENUSYESNOYEAR?MONTH?DATE?HOURS?MINS?TO INCREMENT = XXXXXXXXXXXXXXXXXXXXX")
}
{' DEFINE SYMBOLS
Symbol active_flags = b0     ' Active Flags stores whether outputs are enabled or disabled, in bit0 ~ bit7 of b0.
Symbol status_flags = b1     ' Status_Flags stores the current status of each output port, in bit8 ~ bit15 of b1
Symbol start_time   = w1
Symbol temp1        = b2
Symbol temp2        = b3
Symbol off_time     = w2
Symbol temp3        = b4
Symbol temp4        = b5
Symbol temp5        = b6
' DATE/TIME FUNCTIONS
Symbol seconds       = b7     ' Date/Time function, seconds,     0~59
Symbol minutes       = b8     ' Date/Time function, minutes,     0~59
Symbol hours         = b9     ' Date/Time function, hours,       0~23
Symbol dow           = b10    ' Date/Time function, day of week  1~7
Symbol date          = b11    ' Date/Time function, date         1~31
Symbol month         = b12    ' Date/Time function, month        1~12
Symbol year          = b13    ' Date/Time function, year         0~99
Symbol clock_time    = w7     ' b14 & b15, Minutes since midnight, 0 to 1439 (1439 mins = 11:59 pm)
' RETRIEVING STRINGS FOR MESSAGES
Symbol string_loc    = w8     ' Messaging, w8 comprises of the byte variables 'first' (b16)  & 'last' (b17)  (i.e. w8 = b17 * 256 + b16)
Symbol first         = b16    ' Messaging, (see line re: W8) Position of 'First' character of message string in Table
Symbol last          = b17    ' Messaging, (see line re: W8) Position of 'Last'  character of message string in Table
Symbol char          = b18    ' Messaging, Holds current char to be printed,from Table
' CONVERTING DEC TO BCD AND WRITING TO RTC CLOCK
Symbol bcd_val       = b19    ' Date/Time Conversion, BCD value of time
Symbol lsd           = b20    ' Date/Time Conversion, "Least Significant Digit"
Symbol msd           = b21    ' Date/Time Conversion, "Most Significant Digit"
Symbol date_time_var = b22
Symbol dtv_pos       = b23    ' Position of parameter in hI2cout string
' LOOP COUNTERS
Symbol loop1         = b24    ' Universal loop counter
Symbol loop2         = b25    ' Universal loop counter
Symbol loop3         = b26    ' Universal loop counter
Symbol loop4         = b27    ' Universal loop counter
' ASSIGN PIN NAMES & CONSTANTS
Symbol led_1         = B.6
Symbol red_button    = pinC.0
Symbol blue_button   = pinC.1
Symbol century       = 20     ' It will be a bloody long time before this ticks over
Symbol minus_6       = $10000 - 6    ' Used in BCD conversion, negative value not allowed in calculations so we store the value in a symbol.

' SETUP and DISPLAY INTRODUCTORY INFO
SETUP:
Suspend 1                                               ' Suspend task 1 until main loop starts (Task 1 flashes LED_1 when main loop running)
Pause 100                                               ' Take a deep breath before diving in..
Hi2csetup I2cmaster, %11010000, I2cslow, I2cbyte        ' Initialise I2C coms.
string_loc = 3584  : Gosub PrintString : Gosub NEWLINE  ' Print string "CONTROLLER ver X.x"
string_loc = 12328 : Gosub PrintString                  ' Print string "TIME IS:_"
Gosub read_time                                         ' Get current time from RTC and update "Mins since midnight"
Gosub show_time                                         ' Display current time
string_loc = 18481
Gosub PrintString
Sertxd(#clock_time,Cr,Lf)                               ' Print string "MINUTES SINCE MIDNIGHT:_"
Read 0, b6                                              ' Read active_flags from Eeprom position 0 to variable b6
Let dirsb = %01011111                                   ' Set pins B6,B4,B3,B2,B1,B0 as outputs, pins B7 & B5 are for hi2c, for clock
Let dirsc = %00110000                                   ' Set pins C2,C3,C4,C5,C6,C7 as outputs, pins C0, C1 are for pushbutton inputs.
Gosub Print_Active_Flags
Pause 10
string_loc = 7951
Gosub PrintString
Gosub NEWLINE                                           'Print string "SCHEDULER RUNNING"
Gosub WAIT_FOR_NO_BUTTONS 

'----- MAIN LOOP STARTS ---------------------------------------------------------------------------

Gosub read_time                                           ' NB: Also updates clock_time ( minutes since midnight)
Gosub show_time
string_loc = 29293  : Gosub PrintString                   ' Print string "PRESS_"
string_loc = 34178  : Gosub PrintString                   ' Print string "RED_"
string_loc = 47532  : Gosub PrintString : Gosub NEWLINE   ' Print string "TO ENTER MENUS"
}
'CONTINUED
 
Last edited:

tommo_NZ

New Member
'PART 2
Code:
MAIN_LOOP:
Do While red_button = 0
  Resume 1                                ' Fire up task 1, start the LED_1 flashing to indicate main loop running
  loop3 = 1 : loop4 = 28                  ' Init 1st and last read positions of each loop.
  Gosub read_time                         ' NB: Also updates clock_time (minutes since midnight).
  Pause 2500
  Read 0, active_flags
    For loop1 = 1 To 8
    If bit0 = 0 Then Goto skip
          For loop2 = loop3 To loop4            ' Init loop to step through scheduling data in EPROM two at a time reading start_time and off_time into w0 (LSB into b0 Then MSB into b1) And w1 (LSB into b2 Then MSB into b3)
                  Read loop2, temp1                        ' Start_time, Read LSB from EEPROM into b0 (Least significant BYTE of word w0).
                  Inc  loop2
                  Read loop2, temp2                        ' Start_time, Read MSB from EEPROM into b1 (Most significant BYTE of word w0).
                  Inc  loop2
                  Read loop2, temp3                        ' Off_time,   Read LSB from EEPROM into b2 (Least significant BYTE of word w1).
                  Inc  loop2
                  Read loop2, temp4                        ' Off_Time,   Read MSB from EEPROM into b3 (Most significant BYTE of word w1).
                  If start_time = 0 Then Exit           ' Variable w0, aka start_time = b0 + 256 * b1.
                  If start_time > clock_time Then Exit  ' Not time to start yet, so go to next port
                  If start_time <= clock_time And off_time > clock_time Then ' Time to start has arrived and off time not yet.
                  Gosub Set_Port                      ' Enable port, using 'Case loop1'.
                  Else Gosub Clear_Port               ' Therefore by default, off_time <= clock_time so disable port.
                  Endif
          Next loop2
    loop3 = loop3 + 28 : loop4 = loop4 + 28  ' Set up steps for next port
    skip:
    active_flags = active_flags / 2
    Next loop1  
Loop
'----- MAIN LOOP ENDS -----------------------------------------------------------------------------
Suspend 1                                  ' Suspend task 1 (Flashing LED_1)

'----- EXIT MAIN LOOP & TURN OUTPUTS OFF, SET UP FOR MENUS ----------------------------------------
Low led_1
Let pinsb = %00000000                                    ' Set all pins port B as low, i.e. turn all outputs off
Let pinsc = %00000000                                    ' Set all pins port C as low, i.e. turn all outputs off
Gosub NEWLINE
string_loc = 6159 : Gosub PrintString
string_loc = 10017 : Gosub PrintString  ;Print string "SCHEDULER_STOPPED"
Gosub NEWLINE
Gosub WAIT_FOR_NO_BUTTONS

'--------------------------------------------------------------------------------------------------
MENU_1:     ' REVIEW/ADJUST SCHEDULE
; SELECT MENU_1 OR SKIP TO MENU_2
string_loc = 20041 : Gosub PrintString    ' Print string "MENU_1"
string_loc = 12335 : Gosub PrintString    ' Print string "_"
string_loc = 25173 : Gosub PrintString    ' Print string "REVIEW/ADJUST_"
string_loc = 5647  : Gosub PrintString    ' Print string "SCHEDULE
Gosub NewLine
string_loc = 37997 : Gosub PrintString    ' Print string "Press BLUE To accept Or RED For Next option"
Gosub NEWLINE  
Do
  If red_button  = 1 Then Goto MENU_2
  If blue_button = 1 Then Exit
Loop
Gosub WAIT_FOR_NO_BUTTONS

'MENU_1 BEGINS
Gosub NewLine
Gosub Print_Active_Flags
Sertxd("Menu_1 broken, come back later",Cr,Lf)

'--------------------------------------------------------------------------------------------------      
MENU_2:     ' ENABLE/DISABLE CHANNELS
' SELECT MENU_2 OR SKIP TO MENU_3
Gosub WAIT_FOR_NO_BUTTONS
Gosub NEWLINE
string_loc = 19785 : Gosub PrintString                    ' Print string "MENU_"
string_loc = 20303 : Gosub PrintString                    ' Print string "2"
string_loc = 12335 : Gosub PrintString                    ' Print string ":_"
string_loc = 43925 : Gosub PrintString                    ' Print string "ENABLE/DISABLE CHANNELS"
Gosub NEWLINE
Gosub WAIT_FOR_NO_BUTTONS
string_loc = 37997 : Gosub PrintString : Gosub NEWLINE    ' Print string "Press BLUE To accept Or RED For Next option"
Do
  If red_button  = 1 Then Goto MENU_3
  If blue_button = 1 Then Exit
Loop
Gosub WAIT_FOR_NO_BUTTONS

'MENU_2 BEGINS
Sertxd("Menu2 is a work in progress, good luck",Cr,Lf)
Read 0, active_flags
For loop1 = 1 To 8
    If bit0 = 0 Then Sertxd("Channel ",#loop1," disabled, Enable? (Blue-Yes, Red-No)",Cr,Lf)
    Else Sertxd            ("Channel ",#loop1," enabled, disable? (Blue-Yes, Red-No)",Cr,Lf)
    Endif
    ;Gosub WAIT_FOR_NO_BUTTONS
    Do
      If blue_button = 1 Then Goto Enable_Port
      If red_button  = 1 Then Goto Disable_Port
    Loop

Enable_Port:
     Sertxd("Channel ",#loop1," enabled",Cr,Lf)
     Goto Moving_on
  
Disable_Port:
     Sertxd("Channel ",#loop1," disabled",Cr,Lf)
    Moving_on:
    active_flags = active_flags / 2
    Gosub WAIT_FOR_NO_BUTTONS
Next

'--------------------------------------------------------------------------------------------------
MENU_3:     ' ADJUST CLOCK
' SELECT MENU_3 OR EXIT TO MAIN LOOP
Gosub NEWLINE
string_loc = 19785 : Gosub PrintString                    ' Print string "MENU_"
string_loc = 20560 : Gosub PrintString                    ' Print string "3"
string_loc = 12335 : Gosub PrintString                    ' Print string ":_"
string_loc = 26460 : Gosub PrintString                    ' Print string "ADJUST_CLOCK"
Gosub NEWLINE      : Gosub WAIT_FOR_NO_BUTTONS
string_loc = 37997 : Gosub PrintString : Gosub NEWLINE    ' Print string "Press BLUE To accept Or RED For Next option"
Do
  If red_button  = 1 Then Goto CONTINUE
  If blue_button = 1 Then Exit
Loop

'MENU_3 BEGINS
Gosub read_time
Gosub show_time
Gosub WAIT_FOR_NO_BUTTONS

'--------------------------------------------------------------------------------------------------
CHANGE_HOUR:
string_loc = 25180 : Gosub PrintString                    ' Print string "ADJUST_"
string_loc = 54479 : Gosub PrintString : Gosub NEWLINE    ' Print string "HOURS?"
string_loc = 37997 : Gosub PrintString : Gosub NEWLINE    ' Print string "Press BLUE To accept Or RED For Next option"
Do
  If blue_button = 1 Then Exit
  If red_button = 1 Then Goto CHANGE_MINS
Loop
Gosub WAIT_FOR_NO_BUTTONS
Do
  string_loc = 54223 : Gosub PrintString                  ' Print string "HOURS"
  string_loc = 59622 : Gosub PrintString                  ' Print string "_=_"
  Sertxd (#hours)     : Gosub NEWLINE
  string_loc = 34157 : Gosub PrintString                  ' Print string "PRESS BLUE TO ACCEPT OR RED_"
  string_loc = 58842 : Gosub PrintString                  ' Print string "TO INCREMENT"
  Gosub NEWLINE
  Gosub WAIT_FOR_NO_BUTTONS
  Do
    If blue_button  = 1 Then Goto SAVE_HOURS
    If red_button = 1 Then Exit
  Loop
Inc hours   : If hours > 23 Then Let hours = 0  : endif
Loop
SAVE_HOURS:
date_time_var = hours
dtv_pos = 2
Gosub write_to_clock
'CONTINUED
 
Last edited:

tommo_NZ

New Member
'PART 3
Code:
'--------------------------------------------------------------------------------------------------
CHANGE_MINS:
string_loc = 25180 : Gosub PrintString                    ' Print string "ADJUST_"
string_loc = 55765 : Gosub PrintString : Gosub NEWLINE    ' Print string "MINS?"
string_loc = 37997 : Gosub PrintString : Gosub NEWLINE    ' Print string "Press BLUE To accept Or RED For Next option"
Do
  If blue_button = 1 Then Exit
  If red_button = 1 Then Goto CONTINUE
Loop
Gosub WAIT_FOR_NO_BUTTONS
Do
  string_loc = 55509 : Gosub PrintString                  ' Print string "MINS"
  string_loc = 59622 : Gosub PrintString                  ' Print string "_=_"
  Sertxd (#minutes)     : Gosub NEWLINE
  string_loc = 34157 : Gosub PrintString                  ' Print string "PRESS BLUE TO ACCEPT OR RED_"
  string_loc = 58842 : Gosub PrintString                  ' Print string "TO INCREMENT"
  Gosub NEWLINE
  Gosub WAIT_FOR_NO_BUTTONS
    Do
      If blue_button  = 1 Then Goto SAVE_MINS
      If red_button = 1 Then Exit
    Loop
  Inc minutes
  If minutes > 59 Then Let minutes = 0  : endif
Loop
SAVE_MINS:
date_time_var = minutes
dtv_pos = 1
Gosub write_to_clock

'--------------------------------------------------------------------------------------------------
ZERO_SECONDS:
date_time_var = 0
dtv_pos = 0
Gosub write_to_clock

'--------------------------------------------------------------------------------------------------
CONTINUE:
Gosub WAIT_FOR_NO_BUTTONS
Gosub NEWLINE :  Gosub NEWLINE
Pause 200
Goto SETUP

'XXXXX END OF MENUS XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
start1:
Do
High led_1
Pause 100
Low led_1
Pause 2000
Loop

'XXXXX SUB-ROUTINES XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
SHOW_TIME:
Sertxd (#hours,":",#minutes,":",#seconds,Cr,Lf)
Return

READ_TIME:
Hi2cin 0,(seconds, minutes, hours, dow, date, month, year)  ' Read the time from DS3231 RTC
seconds   = seconds / 16 * minus_6 + seconds                ' convert BCD to decimal
minutes   = minutes / 16 * minus_6 + minutes                ' mins   0~59
hours     = hours   / 16 * minus_6 + hours                  ' hours  0~60
date      = date    / 16 * minus_6 + date                   ' date   0~31
month     = month   / 16 * minus_6 + month                  ' month  0~12
year      = year    / 16 * minus_6 + year                   ' year (decade)
clock_time = hours * 60 : clock_time = clock_time + minutes ' convert hours & minutes into minutes since midnight
Return
    
PRINTSTRING:                                    ' This method lends itself to being a subroutine
                                                ' Start and end parameters are set up in w8
                                                ' Useful to save code space when many or repeated messages are sent to Serial Terminal
For loop1 = first To last                       ' Start a loop
   Readtable loop1 , char                       ' Read value from Table from position 'loopcount' into variable "char"
   Sertxd(char)                                 ' Transmit character to Serial Terminal
Next loop1                                      ' Next character
Return
 
NEWLINE:
Sertxd(Cr,Lf)
Return
    
WAIT_FOR_NO_BUTTONS:
Do While red_button  = 1 : Loop
Do While blue_button = 1 : Loop
Return

WRITE_TO_CLOCK:
Sertxd("Updating RTC",Cr,Lf)
msd = date_time_var/10            ' isolate MSD
lsd = date_time_var//10           ' isolate LSD
bcd_val = msd * 16 + lsd          ' shift MSD bits left 4 places (x16), add LSD, = BCD
Hi2cout dtv_pos,(bcd_val)         ' write parameter to Clock Module
Pause 10
Return                            ' back to doin' the hokey cokey

PRINT_ACTIVE_FLAGS:
Read 0, active_flags                     ' Active consists of #bit0, #bit1, #bit2, #bit3, #bit4, #bit5, #bit6, #bit7)
For loop1 = 1 To 8
  Sertxd("Channel ",#loop1)
  If bit0 = 0 Then Sertxd(" off",Cr,Lf) Else Sertxd(" ON",Cr,Lf)
  endif
  active_flags = active_flags / 2
Next
Return
SET_PORT:
select case loop1
Case 1
      High b.0
Case 2
      High b.1
Case 3
      High b.2
Case 4
      High b.3
Case 5
      High b.4
Case 6
      High b.6
Case 7
      High c.2
Case 8
      High c.3
end select
Return
CLEAR_PORT:
select case loop1
Case 1
      Low b.0
Case 2
      Low b.1
Case 3
      Low b.2
Case 4
      Low b.3
Case 5
      Low b.4
Case 6
      Low b.6
Case 7
      Low c.2
Case 8
      Low c.3
end select
Return
'EOF
 
Last edited:

lbenson

Senior Member
Lots of code, and well-commented. If you use indentation, you can preserve it in postings by putting the code between "[ code]" and " [ /code]" tags (without the spaces). You can edit your posts to do that, and that would probably make your structure easier for viewers to understand.
 

Aries

New Member
Just a quick thought, as you reckon you are running out of code space ...
the 20M2 allows for two slots, each of 2048 bytes. It is a bit cumbersome to use both together, but I regularly do something similar with the 28X2, using all four slots and "calling" between them. It can mess up the internal timer but, if you have the external clock that is far less of a problem. What follows is part of a 20M2 two-slot program. On a valid call to one of the slots, b0 = $F0 and b1 indicates which subroutine to call. When the routine completes, it sets b1 and runs the other slot (via RunSlot1 in this case).
Code:
#slot 0
main:
     if b0 <> $F0 then EnviroInitialise ' invalid call, treat as full restart

    select case b1
    case 2                                                            ' return from EnviroInitialise
        gosub InitialiseSSN                                ' initialise NRF24L01 and start-up
        goto CheckEnviro
    case 3                                                            ' return from LoadEnviro
        goto PrepareEnviroPacket
    else
        b0 = 0
        w3 = 0
        goto main                                                    ' invalid, so restart
    endselect

...
<various subroutines>

 ......
RunSlot1:
    b0 = $F0
    w3 = InternalTimer
    run 1
 

hippy

Technical Support
Staff member
Code optimisation is always a challenge. Some is easy some is hard. Most times there is some low-hanging fruit which can save enough to not make further optimisation necessary. For example, the SET_PORT and CLEAR_PORT routines can be optimised reduce the 174 bytes they currently use to just 30 -
Code:
SET_PORT:
  ;                 1   2   3   4   5   6   7   8
  LookUp loop1, (0,B.0,B.1,B.2,B.3,B.4,B.6,C.2,C.3), temp1
  High temp1
  Return

CLEAR_PORT:
  ;                 1   2   3   4   5   6   7   8
  LookUp loop1, (0,B.0,B.1,B.2,B.3,B.4,B.6,C.2,C.3), temp1
  Low temp1
  Return
So that's a saving of 144 bytes, near 10%.

You also have a number of -
Code:
Do
  :
  If xxxx_button = 1 Then Exit
Loop
Those can be refactored to -
Code:
Do
  :
Loop Until  xxxx_button = 1
You will save a couple of bytes for each.

But most of the code is menu handling and displaying, and that can take up a lot of code space, or require a lot of effort just to save a few bytes.

You might be able to reduce your printing routine as your table is only 400 bytes, with only a few messages. That would save a byte per "string_loc = nnnnn".
 

tommo_NZ

New Member
Lots of code, and well-commented. If you use indentation, you can preserve it in postings by putting the code between "[ code]" and " [ /code]" tags (without the spaces). You can edit your posts to do that, and that would probably make your structure easier for viewers to understand.
Thanks ibenson, have done that and it is now readable, I think..
Regards,
Kevin
 

Aries

New Member
Following on from Hippy's thoughts ...
(1) I believe it uses less code space to loop from 0 to 7 rather than 1 to 8;
(2) If you do loop from 1 to 7, then Hippy's lookups need one fewer entries (save 1 byte each)
(3) As I mentioned before, using null-terminated messages saves one byte for each call, at the expense of one extra byte for each message in TABLE.
 

tommo_NZ

New Member
If the resolution you seek is 1 minute, have you consider to set the DS3231 to generate an interrupt at 1
Minute intervals?
That would reduce the need to polling and on receipt of an interrupt the PICAXE can check the actual time and determine if an action is to occur.
Thank you Westaust55, I shall add that to my list of optimisations, very much worth pursuing.
Regards,
Kevin
 

tommo_NZ

New Member
Following on from Hippy's thoughts ...
(1) I believe it uses less code space to loop from 0 to 7 rather than 1 to 8;
(2) If you do loop from 1 to 7, then Hippy's lookups need one fewer entries (save 1 byte each)
(3) As I mentioned before, using null-terminated messages saves one byte for each call, at the expense of one extra byte for each message in TABLE.
Thanks, this too will be added to my list of optimisations to be considered.
This is all invaluable stuff,
I can't understate how ,much I appreciate the advice.
Regards,
Kevin.
 

tommo_NZ

New Member
Following on from Hippy's thoughts ...
(1) I believe it uses less code space to loop from 0 to 7 rather than 1 to 8;
(2) If you do loop from 1 to 7, then Hippy's lookups need one fewer entries (save 1 byte each)
(3) As I mentioned before, using null-terminated messages saves one byte for each call, at the expense of one extra byte for each message in TABLE.
Thanks Aries for this and the other post, more food for thought, I thank you all for taking the time to look over my rather messy code. The matters you mention are now on my list. I have taken it all on board.
Regards,
Kevin.
 

hippy

Technical Support
Staff member
Before optimising it's probably worth considering where the most code is used.

The following code, though untested, should handle the timing, the turning on and turning off of outputs, and handles setting outputs correctly no matter when the PICAXE loses power or gets it back again.

This does it in under 200 bytes though it doesn't handle clock setting, display or menu handling.
Code:
#Picaxe 20M2

Symbol reserveW0  = w0 ; b1:b0
Symbol now        = w1 ; b3:b2
Symbol now.mins   = b2
Symbol now.hour   = b3
Symbol rtc        = w2 ; b5:b4
Symbol rtc.mins   = b4
Symbol rtc.hour   = b5
Symbol index      = b6     
Symbol whichPin   = b7
Symbol outputBits = b8

MainLoop:
  HI2cSetup I2CMASTER, $D0, I2CSLOW, I2CBYTE
  Do
    Gosub CatchUp
    Gosub SetOutputs
    Do
      Sleep 1
      HI2cIn 1, ( rtc.mins, rtc.hour )
    Loop Until rtc <> now
  Loop

CatchUp:
  Gosub UpdateOutputBitsForNowTime
  HI2cIn 1, ( rtc.mins, rtc.hour )
  If now <> rtc Then
    Gosub IncrementNow
    Goto CatchUp
  End If
  Return

UpdateOutputBitsForNowTime:
  For index = 0 To $FE Step 2
    Read index, Word w0
    If w0 <= $6359 Then
      whichPin = b2 / $20
      LookUp whichPin,( $80, $40, $20, $10, $08, $04, $02, $01 ), whichPin
      If w0 >= $0000 And w0 <= $2359 Then
        If w0 = now Then
          ; Set bit
          outputBits = outputBits | whichPin
        End If
      Else
        w0 = w0 - $4000
        If w0 = now Then
          ; Clear bit
          outputBits = outputBits &/ whichPin
        End If
      End If
    End If
  Next
  Return

SetOutputs:
  b1 = outputBits
  For b0 = 0 To 7
    LookUp b0,( B.0, B.1, B.2, B.3, B.4, B.6, C.2, C.3 ), whichPin
    If b1 >= $80 Then
      High whichPin
    Else
      Low whichPin
    End If
    b1 = b1 * 2
  Next
  Return

IncrementNow:
  now.mins = now.mins / 16 * $FA + now.mins
  now.mins = now.mins + 1 // 60
  now.mins = now.mins / 10 * 6 + now.mins
  If now.mins = 0 Then
    now.hour = now.hour / 16 * $FA + now.hour
    now.hour = now.hour + 1 // 24
    now.hour = now.hour / 10 * 6 + now.hour
  End If
  Return

; Programmed times ($hhmm)
;
; $0000-$2359 - On time
; $4000-$6359 - Off time
; $FFFF       - Unused / Ignore
;        
;                T1        T2        T3       T4        T5        T6        T7       T8        T9       T10       T11      T12       T13       T14       T15      T16
Eeprom $00, ( $FF,$FF , $FF,$FF , $FF,$FF, $FF, $FF ,$FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF ) ; B.0
Eeprom $20, ( $FF,$FF , $FF,$FF , $FF,$FF, $FF, $FF ,$FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF ) ; B.1
Eeprom $40, ( $FF,$FF , $FF,$FF , $FF,$FF, $FF, $FF ,$FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF ) ; B.2
Eeprom $60, ( $FF,$FF , $FF,$FF , $FF,$FF, $FF, $FF ,$FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF ) ; B.3
Eeprom $80, ( $FF,$FF , $FF,$FF , $FF,$FF, $FF, $FF ,$FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF ) ; B.4
Eeprom $A0, ( $FF,$FF , $FF,$FF , $FF,$FF, $FF, $FF ,$FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF ) ; B.6
Eeprom $C0, ( $FF,$FF , $FF,$FF , $FF,$FF, $FF, $FF ,$FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF ) ; C.2
Eeprom $E0, ( $FF,$FF , $FF,$FF , $FF,$FF, $FF, $FF ,$FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF , $FF,$FF , $FF,$FF , $FF,$FF, $FF,$FF ) ; C.3
 
Top