Code — set_timer
set_timer.py
Section titled “set_timer.py”#!/usr/bin/env python3# Copyright 2026 Allen Synthesis## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License."""Set Timer
Acts as a simple stopwatch/timer module to help manage recording durations, set timing, etc...
@author Chris Iverach-Brereton <ve4cib@gmail.com>@year 2026"""
from europi import *from europi_script import EuroPiScript
import configurationimport time
MODE_GATE = "gate"MODE_TRIGGER = "trigger"
# The duration of the trigger output on CV2TRIGGER_DURATION_MS = 10
from experimental.a_to_d import AnalogReaderDigitalWrapper
def time2str(t): """ Convert an incrementing timer to a formatted string.
@return A string of the form HH:MM:SS.ss """ ms = t % 1000 s = (t - ms) // 1000 m = (s // 60) h = m // 60 s = s%60 m = m%60 return f"{h:02}:{m:02}:{s:02}.{ms//10:02}"
class SetTimer(EuroPiScript): def __init__(self): super().__init__()
# Is the clock currently running? self.is_running = False
# How many times have we split? self.n_splits = 1
# The total elapsed time since we started (ms resolution) self.elapsed_time = 0
# The current split time (ms resolution) self.split_time = 0
# The clock time of our last tick self.last_tick_at = time.ticks_ms()
@b1.handler def b1_press(): self.toggle()
@b2.handler def b2_press(): if self.is_running: self.split() else: self.reset()
@din.handler def din_rise(): if self.config.MODE == MODE_GATE: self.start() else: self.toggle()
@din.handler_falling def din_fall(): if self.config.MODE == MODE_GATE: self.stop()
def ain_rise(): self.split()
# We use AIN as a second digital input self.din2 = AnalogReaderDigitalWrapper(ain, cb_rising=ain_rise)
@classmethod def config_points(cls): return [ configuration.choice( name="MODE", choices=[MODE_TRIGGER, MODE_GATE], default=MODE_GATE ), ]
def start(self): cv1.on() self.is_running = True
def stop(self): cv1.off() self.is_running = False
def toggle(self): if self.is_running: self.stop() else: self.start()
def reset(self): self.n_splits = 0 self.elapsed_time = 0 self.split_time = 0
def split(self): if self.is_running: self.n_splits += 1 self.split_time = 0
def draw(self): oled.centre_text(f"""{time2str(self.elapsed_time)}{time2str(self.split_time)}({self.n_splits})""")
def tick(self): now = time.ticks_ms()
if self.is_running: delta = time.ticks_diff(now, self.last_tick_at)
# Note: this _could_ cause some wind-up error over time. # A more accurate way would be to record the start time of the main & split time # but that wouldn't handle start/stop cycles gracefully. # For the purposes of this program, I'm willing to accept some clock drift # in order to keep the code simpler. self.elapsed_time += delta self.split_time += delta
if self.split_time <= TRIGGER_DURATION_MS: cv2.on() else: cv2.off()
self.last_tick_at = now
def main(self): while True: self.din2.update() self.tick() self.draw()
if __name__ == "__main__": SetTimer().main()