RTCClockApp.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. from Debug import Debug
  2. from machine import Pin
  3. from machine import RTC
  4. import utime as time
  5. from tm1637 import TM1637
  6. from TimeSource import TimeSource
  7. from TimeSyncer import TimeSyncer
  8. from micropython import schedule
  9. import _thread
  10. from machine import idle
  11. MODE_TIME = 0
  12. MODE_DATE = 1
  13. MODE_WDAY = 2
  14. MODE_SEC = 3
  15. WEEKDAYS=("non","tuE","uEd","tHu","Fri","SAt","Sun")
  16. class RTCClockApp(Debug):
  17. """RTCを用いた時計アプリケーションクラス"""
  18. def __init__(self, display_dev: TM1637, time_source: TimeSource, time_sync: TimeSyncer, mode_select_pin: int, force_sync_pin: int):
  19. """
  20. コンストラクタ
  21. Args:
  22. display_dev: TM1637のインスタンス
  23. time_source: TimeSourceのインスタンス
  24. time_sync: TimeSyncerのインスタンス
  25. mode_select_pin: 表示モード切替スイッチが接続されているGPIO番号
  26. force_sync_pin: 強制受信スイッチが接続されているGPIO番号
  27. """
  28. self.disp = display_dev
  29. self.timesrc = time_source
  30. self.timesync = time_sync
  31. # 継続フラグ
  32. self._continue = False
  33. # 1つ前の秒
  34. self.prev_sec = -1
  35. # 初期表示
  36. self.disp.show_str("----")
  37. # RTCに現在日時が設定されているか?
  38. self.setup_rtc = False
  39. # TimeSourceにコールバック関数を登録
  40. self.timesrc.add_callback(self.adjust_rtc)
  41. # 表示モード
  42. self.mode = MODE_TIME
  43. # 1つ前の表示モード
  44. self.prev_mode = self.mode
  45. # キースイッチ設定
  46. self._last_keydown_times = {} # キー押下時間を格納する辞書
  47. self.mode_select = Pin(mode_select_pin, Pin.IN, pull=Pin.PULL_UP)
  48. self.force_sync = Pin(force_sync_pin, mode=Pin.IN, pull=Pin.PULL_UP)
  49. self.mode_select.irq(self._key_event_handler,trigger=Pin.IRQ_FALLING)
  50. self.force_sync.irq(self._key_event_handler,trigger=Pin.IRQ_FALLING)
  51. def _key_event_handler(self, p):
  52. """スイッチ押下時の割り込みハンドラ"""
  53. key_id = id(p)
  54. now = time.ticks_ms()
  55. last_time = self._last_keydown_times.get(key_id, 0)
  56. if time.ticks_diff(now, last_time) > 500: # チャタリング防止: 500ms以上経過していたら
  57. self._last_keydown_times[key_id] = now
  58. schedule(self._key_down, p)
  59. def _key_down(self, p):
  60. """キーダウンイベント処理(非同期コンテキストで実行)"""
  61. if p == self.mode_select: # 表示モード選択
  62. self.mode = (self.mode + 1) % 4
  63. elif p == self.force_sync: # 強制受信
  64. self.timesync.sync_start()
  65. def adjust_rtc(self, args):
  66. """
  67. RTCに現在日時を設定するコールバック関数
  68. Args:
  69. args: (jjy_time, ticks_ms)
  70. jjy_time: RTCに書き込むUNIXエポックタイム
  71. ticks_ms: この時間を受信したtime.ticks_ms()
  72. """
  73. jjy_time, ticks_ms = (args)
  74. rtc = RTC()
  75. elapsed_ms = time.ticks_diff(time.ticks_ms(), ticks_ms)
  76. rtc_time = jjy_time + (elapsed_ms // 1000) + 1
  77. now = time.localtime(rtc_time)
  78. # 次の秒が来るまでスリープしてタイミングを合わせる
  79. time.sleep_ms(1000 - (elapsed_ms % 1000))
  80. rtc.datetime((now[0], now[1], now[2], now[6], now[3], now[4], now[5], 0))
  81. self.dprint("Set RTC to %d/%d/%d, %d:%d:%d" % (now[0], now[1], now[2], now[3], now[4], now[5]))
  82. self.setup_rtc = True
  83. def _update_display(self):
  84. """
  85. 表示の更新を行うスレッド
  86. """
  87. while self._continue:
  88. time.sleep_ms(50) # 50ms
  89. now = time.localtime()
  90. sec = now[5]
  91. if self.prev_sec != sec or self.prev_mode != self.mode:
  92. self.prev_sec = sec
  93. self.prev_mode = self.mode
  94. # 表示更新
  95. disp_str = "----"
  96. if self.setup_rtc:
  97. if self.mode == MODE_TIME:
  98. if now[5] % 2 == 0:
  99. disp_str = "%2d:%02d" % (now[3],now[4])
  100. else:
  101. disp_str = "%2d%02d" % (now[3],now[4])
  102. elif self.mode == MODE_DATE:
  103. disp_str = "%2d%2d" % (now[1], now[2])
  104. elif self.mode == MODE_WDAY:
  105. disp_str = " %s" % (WEEKDAYS[now[6]])
  106. elif self.mode == MODE_SEC:
  107. disp_str = "%2d%2d" % (now[4], now[5])
  108. self.disp.show_str(disp_str)
  109. def run(self):
  110. """
  111. アプリケーションメインループ
  112. """
  113. self._continue = True # スレッド起動
  114. _thread.start_new_thread(self._update_display,())
  115. try:
  116. while True:
  117. idle()
  118. except Exception as e: # なにか起きたら停止する
  119. self.dprint(f"Main loop error: {e}")
  120. finally:
  121. self._continue = False