ESP32, deepsleep and slow pulse counter

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
Post Reply
Posts: 4
Joined: Fri Aug 10, 2018 1:00 pm

ESP32, deepsleep and slow pulse counter

Post by Pivert » Thu Oct 01, 2020 4:23 pm


Gong is a french word, can be translated as "bell strikes" from a clock.

I'm trying to get an efficient way to count the gongs from a mechanical clock, to figure out the clock time, stop it for about 12h, and restart it on time with ntptime syncrhonization. HW is TTGO T-Display ESP32.

As it will run on battery, I would like to spend as much as possible time in deepsleep.

But I face a couple of problems:
  1. The wake up form deepsleep with the gong_pin works, but I typically miss the second gong during boot. Do you have some recommendations or documentation to fix the problem ?
  2. ULP seems a good way to address the problem. But I did not find any documentation or pulse counter examples in English with MicroPython integrations. I'm not afraid to write some assembly instructions if it's the only way to get proper count. Could you confirm we can achieve the gong count using ULP and wake up from DeepSleep once a timer expires (to confirm gongs stopped). If yes, would you have any example / documentation ?
Context / code:

Code: Select all

class gong:
    """Manage the cuckoo gong switch used to get the time of the clock"""
    # Consider the following as personal documentation for time management on ESP32
    # utime.localtime()              = localtime tupple of the form (2020, 9, 27, 23, 27, 47, 6, 271)
    # utime.localtime( seconds )     = time tupple of epoch + int seconds
    # utime.time()                   = localtime as seconds since epoch (2020 01 01)
    # utime.mktime(date_as_tupple)   = inverse of localtime = seconds from epoch to date_as_tupple
    # (PRINT_DATETIME_TEMPLATE + " {t}").format(*(localtime()[:6]), t = "TEST")
    def __init__(self, cfg, pendulum_servo, init_gong_count):
        self.config = cfg
        self.gong_pin = Pin(cfg.GONG_PIN, Pin.IN, Pin.PULL_UP)
        self.rtc = RTC()
        self.pendulum_servo = pendulum_servo
        self.init_gong_count = init_gong_count
        self.gong_count = 0
        self.gong_count_timer = Timer(1)
        self.gong_lock_until = 0

        # Configure the gong_pin to both register a callback, and also wake from deepsleep
        self.gong_pin.irq(trigger=Pin.IRQ_FALLING, handler=self.gong_detected)
        esp32.wake_on_ext0(pin=self.gong_pin, level=esp32.WAKEUP_ALL_LOW)

    def gong_detected(self, p):
        if ticks_ms() > self.gong_lock_until:
            self.gong_lock_until = ticks_ms() + 400  # Prevent contact bounces false counts by locking count for x ms
            self.gong_count += 1
            log("Gong {}".format(self.gong_count))
            # (Re)Set the timer that will detect last gong after timer overflow
            self.gong_count_timer.init(period=2000, mode=Timer.ONE_SHOT, callback=self.gongs_stopped)

    def gongs_stopped(self, tmr):
        """Provides the total number of gongs after the call back timer expired"""
        if self.gong_count >= 2:
            # It is gong_count o'clock
            # Find the closest o'clock (+/-30m):
            # start from localtime
            dt = list(localtime())            # Let's use a mutable object for efficiency (list vs tuple) !
            if dt[TF.index('minute')] < 30:
                oclock_24 = dt[TF.index('hour')]
                oclock_24 = dt[TF.index('hour')] + 1
            pm = True if oclock_24 >= 13 else False   # Fixme: must take deviation or cuckoo time into account, not localtime
            dt[TF.index('hour')] = self.gong_count + (12 if pm else 0)
            cuckoo_time = dt[:TF.index('minute')] + [0] * 4
            log("We're close to {} o'clock, so we are {}, and cuckoo just strikes {} o'clock.".format(str(oclock_24), "pm" if pm else "am", cuckoo_time[3]))
            # Fixme: Calculate the clock deviation from ntp localtime.

            if self.gong_count + self.init_gong_count == self.config.CUCKOO_STOP_HOUR24 - (12 if pm else 0):
                # Let's stop the cuckoo
                # Stop servo PWM to save battery
                # Fixme: This did not work, the servo is still trying to keep the angle. Solution might be to unpower the servo ?
                # self.pendulum_servo.servo.servo_deinit()
                # Let's sleep until 10m before wake up time to restart the pendulum on time.
                sleep_duration = 12 * 3600 + (mktime(cuckoo_time) - time()) - 600
                log("Sleeping for the next {} minutes.".format(sleep_duration//60))

                # Save the state
                with open('state.json', 'w') as f:
                    ujson.dump(state, f)
                sleep(5)  # Always sleep  a bit before deepsleep
                deepsleep(sleep_duration * 1000) # deepsleep argument is in milliseconds

        # Reset Gong Counter
        self.gong_count = 0
Full project code on GitLab

Post Reply