Shop OBEX P1 Docs P2 Docs Learn Events
RTC DS3231 DOW not accessible? — Parallax Forums

RTC DS3231 DOW not accessible?

Hi,
I need someone to help with these lines. I am using Propeller 1 with DS3231. I am trying to increase the "CounterX" every time it reaches Monday, 1 o'clock afternoon at 30 minutes and 30 seconds. It works without the rtc.clockDOW == 2.
I wonder why?
Thanks,
Hai

rtc.readClock
if rtc.clockDOW == 2 and rtc.clockampm == rtc#PM and rtc.clockHour == 1 and rtc.clockMinute == 30     
' if rtc.clockampm == rtc#PM and rtc.clockHour == 1 and rtc.clockMinute == 30
   CounterX ++

Comments

  • Hi @hailumei ,
    Have you tried, as a debugging aid, printing the value of rtc.clockDOW, and the others, to a terminal, to see what value you're getting (i.e., are you sure the DS3231 object you're using returns 2 for Monday, or is it 1, for a zero-based week starting on Sunday?)
    Are you able to share the rest of the code in case that doesn't help you down the road further? There might be a zillion different DS3231 objects written - we won't know exactly how it works without seeing the code of the particular one you're using.

    Cheers

  • Hi @avsa242 ,
    Thanks for the reply. Yes, the rtc.clockDOW is correct from the display. please see the file attached as you mentioned.

    Thanks,

  • @hailumei
    I don't have the specific DS3231 object you're using, but I created a simple 'dummy' object with the required methods defined that just return static values. One thing I noticed just now though: in the code you quoted above, you check clockDow() for equality to the number 2, but later on in the source you attached, you try to print a string to the LCD using clockDow() as the string source (the line reads LCD1.str(rtc.clockDOW). This won't work if the clockDOW() method returns a number, and could be at least part of your issue. Does that method actually return an ASCII representation of the number (i.e., for the number 2, it'd return the ASCII code 50)? It seems an unlikely way to do it, but if that's the case, you can try changing your comparison to read: if rtc.clockDOW == "2"
    Otherwise, we'd need more info (best would be to attach the particular DS3231 object you're using.

  • Hi @avsa242,

    Man, you are good! You were spot on.

    Thanks,

    Hai

  • Hi @avsa242,

    please see the Driver file attached..

    Thanks,

  • JonnyMacJonnyMac Posts: 9,157
    edited 2021-04-15 16:42

    @hailumei Perhaps you'd consider an alternate driver -- the one you're using seems unnecessarily convoluted. I have a DS3231 driver that I've used on the P1 for a long time, and recently ported to the P2 for my Spin programming presentations. In the attached demo, your core problem is simplified to this.

    dat
    
      AlarmCode     long    $02_13_30_30                            ' Monday (2), 1pm (13), 30m, 30s     
    
    
    var
    
      long  counterx
    
    
    pub main | now
    
      setup
    
      repeat
        rtc.read(4, @now, rtc#R_SCS)                                ' get secs, mins, hrs, & day into now    
        if (now == AlarmCode)                                       ' match for alarm setting?
          ++counterx                                                ' yes
          time.pause(1000)                                          ' prevent double-dip on this second 
    

    This uses a bit of a trick that requires a little understanding of the DS3231 register layout and how Px memory works
    -- Luckily, you want to test four registers that are contiguous in the RTC; this means we can house them in a long (which is four bytes)
    -- The read() method from my driver lets you move any number of contiguous registers from the RTC to a byte array (again, a long can be treated as an array of four bytes)
    -- The native format of RTC registers is BCD, which we can describe as hex

    With this understanding you can see how easy the solution becomes. We read the seconds, minutes, hours, and day register into a long called now, and then compare that with a known value that is defined in a dat section. Using this strategy makes dealing with multiple alarms much simpler, because each alarm is a single value and comparison. Remember, multi-byte Propeller variables are stored Little Endian, which is why the seconds end up in the byte[0] of now and the day ends up in byte[3] of now.

    I helped a friend using code like this. His elderly mother needed reminders to take medicine, but he couldn't always call. He made a box with a Propeller and an audio player (my design) and I crafted code that would play reminders -- that he recorded in his voice -- for his mother at specific times of the day.

    Good luck with your project.

  • Hi Jon,

    This is absolutely a better way to program it. Let me put everything together and let you know the results. I have learned something today.

    Thanks,

    Hai

  • JonnyMacJonnyMac Posts: 9,157
    edited 2021-04-16 01:30

    I just looked at your project code. If I may, I'd make these suggestions.

    -- Put all your initialization code into one method (outside of main)
    * for some reason you are re-initializing the LCD through ever iteration of the main loop

    -- Put your date display in a method. If you decide to use my driver, you could do it like this:

    pub show_now 
    
      lcd.cursor(OFF)
      lcd.cls
      lcd.backlight(ON)
    
      lcd.gotoxy(1, 0)
      lcd.str(rtc.date_mmddyy(-1))                                  ' show current date mm/dd/yy  
    
      lcd.gotoxy(1, 1)
      lcd.str(rtc.time_ampm(-1))                                    ' show current time hh:mm:ss xM
    

    -- Your motor status display can be simplified with this:

    dat
    
      M_Off         byte    "Motor Off:", 0
    
      M_Status      byte    " #4 & 5", 0  
                    byte    " #3 & 4", 0  
                    byte    " #1 & 5", 0  
                    byte    " #2 & 3", 0  
                    byte    " #2 & 4", 0  
                    byte    " #2 & 5", 0  
                    byte    " #1 & 3", 0  
                    byte    " #1 & 4", 0  
                    byte    " #3 & 5", 0  
                    byte    " #1 & 2", 0
    
    
    pub show_status
    
      lcd.gotoxy(1, 2)
      lcd.str(@M_Off)
      lcd.str(@M_Status[counterx << 3])
    

    This takes advantage of the strings being the same length. Note, too, that I moved the space from after Off: to front of the status strings. Why? Well this makes them eight bytes long (you have to count the terminating 0) which allows the use of << 3 to multiply by eight. Whenever you have a time-sensitive app like this, you want to look for time savings -- using shifts is always faster than multiplication or division, but can only be done with a power-of-2 value. BTW, you can put dat sections anywhere in your program. In cases like this, I put them right above the method that uses data from them.

    -- This routine doesn't require a temporary variable -- making a copy of your parameter just takes time. And don't call .dec() unless you have to.

    pub pad(value)
    
      if (value < 10)
        lcd.putc("0")
        lcd.putc("0" + value)
      else
        lcd.dec(value)  
    

    In the end, your main loop could look something like this:

    pub main | t, now
    
      setup
    
      t := cnt                                                      ' sync loop timer
      repeat
        rtc.read(4, @now, rtc#R_SCS)                                ' get secs, mins, hrs, & day into now
        if (now == AlarmCode)                                       ' match for alarm setting?
          if (++counterx == 10)                                     ' yes, bump counterx
            counterx := 0
    
        show_now 
        show_status 
        waitcnt(t += clkfreq)                                       ' run loop every second
    

    Note how this loop uses waitcnt versus how you're doing it. In your version, you're simply adding a 1-second delay at the end of the loop. I suspect, though, you'd like to run the loop on a 1-second interval. The loop shown above does that.

    Also note the little trick for incrementing and rolling-over counterx -- you don't need two if statements to do that. If you wanted to go backward you could do it like this:

        if (--counterx == $FF)
          counterx := 9
    

    Since counterx is a byte, $FF is -1.

    -- This modification to one of your methods shows how you can set a bunch of contiguous variables, and how to replace a big stack of if statements with case.

    pri output_control | t
    
      bytefill(@motorFlag0, 0, 10)                                  ' clear all flags
    
      t := cnt
      repeat
        if ((counterx < 0) or (counterx > 9))
          houston_we_have_a_problem
        else
          case counterx
            0 : motor_set_0
            1 : motor_set_1
            2 : motor_set_2
            3 : motor_set_3
            4 : motor_set_4
            5 : motor_set_5
            6 : motor_set_6
            7 : motor_set_7
            8 : motor_set_8
            9 : motor_set_9
    
        waitcnt(t += clkfreq)
    

    Finally, let me suggest that you treat yourself to more whitespace -- it will make code easier to read. Another thing I do is put chunks of code into a method, even if they are only called once (as from main). I think this makes the code easier to follow -- and this is one area I will give up a tiny bit of time (to make the call) for the sake of clarity.

    Anyway, I hope these suggestions are helpful.

  • Hi Jon,

    I feel like this is a completely new chapter to me. It might take me sometime to study and understand it. Thanks for all the suggestions and they are absolutely helpful.

    Thanks,

    Hai

  • Good morning Jon,

    Does your fullduplexserial driver support Parallax 4x20 Serial LCD 27979? How to setup a terminal and display time?

    Thanks,

    Hai

  • JonnyMacJonnyMac Posts: 9,157
    edited 2021-04-19 15:36

    It's a general serial driver, so yes, it will. As with versions of FDS, you don't have to use both pins; if a pin is not going to be used (RX in this case), pass -1. Let's say you have an instance of jm_fullduplexserial called slcd, you might start it like this:

      slcd.start(-1, TX_SLCD, %0000, BR_SLCD)
    

    You will have constants for the IO pin used and for the baud rate. Pretty easy.

    If you wanted to display the time from the RTC object in the upper left corner of the string you might do this (not tested -- but I can read docs).

    pub main
    
      start
    
      repeat
        goto_xy(0, 0)
        lcd.str(rtc.time_ampm(-1))
        time.pause(1000)
    
    
    pub goto_xy(x, y) 
    
      if (x >= 0) and (x < SLCD_COLS)
        case y
          0 : slcd.tx($80 + x)
          1 : slcd.tx($94 + x)
          2 : slcd.tx($A8 + x)
          3 : slcd.tx($BC + x)
    

    There are a lot of helpful formatting routines in jm_fullduplexserial, so you should be able to create a nice display with just a bit of code.

  • Hi Jon,

    Thanks for teaching.

    Hai

Sign In or Register to comment.