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:
- 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 ?
- 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 ?
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')]
else:
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
self.pendulum_servo.set_pendulum_running(False)
# 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