first commit

This commit is contained in:
Duncan Tourolle 2025-11-10 18:05:25 +01:00
commit 85794d9f2f
10 changed files with 897 additions and 0 deletions

67
.gitignore vendored Normal file
View File

@ -0,0 +1,67 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Virtual environments
venv/
ENV/
env/
.venv
# IDEs
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2016 Adafruit Industries
Copyright (c) 2025 Duncan Tourolle (Python conversion)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

4
MANIFEST.in Normal file
View File

@ -0,0 +1,4 @@
include README.md
include LICENSE
include pyproject.toml
recursive-include examples *.py

194
README.md Normal file
View File

@ -0,0 +1,194 @@
# PyPCF8523 - Python Driver for PCF8523 Real Time Clock
A Python 3.8+ driver for the PCF8523 Real Time Clock (RTC) chip, designed for Raspberry Pi and similar Linux boards with I2C support.
This library is a Python conversion of [Adafruit's CircuitPython PCF8523 library](https://github.com/adafruit/Adafruit_CircuitPython_PCF8523), adapted to work with standard Python and smbus2.
## Features
- **Battery-backed RTC**: Maintains accurate time even when main power is lost
- **Dual voltage support**: Works with 3.3V or 5V logic
- **Alarm functionality**: Set alarms with minute precision
- **Calibration support**: Adjust clock accuracy with offset calibration
- **Low battery detection**: Monitor backup battery status
- **Power management**: Configurable battery switchover modes
- **Python 3.8+ compatible**: Modern Python with type hints
## Hardware Requirements
- Raspberry Pi (any model with I2C) or compatible Linux board
- PCF8523 RTC module (e.g., Adafruit PCF8523 breakout)
- I2C connection to the RTC module
## Installation
### Prerequisites
Enable I2C on your Raspberry Pi:
```bash
sudo raspi-config
# Navigate to: Interface Options > I2C > Enable
```
### Install the package
```bash
pip install smbus2
pip install -e /path/to/PyPCF8523
```
Or install dependencies directly:
```bash
cd /path/to/PyPCF8523
pip install -r requirements.txt
pip install -e .
```
## Quick Start
### Basic Usage
```python
import time
from pypcf8523 import PCF8523
# Initialize RTC on I2C bus 1 (default for Raspberry Pi)
rtc = PCF8523(i2c_bus=1)
# Set the current time
current_time = time.localtime()
rtc.datetime = current_time
# Read the current time
dt = rtc.datetime
print(f"Current time: {dt.tm_year}-{dt.tm_mon:02d}-{dt.tm_mday:02d} "
f"{dt.tm_hour:02d}:{dt.tm_min:02d}:{dt.tm_sec:02d}")
```
### Using Context Manager
```python
from pypcf8523 import PCF8523
with PCF8523(1) as rtc:
print(f"RTC Time: {rtc.datetime}")
# Check if power was lost
if rtc.lost_power:
print("Warning: RTC lost power, time may be incorrect")
```
### Setting an Alarm
```python
from pypcf8523 import PCF8523
rtc = PCF8523(1)
# Set alarm for 8:30 AM every day
rtc.set_alarm(minute=30, hour=8)
# Enable alarm interrupt
rtc.alarm_interrupt = True
# Check if alarm triggered
if rtc.alarm_status:
print("Alarm triggered!")
rtc.alarm_status = False # Clear the alarm
```
### Calibration
```python
from pypcf8523 import PCF8523
rtc = PCF8523(1)
# Set calibration offset (-64 to +63)
# Positive values speed up the clock, negative values slow it down
rtc.calibration = 5
# Set calibration schedule
rtc.calibration_schedule_per_minute = True # Apply every minute
# or
rtc.calibration_schedule_per_minute = False # Apply every 2 hours
```
## API Reference
### PCF8523 Class
#### Constructor
```python
PCF8523(i2c_bus=1, address=0x68)
```
- `i2c_bus`: I2C bus number (default: 1)
- `address`: I2C device address (default: 0x68)
#### Properties
- **`datetime`** (struct_time): Get or set the current date and time
- **`lost_power`** (bool): True if device lost power since time was set
- **`power_management`** (int): Battery switchover mode (0-7)
- **`alarm_interrupt`** (bool): Enable/disable alarm interrupt output
- **`alarm_status`** (bool): Check if alarm triggered (write False to clear)
- **`battery_low`** (bool): True if backup battery is low (read-only)
- **`high_capacitance`** (bool): Oscillator capacitance mode
- **`calibration`** (int): Clock calibration offset (-64 to +63)
- **`calibration_schedule_per_minute`** (bool): Calibration schedule mode
#### Methods
- **`set_alarm(minute=None, hour=None, day=None, weekday=None)`**: Set alarm
- **`clear_alarm()`**: Disable and clear alarm
- **`close()`**: Close I2C bus connection
## Pin Connections (Raspberry Pi)
| PCF8523 | Raspberry Pi |
|---------|--------------|
| VCC | 3.3V (Pin 1) |
| GND | GND (Pin 6) |
| SDA | SDA (Pin 3) |
| SCL | SCL (Pin 5) |
## Troubleshooting
### I2C Device Not Found
Check if the device is detected:
```bash
i2cdetect -y 1
```
You should see `68` in the output grid.
### Permission Denied
Add your user to the i2c group:
```bash
sudo usermod -a -G i2c $USER
```
Then log out and back in.
### Accuracy Issues
The PCF8523 can drift up to 2 seconds per day. For critical timing applications:
1. Use the calibration feature to compensate for drift
2. Consider periodic synchronization with NTP
3. Monitor temperature (affects crystal oscillator)
## Credits
- **Original CircuitPython Library**: [Adafruit Industries](https://github.com/adafruit/Adafruit_CircuitPython_PCF8523)
- **Original Authors**: Philip R. Moyer and Radomir Dopieralski
- **Python Conversion**: Duncan Tourolle
## License
MIT License - See LICENSE file for details
## Contributing
This is a conversion of Adafruit's CircuitPython library. For core functionality improvements, please contribute to the [original repository](https://github.com/adafruit/Adafruit_CircuitPython_PCF8523).
For Python-specific issues or improvements, feel free to submit issues or pull requests.

82
examples/alarm_example.py Normal file
View File

@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Alarm example for PyPCF8523 RTC driver.
This example demonstrates how to use the alarm functionality:
- Setting an alarm
- Checking alarm status
- Clearing an alarm
Hardware setup:
- Connect PCF8523 to Raspberry Pi I2C bus 1
- Optionally connect INT pin to a GPIO for interrupt handling
"""
import time
from pypcf8523 import PCF8523
def main():
print("PCF8523 Alarm Example")
print("=" * 50)
# Initialize the RTC
rtc = PCF8523(i2c_bus=1)
# Get current time
current = rtc.datetime
print(f"Current time: {current.tm_hour:02d}:{current.tm_min:02d}:{current.tm_sec:02d}")
# Set an alarm for 2 minutes from now
alarm_minute = (current.tm_min + 2) % 60
alarm_hour = current.tm_hour
if alarm_minute < current.tm_min: # Handle hour rollover
alarm_hour = (alarm_hour + 1) % 24
print(f"Setting alarm for: {alarm_hour:02d}:{alarm_minute:02d}")
rtc.set_alarm(minute=alarm_minute, hour=alarm_hour)
# Enable the alarm interrupt
rtc.alarm_interrupt = True
print("Alarm interrupt enabled")
# Clear any existing alarm status
rtc.alarm_status = False
print("\nWaiting for alarm... (Press Ctrl+C to stop)")
print("-" * 50)
try:
while True:
# Read current time
current = rtc.datetime
time_str = f"{current.tm_hour:02d}:{current.tm_min:02d}:{current.tm_sec:02d}"
# Check if alarm triggered
if rtc.alarm_status:
print(f"\n🔔 ALARM! Triggered at {time_str}")
# Clear the alarm
rtc.alarm_status = False
print("Alarm cleared")
# Optionally disable the alarm
# rtc.clear_alarm()
# print("Alarm disabled")
break
else:
print(f"Current time: {time_str} - Waiting for alarm...", end="\r")
time.sleep(1.0)
except KeyboardInterrupt:
print("\n\nExample stopped by user")
finally:
# Clean up
rtc.clear_alarm()
rtc.close()
print("Alarm cleared and RTC connection closed")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,101 @@
#!/usr/bin/env python3
"""Calibration example for PyPCF8523 RTC driver.
This example demonstrates how to calibrate the RTC for better accuracy:
- Reading calibration settings
- Adjusting calibration offset
- Setting calibration schedule
The PCF8523 can drift up to 2 seconds per day. Calibration helps
compensate for this drift.
Calibration offset range: -64 to +63
- Positive values speed up the clock
- Negative values slow it down
Calibration schedule:
- Per minute: 1 LSB = 4.069 ppm
- Per 2 hours: 1 LSB = 4.340 ppm
"""
import time
from pypcf8523 import PCF8523
def main():
print("PCF8523 Calibration Example")
print("=" * 50)
# Initialize the RTC
rtc = PCF8523(i2c_bus=1)
# Read current calibration settings
current_offset = rtc.calibration
per_minute = rtc.calibration_schedule_per_minute
print(f"Current calibration offset: {current_offset}")
print(f"Calibration schedule: {'Per minute' if per_minute else 'Per 2 hours'}")
# Calculate ppm (parts per million) offset
ppm_per_lsb = 4.069 if per_minute else 4.340
ppm_offset = current_offset * ppm_per_lsb
print(f"Approximate offset: {ppm_offset:.2f} ppm")
# Example: Set calibration
print("\n" + "-" * 50)
print("Example calibration adjustment:")
print("-" * 50)
# If your RTC is running fast (gaining time), use negative offset
# If your RTC is running slow (losing time), use positive offset
# Example: Clock gains 2 seconds per day
# 2 seconds / 86400 seconds = 23.15 ppm
# Offset needed: 23.15 / 4.069 ≈ -6 (per minute mode)
new_offset = 0 # Change this based on your measurements
print(f"\nTo set calibration offset to {new_offset}:")
print(f" rtc.calibration = {new_offset}")
if new_offset != 0:
print("\nUncomment the following lines to apply:")
print(" # rtc.calibration_schedule_per_minute = True")
print(f" # rtc.calibration = {new_offset}")
print(f" # This would give approximately {new_offset * 4.069:.2f} ppm offset")
# Uncomment to actually apply calibration:
# rtc.calibration_schedule_per_minute = True
# rtc.calibration = new_offset
# How to measure drift:
print("\n" + "=" * 50)
print("How to measure and calibrate your RTC:")
print("=" * 50)
print("1. Set the RTC to accurate time (sync with NTP)")
print("2. Wait 24-48 hours")
print("3. Compare RTC time with accurate time")
print("4. Calculate drift in seconds per day")
print("5. Convert to ppm: (drift_seconds / 86400) * 1,000,000")
print("6. Calculate offset: ppm / 4.069 (per minute mode)")
print("7. Apply opposite sign: if fast use negative, if slow use positive")
print("8. Set the calibration offset")
print("\nExample:")
print(" If RTC gains 2 seconds/day:")
print(" 2 / 86400 * 1000000 = 23.15 ppm")
print(" 23.15 / 4.069 = 5.69 ≈ 6")
print(" Use offset = -6 (negative because it's fast)")
# Check battery status
print("\n" + "-" * 50)
if rtc.battery_low:
print("⚠️ WARNING: Backup battery is low!")
else:
print("✓ Backup battery is OK")
# Clean up
rtc.close()
print("\nRTC connection closed")
if __name__ == "__main__":
main()

75
examples/simple_test.py Normal file
View File

@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Simple test example for PyPCF8523 RTC driver.
This example demonstrates basic usage of the PCF8523 RTC:
- Reading the current time
- Setting the time
- Checking power loss status
Hardware setup:
- Connect PCF8523 to Raspberry Pi I2C bus 1
- VCC -> 3.3V, GND -> GND, SDA -> GPIO2, SCL -> GPIO3
"""
import time
from pypcf8523 import PCF8523
# Days of the week for display
DAYS = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
def main():
# Initialize the RTC on I2C bus 1
print("Initializing PCF8523 RTC...")
rtc = PCF8523(i2c_bus=1)
# Check if the RTC lost power
if rtc.lost_power:
print("WARNING: RTC lost power. Setting time to system time...")
# Set the RTC to the current system time
# In a real application, you might want to sync with NTP first
current_time = time.localtime()
rtc.datetime = current_time
print(f"Time set to: {time.strftime('%Y-%m-%d %H:%M:%S', current_time)}")
else:
print("RTC power OK")
# To manually set the time, uncomment and modify this section:
# ================================================================
# import time
# # Set to a specific time: 2025-11-09 15:30:00 (Saturday)
# set_time = time.struct_time((2025, 11, 9, 15, 30, 0, 5, -1, -1))
# rtc.datetime = set_time
# print(f"Time manually set to: {time.strftime('%Y-%m-%d %H:%M:%S', set_time)}")
# ================================================================
print("\nReading time from RTC (Press Ctrl+C to stop):")
print("-" * 50)
try:
while True:
# Read the current time from the RTC
current = rtc.datetime
# Format and display the time
day_name = DAYS[current.tm_wday]
time_str = (f"{day_name} "
f"{current.tm_year}/{current.tm_mon:02d}/{current.tm_mday:02d} "
f"{current.tm_hour:02d}:{current.tm_min:02d}:{current.tm_sec:02d}")
print(time_str)
# Wait one second before next read
time.sleep(1.0)
except KeyboardInterrupt:
print("\n\nTest stopped by user")
finally:
# Clean up
rtc.close()
print("RTC connection closed")
if __name__ == "__main__":
main()

15
pypcf8523/__init__.py Normal file
View File

@ -0,0 +1,15 @@
"""PyPCF8523 - PCF8523 Real Time Clock Driver for Python 3.14+
This library provides a Python interface for the PCF8523 RTC chip on Raspberry Pi.
Converted from Adafruit's CircuitPython library for standard Python.
Author: Converted for Python 3.14
Original: Philip R. Moyer and Radomir Dopieralski for Adafruit Industries
License: MIT
"""
__version__ = "1.0.0"
from .pcf8523 import PCF8523
__all__ = ["PCF8523"]

295
pypcf8523/pcf8523.py Normal file
View File

@ -0,0 +1,295 @@
"""PCF8523 Real Time Clock Driver
This module provides an interface to the PCF8523 RTC chip via I2C.
Designed for Raspberry Pi and similar Linux boards with I2C support.
SPDX-FileCopyrightText: 2016 Philip R. Moyer for Adafruit Industries
SPDX-FileCopyrightText: 2016 Radomir Dopieralski for Adafruit Industries
SPDX-FileCopyrightText: 2025 Converted for Python 3.14
SPDX-License-Identifier: MIT
"""
import time
from typing import Optional
try:
from smbus2 import SMBus
except ImportError:
raise ImportError("smbus2 is required. Install with: pip install smbus2")
# Power management constants
STANDARD_BATTERY_SWITCHOVER_AND_DETECTION = 0b000
BATTERY_SWITCHOVER_OFF = 0b111
# I2C address for PCF8523
PCF8523_ADDRESS = 0x68
# Register addresses
_CONTROL_1 = 0x00
_CONTROL_2 = 0x01
_CONTROL_3 = 0x02
_SECONDS = 0x03
_MINUTES = 0x04
_HOURS = 0x05
_DAYS = 0x06
_WEEKDAYS = 0x07
_MONTHS = 0x08
_YEARS = 0x09
_MINUTE_ALARM = 0x0A
_HOUR_ALARM = 0x0B
_DAY_ALARM = 0x0C
_WEEKDAY_ALARM = 0x0D
_OFFSET = 0x0E
_TMR_CLKOUT_CTRL = 0x0F
def _bcd2bin(value: int) -> int:
"""Convert binary coded decimal to binary."""
return value - 6 * (value >> 4)
def _bin2bcd(value: int) -> int:
"""Convert binary to binary coded decimal."""
return value + 6 * (value // 10)
class PCF8523:
"""Interface to the PCF8523 RTC.
Args:
i2c_bus: I2C bus number (typically 1 for Raspberry Pi)
address: I2C address of the PCF8523 (default: 0x68)
Example:
>>> rtc = PCF8523(1) # Use I2C bus 1
>>> print(rtc.datetime)
time.struct_time(tm_year=2025, tm_mon=11, tm_mday=9, ...)
"""
def __init__(self, i2c_bus: int = 1, address: int = PCF8523_ADDRESS):
"""Initialize the PCF8523 driver.
Args:
i2c_bus: I2C bus number (default: 1)
address: I2C device address (default: 0x68)
"""
self._bus = SMBus(i2c_bus)
self._address = address
def _read_byte(self, register: int) -> int:
"""Read a single byte from a register."""
return self._bus.read_byte_data(self._address, register)
def _write_byte(self, register: int, value: int) -> None:
"""Write a single byte to a register."""
self._bus.write_byte_data(self._address, register, value)
def _read_bit(self, register: int, bit: int) -> bool:
"""Read a specific bit from a register."""
value = self._read_byte(register)
return bool((value >> bit) & 1)
def _write_bit(self, register: int, bit: int, value: bool) -> None:
"""Write a specific bit in a register."""
reg_value = self._read_byte(register)
if value:
reg_value |= (1 << bit)
else:
reg_value &= ~(1 << bit)
self._write_byte(register, reg_value)
def _read_bits(self, register: int, bit: int, length: int) -> int:
"""Read multiple bits from a register."""
value = self._read_byte(register)
mask = (1 << length) - 1
return (value >> bit) & mask
def _write_bits(self, register: int, bit: int, length: int, value: int) -> None:
"""Write multiple bits to a register."""
reg_value = self._read_byte(register)
mask = ((1 << length) - 1) << bit
reg_value = (reg_value & ~mask) | ((value << bit) & mask)
self._write_byte(register, reg_value)
@property
def lost_power(self) -> bool:
"""True if the device has lost power since the time was set."""
return self._read_bit(_CONTROL_3, 7)
@lost_power.setter
def lost_power(self, value: bool) -> None:
"""Clear or set the power lost flag."""
self._write_bit(_CONTROL_3, 7, value)
@property
def power_management(self) -> int:
"""Power management state that dictates battery switchover.
Defaults to BATTERY_SWITCHOVER_OFF (0b111).
"""
return self._read_bits(_CONTROL_3, 5, 3)
@power_management.setter
def power_management(self, value: int) -> None:
"""Set power management mode."""
self._write_bits(_CONTROL_3, 5, 3, value)
@property
def datetime(self) -> time.struct_time:
"""Get the current date and time as a time.struct_time object."""
# Read all time registers at once
buffer = self._bus.read_i2c_block_data(self._address, _SECONDS, 7)
# Convert BCD to binary
seconds = _bcd2bin(buffer[0] & 0x7F)
minutes = _bcd2bin(buffer[1] & 0x7F)
hours = _bcd2bin(buffer[2] & 0x3F)
days = _bcd2bin(buffer[3] & 0x3F)
weekday = buffer[4] & 0x07
months = _bcd2bin(buffer[5] & 0x1F)
years = _bcd2bin(buffer[6]) + 2000
return time.struct_time((years, months, days, hours, minutes, seconds,
weekday, -1, -1))
@datetime.setter
def datetime(self, value: time.struct_time) -> None:
"""Set the current date and time from a time.struct_time object.
Args:
value: time.struct_time with year, month, day, hour, minute, second, weekday
"""
# Enable battery switchover and clear lost power flag
self.power_management = STANDARD_BATTERY_SWITCHOVER_AND_DETECTION
# Convert to BCD and write to registers
buffer = [
_bin2bcd(value.tm_sec) & 0x7F,
_bin2bcd(value.tm_min) & 0x7F,
_bin2bcd(value.tm_hour) & 0x3F,
_bin2bcd(value.tm_mday) & 0x3F,
value.tm_wday & 0x07,
_bin2bcd(value.tm_mon) & 0x1F,
_bin2bcd(value.tm_year - 2000) & 0xFF,
]
self._bus.write_i2c_block_data(self._address, _SECONDS, buffer)
# Clear the power lost flag
self.lost_power = False
@property
def alarm_interrupt(self) -> bool:
"""True if the interrupt pin will output when alarm is alarming."""
return self._read_bit(_CONTROL_1, 1)
@alarm_interrupt.setter
def alarm_interrupt(self, value: bool) -> None:
"""Enable or disable alarm interrupt output."""
self._write_bit(_CONTROL_1, 1, value)
@property
def alarm_status(self) -> bool:
"""True if alarm is alarming. Set to False to reset."""
return self._read_bit(_CONTROL_2, 3)
@alarm_status.setter
def alarm_status(self, value: bool) -> None:
"""Clear alarm status flag."""
self._write_bit(_CONTROL_2, 3, value)
@property
def battery_low(self) -> bool:
"""True if the battery is low and should be replaced."""
return self._read_bit(_CONTROL_3, 2)
@property
def high_capacitance(self) -> bool:
"""True for high oscillator capacitance (12.5pF), False for lower (7pF)."""
return self._read_bit(_CONTROL_1, 7)
@high_capacitance.setter
def high_capacitance(self, value: bool) -> None:
"""Set oscillator capacitance mode."""
self._write_bit(_CONTROL_1, 7, value)
@property
def calibration_schedule_per_minute(self) -> bool:
"""False to apply calibration offset every 2 hours (1 LSB = 4.340ppm);
True to offset every minute (1 LSB = 4.069ppm).
"""
return self._read_bit(_OFFSET, 7)
@calibration_schedule_per_minute.setter
def calibration_schedule_per_minute(self, value: bool) -> None:
"""Set calibration schedule mode."""
self._write_bit(_OFFSET, 7, value)
@property
def calibration(self) -> int:
"""Calibration offset to apply, from -64 to +63."""
value = self._read_bits(_OFFSET, 0, 7)
# Convert to signed integer
if value > 63:
value -= 128
return value
@calibration.setter
def calibration(self, value: int) -> None:
"""Set calibration offset (-64 to +63)."""
if not -64 <= value <= 63:
raise ValueError("Calibration must be between -64 and +63")
# Convert to unsigned for register
if value < 0:
value += 128
self._write_bits(_OFFSET, 0, 7, value)
def set_alarm(self, minute: Optional[int] = None, hour: Optional[int] = None,
day: Optional[int] = None, weekday: Optional[int] = None) -> None:
"""Set an alarm.
Args:
minute: Minute to trigger (0-59) or None to disable
hour: Hour to trigger (0-23) or None to disable
day: Day to trigger (1-31) or None to disable
weekday: Weekday to trigger (0-6) or None to disable
Note: Alarms only fire at full minutes (seconds are ignored).
"""
alarm_regs = [0x80, 0x80, 0x80, 0x80] # All disabled by default
if minute is not None:
alarm_regs[0] = _bin2bcd(minute) & 0x7F
if hour is not None:
alarm_regs[1] = _bin2bcd(hour) & 0x3F
if day is not None:
alarm_regs[2] = _bin2bcd(day) & 0x3F
if weekday is not None:
alarm_regs[3] = weekday & 0x07
self._bus.write_i2c_block_data(self._address, _MINUTE_ALARM, alarm_regs)
def clear_alarm(self) -> None:
"""Disable and clear the alarm."""
self.alarm_status = False
self.set_alarm() # Disable all alarm fields
def close(self) -> None:
"""Close the I2C bus connection."""
self._bus.close()
def __enter__(self):
"""Context manager entry."""
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit."""
self.close()
return False

42
pyproject.toml Normal file
View File

@ -0,0 +1,42 @@
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "pypcf8523"
version = "1.0.0"
description = "Python 3.14+ driver for the PCF8523 Real Time Clock"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
{name = "Duncan Tourolle", email = "duncan@tourolle.paris"}
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Operating System :: POSIX :: Linux",
"Topic :: System :: Hardware",
"Topic :: Software Development :: Libraries :: Python Modules",
]
keywords = ["pcf8523", "rtc", "real-time-clock", "i2c", "raspberry-pi", "hardware"]
dependencies = [
"smbus2>=0.4.0",
]
[project.urls]
"Homepage" = "https://github.com/adafruit/Adafruit_CircuitPython_PCF8523"
"Original CircuitPython Library" = "https://github.com/adafruit/Adafruit_CircuitPython_PCF8523"
[tool.setuptools]
packages = ["pypcf8523"]
package-dir = {"pypcf8523" = "pypcf8523"}