kopia lustrzana https://github.com/peterhinch/micropython-samples
Add general purpose DS3231 driver.
rodzic
4691354a40
commit
14ebf88797
133
DS3231/README.md
133
DS3231/README.md
|
@ -5,23 +5,134 @@ is an ideal way rapidly to calibrate the Pyboard's RTC which can then achieve
|
|||
similar levels of accuracy (+- ~2 mins/year). The chip can also provide
|
||||
accurate time to platforms lacking a good RTC (notably the ESP8266).
|
||||
|
||||
Two drivers are provided:
|
||||
1. `ds3231_port.py` A multi-platform driver.
|
||||
2. `ds3231_pb.py` A Pyboard-specific driver with RTC calibration facility. For
|
||||
Three drivers are provided:
|
||||
1. `ds3231_gen.py` General purpose portable driver supporting alarms.
|
||||
2. `ds3231_port.py` Portable driver: main purpose is to test accuracy of a
|
||||
platform's RTC.
|
||||
3. `ds3231_pb.py` A Pyboard-specific driver with RTC calibration facility. For
|
||||
Pyboard 1.x and Pyboard D.
|
||||
|
||||
Breakout boards are widely available. The interface is I2C. Pullups to 3.3V
|
||||
(typically 10KΩ) should be provided on the `SCL` and `SDA` lines if these are
|
||||
not supplied on the breakout board.
|
||||
|
||||
Both divers use edge detection to achieve millisecond-level precision from the
|
||||
DS3231. This enables relatively rapid accuracy testing of the platform's RTC,
|
||||
and fast calibration of the Pyboard's RTC. To quantify this, a sufficiently
|
||||
precise value of calibration may be acquired in 5-10 minutes.
|
||||
Drivers 2 and 3 use edge detection to achieve millisecond-level precision from
|
||||
the DS3231. This enables relatively rapid accuracy testing of the platform's
|
||||
RTC, and fast calibration of the Pyboard's RTC. To quantify this, a
|
||||
sufficiently precise value of calibration may be acquired in 5-10 minutes.
|
||||
|
||||
###### [Main README](../README.md)
|
||||
|
||||
# 1. The multi-platform driver
|
||||
# 1. General purpose driver ds3231_gen.py
|
||||
|
||||
This uses datetime tuples to set and read time values. These are of form
|
||||
(year, month, day, hour, minute, second, weekday, yearday)
|
||||
as used by [time.localtime](http://docs.micropython.org/en/latest/library/time.html#time.localtime).
|
||||
|
||||
## 1.1 The DS3231 class
|
||||
|
||||
#### Constructor:
|
||||
This takes one mandatory argument, an initialised I2C bus.
|
||||
|
||||
#### Public methods:
|
||||
1. `get_time(set_rtc=False)`. If `set_rtc` is `True` it sets the platform's
|
||||
RTC from the DS3231. Returns the DS3231 time as a datetime tuple with
|
||||
`yearday=0`.
|
||||
On ports/platforms which don't support an RTC, if `set_rtc` is `True`, the
|
||||
system time will be set from the DS3231. If setting the RTC, for accuracy the
|
||||
method will pause until a seconds transition occurs on the DS3231.
|
||||
2. `set_time(tt=None)`. Sets the DS3231 time. By default it uses the
|
||||
platform's syatem time, otherwise the passed `datetime` tuple. If passing a
|
||||
tuple, see the note below.
|
||||
3. `__str__()` Returns a dump of the device's registers for debug in a "pretty
|
||||
print" format.
|
||||
4. `temperature()` A float, temperature in °C. Datasheet specifies +-3°C
|
||||
accuracy. It really is that bad.
|
||||
|
||||
#### Public bound variables:
|
||||
|
||||
1. `alarm1` `Alarm` instances (see below). Can be set to 1s precision.
|
||||
2. `alarm2` Can be set to 1min precision.
|
||||
|
||||
#### Alarm Public methods
|
||||
|
||||
1. `set(when, day=0, hr=0, min=0, sec=0)` Arg `when` is one of the module
|
||||
constants listed below. Alarm operation is started.
|
||||
2. `clear()` Clears the alarm status and releases the alarm pin. The alarm
|
||||
will occur again the next time the parameters match.
|
||||
3. `__call__()` No args. Return `True` if alarm has occurred.
|
||||
4. `enable(run)` If `run` is `False` the alarm is cleared and will enter a
|
||||
stopped state; in that state the alarm will not occur again. If `True` a
|
||||
stopped alarm is restarted and will occur on the next match.
|
||||
|
||||
#### Alarm bound variables
|
||||
|
||||
1. `alno` Alarm no. (1 or 2).
|
||||
|
||||
#### Module constants
|
||||
|
||||
These are the allowable options for the alarm's `when` arg, along with the
|
||||
relevant `Alarm.set()` args:
|
||||
`EVERY_SECOND` Only supported by alarm1.
|
||||
`EVERY_MINUTE` `sec`
|
||||
`EVERY_HOUR` `min`, `sec`
|
||||
`EVERY_DAY` `hr`, `min`, `sec`
|
||||
`EVERY_WEEK` `day` (weekday 0..6), `hr`, `min`, `sec`
|
||||
`EVERY_MONTH` `day` (month day 1..month end), `hr`, `min`, `sec`
|
||||
|
||||
In all cases `sec` values are ignored by alarm2: alarms occur on minute
|
||||
boundaries. This is a hardware restriction.
|
||||
|
||||
#### Setting DS3231 time
|
||||
|
||||
Where this is to be set using a datetime tuple rather than from system time, it
|
||||
is necessary to pass the correct value of weekday. This can be acquired with
|
||||
this function. It can be passed a tuple with `dt[6] == 0` and will return a
|
||||
corrected tuple:
|
||||
```python
|
||||
import time
|
||||
def dt_tuple(dt):
|
||||
return time.localtime(time.mktime(dt)) # Populate weekday field
|
||||
```
|
||||
|
||||
#### Alarms
|
||||
|
||||
Comments assume that a backup battery is in use.
|
||||
|
||||
The battery ensures that alarm settings are stored through a power outage. If
|
||||
an alarm occurs during an outage the pin will be driven low and will stay low
|
||||
until power is restored and `clear` or `disable` are issued.
|
||||
|
||||
If an alarm is set and a power outage occurs, when power is restored the alarm
|
||||
will continue to operate at the specified frequency. Setting an alarm:
|
||||
```python
|
||||
from machine import SoftI2C, Pin
|
||||
from ds3231_gen import *
|
||||
i2c = SoftI2C(scl=Pin(16, Pin.OPEN_DRAIN, value=1), sda=Pin(17, Pin.OPEN_DRAIN, value=1))
|
||||
d = DS3231(i2c)
|
||||
dt.alarm1.set(EVERY_MINUTE, sec=30)
|
||||
```
|
||||
If a power outage occurs here the following code will ensure alarms continue to
|
||||
occur at one minute intervals:
|
||||
```python
|
||||
from machine import SoftI2C, Pin
|
||||
from ds3231_gen import *
|
||||
i2c = SoftI2C(scl=Pin(16, Pin.OPEN_DRAIN, value=1), sda=Pin(17, Pin.OPEN_DRAIN, value=1))
|
||||
d = DS3231(i2c)
|
||||
while True:
|
||||
d.alarm1.clear() # Clear pending alarm
|
||||
while not d.alarm1(): # Wait for alarm
|
||||
pass
|
||||
time.sleep(0.3) # Pin stays low for 300ms
|
||||
```
|
||||
Note that the DS3231 alarm2 does not have a seconds register: `sec` values will
|
||||
be ignored and `EVERY_SECOND` is unsupported.
|
||||
|
||||
Re the `INT\` (alarm) pin the datasheet (P9) states "The pullup voltage can be
|
||||
up to 5.5V, regardless of the voltage on Vcc". Note that some breakout boards
|
||||
have a pullup resistor between this pin and Vcc.
|
||||
|
||||
# 2. Portable driver ds3231_port
|
||||
|
||||
This can use soft I2C so any pins may be used.
|
||||
|
||||
|
@ -47,7 +158,7 @@ In my testing the ESP8266 RTC was out by 5%. The ESP32 was out by 6.7ppm or
|
|||
about 12 minutes/yr. A PiPico was out by 1.7ppm, 3.2mins/yr. Hardware samples
|
||||
will vary.
|
||||
|
||||
## 1.1 The DS3231 class
|
||||
## 2.1 The DS3231 class
|
||||
|
||||
Constructor:
|
||||
This takes one mandatory argument, an initialised I2C bus.
|
||||
|
@ -69,7 +180,7 @@ Public methods:
|
|||
devices with poor RTC's (e.g. ESP8266).
|
||||
If `machine.RTC` is unsupported a `RuntimeError` will be thrown.
|
||||
|
||||
# 2. The Pyboard driver
|
||||
# 3. The Pyboard driver
|
||||
|
||||
The principal reason to use this driver is to calibrate the Pyboard's RTC. This
|
||||
supports the Pyboard 1.x and Pyboard D. Note that the RTC on the Pyboard D is
|
||||
|
@ -102,7 +213,7 @@ ds3231.calibrate()
|
|||
Calibration data is stored in battery-backed memory. So if a backup cell is
|
||||
used the RTC will run accurately in the event of a power outage.
|
||||
|
||||
## 2.1 The DS3231 class
|
||||
## 3.1 The DS3231 class
|
||||
|
||||
Constructor:
|
||||
This takes one mandatory argument, an I2C bus instantiated using the `machine`
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
# ds3231_gen.py General purpose driver for DS3231 precison real time clock.
|
||||
|
||||
# Author: Peter Hinch
|
||||
# Copyright Peter Hinch 2023 Released under the MIT license.
|
||||
|
||||
# Rewritten from datasheet to support alarms. Sources studied:
|
||||
# WiPy driver at https://github.com/scudderfish/uDS3231
|
||||
# https://github.com/notUnique/DS3231micro
|
||||
|
||||
# Assumes date > Y2K and 24 hour clock.
|
||||
|
||||
import time
|
||||
import machine
|
||||
|
||||
|
||||
_ADDR = const(104)
|
||||
|
||||
EVERY_SECOND = 0x0F # Exported flags
|
||||
EVERY_MINUTE = 0x0E
|
||||
EVERY_HOUR = 0x0C
|
||||
EVERY_DAY = 0x80
|
||||
EVERY_WEEK = 0x40
|
||||
EVERY_MONTH = 0
|
||||
|
||||
try:
|
||||
rtc = machine.RTC()
|
||||
except:
|
||||
print("Warning: machine module does not support the RTC.")
|
||||
rtc = None
|
||||
|
||||
|
||||
class Alarm:
|
||||
def __init__(self, device, n):
|
||||
self._device = device
|
||||
self._i2c = device.ds3231
|
||||
self.alno = n # Alarm no.
|
||||
self.offs = 7 if self.alno == 1 else 0x0B # Offset into address map
|
||||
self.mask = 0
|
||||
|
||||
def _reg(self, offs : int, buf = bytearray(1)) -> int: # Read a register
|
||||
self._i2c.readfrom_mem_into(_ADDR, offs, buf)
|
||||
return buf[0]
|
||||
|
||||
def enable(self, run):
|
||||
flags = self._reg(0x0E) | 4 # Disable square wave
|
||||
flags = (flags | self.alno) if run else (flags & ~self.alno & 0xFF)
|
||||
self._i2c.writeto_mem(_ADDR, 0x0E, flags.to_bytes(1, "little"))
|
||||
|
||||
def __call__(self): # Return True if alarm is set
|
||||
return bool(self._reg(0x0F) & self.alno)
|
||||
|
||||
def clear(self):
|
||||
flags = (self._reg(0x0F) & ~self.alno) & 0xFF
|
||||
self._i2c.writeto_mem(_ADDR, 0x0F, flags.to_bytes(1, "little"))
|
||||
|
||||
def set(self, when, day=0, hr=0, min=0, sec=0):
|
||||
if when not in (0x0F, 0x0E, 0x0C, 0x80, 0x40, 0):
|
||||
raise ValueError("Invalid alarm specifier.")
|
||||
self.mask = when
|
||||
if when == EVERY_WEEK:
|
||||
day += 1 # Setting a day of week
|
||||
self._device.set_time((0, 0, day, hr, min, sec, 0, 0), self)
|
||||
self.enable(True)
|
||||
|
||||
|
||||
class DS3231:
|
||||
def __init__(self, i2c):
|
||||
self.ds3231 = i2c
|
||||
self.alarm1 = Alarm(self, 1)
|
||||
self.alarm2 = Alarm(self, 2)
|
||||
if _ADDR not in self.ds3231.scan():
|
||||
raise RuntimeError(f"DS3231 not found on I2C bus at {_ADDR}")
|
||||
|
||||
def get_time(self, set_rtc=False, data=bytearray(7)):
|
||||
def bcd2dec(bcd): # Strip MSB
|
||||
return ((bcd & 0x70) >> 4) * 10 + (bcd & 0x0F)
|
||||
|
||||
self.ds3231.readfrom_mem_into(_ADDR, 0, data)
|
||||
if set_rtc: # For accuracy set RTC immediately after a seconds transition
|
||||
ss = data[0]
|
||||
while ss == data[0]:
|
||||
self.ds3231.readfrom_mem_into(_ADDR, 0, data)
|
||||
ss, mm, hh, wday, DD, MM, YY = [bcd2dec(x) for x in data]
|
||||
MM &= 0x1F # Strip century
|
||||
YY += 2000
|
||||
# Time from DS3231 in time.localtime() format (less yday)
|
||||
result = YY, MM, DD, hh, mm, ss, wday - 1, 0
|
||||
if set_rtc:
|
||||
if rtc is None: # Best we can do is to set local time
|
||||
secs = time.mktime(result)
|
||||
time.localtime(secs)
|
||||
else:
|
||||
rtc.datetime((YY, MM, DD, wday, hh, mm, ss, 0))
|
||||
return result
|
||||
|
||||
# Output time or alarm data to device
|
||||
# args: tt A datetime tuple. If absent uses localtime.
|
||||
# alarm: An Alarm instance or None if setting time
|
||||
def set_time(self, tt=None, alarm=None):
|
||||
# Given BCD value return a binary byte. Modifier:
|
||||
# Set MSB if any of bit(1..4) or bit 7 set, set b6 if mod[6]
|
||||
def gbyte(dec, mod=0):
|
||||
tens, units = divmod(dec, 10)
|
||||
n = (tens << 4) + units
|
||||
n |= 0x80 if mod & 0x0F else mod & 0xC0
|
||||
return n.to_bytes(1, "little")
|
||||
|
||||
YY, MM, mday, hh, mm, ss, wday, yday = time.localtime() if tt is None else tt
|
||||
mask = 0 if alarm is None else alarm.mask
|
||||
offs = 0 if alarm is None else alarm.offs
|
||||
if alarm is None or alarm.alno == 1: # Has a seconds register
|
||||
self.ds3231.writeto_mem(_ADDR, offs, gbyte(ss, mask & 1))
|
||||
offs += 1
|
||||
self.ds3231.writeto_mem(_ADDR, offs, gbyte(mm, mask & 2))
|
||||
offs += 1
|
||||
self.ds3231.writeto_mem(_ADDR, offs, gbyte(hh, mask & 4)) # Sets to 24hr mode
|
||||
offs += 1
|
||||
if alarm is not None: # Setting an alarm - mask holds MS 2 bits
|
||||
self.ds3231.writeto_mem(_ADDR, offs, gbyte(mday, mask))
|
||||
else: # Setting time
|
||||
self.ds3231.writeto_mem(_ADDR, offs, gbyte(wday + 1)) # 1 == Monday, 7 == Sunday
|
||||
offs += 1
|
||||
self.ds3231.writeto_mem(_ADDR, offs, gbyte(mday)) # Day of month
|
||||
offs += 1
|
||||
self.ds3231.writeto_mem(_ADDR, offs, gbyte(MM, 0x80)) # Century bit (>Y2K)
|
||||
offs += 1
|
||||
self.ds3231.writeto_mem(_ADDR, offs, gbyte(YY - 2000))
|
||||
|
||||
def temperature(self):
|
||||
def twos_complement(input_value: int, num_bits: int) -> int:
|
||||
mask = 2 ** (num_bits - 1)
|
||||
return -(input_value & mask) + (input_value & ~mask)
|
||||
|
||||
t = self.ds3231.readfrom_mem(_ADDR, 0x11, 2)
|
||||
i = t[0] << 8 | t[1]
|
||||
return twos_complement(i >> 6, 10) * 0.25
|
||||
|
||||
def __str__(self, buf=bytearray(0x13)): # Debug dump of device registers
|
||||
self.ds3231.readfrom_mem_into(_ADDR, 0, buf)
|
||||
s = ""
|
||||
for n, v in enumerate(buf):
|
||||
s = f"{s}0x{n:02x} 0x{v:02x} {v >> 4:04b} {v & 0xF :04b}\n"
|
||||
if not (n + 1) % 4:
|
||||
s = f"{s}\n"
|
||||
return s
|
|
@ -0,0 +1,174 @@
|
|||
# ds3231_gen_test.py Test script for ds3231_gen.oy.
|
||||
|
||||
# Author: Peter Hinch
|
||||
# Copyright Peter Hinch 2023 Released under the MIT license.
|
||||
|
||||
from machine import SoftI2C, Pin
|
||||
from ds3231_gen import *
|
||||
import time
|
||||
import uasyncio as asyncio
|
||||
|
||||
def dt_tuple(dt):
|
||||
return time.localtime(time.mktime(dt)) # Populate weekday field
|
||||
|
||||
i2c = SoftI2C(scl=Pin(16, Pin.OPEN_DRAIN, value=1), sda=Pin(17, Pin.OPEN_DRAIN, value=1))
|
||||
d = DS3231(i2c)
|
||||
|
||||
async def wait_for_alarm(alarm, t, target): # Wait for n seconds for an alarm, check time of occurrence
|
||||
print(f"Wait {t} secs for alarm...")
|
||||
if alarm.alno == 2:
|
||||
target = 0 # Alarm 2 does not support secs
|
||||
while t:
|
||||
if alarm():
|
||||
return target - 1 <= d.get_time()[5] <= target + 1
|
||||
await asyncio.sleep(1)
|
||||
t -= 1
|
||||
return False
|
||||
|
||||
async def test_alarm(alarm):
|
||||
print("Test weekly alarm")
|
||||
result = True
|
||||
dt = dt_tuple((2023, 2, 28, 23, 59, 50, 0, 0))
|
||||
d.set_time(dt) # day is 1
|
||||
alarm.set(EVERY_WEEK, day=2, sec=5) # Weekday
|
||||
alarm.clear()
|
||||
if await wait_for_alarm(alarm, 20, 5): # Should alarm on rollover from day 1 to 2
|
||||
print("\x1b[32mWeek test 1 pass\x1b[39m")
|
||||
else:
|
||||
print("\x1b[91mWeek test 1 fail\x1b[39m")
|
||||
result = False
|
||||
|
||||
dt = dt_tuple((2023, 2, 27, 23, 59, 50, 0, 0))
|
||||
d.set_time(dt) # day is 0
|
||||
alarm.set(EVERY_WEEK, day=2, sec=5)
|
||||
alarm.clear()
|
||||
if await wait_for_alarm(alarm, 20, 5): # Should not alarm on rollover from day 0 to 1
|
||||
print("\x1b[91mWeek test 2 fail\x1b[39m")
|
||||
result = False
|
||||
else:
|
||||
print("\x1b[32mWeek test 2 pass\x1b[39m")
|
||||
|
||||
print("Test monthly alarm")
|
||||
dt = dt_tuple((2023, 2, 28, 23, 59, 50, 0, 0))
|
||||
d.set_time(dt) # day is 1
|
||||
alarm.set(EVERY_MONTH, day=1, sec=5) # Day of month
|
||||
alarm.clear()
|
||||
if await wait_for_alarm(alarm, 20, 5): # Should alarm on rollover from 28th to 1st
|
||||
print("\x1b[32mMonth test 1 pass\x1b[39m")
|
||||
else:
|
||||
print("\x1b[91mMonth test 1 fail\x1b[39m")
|
||||
result = False
|
||||
|
||||
dt = dt_tuple((2023, 2, 27, 23, 59, 50, 0, 0))
|
||||
d.set_time(dt) # day is 0
|
||||
alarm.set(EVERY_MONTH, day=1, sec=5)
|
||||
alarm.clear()
|
||||
if await wait_for_alarm(alarm, 20, 5): # Should not alarm on rollover from day 27 to 28
|
||||
print("\x1b[91mMonth test 2 fail\x1b[39m")
|
||||
result = False
|
||||
else:
|
||||
print("\x1b[32mMonth test 2 pass\x1b[39m")
|
||||
|
||||
print("Test daily alarm")
|
||||
dt = dt_tuple((2023, 2, 1, 23, 59, 50, 0, 0))
|
||||
d.set_time(dt) # 23:59:50
|
||||
alarm.set(EVERY_DAY, hr=0, sec=5)
|
||||
alarm.clear()
|
||||
if await wait_for_alarm(alarm, 20, 5): # Should alarm at 00:00:05
|
||||
print("\x1b[32mDaily test 1 pass\x1b[39m")
|
||||
else:
|
||||
print("\x1b[91mDaily test 1 fail\x1b[39m")
|
||||
result = False
|
||||
|
||||
dt = dt_tuple((2023, 2, 1, 22, 59, 50, 0, 0))
|
||||
d.set_time(dt) # 22:59:50
|
||||
alarm.set(EVERY_DAY, hr=0, sec=5)
|
||||
alarm.clear()
|
||||
if await wait_for_alarm(alarm, 20, 5): # Should not alarm at 22:00:05
|
||||
print("\x1b[91mDaily test 2 fail\x1b[39m")
|
||||
result = False
|
||||
else:
|
||||
print("\x1b[32mDaily test 2 pass\x1b[39m")
|
||||
|
||||
print("Test hourly alarm")
|
||||
dt = dt_tuple((2023, 2, 1, 20, 9, 50, 0, 0))
|
||||
d.set_time(dt) # 20:09:50
|
||||
alarm.set(EVERY_HOUR, min=10, sec=5)
|
||||
alarm.clear()
|
||||
if await wait_for_alarm(alarm, 20, 5): # Should alarm at xx:10:05
|
||||
print("\x1b[32mDaily test 1 pass\x1b[39m")
|
||||
else:
|
||||
print("\x1b[91mDaily test 1 fail\x1b[39m")
|
||||
result = False
|
||||
|
||||
dt = dt_tuple((2023, 2, 1, 20, 29, 50, 0, 0))
|
||||
d.set_time(dt) # 20:29:50
|
||||
alarm.set(EVERY_HOUR, min=10, sec=5)
|
||||
alarm.clear()
|
||||
if await wait_for_alarm(alarm, 20, 5): # Should not alarm at xx:30:05
|
||||
print("\x1b[91mDaily test 2 fail\x1b[39m")
|
||||
result = False
|
||||
else:
|
||||
print("\x1b[32mDaily test 2 pass\x1b[39m")
|
||||
|
||||
print("Test minute alarm")
|
||||
dt = dt_tuple((2023, 2, 1, 20, 9, 50, 0, 0))
|
||||
d.set_time(dt) # 20:09:50
|
||||
alarm.set(EVERY_MINUTE, sec=5)
|
||||
alarm.clear()
|
||||
if await wait_for_alarm(alarm, 20, 5): # Should alarm at xx:xx:05
|
||||
print("\x1b[32mMinute test 1 pass\x1b[39m")
|
||||
else:
|
||||
print("\x1b[91mMinute test 1 fail\x1b[39m")
|
||||
result = False
|
||||
|
||||
if alarm.alno == 2:
|
||||
print("Skipping minute test 2: requires seconds resolution unsupported by alarm2.")
|
||||
else:
|
||||
dt = dt_tuple((2023, 2, 1, 20, 29, 50, 0, 0))
|
||||
d.set_time(dt) # 20:29:50
|
||||
alarm.set(EVERY_MINUTE, sec=30)
|
||||
alarm.clear()
|
||||
if await wait_for_alarm(alarm, 20, 5): # Should not alarm at xx:xx:05
|
||||
print("\x1b[91mMinute test 2 fail\x1b[39m")
|
||||
result = False
|
||||
else:
|
||||
print("\x1b[32mMinute test 2 pass\x1b[39m")
|
||||
|
||||
if alarm.alno == 2:
|
||||
print("Skipping seconds test: unsupported by alarm2.")
|
||||
else:
|
||||
print("Test seconds alarm (test takes 1 minute)")
|
||||
dt = dt_tuple((2023, 2, 1, 20, 9, 20, 0, 0))
|
||||
d.set_time(dt) # 20:09:20
|
||||
alarm.set(EVERY_SECOND)
|
||||
alarm_count = 0
|
||||
t = time.ticks_ms()
|
||||
while time.ticks_diff(time.ticks_ms(), t) < 60_000:
|
||||
alarm.clear()
|
||||
while not d.alarm1():
|
||||
await asyncio.sleep(0)
|
||||
alarm_count += 1
|
||||
if 59 <= alarm_count <= 61:
|
||||
print("\x1b[32mSeconds test 1 pass\x1b[39m")
|
||||
else:
|
||||
print("\x1b[91mSeconds test 2 fail\x1b[39m")
|
||||
result = False
|
||||
alarm.enable(False)
|
||||
return result
|
||||
|
||||
|
||||
async def main():
|
||||
print("Testing alarm 1")
|
||||
result = await test_alarm(d.alarm1)
|
||||
print("Teting alarm 2")
|
||||
result |= await test_alarm(d.alarm2)
|
||||
if result:
|
||||
print("\x1b[32mAll tests passed\x1b[39m")
|
||||
else:
|
||||
print("\x1b[91mSome tests failed\x1b[39m")
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
|
||||
|
Ładowanie…
Reference in New Issue