From f674a51d465f8b645e70afc955a7d704fca5288d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20=C5=A0imun=20Ku=C4=8Di?= Date: Mon, 15 Sep 2025 12:44:37 +0200 Subject: [PATCH 1/5] Add DS3234 RTC component --- .../DS3234/Examples/ds3234-deepSleep.py | 98 +++ .../DS3234/Examples/ds3234-simpleRTC.py | 132 ++++ .../DS3234/Examples/ds3234-writeSRAM.py | 145 ++++ Actuators/DS3234/DS3234/ds3234.py | 619 ++++++++++++++++++ Actuators/DS3234/package.json | 22 + 5 files changed, 1016 insertions(+) create mode 100644 Actuators/DS3234/DS3234/Examples/ds3234-deepSleep.py create mode 100644 Actuators/DS3234/DS3234/Examples/ds3234-simpleRTC.py create mode 100644 Actuators/DS3234/DS3234/Examples/ds3234-writeSRAM.py create mode 100644 Actuators/DS3234/DS3234/ds3234.py create mode 100644 Actuators/DS3234/package.json diff --git a/Actuators/DS3234/DS3234/Examples/ds3234-deepSleep.py b/Actuators/DS3234/DS3234/Examples/ds3234-deepSleep.py new file mode 100644 index 0000000..e3503fd --- /dev/null +++ b/Actuators/DS3234/DS3234/Examples/ds3234-deepSleep.py @@ -0,0 +1,98 @@ +# FILE: DS3234-simpleRTC.py +# AUTHOR: Soldered +# BRIEF: Example showing how to wake up from deep sleep +# Using the INT pin on the breakout board +# WORKS WITH: DS3234 RTC Breakout: www.solde.red/333358 +# LAST UPDATED: 2025-09-15 + +import machine +import time +import esp32 +from DS3234 import DS3234 + +# Define CS pin for DS3234 +RTC_CS_PIN = 5 + +# Define wake-up pin (must be RTC-capable GPIO) +# ESP32 RTC-capable pins: 0, 2, 4, 12-15, 25-27, 32-39 +WAKEUP_PIN = 26 # Must be RTC-capable pin + +# Initialize SPI and CS pin for DS3234 +# Using VSPI (SPI ID 2) with default ESP32 pins +spi = machine.SPI(2, baudrate=1000000, polarity=1, phase=1, + sck=machine.Pin(18), mosi=machine.Pin(23), miso=machine.Pin(19)) +cs_pin = machine.Pin(RTC_CS_PIN, machine.Pin.OUT) + +# Create an instance of the RTC object +rtc = DS3234(spi, cs_pin) + +print("Initializing DS3234 RTC...") + +# Check if this is a wake-up from deep sleep +wake_reason = machine.wake_reason() + +if wake_reason == machine.PIN_WAKE: + print("Woke up from deep sleep (RTC alarm)!") + + # Read and display current time + rtc.update() + + print("Current time: {0}:{1:02d}:{2:02d}".format( + rtc.hour(), rtc.minute(), rtc.second())) + + print("Date: {0}/{1}/{2}".format( + rtc.month(), rtc.date(), rtc.year() + 2000)) + + # Clear alarm flags + if rtc.alarm1(clear=True): + print("Cleared Alarm 1 flag") + if rtc.alarm2(clear=True): + print("Cleared Alarm 2 flag") + +else: + # First boot - set up RTC and configure alarm + print("First boot - setting up RTC") + + # Clear any existing alarm flags first + rtc.alarm1(clear=True) + rtc.alarm2(clear=True) + + # Set RTC time (use autoTime or set manually) + rtc.autoTime() + + # Display current time + rtc.update() + print("Current RTC time: {0}:{1:02d}:{2:02d}".format( + rtc.hour(), rtc.minute(), rtc.second())) + + # Configure alarm to trigger in 30 seconds from now + current_second = rtc.second() + alarm_second = (current_second + 30) % 60 + alarm_minute = rtc.minute() + ((current_second + 30) // 60) + + if alarm_minute >= 60: + alarm_minute %= 60 + + # Set alarm 1 to trigger at specific second (ignore minutes, hours, date) + # 255 = "don't care" for that field + rtc.setAlarm1(second=alarm_second, minute=255, hour=255, date=255, day=False) + + # Enable alarm 1 interrupt - THIS IS CRITICAL! + rtc.enableAlarmInterrupt(alarm1=True, alarm2=False) + + # Verify alarm is set correctly + print("Alarm set to trigger at second:", alarm_second) + +# Configure the interrupt pin as input with pullup +wakeup_pin = machine.Pin(WAKEUP_PIN, machine.Pin.IN, machine.Pin.PULL_UP) + +# Configure deep sleep with RTC alarm wakeup +print("Going to deep sleep...") +time.sleep(1) + +# Set up wakeup source - use external pin triggered by RTC alarm +# The DS3234 INT pin goes LOW when alarm triggers +esp32.wake_on_ext0(pin=wakeup_pin, level=esp32.WAKEUP_ALL_LOW) + +# Go to deep sleep +machine.deepsleep() diff --git a/Actuators/DS3234/DS3234/Examples/ds3234-simpleRTC.py b/Actuators/DS3234/DS3234/Examples/ds3234-simpleRTC.py new file mode 100644 index 0000000..0c39f5d --- /dev/null +++ b/Actuators/DS3234/DS3234/Examples/ds3234-simpleRTC.py @@ -0,0 +1,132 @@ +# FILE: DS3234-simpleRTC.py +# AUTHOR: Soldered +# BRIEF: Example showing how to Initialize the DS3234 RTC +# and set the time and alarms +# WORKS WITH: DS3234 RTC Breakout: www.solde.red/333358 +# LAST UPDATED: 2025-09-15 + +import machine +import time +from ds3234 import DS3234 + +# Configurable Pin Definitions for ESP32 +DS3234_CS_PIN = 5 # DS3234 RTC Chip-select pin + +# ESP32 SPI pin assignments (change these based on your wiring) +# Default ESP32 SPI pins: +# HSPI: SCLK=14, MOSI=13, MISO=12 +# VSPI: SCLK=18, MOSI=23, MISO=19 + +# Using HSPI +spi = machine.SPI(1, baudrate=1000000, polarity=1, phase=1, sck=machine.Pin(18), mosi=machine.Pin(23), miso=machine.Pin(19)) +cs_pin = machine.Pin(DS3234_CS_PIN, machine.Pin.OUT) + +# Create an instance of the RTC object +rtc = DS3234(spi, cs_pin) + +# Create an instance of the RTC object +# If using the SQW pin as an interrupt (uncomment if needed) +# interrupt_pin = machine.Pin(INTERRUPT_PIN, machine.Pin.IN, machine.Pin.PULL_UP) + +# Use the serial monitor to view time/date output +print("Initializing DS3234 RTC...") + +# Initialize the RTC +# (The __init__ method already handles initialization in the class) + +# Set to 12-hour mode if desired (uncomment) +# rtc.set12Hour() + +# Now set the time... +# You can use the autoTime() function to set the RTC's clock and +# date to the system's current time +rtc.autoTime() + +# Or you can use the rtc.setTime(s, m, h, day, date, month, year) +# function to explicitly set the time: +# e.g. 7:32:16 | Monday October 31, 2016: +# rtc.setTime(16, 32, 7, 2, 31, 10, 16) # Uncomment to manually set time + +# Update time/date values, so we can set alarms +rtc.update() + +# Configure Alarm(s): +# (Optional: enable SQW pin as an interrupt) +rtc.enableAlarmInterrupt() + +# Set alarm1 to alert when seconds hits 30 +rtc.setAlarm1(30) + +# Set alarm2 to alert when minute increments by 1 +rtc.setAlarm2(rtc.minute() + 1) + +print("RTC initialized and alarms set") + +def print_time(): + # Print hour + print(str(rtc.hour()) + ":", end="") + + # Print minute with leading zero if needed + if rtc.minute() < 10: + print("0", end="") + print(str(rtc.minute()) + ":", end="") + + # Print second with leading zero if needed + if rtc.second() < 10: + print("0", end="") + print(str(rtc.second()), end="") + + # If we're in 12-hour mode + if rtc.is12hour(): + # Use rtc.pm() to read the AM/PM state of the hour + if rtc.pm(): + print(" PM", end="") # Returns true if PM + else: + print(" AM", end="") + + print(" | ", end="") + + # Few options for printing the day, pick one: + print(rtc.dayStr(), end="") # Print day string + # print(rtc.dayChar(), end="") # Print day character + # print(rtc.day(), end="") # Print day integer (1-7, Sun-Sat) + + print(" - ", end="") + + # Print date in USA format (MM/DD/YYYY) or international (DD/MM/YYYY) + PRINT_USA_DATE = True # Change to False for international format + + if PRINT_USA_DATE: + print(str(rtc.month()) + "/" + str(rtc.date()) + "/", end="") # Print month/date + else: + print(str(rtc.date()) + "/" + str(rtc.month()) + "/", end="") # Print date/month + + print(str(rtc.year())) # Print year + +last_second = -1 + +while True: + rtc.update() + + if rtc.second() != last_second: # If the second has changed + print_time() # Print the new time + last_second = rtc.second() # Update last_second value + + # Check for alarm interrupts + # If using interrupt pin (uncomment if needed): + # if interrupt_pin.value() == 0: # Interrupt pin is active-low + # Check rtc.alarm1() to see if alarm 1 triggered the interrupt + + if rtc.alarm1(clear=True) == True: # Clear the alarm flag + print("ALARM 1!") + # Re-set the alarm for when s=30: + rtc.setAlarm1(30) + + # Check rtc.alarm2() to see if alarm 2 triggered the interrupt + if rtc.alarm2(clear=True) == True: # Clear the alarm flag + print("ALARM 2!") + # Re-set the alarm for when m increments by 1 + rtc.setAlarm2(rtc.minute() + 1) + + + time.sleep(0.1) # Small delay to prevent busy waiting diff --git a/Actuators/DS3234/DS3234/Examples/ds3234-writeSRAM.py b/Actuators/DS3234/DS3234/Examples/ds3234-writeSRAM.py new file mode 100644 index 0000000..64c3e76 --- /dev/null +++ b/Actuators/DS3234/DS3234/Examples/ds3234-writeSRAM.py @@ -0,0 +1,145 @@ +# FILE: DS3234-writeSRAM.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: Example showing how to write and retrieve +# data from RTC SRAM +# WORKS WITH: DS3234 RTC Breakout: www.solde.red/333358 +# LAST UPDATED: 2025-09-15 + +import machine +import time +import struct +from DS3234 import DS3234 + +# Configurable Pin Definitions +DS3234_CS_PIN = 5 # DS3234 RTC Chip-select pin + +# Initialize SPI and CS pin +# Using VSPI (SPI ID 2) with default ESP32 pins +spi = machine.SPI(2, baudrate=1000000, polarity=1, phase=1, + sck=machine.Pin(18), mosi=machine.Pin(23), miso=machine.Pin(19)) +cs_pin = machine.Pin(DS3234_CS_PIN, machine.Pin.OUT) + +# Create an instance of the RTC object +rtc = DS3234(spi, cs_pin) + +print("\n### Starting DS3234_RTC_SRAM_Demo! ###\n") + +# We don't set time or do other clock-related stuff here. +# This is a demo about SRAM functionality + +""" Write and read single bytes to/from SRAM """ + +# The DS3234 has 256 bytes of SRAM +# The address is a uint8_t ranging from 0 to 255 +sram_address = 161 # == 0xA1 + +# Choose some data to store in SRAM +data_byte = 42 + +print("Writing a byte with value", data_byte, "to memory address 0x{:02X}.".format(sram_address)) + +# Do the writing here... +# Note: If you have battery support on your RTC module, this value +# will survive reboots and even extended periods without external power. +rtc.writeToSRAM(sram_address, data_byte) + +# Now we read the value back from SRAM +read_back_byte = rtc.readFromSRAM(sram_address) + +print("We have read back a byte with value", read_back_byte, "from memory address 0x{:02X}.".format(sram_address)) + +""" Write and read several bytes to/from an array """ + +# Put some data into an array of bytes +write_buffer = bytearray([101, 102, 103, 104, 199, 255]) + +# Get the length of the array to write +length = len(write_buffer) + +print("Writing", length, "byte(s) to SRAM, starting at address 0x{:02X}.".format(sram_address)) +print("Values to write:", " ".join(str(b) for b in write_buffer)) + +# Now we do the writing +rtc.writeToSRAMBuffer(sram_address, write_buffer) + +# Create an array to hold the data we read back from SRAM +read_buffer = bytearray(length) + +# Now read back the stored data into read_buffer +read_data = rtc.readFromSRAMBuffer(sram_address, length) + +print("Data we have read back starting from SRAM address 0x{:02X}:".format(sram_address), + " ".join(str(b) for b in read_data)) + +""" Write and read other data types directly to/from SRAM """ + +# Note: +# You are responsible for non-overlapping memory ranges. If you write some data type with +# a width of, say, four byte to SRAM address x, the next value to store can start at SRAM +# address x+4 (because locations x, x+1, x+2 and x+3 are already in use). + +# Warning: +# writeToSRAMValue() and readFromSRAMValue() do not take byte-ordering (i.e. "endian-ness") into +# account. If you want to write data to SRAM of the DS3234 on one type of microcontroller, +# unplug it, re-plug it to another type of microcontroller, you have to make sure that both +# types of MC work with the same byte ordering, otherwise you might get garbage. + +# Example 1: Store and read a uint16_t +uint16_data = 32769 + +# write uint16_t to SRAM +print("Writing a uint16_t with value", uint16_data, "to memory address 0x{:02X}.".format(sram_address)) + +rtc.writeToSRAMValue(sram_address, uint16_data, 'uint16') + +# And now read the value back: +read_back_uint16 = rtc.readFromSRAMValue(sram_address, 'uint16') + +print("We have read back a uint16_t with value", read_back_uint16, "from memory address 0x{:02X}.".format(sram_address)) + +print("Written and read uint16_t values are equal:", uint16_data == read_back_uint16) + +# Example 2: Using a signed integer this time +int32_data = -128653 +rtc.writeToSRAMValue(sram_address, int32_data, 'int32') + +read_back_int32 = rtc.readFromSRAMValue(sram_address, 'int32') + +print("Written and read int32_t values are equal:", int32_data == read_back_int32) + +# Example 3: Using a floating point +float_data = 3.14159265358979 # the number pi +rtc.writeToSRAMValue(sram_address, float_data, 'float') + +read_back_float = rtc.readFromSRAMValue(sram_address, 'float') + +print("Written and read float values are equal:", abs(float_data - read_back_float) < 0.0001) + +# Example 4: Using a string (custom implementation) +string_data = "Hello RTC!" +string_address = sram_address + 20 # Different address to avoid overlap + +# Convert string to bytes and write +string_bytes = string_data.encode('utf-8') +rtc.writeToSRAMBuffer(string_address, string_bytes) + +# Read back and convert to string +read_string_bytes = rtc.readFromSRAMBuffer(string_address, len(string_bytes)) +read_string = read_string_bytes.decode('utf-8') + +print("Original string:", string_data) +print("Read back string:", read_string) +print("Strings are equal:", string_data == read_string) + +# Example 5: Demonstrate persistence across resets +print("\nTesting SRAM persistence across reset...") +persistent_address = 200 + +# Write a value that should persist +persistent_value = 123 +rtc.readFromSRAM(persistent_address) + +print("Wrote value", persistent_value, "to address 0x{:02X}".format(persistent_address)) +print("Reset the board and run this script again to check if the value persists") + +print("\n### DS3234_RTC_SRAM_Demo finished! ###\n") diff --git a/Actuators/DS3234/DS3234/ds3234.py b/Actuators/DS3234/DS3234/ds3234.py new file mode 100644 index 0000000..926efe9 --- /dev/null +++ b/Actuators/DS3234/DS3234/ds3234.py @@ -0,0 +1,619 @@ +# FILE: ds3234.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: Module for the Soldered DS3234 RTC Breakout board +# LAST UPDATED: 2025-09-15 + +import machine +import time +import struct +from micropython import const + +# Constants from the header file +TWELVE_HOUR_MODE = const(1 << 6) +TWELVE_HOUR_PM = const(1 << 5) + +DS3234_AM = False +DS3234_PM = True + +SQW_CONTROL_MASK = const(0xE3) +SQW_ENABLE_BIT = const(1 << 2) + +ALARM_MODE_BIT = const(1 << 7) +ALARM_DAY_BIT = const(1 << 6) +ALARM_1_FLAG_BIT = const(1 << 0) +ALARM_2_FLAG_BIT = const(1 << 1) +ALARM_INTCN_BIT = const(1 << 2) + +TIME_ARRAY_LENGTH = const(7) + +# Time order constants +TIME_SECONDS = const(0) +TIME_MINUTES = const(1) +TIME_HOURS = const(2) +TIME_DAY = const(3) +TIME_DATE = const(4) +TIME_MONTH = const(5) +TIME_YEAR = const(6) + +# SQW rate constants +SQW_SQUARE_1 = const(0) +SQW_SQUARE_1K = const(1) +SQW_SQUARE_4K = const(2) +SQW_SQUARE_8K = const(3) + +# DS3234 register addresses +DS3234_REGISTER_SECONDS = const(0x00) +DS3234_REGISTER_MINUTES = const(0x01) +DS3234_REGISTER_HOURS = const(0x02) +DS3234_REGISTER_DAY = const(0x03) +DS3234_REGISTER_DATE = const(0x04) +DS3234_REGISTER_MONTH = const(0x05) +DS3234_REGISTER_YEAR = const(0x06) +DS3234_REGISTER_A1SEC = const(0x07) +DS3234_REGISTER_A1MIN = const(0x08) +DS3234_REGISTER_A1HR = const(0x09) +DS3234_REGISTER_A1DA = const(0x0A) +DS3234_REGISTER_A2MIN = const(0x0B) +DS3234_REGISTER_A2HR = const(0x0C) +DS3234_REGISTER_A2DA = const(0x0D) +DS3234_REGISTER_CONTROL = const(0x0E) +DS3234_REGISTER_STATUS = const(0x0F) +DS3234_REGISTER_XTAL = const(0x10) +DS3234_REGISTER_TEMPM = const(0x11) +DS3234_REGISTER_TEMPL = const(0x12) +DS3234_REGISTER_TEMPEN = const(0x13) +DS3234_REGISTER_RESERV1 = const(0x14) +DS3234_REGISTER_RESERV2 = const(0x15) +DS3234_REGISTER_RESERV3 = const(0x16) +DS3234_REGISTER_RESERV4 = const(0x17) +DS3234_REGISTER_SRAMA = const(0x18) +DS3234_REGISTER_SRAMD = const(0x19) + +DS3234_REGISTER_BASE = DS3234_REGISTER_SECONDS + +# Day conversion arrays +dayIntToStr = [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" +] + +dayIntToChar = ['U', 'M', 'T', 'W', 'R', 'F', 'S'] + + +class DS3234: + def __init__(self, spi, cs_pin): + self.spi = spi + self.cs_pin = cs_pin + self._time = [0] * TIME_ARRAY_LENGTH + self._pm = False + + # Initialize CS pin + self.cs_pin.init(machine.Pin.OUT) + self.cs_pin.value(1) + + def _spi_write_bytes(self, reg, values): + """Write bytes to SPI device, incrementing from a register""" + write_reg = reg | 0x80 + self.cs_pin.value(0) + self.spi.write(bytearray([write_reg])) + self.spi.write(bytearray(values)) + self.cs_pin.value(1) + + def _spi_write_byte(self, reg, value): + """Write a byte to an SPI device's register""" + write_reg = reg | 0x80 + self.cs_pin.value(0) + self.spi.write(bytearray([write_reg, value])) + self.cs_pin.value(1) + + def _spi_read_byte(self, reg): + """Read a byte from an SPI device's register""" + self.cs_pin.value(0) + self.spi.write(bytearray([reg])) + value = self.spi.read(1)[0] + self.cs_pin.value(1) + return value + + def _spi_read_bytes(self, reg, length): + """Read bytes from an SPI device, incrementing from a register""" + self.cs_pin.value(0) + self.spi.write(bytearray([reg])) + values = self.spi.read(length) + self.cs_pin.value(1) + return values + + @staticmethod + def BCDtoDEC(val): + """Convert binary-coded decimal (BCD) to decimal""" + return ((val // 0x10) * 10) + (val % 0x10) + + @staticmethod + def DECtoBCD(val): + """Convert decimal to binary-coded decimal (BCD)""" + return ((val // 10) * 0x10) + (val % 10) + + def setTime(self, sec, min, hour, day, date, month, year): + """Set time and date/day registers of DS3234""" + self._time[TIME_SECONDS] = self.DECtoBCD(sec) + self._time[TIME_MINUTES] = self.DECtoBCD(min) + self._time[TIME_HOURS] = self.DECtoBCD(hour) + self._time[TIME_DAY] = self.DECtoBCD(day) + self._time[TIME_DATE] = self.DECtoBCD(date) + self._time[TIME_MONTH] = self.DECtoBCD(month) + self._time[TIME_YEAR] = self.DECtoBCD(year) + + self._spi_write_bytes(DS3234_REGISTER_BASE, self._time) + + def setTime12h(self, sec, min, hour12, pm, day, date, month, year): + """Set time and date/day registers of DS3234 in 12-hour format""" + self._time[TIME_SECONDS] = self.DECtoBCD(sec) + self._time[TIME_MINUTES] = self.DECtoBCD(min) + self._time[TIME_HOURS] = self.DECtoBCD(hour12) + self._time[TIME_HOURS] |= TWELVE_HOUR_MODE + if pm: + self._time[TIME_HOURS] |= TWELVE_HOUR_PM + self._time[TIME_DAY] = self.DECtoBCD(day) + self._time[TIME_DATE] = self.DECtoBCD(date) + self._time[TIME_MONTH] = self.DECtoBCD(month) + self._time[TIME_YEAR] = self.DECtoBCD(year) + + self._spi_write_bytes(DS3234_REGISTER_BASE, self._time) + + def setTimeArray(self, time_array): + """Set time and date/day registers of DS3234 (using data array)""" + if len(time_array) != TIME_ARRAY_LENGTH: + return + + self._spi_write_bytes(DS3234_REGISTER_BASE, time_array) + + def autoTime(self): + """Fill DS3234 time registers with current time/date""" + # Get current time from MicroPython's RTC + rtc = machine.RTC() + now = rtc.datetime() + + # Format: (year, month, day, weekday, hour, minute, second, microsecond) + year = now[0] % 100 # Convert to 2-digit year + month = now[1] + day = now[2] + weekday = now[3] + 1 # MicroPython weekday: 0-6 for Monday-Sunday, DS3234: 1-7 for Sunday-Saturday + if weekday > 7: + weekday = 1 + + hour = now[4] + minute = now[5] + second = now[6] + + # Convert to 12-hour format if needed + if self.is12hour(): + pm_bit = 0 + if hour <= 11: + if hour == 0: + hour = 12 + else: + pm_bit = TWELVE_HOUR_PM + if hour >= 13: + hour -= 12 + + hour = self.DECtoBCD(hour) + hour |= pm_bit + hour |= TWELVE_HOUR_MODE + else: + hour = self.DECtoBCD(hour) + + self._time[TIME_SECONDS] = self.DECtoBCD(second) + self._time[TIME_MINUTES] = self.DECtoBCD(minute) + self._time[TIME_HOURS] = hour + self._time[TIME_MONTH] = self.DECtoBCD(month) + self._time[TIME_DATE] = self.DECtoBCD(day) + self._time[TIME_YEAR] = self.DECtoBCD(year) + self._time[TIME_DAY] = self.DECtoBCD(weekday) + + self._spi_write_bytes(DS3234_REGISTER_BASE, self._time) + + def update(self): + """Read all time/date registers and update the _time array""" + rtc_reads = self._spi_read_bytes(DS3234_REGISTER_BASE, TIME_ARRAY_LENGTH) + + for i in range(TIME_ARRAY_LENGTH): + self._time[i] = rtc_reads[i] + + if self._time[TIME_HOURS] & TWELVE_HOUR_MODE: + if self._time[TIME_HOURS] & TWELVE_HOUR_PM: + self._pm = True + else: + self._pm = False + + self._time[TIME_HOURS] &= 0x1F # Mask out 24-hour bit, am/pm from hours + else: + self._time[TIME_HOURS] &= 0x3F # Mask out 24-hour bit from hours + + def second(self): + return self.BCDtoDEC(self._time[TIME_SECONDS]) + + def minute(self): + return self.BCDtoDEC(self._time[TIME_MINUTES]) + + def hour(self): + return self.BCDtoDEC(self._time[TIME_HOURS]) + + def day(self): + return self.BCDtoDEC(self._time[TIME_DAY]) + + def dayChar(self): + day_val = self.BCDtoDEC(self._time[TIME_DAY]) + if 1 <= day_val <= 7: + return dayIntToChar[day_val - 1] + return '?' # Or handle error appropriately + + def dayStr(self): + day_val = self.BCDtoDEC(self._time[TIME_DAY]) + if 1 <= day_val <= 7: + return dayIntToStr[day_val - 1] + return "Unknown" # Or handle error appropriately + + def date(self): + return self.BCDtoDEC(self._time[TIME_DATE]) + + def month(self): + return self.BCDtoDEC(self._time[TIME_MONTH]) + + def year(self): + return self.BCDtoDEC(self._time[TIME_YEAR]) + + def getSecond(self): + self._time[TIME_SECONDS] = self._spi_read_byte(DS3234_REGISTER_SECONDS) + return self.BCDtoDEC(self._time[TIME_SECONDS]) + + def getMinute(self): + self._time[TIME_MINUTES] = self._spi_read_byte(DS3234_REGISTER_MINUTES) + return self.BCDtoDEC(self._time[TIME_MINUTES]) + + def getHour(self): + hour_register = self._spi_read_byte(DS3234_REGISTER_HOURS) + + if hour_register & TWELVE_HOUR_MODE: + hour_register &= 0x1F # Mask out am/pm, 24-hour bit + self._time[TIME_HOURS] = hour_register + + return self.BCDtoDEC(self._time[TIME_HOURS]) + + def getDay(self): + self._time[TIME_DAY] = self._spi_read_byte(DS3234_REGISTER_DAY) + return self.BCDtoDEC(self._time[TIME_DAY]) + + def getDate(self): + self._time[TIME_DATE] = self._spi_read_byte(DS3234_REGISTER_DATE) + return self.BCDtoDEC(self._time[TIME_DATE]) + + def getMonth(self): + self._time[TIME_MONTH] = self._spi_read_byte(DS3234_REGISTER_MONTH) + self._time[TIME_MONTH] &= 0x7F # Mask out century bit + return self.BCDtoDEC(self._time[TIME_MONTH]) + + def getYear(self): + self._time[TIME_YEAR] = self._spi_read_byte(DS3234_REGISTER_YEAR) + return self.BCDtoDEC(self._time[TIME_YEAR]) + + def setSecond(self, s): + if s <= 59: + _s = self.DECtoBCD(s) + self._spi_write_byte(DS3234_REGISTER_SECONDS, _s) + + def setMinute(self, m): + if m <= 59: + _m = self.DECtoBCD(m) + self._spi_write_byte(DS3234_REGISTER_MINUTES, _m) + + def setHour(self, h): + if h <= 23: + _h = self.DECtoBCD(h) + self._spi_write_byte(DS3234_REGISTER_HOURS, _h) + + def setDay(self, d): + if 1 <= d <= 7: + _d = self.DECtoBCD(d) + self._spi_write_byte(DS3234_REGISTER_DAY, _d) + + def setDate(self, d): + if d <= 31: + _d = self.DECtoBCD(d) + self._spi_write_byte(DS3234_REGISTER_DATE, _d) + + def setMonth(self, mo): + if 1 <= mo <= 12: + _mo = self.DECtoBCD(mo) + self._spi_write_byte(DS3234_REGISTER_MONTH, _mo) + + def setYear(self, y): + if y <= 99: + _y = self.DECtoBCD(y) + self._spi_write_byte(DS3234_REGISTER_YEAR, _y) + + def set12hour(self, enable12=True): + if enable12: + self.set24hour(False) + else: + self.set24hour(True) + + def set24hour(self, enable24=True): + hour_register = self._spi_read_byte(DS3234_REGISTER_HOURS) + + hour12 = hour_register & TWELVE_HOUR_MODE + if (hour12 and not enable24) or (not hour12 and enable24): + return + + if enable24: + old_hour = hour_register & 0x1F # Mask out am/pm and 12-hour mode + old_hour = self.BCDtoDEC(old_hour) # Convert to decimal + new_hour = old_hour + + hour_pm = hour_register & TWELVE_HOUR_PM + if hour_pm and old_hour >= 1: + new_hour += 12 + elif not hour_pm and old_hour == 12: + new_hour = 0 + new_hour = self.DECtoBCD(new_hour) + else: + old_hour = hour_register & 0x3F # Mask out am/pm and 12-hour mode + old_hour = self.BCDtoDEC(old_hour) # Convert to decimal + new_hour = old_hour + + if old_hour == 0: + new_hour = 12 + elif old_hour >= 13: + new_hour -= 12 + + new_hour = self.DECtoBCD(new_hour) + new_hour |= TWELVE_HOUR_MODE # Set bit 6 to set 12-hour mode + if old_hour >= 12: + new_hour |= TWELVE_HOUR_PM # Set PM bit if necessary + + self._spi_write_byte(DS3234_REGISTER_HOURS, new_hour) + + def is12hour(self): + hour_register = self._spi_read_byte(DS3234_REGISTER_HOURS) + return bool(hour_register & TWELVE_HOUR_MODE) + + def pm(self): + hour_register = self._spi_read_byte(DS3234_REGISTER_HOURS) + return bool(hour_register & TWELVE_HOUR_PM) + + def enable(self): + control_register = self._spi_read_byte(DS3234_REGISTER_CONTROL) + control_register &= ~(1 << 7) + self._spi_write_byte(DS3234_REGISTER_CONTROL, control_register) + + def disable(self): + control_register = self._spi_read_byte(DS3234_REGISTER_CONTROL) + control_register |= (1 << 7) + self._spi_write_byte(DS3234_REGISTER_CONTROL, control_register) + + def setAlarm1(self, second=255, minute=255, hour=255, date=255, day=False): + # Read current alarm settings + alarm_reg = list(self._spi_read_bytes(DS3234_REGISTER_A1SEC, 4)) + + # Set seconds + if second == 255: + alarm_reg[0] |= ALARM_MODE_BIT # Don't care about seconds + else: + alarm_reg[0] &= ~ALARM_MODE_BIT # Match specific seconds + alarm_reg[0] = self.DECtoBCD(second) + + # Set minutes + if minute == 255: + alarm_reg[1] |= ALARM_MODE_BIT # Don't care about minutes + else: + alarm_reg[1] &= ~ALARM_MODE_BIT # Match specific minutes + alarm_reg[1] = self.DECtoBCD(minute) + + # Set hours + if hour == 255: + alarm_reg[2] |= ALARM_MODE_BIT # Don't care about hours + else: + alarm_reg[2] &= ~ALARM_MODE_BIT # Match specific hours + alarm_reg[2] = self.DECtoBCD(hour) + + # Set day/date + if date == 255: + alarm_reg[3] |= ALARM_MODE_BIT # Don't care about day/date + else: + alarm_reg[3] &= ~ALARM_MODE_BIT # Match specific day/date + if day: + alarm_reg[3] |= ALARM_DAY_BIT # Day of week (1-7) + else: + alarm_reg[3] &= ~ALARM_DAY_BIT # Date of month (1-31) + alarm_reg[3] = self.DECtoBCD(date) + + self._spi_write_bytes(DS3234_REGISTER_A1SEC, alarm_reg) + + def setAlarm2(self, minute=255, hour=255, date=255, day=False): + # Read current alarm settings (Alarm2 has no seconds) + alarm_reg = list(self._spi_read_bytes(DS3234_REGISTER_A2MIN, 3)) + + # Set minutes + if minute == 255: + alarm_reg[0] |= ALARM_MODE_BIT # Don't care about minutes + else: + alarm_reg[0] &= ~ALARM_MODE_BIT # Match specific minutes + alarm_reg[0] = self.DECtoBCD(minute) + + # Set hours + if hour == 255: + alarm_reg[1] |= ALARM_MODE_BIT # Don't care about hours + else: + alarm_reg[1] &= ~ALARM_MODE_BIT # Match specific hours + alarm_reg[1] = self.DECtoBCD(hour) + + # Set day/date + if date == 255: + alarm_reg[2] |= ALARM_MODE_BIT # Don't care about day/date + else: + alarm_reg[2] &= ~ALARM_MODE_BIT # Match specific day/date + if day: + alarm_reg[2] |= ALARM_DAY_BIT # Day of week (1-7) + else: + alarm_reg[2] &= ~ALARM_DAY_BIT # Date of month (1-31) + alarm_reg[2] = self.DECtoBCD(date) + + self._spi_write_bytes(DS3234_REGISTER_A2MIN, alarm_reg) + + def alarm1(self, clear:bool): + statusRegister = self._spi_read_byte(DS3234_REGISTER_STATUS) + if (statusRegister & ALARM_1_FLAG_BIT) != 0: # Fixed comparison + if clear: + # Clear the alarm flag by writing 0 to it + statusRegister &= ~ALARM_1_FLAG_BIT + self._spi_write_byte(DS3234_REGISTER_STATUS, statusRegister) + return True + return False + + def alarm2(self, clear:bool): + statusRegister = self._spi_read_byte(DS3234_REGISTER_STATUS) + if (statusRegister & ALARM_2_FLAG_BIT) != 0: # Fixed comparison + if clear: + # Clear the alarm flag by writing 0 to it + statusRegister &= ~ALARM_2_FLAG_BIT + self._spi_write_byte(DS3234_REGISTER_STATUS, statusRegister) + return True + return False + + def enableAlarmInterrupt(self, alarm1=True, alarm2=True): + """Enable the SQW interrupt output on one, or both, alarms""" + control_register = self._spi_read_byte(DS3234_REGISTER_CONTROL) + + # Set INTCN bit to enable alarm interrupts (SQW pin as interrupt) + control_register |= ALARM_INTCN_BIT + + # Enable the specific alarms + if alarm1: + control_register |= (1 << 0) # Set A1IE bit + else: + control_register &= ~(1 << 0) # Clear A1IE bit + + if alarm2: + control_register |= (1 << 1) # Set A2IE bit + else: + control_register &= ~(1 << 1) # Clear A2IE bit + + self._spi_write_byte(DS3234_REGISTER_CONTROL, control_register) + + def writeSQW(self, value): + """Set the SQW pin high, low, or to one of the square wave frequencies""" + control_register = self._spi_read_byte(DS3234_REGISTER_CONTROL) + + control_register &= SQW_CONTROL_MASK # Mask out RS1, RS2 bits (bits 3 and 4) + control_register |= (value << 3) # Add rate bits, shift left 3 + control_register &= ~SQW_ENABLE_BIT # Clear INTCN bit to enable SQW output + self._spi_write_byte(DS3234_REGISTER_CONTROL, control_register) + + def temperature(self): + """Read the DS3234's die-temperature in degrees Celsius""" + temp_register = self._spi_read_bytes(DS3234_REGISTER_TEMPM, 2) + + integer = temp_register[0] + if integer > 127: # Handle negative temperatures (two's complement) + integer = integer - 256 + + fractional = (temp_register[1] >> 6) * 0.25 + return integer + fractional + + def writeToSRAM(self, address, data): + """Write a single byte to SRAM""" + self._spi_write_byte(DS3234_REGISTER_SRAMA, address) + self._spi_write_byte(DS3234_REGISTER_SRAMD, data) + + def writeToSRAMBuffer(self, address, values): + """Write multiple bytes to SRAM""" + self._spi_write_byte(DS3234_REGISTER_SRAMA, address) + self._spi_write_bytes(DS3234_REGISTER_SRAMD, values) + + def writeToSRAMValue(self, address, value, data_type): + """Write a value of specified type to SRAM""" + if data_type == 'uint8': + buf = bytes([value & 0xFF]) + elif data_type == 'uint16': + buf = struct.pack(' Date: Mon, 22 Sep 2025 12:02:28 +0200 Subject: [PATCH 2/5] Add TMP117 temperature sensor --- .../TMP117/TMP117/Examples/tmp117-alarmPin.py | 45 +++ .../TMP117/Examples/tmp117-customConfig.py | 68 ++++ .../TMP117/Examples/tmp117-simpleRead.py | 28 ++ Sensors/TMP117/TMP117/tmp117.py | 339 ++++++++++++++++++ Sensors/TMP117/package.json | 22 ++ 5 files changed, 502 insertions(+) create mode 100644 Sensors/TMP117/TMP117/Examples/tmp117-alarmPin.py create mode 100644 Sensors/TMP117/TMP117/Examples/tmp117-customConfig.py create mode 100644 Sensors/TMP117/TMP117/Examples/tmp117-simpleRead.py create mode 100644 Sensors/TMP117/TMP117/tmp117.py create mode 100644 Sensors/TMP117/package.json diff --git a/Sensors/TMP117/TMP117/Examples/tmp117-alarmPin.py b/Sensors/TMP117/TMP117/Examples/tmp117-alarmPin.py new file mode 100644 index 0000000..0a00cf2 --- /dev/null +++ b/Sensors/TMP117/TMP117/Examples/tmp117-alarmPin.py @@ -0,0 +1,45 @@ +# FILE: tmp117-simplePin.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: Example showing how to detect and configure an interrupt +# on the ALR (alarm) Pin when the LOW or HIGH temperature treshold +# is crossed +# WORKS WITH: Temperature Sensor TMP117 Breakout: www.solde.red/333175 +# LAST UPDATED: 2025-09-22 +from machine import I2C, Pin +import tmp117 +import time + +# Initialize I2C, if you're using our board, the constructor can be left blank, +# as then it will automatically be assigned to the Qwiic connector +i2c = I2C() + +# Create TMP117 instance +sensor = tmp117.TMP117(i2c, addr=0x49) + +# Initialize with default settings +sensor.init() + +# Set alert callback +def alert_callback(pin): + alert_type = sensor.getAlertType() + if alert_type == tmp117.TMP117_ALERT.HIGHALERT: + print("High temperature alert!") + elif alert_type == tmp117.TMP117_ALERT.LOWALERT: + print("Low temperature alert!") + +# Set at what pin the ALR pin of the Breakout is connected to, as well +# as the function that will be called when an interrupt happens on that pin +sensor.setAlertCallback(alert_callback, pin=43) + +# Set the lower and upper temperature tresholds for the interrupt +sensor.setAlertTemperature(20.0, 30.0) # Set alert boundaries + +# Infinite loop +while True: + # Update sensor register values and check for new measurements + sensor.update() + # Read temperature + temperature = sensor.getTemperature() + print(f"Temperature: {temperature:.2f}°C") + # Delay a bit until a new measurement can be made + time.sleep(0.2) \ No newline at end of file diff --git a/Sensors/TMP117/TMP117/Examples/tmp117-customConfig.py b/Sensors/TMP117/TMP117/Examples/tmp117-customConfig.py new file mode 100644 index 0000000..6cf0143 --- /dev/null +++ b/Sensors/TMP117/TMP117/Examples/tmp117-customConfig.py @@ -0,0 +1,68 @@ +# FILE: tmp117-customConfig.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: Example showing how use custom configurations to +# set different measurement modes as well as measurement times +# WORKS WITH: Temperature Sensor TMP117 Breakout: www.solde.red/333175 +# LAST UPDATED: 2025-09-22 + +from machine import I2C, Pin +import time +from tmp117 import TMP117, TMP117_CMODE, TMP117_CONVT, TMP117_AVE + +# Initialize I2C +i2c = I2C() + +# Create TMP117 instance +tmp = TMP117(i2c, addr=0x49) + +def new_temperature(): + """Callback function for new temperature data""" + temperature = tmp.getTemperature() + print("Temperature : {:.2f} °C".format(temperature)) + +# Initialize with callback function +tmp.init(new_temperature) + +# Set continuous measurement mode +tmp.setConvMode(TMP117_CMODE.CONTINUOUS) + +setup_nr = 3 # Select an example setup to see different modes + +if setup_nr == 1: + # Setup 1: C15mS5 + NOAVE = 15.5 mS measurement time + tmp.setConvTime(TMP117_CONVT.C15mS5) + tmp.setAveraging(TMP117_AVE.NOAVE) + print("Setup 1: 15.5ms measurement time, no averaging") + +elif setup_nr == 2: + # Setup 2: C125mS + AVE8 = 125 mS measurement time + tmp.setConvTime(TMP117_CONVT.C125mS) + tmp.setAveraging(TMP117_AVE.AVE8) + print("Setup 2: 125ms measurement time, 8x averaging") + +elif setup_nr == 3: + # Setup 3: C125mS + AVE32 = 500 mS measurement time + tmp.setConvTime(TMP117_CONVT.C125mS) + tmp.setAveraging(TMP117_AVE.AVE32) + print("Setup 3: 500ms measurement time, 32x averaging") + +elif setup_nr == 4: + # Setup 4: C4S + AVE64 = 4000 mS measurement time + tmp.setConvTime(TMP117_CONVT.C4S) + tmp.setAveraging(TMP117_AVE.AVE64) + print("Setup 4: 4000ms measurement time, 64x averaging") + +else: + # Default to setup 1 + tmp.setConvTime(TMP117_CONVT.C15mS5) + tmp.setAveraging(TMP117_AVE.NOAVE) + print("Default setup: 15.5ms measurement time, no averaging") + +# Print current configuration for verification +print("Current configuration:") +tmp.printConfig() + +while True: + """Infinite loop""" + tmp.update() # Update the sensor/read configuration register + time.sleep(0.5) # Small delay to prevent busy waiting \ No newline at end of file diff --git a/Sensors/TMP117/TMP117/Examples/tmp117-simpleRead.py b/Sensors/TMP117/TMP117/Examples/tmp117-simpleRead.py new file mode 100644 index 0000000..49902a7 --- /dev/null +++ b/Sensors/TMP117/TMP117/Examples/tmp117-simpleRead.py @@ -0,0 +1,28 @@ +# FILE: tmp117-simpleRead.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: Example showing how to take a simple temperature +# measurement every 100ms in Celsius +# WORKS WITH: Temperature Sensor TMP117 Breakout: www.solde.red/333175 +# LAST UPDATED: 2025-09-22 + +from machine import I2C, Pin +import tmp117 +import time + +# Initialize I2C +i2c = I2C() + +# Create TMP117 instance +sensor = tmp117.TMP117(i2c, addr=0x49) + +# Initialize with default settings +sensor.init() + +# Infinite loop +while True: + # Update sensor register values and check for new measurements + sensor.update() + # Read temperature + temperature = sensor.getTemperature() + print(f"Temperature: {temperature:.2f}°C") + time.sleep(0.2) \ No newline at end of file diff --git a/Sensors/TMP117/TMP117/tmp117.py b/Sensors/TMP117/TMP117/tmp117.py new file mode 100644 index 0000000..7ec6035 --- /dev/null +++ b/Sensors/TMP117/TMP117/tmp117.py @@ -0,0 +1,339 @@ +# FILE: TMP117.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: A MicroPython module for the TMP117 Temperature Sensor Breakout +# LAST UPDATED: 2025-09-22 +from micropython import const +import machine +import time + +# Register addresses +_TMP117_REG_TEMPERATURE = const(0x00) +_TMP117_REG_CONFIGURATION = const(0x01) +_TMP117_REG_TEMP_HIGH_LIMIT = const(0x02) +_TMP117_REG_TEMP_LOW_LIMIT = const(0x03) +_TMP117_REG_EEPROM_UNLOCK = const(0x04) +_TMP117_REG_EEPROM1 = const(0x05) +_TMP117_REG_EEPROM2 = const(0x06) +_TMP117_REG_EEPROM3 = const(0x08) +_TMP117_REG_TEMPERATURE_OFFSET = const(0x07) +_TMP117_REG_DEVICE_ID = const(0x0F) + +_TMP117_RESOLUTION = 0.0078125 + +# Enums as classes +class TMP117_PMODE: + THERMAL = 0 + ALERT = 1 + DATA = 2 + +class TMP117_CMODE: + CONTINUOUS = 0 + SHUTDOWN = 1 + ONESHOT = 3 + +class TMP117_CONVT: + C15mS5 = 0 + C125mS = 1 + C250mS = 2 + C500mS = 3 + C1S = 4 + C4S = 5 + C8S = 6 + C16S = 7 + +class TMP117_AVE: + NOAVE = 0 + AVE8 = 1 + AVE32 = 2 + AVE64 = 3 + +class TMP117_ALERT: + NOALERT = 0 + HIGHALERT = 1 + LOWALERT = 2 + +class TMP117: + def __init__(self, i2c, addr=0x49): + self.i2c = i2c + self.address = addr + self.alert_pin = None + self.alert_type = TMP117_ALERT.NOALERT + self.newDataCallback = None + + def init(self, newDataCallback=None): + """Initialize in default mode""" + self.setConvMode(TMP117_CMODE.CONTINUOUS) + self.setConvTime(TMP117_CONVT.C125mS) + self.setAveraging(TMP117_AVE.AVE8) + self.setAlertMode(TMP117_PMODE.DATA) + self.setOffsetTemperature(0) + + self.newDataCallback = newDataCallback + + def update(self): + """Read configuration register and handle events""" + return self.readConfig() + + def softReset(self): + """Performs a soft reset""" + reg_value = 1 << 1 + self.writeConfig(reg_value) + + def setAlertMode(self, mode): + """Set alert pin mode""" + reg_value = self.readConfig() + + if mode == TMP117_PMODE.THERMAL: + reg_value |= 1 << 4 # change to thermal mode + reg_value &= ~(1 << 2) # set pin as alert flag + reg_value &= ~(1 << 3) # alert pin low active + elif mode == TMP117_PMODE.ALERT: + reg_value &= ~(1 << 4) # change to alert mode + reg_value &= ~(1 << 2) # set pin as alert flag + reg_value &= ~(1 << 3) # alert pin low active + elif mode == TMP117_PMODE.DATA: + reg_value |= 1 << 2 # set pin as data ready flag + + self.writeConfig(reg_value) + + def setAlertCallback(self, alert_callback, pin): + """Set alert callback function with proper configuration""" + # Configure alert pin with strong pull-up + self.alert_pin = machine.Pin(pin, machine.Pin.IN, machine.Pin.PULL_UP) + + # First, ensure we're in proper alert mode + self.configureAlertMode() + + # Clear any existing alerts + self.clearAlertFlags() + + # Wait for pin to stabilize + time.sleep_ms(100) + + # Only set interrupt if pin is not currently active + if self.alert_pin.value() == 1: # Should be high when no alert + self.alert_pin.irq(trigger=machine.Pin.IRQ_FALLING, handler=alert_callback) + print("Alert interrupt configured") + else: + print("Warning: Alert pin is active during setup!") + # Try to clear and reconfigure + self.clearAlertFlags() + time.sleep_ms(50) + if self.alert_pin.value() == 1: + self.alert_pin.irq(trigger=machine.Pin.IRQ_FALLING, handler=alert_callback) + else: + print("Alert pin still active - check hardware") + + def configureAlertMode(self): + """Ensure proper alert mode configuration""" + reg_value = self.i2cRead2B(_TMP117_REG_CONFIGURATION) + + # Set to alert mode (not thermal mode) + reg_value &= ~(1 << 4) # Clear T/nA bit (0 = Alert mode) + + # Set pin as alert flag (not data ready) + reg_value &= ~(1 << 2) # Clear DR/Alert bit (0 = Alert mode) + + # Set active low polarity (typical for open-drain) + reg_value &= ~(1 << 3) # Clear POL bit (0 = Active Low) + + self.writeConfig(reg_value) + print("Alert mode configured") + + def clearAlertFlags(self): + """Force clear any alert flags""" + reg_value = self.i2cRead2B(_TMP117_REG_CONFIGURATION) + # Clear both alert flags + clear_value = reg_value & ~((1 << 15) | (1 << 14)) + self.i2cWrite2B(_TMP117_REG_CONFIGURATION, clear_value) + + def setAlertTemperature(self, lowtemp, hightemp): + """Set alert temperature boundaries""" + high_temp_value = int(hightemp / _TMP117_RESOLUTION) + low_temp_value = int(lowtemp / _TMP117_RESOLUTION) + + self.i2cWrite2B(_TMP117_REG_TEMP_HIGH_LIMIT, high_temp_value) + self.i2cWrite2B(_TMP117_REG_TEMP_LOW_LIMIT, low_temp_value) + + def setConvMode(self, cmode): + """Set conversion mode""" + reg_value = self.readConfig() + reg_value &= ~((1 << 11) | (1 << 10)) # clear bits + reg_value |= (cmode & 0x03) << 10 # set bits + self.writeConfig(reg_value) + + def setConvTime(self, convtime): + """Set conversion time""" + reg_value = self.readConfig() + reg_value &= ~((1 << 9) | (1 << 8) | (1 << 7)) # clear bits + reg_value |= (convtime & 0x07) << 7 # set bits + self.writeConfig(reg_value) + + def setAveraging(self, ave): + """Set averaging mode""" + reg_value = self.readConfig() + reg_value &= ~((1 << 6) | (1 << 5)) # clear bits + reg_value |= (ave & 0x03) << 5 # set bits + self.writeConfig(reg_value) + + def setOffsetTemperature(self, offset): + """Set offset temperature""" + offset_temp_value = int(offset / _TMP117_RESOLUTION) + self.i2cWrite2B(_TMP117_REG_TEMPERATURE_OFFSET, offset_temp_value) + + def setTargetTemperature(self, target): + """Set target temperature for calibration""" + actual_temp = self.getTemperature() + delta_temp = target - actual_temp + self.setOffsetTemperature(delta_temp) + + def getTemperature(self): + """Get temperature in °C""" + temp = self.i2cRead2B(_TMP117_REG_TEMPERATURE) + # Convert to signed 16-bit integer + if temp & 0x8000: + temp = temp - 0x10000 + return temp * _TMP117_RESOLUTION + + def getDeviceID(self): + """Get Device ID (bits [11:0])""" + raw = self.i2cRead2B(_TMP117_REG_DEVICE_ID) + return raw & 0x0FFF + + def getDeviceRev(self): + """Get Device Revision (bits [15:12])""" + raw = self.i2cRead2B(_TMP117_REG_DEVICE_ID) + return (raw >> 12) & 0x3 + + def getOffsetTemperature(self): + """Get offset temperature in °C""" + temp = self.i2cRead2B(_TMP117_REG_TEMPERATURE_OFFSET) + # Convert to signed 16-bit integer + if temp & 0x8000: + temp = temp - 0x10000 + return temp * _TMP117_RESOLUTION + + def getAlertType(self): + """Get alert type""" + return self.alert_type + + def writeEEPROM(self, data, eeprom_nr): + """Write data to EEPROM""" + if not self.EEPROMisBusy(): + self.unlockEEPROM() + if eeprom_nr == 1: + self.i2cWrite2B(_TMP117_REG_EEPROM1, data) + elif eeprom_nr == 2: + self.i2cWrite2B(_TMP117_REG_EEPROM2, data) + elif eeprom_nr == 3: + self.i2cWrite2B(_TMP117_REG_EEPROM3, data) + else: + print("EEPROM value must be between 1 and 3") + self.lockEEPROM() + else: + print("EEPROM is busy") + + def readEEPROM(self, eeprom_nr): + """Read data from EEPROM""" + if not self.EEPROMisBusy(): + if eeprom_nr == 1: + return self.i2cRead2B(_TMP117_REG_EEPROM1) + elif eeprom_nr == 2: + return self.i2cRead2B(_TMP117_REG_EEPROM2) + elif eeprom_nr == 3: + return self.i2cRead2B(_TMP117_REG_EEPROM3) + else: + print("EEPROM value must be between 1 and 3") + return 0 + else: + print("EEPROM is busy") + return 0 + + def readConfig(self): + """Read configuration register and clear alert flags""" + reg_value = self.i2cRead2B(_TMP117_REG_CONFIGURATION) + data_ready = (reg_value >> 13) & 0x1 + + # Handle data ready callback + if data_ready and self.newDataCallback: + self.newDataCallback() + + # Check for alert flags + high_alert = (reg_value >> 15) & 0x1 + low_alert = (reg_value >> 14) & 0x1 + + # Update alert type + if high_alert: + self.alert_type = TMP117_ALERT.HIGHALERT + elif low_alert: + self.alert_type = TMP117_ALERT.LOWALERT + else: + self.alert_type = TMP117_ALERT.NOALERT + + # Clear alert flags if they are set + if high_alert or low_alert: + # Clear both alert flags while preserving other bits + clear_value = reg_value & ~((1 << 15) | (1 << 14)) + self.i2cWrite2B(_TMP117_REG_CONFIGURATION, clear_value) + + return reg_value + + def printConfig(self): + """Print configuration in readable format""" + reg_value = self.i2cRead2B(_TMP117_REG_CONFIGURATION) + print(f"Configuration: {bin(reg_value)}") + print(f"HIGH alert: {(reg_value >> 15) & 0x1}") + print(f"LOW alert: {(reg_value >> 14) & 0x1}") + print(f"Data ready: {(reg_value >> 13) & 0x1}") + print(f"EEPROM busy: {(reg_value >> 12) & 0x1}") + print(f"MOD[1:0]: {(reg_value >> 10) & 0x3}") + print(f"CONV[2:0]: {(reg_value >> 7) & 0x7}") + print(f"AVG[1:0]: {(reg_value >> 5) & 0x3}") + print(f"T/nA: {(reg_value >> 4) & 0x1}") + print(f"POL: {(reg_value >> 3) & 0x1}") + print(f"DR/Alert: {(reg_value >> 2) & 0x1}") + print(f"Soft_Reset: {(reg_value >> 1) & 0x1}") + + # Private methods + def i2cWrite2B(self, reg, data): + """Write two bytes to I2C device""" + buf = bytearray(3) + buf[0] = reg + buf[1] = (data >> 8) & 0xFF + buf[2] = data & 0xFF + self.i2c.writeto(self.address, buf) + time.sleep_ms(10) + + def i2cRead2B(self, reg): + """Read two bytes from I2C device""" + buf = bytearray(2) + self.i2c.writeto(self.address, bytes([reg])) + self.i2c.readfrom_into(self.address, buf) + return (buf[0] << 8) | buf[1] + + def writeConfig(self, config_data): + """Write configuration to config register""" + if not self.EEPROMisBusy(): + self.unlockEEPROM() + self.i2cWrite2B(_TMP117_REG_CONFIGURATION, config_data) + self.lockEEPROM() + else: + print("EEPROM is busy") + + def lockEEPROM(self): + """Lock EEPROM, write protection""" + code = 0 + code &= ~(1 << 15) + self.i2cWrite2B(_TMP117_REG_EEPROM_UNLOCK, code) + time.sleep_ms(100) + + def unlockEEPROM(self): + """Unlock EEPROM, remove write protection""" + code = 1 << 15 + self.i2cWrite2B(_TMP117_REG_EEPROM_UNLOCK, code) + time.sleep_ms(100) + + def EEPROMisBusy(self): + """Check if EEPROM is busy""" + code = self.i2cRead2B(_TMP117_REG_EEPROM_UNLOCK) + return bool((code >> 14) & 0x01) \ No newline at end of file diff --git a/Sensors/TMP117/package.json b/Sensors/TMP117/package.json new file mode 100644 index 0000000..783d582 --- /dev/null +++ b/Sensors/TMP117/package.json @@ -0,0 +1,22 @@ +{ + "urls": [ + [ + "tmp117.py", + "github:SolderedElectronics/Soldered-MicroPython-Modules/Sensors/TMP117/TMP117/tmp117.py" + ], + [ + "Examples/tmp117-simpleRead.py", + "github:SolderedElectronics/Soldered-MicroPython-Modules/Sensors/TMP117/TMP117/Examples/tmp117-simpleRead.py" + ], + [ + "Examples/tmp117-alarmPin.py", + "github:SolderedElectronics/Soldered-MicroPython-Modules/Sensors/TMP117/TMP117/Examples/tmp117-alarmPin.py" + ], + [ + "Examples/tmp117-customConfig.py", + "github:SolderedElectronics/Soldered-MicroPython-Modules/Sensors/TMP117/TMP117/Examples/tmp117-customConfig.py" + ] + ], + "deps": [], + "version": "0.2" +} \ No newline at end of file From 7f93c23036ea8c43287ffbfae0e1291ef8484626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20=C5=A0imun=20Ku=C4=8Di?= Date: Mon, 22 Sep 2025 12:10:44 +0200 Subject: [PATCH 3/5] update package versioning --- Sensors/TMP117/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sensors/TMP117/package.json b/Sensors/TMP117/package.json index 783d582..91db9b1 100644 --- a/Sensors/TMP117/package.json +++ b/Sensors/TMP117/package.json @@ -18,5 +18,5 @@ ] ], "deps": [], - "version": "0.2" + "version": "1.0" } \ No newline at end of file From 320529fcb6935535a38455318546850ba05d7f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20=C5=A0imun=20Ku=C4=8Di?= Date: Mon, 6 Oct 2025 09:15:27 +0200 Subject: [PATCH 4/5] Add RFID 125kHz module --- .../RFID/RFID/Examples/RFID-I2CExample.py | 25 +++ .../RFID/RFID/Examples/RFID-UARTExample.py | 47 ++++ Communication/RFID/RFID/rfid.py | 206 ++++++++++++++++++ Communication/RFID/package.json | 18 ++ 4 files changed, 296 insertions(+) create mode 100644 Communication/RFID/RFID/Examples/RFID-I2CExample.py create mode 100644 Communication/RFID/RFID/Examples/RFID-UARTExample.py create mode 100644 Communication/RFID/RFID/rfid.py create mode 100644 Communication/RFID/package.json diff --git a/Communication/RFID/RFID/Examples/RFID-I2CExample.py b/Communication/RFID/RFID/Examples/RFID-I2CExample.py new file mode 100644 index 0000000..1b164de --- /dev/null +++ b/Communication/RFID/RFID/Examples/RFID-I2CExample.py @@ -0,0 +1,25 @@ +# FILE: RFID-I2CExample.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: An example showing how to configure I2C communication for the RFID reader as +# well as detecting a scanned tag and printing its raw data +# WORKS WITH: 125kHz RFID tag reader board: www.solde.red/333273 +# LAST UPDATED: 2025-10-06 +# Import needed libraries +from rfid import RFID +from machine import I2C, Pin + +i2c = I2C(0, scl=Pin(22), sda=Pin(21)) +rfid = RFID(i2c=i2c, i2c_address=0x30) + +if rfid.checkHW(): + print("RFID Reader detected") + + while True: + if rfid.available(): + tag_id = rfid.getId() + raw_data = rfid.getRaw() + print(f"Tag ID: {tag_id}") + print("Raw Data: ", end="") + rfid.printHex64(raw_data) +else: + print("Couldn't detect RFID reader") diff --git a/Communication/RFID/RFID/Examples/RFID-UARTExample.py b/Communication/RFID/RFID/Examples/RFID-UARTExample.py new file mode 100644 index 0000000..ed6ba59 --- /dev/null +++ b/Communication/RFID/RFID/Examples/RFID-UARTExample.py @@ -0,0 +1,47 @@ +# FILE: RFID-UARTExample.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: An example showing how to configure UART communication for the RFID reader as +# well as detecting a scanned tag and printing its raw data +# WORKS WITH: 125kHz RFID tag reader board: www.solde.red/333154 +# LAST UPDATED: 2025-10-06 +# Import needed libraries +from rfid import RFID +from machine import I2C, Pin +""" +Create an instance of the RFID object in UART mode +NOTE: Default UART speed is 9600, but communication speed can be changed by changing the position of the DIP +switches on the breakout. + + Switch 1 2 3 + 9600 0 0 0 + 2400 1 0 0 + 4800 0 1 0 + 19200 1 1 0 + 38400 0 0 1 + 57600 1 0 1 + 115200 0 1 1 + 230400 1 1 1 +""" +rfid = RFID(rx_pin=18, tx_pin=19, baud_rate=9600) + +# Check if there is a connection to the board +if rfid.checkHW(): + print("RFID Reader detected") + + # If connection was successfull, wait for an ID tag to be sensed + while True: + # Check if a tag was sensed + if rfid.available(): + # If it was, get its ID and raw data + tag_id = rfid.getId() + raw_data = rfid.getRaw() + # Print out the values + print(f"Tag ID: {tag_id}") + print("Raw Data: ", end="") + rfid.printHex64(raw_data) + + # Remove the data and wait for a new tag + rfid.clear() +else: + # If there isn't, inform the user and quit program + print("Couldn't detect RFID reader") diff --git a/Communication/RFID/RFID/rfid.py b/Communication/RFID/RFID/rfid.py new file mode 100644 index 0000000..a6d7dc7 --- /dev/null +++ b/Communication/RFID/RFID/rfid.py @@ -0,0 +1,206 @@ +# FILE: rfid.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: A MicroPython module for the RFID 125kHz reader board +# LAST UPDATED: 2025-10-06 +from machine import UART, Pin, I2C +import time + +class RFID: + SERIAL_TIMEOUT_MS = 100 + + # I2C Register addresses + REG_AVAILABLE = 0 + REG_TAG_ID = 1 + REG_RAW_DATA = 2 + REG_CLEAR = 3 + + def __init__(self, i2c=None, i2c_address=0x30, rx_pin=None, tx_pin=None, baud_rate=9600): + """ + Initialize RFID reader + Priority: I2C mode if i2c provided, otherwise UART mode + """ + if i2c is not None: + # I2C mode (priority) + self.native = False + self.i2c = i2c + self.address = i2c_address + self.tag_id = 0 + self.rfid_raw = 0 + elif rx_pin is not None and tx_pin is not None: + # UART mode + self.native = True + self.rfid_serial = UART(1, baudrate=baud_rate, rx=Pin(rx_pin), tx=Pin(tx_pin)) + self.rx_pin = rx_pin + self.tx_pin = tx_pin + self.baud_rate = baud_rate + self.tag_id = 0 + self.rfid_raw = 0 + else: + raise ValueError("Either I2C object or RX/TX pins must be provided") + + def checkHW(self): + """Check if hardware is responding""" + if not self.native: + # I2C mode - ping the module on its I2C address + try: + self.i2c.writeto(self.address, b'') + # Clear all previous data from the RFID reader + self.clear() + return True + except OSError: + return False + else: + # UART mode + self.rfid_serial.write("#rfping\n") + time.sleep_ms(15) + + serial_data = self.getSerialData(25, self.SERIAL_TIMEOUT_MS) + if serial_data and "#hello" in serial_data: + return True + return False + + def available(self): + """Check if there is new RFID data available""" + if not self.native: + # I2C mode + try: + # Set register address to REG_AVAILABLE (0) + self.i2c.writeto(self.address, bytes([self.REG_AVAILABLE])) + # Read 1 byte + data = self.i2c.readfrom(self.address, 1) + return bool(data[0]) + except OSError: + return False + else: + # UART mode + available_flag = False + serial_data = self.getSerialData(30, self.SERIAL_TIMEOUT_MS) + + if serial_data: + tag_id_start = serial_data.find('$') + tag_raw_start = serial_data.find('&') + + if tag_id_start != -1 and tag_raw_start != -1: + tag_id_str = serial_data[tag_id_start + 1:tag_raw_start] + raw_str = serial_data[tag_raw_start + 1:] + + try: + self.tag_id = int(tag_id_str) + self.rfid_raw = self.getUint64(raw_str) + + if self.tag_id and self.rfid_raw: + available_flag = True + except ValueError: + pass + + return available_flag + + def getId(self): + """Get the RFID Tag ID number and clear it after reading""" + if not self.native: + # I2C mode + try: + # Set register address to REG_TAG_ID (1) + self.i2c.writeto(self.address, bytes([self.REG_TAG_ID])) + # Read 4 bytes for tag ID + data = self.i2c.readfrom(self.address, 4) + # Convert bytes to uint32 (little endian) + tag_id = int.from_bytes(data, 'little') + return tag_id + except OSError: + return 0 + else: + # UART mode + tag_id = self.tag_id + self.tag_id = 0 + return tag_id + + def getRaw(self): + """Get the RFID RAW data with headers, RAW data, parity bits, etc.""" + if not self.native: + # I2C mode + try: + # Set register address to REG_RAW_DATA (2) + self.i2c.writeto(self.address, bytes([self.REG_RAW_DATA])) + # Read 8 bytes for raw RFID data + data = self.i2c.readfrom(self.address, 8) + # Convert bytes to uint64 (little endian) + rfid_raw = int.from_bytes(data, 'little') + return rfid_raw + except OSError: + return 0 + else: + # UART mode + rfid_raw = self.rfid_raw + self.rfid_raw = 0 + return rfid_raw + + def clear(self): + """Clear all previous data from the RFID reader (I2C mode only)""" + if not self.native: + try: + # Set register address to REG_CLEAR (3) to clear data + self.i2c.writeto(self.address, bytes([self.REG_CLEAR])) + except OSError: + pass + + def getSerialData(self, max_length, timeout_ms): + """Get data from serial with timeout""" + if not self.native: + return None + + timeout = time.ticks_ms() + data = bytearray() + + while time.ticks_diff(time.ticks_ms(), timeout) < timeout_ms: + if self.rfid_serial.any(): + char = self.rfid_serial.read(1) + if char: + data.extend(char) + timeout = time.ticks_ms() # Reset timeout on new data + + if len(data) >= max_length: + break + + return data.decode('utf-8', 'ignore') if data else None + + def getUint64(self, hex_string): + """Convert HEX string to 64-bit integer""" + try: + # Clean the hex string - remove any non-hex characters + hex_string = ''.join(c for c in hex_string if c in '0123456789ABCDEFabcdef') + hex_string = hex_string.upper() + + # Ensure we have exactly 16 characters for 64-bit + # If shorter, pad with zeros; if longer, truncate + if len(hex_string) < 16: + hex_string = hex_string + '0' * (16 - len(hex_string)) + else: + hex_string = hex_string[:16] + + return int(hex_string, 16) + except (ValueError, AttributeError): + return 0 + + def printHex64(self, number): + """Print 64-bit number in HEX format""" + hex_str = '{:016X}'.format(number & 0xFFFFFFFFFFFFFFFF) + print(hex_str) + + def hexToInt(self, char): + """Convert HEX char to integer (0-15)""" + char = char.upper() + if '0' <= char <= '9': + return ord(char) - ord('0') + elif 'A' <= char <= 'F': + return ord(char) - ord('A') + 10 + return 0 + + def intToHex(self, number): + """Convert integer (0-15) to HEX char""" + number &= 0x0F + if 0 <= number <= 9: + return chr(ord('0') + number) + elif 10 <= number <= 15: + return chr(ord('A') + number - 10) + return '0' \ No newline at end of file diff --git a/Communication/RFID/package.json b/Communication/RFID/package.json new file mode 100644 index 0000000..13dc980 --- /dev/null +++ b/Communication/RFID/package.json @@ -0,0 +1,18 @@ +{ + "urls": [ + [ + "rfid.py", + "github:SolderedElectronics/Soldered-MicroPython-Modules/Communication/RFID/RFID/rfid.py" + ], + [ + "Examples/RFID-UARTExample.py", + "github:SolderedElectronics/Soldered-MicroPython-Modules/Communication/RFID/RFID/Examples/RFID-UARTExample.py" + ], + [ + "Examples/RFID-I2CExample.py", + "github:SolderedElectronics/Soldered-MicroPython-Modules/Communication/RFID/RFID/Examples/RFID-I2CExample.py" + ] + ], + "deps": [], + "version": "1.0" +} \ No newline at end of file From c4d3f4d83349a975d570d9ee638ece6724ae5a6e Mon Sep 17 00:00:00 2001 From: JosipKuci Date: Mon, 6 Oct 2025 07:18:38 +0000 Subject: [PATCH 5/5] Apply Ruff formatting (MicroPython-safe) --- .../DS3234/Examples/ds3234-deepSleep.py | 60 ++-- .../DS3234/Examples/ds3234-simpleRTC.py | 59 ++-- .../DS3234/Examples/ds3234-writeSRAM.py | 74 +++-- Actuators/DS3234/DS3234/ds3234.py | 272 +++++++++--------- .../RFID/RFID/Examples/RFID-I2CExample.py | 4 +- .../RFID/RFID/Examples/RFID-UARTExample.py | 7 +- Communication/RFID/RFID/rfid.py | 97 ++++--- .../TMP117/TMP117/Examples/tmp117-alarmPin.py | 10 +- .../TMP117/Examples/tmp117-customConfig.py | 16 +- .../TMP117/Examples/tmp117-simpleRead.py | 6 +- Sensors/TMP117/TMP117/tmp117.py | 122 ++++---- 11 files changed, 401 insertions(+), 326 deletions(-) diff --git a/Actuators/DS3234/DS3234/Examples/ds3234-deepSleep.py b/Actuators/DS3234/DS3234/Examples/ds3234-deepSleep.py index e3503fd..83da831 100644 --- a/Actuators/DS3234/DS3234/Examples/ds3234-deepSleep.py +++ b/Actuators/DS3234/DS3234/Examples/ds3234-deepSleep.py @@ -1,9 +1,9 @@ -# FILE: DS3234-simpleRTC.py +# FILE: DS3234-simpleRTC.py # AUTHOR: Soldered # BRIEF: Example showing how to wake up from deep sleep # Using the INT pin on the breakout board # WORKS WITH: DS3234 RTC Breakout: www.solde.red/333358 -# LAST UPDATED: 2025-09-15 +# LAST UPDATED: 2025-09-15 import machine import time @@ -19,8 +19,15 @@ # Initialize SPI and CS pin for DS3234 # Using VSPI (SPI ID 2) with default ESP32 pins -spi = machine.SPI(2, baudrate=1000000, polarity=1, phase=1, - sck=machine.Pin(18), mosi=machine.Pin(23), miso=machine.Pin(19)) +spi = machine.SPI( + 2, + baudrate=1000000, + polarity=1, + phase=1, + sck=machine.Pin(18), + mosi=machine.Pin(23), + miso=machine.Pin(19), +) cs_pin = machine.Pin(RTC_CS_PIN, machine.Pin.OUT) # Create an instance of the RTC object @@ -33,56 +40,61 @@ if wake_reason == machine.PIN_WAKE: print("Woke up from deep sleep (RTC alarm)!") - + # Read and display current time rtc.update() - - print("Current time: {0}:{1:02d}:{2:02d}".format( - rtc.hour(), rtc.minute(), rtc.second())) - - print("Date: {0}/{1}/{2}".format( - rtc.month(), rtc.date(), rtc.year() + 2000)) - + + print( + "Current time: {0}:{1:02d}:{2:02d}".format( + rtc.hour(), rtc.minute(), rtc.second() + ) + ) + + print("Date: {0}/{1}/{2}".format(rtc.month(), rtc.date(), rtc.year() + 2000)) + # Clear alarm flags if rtc.alarm1(clear=True): print("Cleared Alarm 1 flag") if rtc.alarm2(clear=True): print("Cleared Alarm 2 flag") - + else: # First boot - set up RTC and configure alarm print("First boot - setting up RTC") - + # Clear any existing alarm flags first rtc.alarm1(clear=True) rtc.alarm2(clear=True) - + # Set RTC time (use autoTime or set manually) rtc.autoTime() - + # Display current time rtc.update() - print("Current RTC time: {0}:{1:02d}:{2:02d}".format( - rtc.hour(), rtc.minute(), rtc.second())) - + print( + "Current RTC time: {0}:{1:02d}:{2:02d}".format( + rtc.hour(), rtc.minute(), rtc.second() + ) + ) + # Configure alarm to trigger in 30 seconds from now current_second = rtc.second() alarm_second = (current_second + 30) % 60 alarm_minute = rtc.minute() + ((current_second + 30) // 60) - + if alarm_minute >= 60: alarm_minute %= 60 - + # Set alarm 1 to trigger at specific second (ignore minutes, hours, date) # 255 = "don't care" for that field rtc.setAlarm1(second=alarm_second, minute=255, hour=255, date=255, day=False) - + # Enable alarm 1 interrupt - THIS IS CRITICAL! rtc.enableAlarmInterrupt(alarm1=True, alarm2=False) - + # Verify alarm is set correctly print("Alarm set to trigger at second:", alarm_second) - + # Configure the interrupt pin as input with pullup wakeup_pin = machine.Pin(WAKEUP_PIN, machine.Pin.IN, machine.Pin.PULL_UP) diff --git a/Actuators/DS3234/DS3234/Examples/ds3234-simpleRTC.py b/Actuators/DS3234/DS3234/Examples/ds3234-simpleRTC.py index 0c39f5d..9bee22a 100644 --- a/Actuators/DS3234/DS3234/Examples/ds3234-simpleRTC.py +++ b/Actuators/DS3234/DS3234/Examples/ds3234-simpleRTC.py @@ -1,9 +1,9 @@ -# FILE: DS3234-simpleRTC.py +# FILE: DS3234-simpleRTC.py # AUTHOR: Soldered # BRIEF: Example showing how to Initialize the DS3234 RTC -# and set the time and alarms +# and set the time and alarms # WORKS WITH: DS3234 RTC Breakout: www.solde.red/333358 -# LAST UPDATED: 2025-09-15 +# LAST UPDATED: 2025-09-15 import machine import time @@ -17,8 +17,16 @@ # HSPI: SCLK=14, MOSI=13, MISO=12 # VSPI: SCLK=18, MOSI=23, MISO=19 -# Using HSPI -spi = machine.SPI(1, baudrate=1000000, polarity=1, phase=1, sck=machine.Pin(18), mosi=machine.Pin(23), miso=machine.Pin(19)) +# Using HSPI +spi = machine.SPI( + 1, + baudrate=1000000, + polarity=1, + phase=1, + sck=machine.Pin(18), + mosi=machine.Pin(23), + miso=machine.Pin(19), +) cs_pin = machine.Pin(DS3234_CS_PIN, machine.Pin.OUT) # Create an instance of the RTC object @@ -62,20 +70,21 @@ print("RTC initialized and alarms set") + def print_time(): # Print hour print(str(rtc.hour()) + ":", end="") - + # Print minute with leading zero if needed if rtc.minute() < 10: print("0", end="") print(str(rtc.minute()) + ":", end="") - + # Print second with leading zero if needed if rtc.second() < 10: print("0", end="") print(str(rtc.second()), end="") - + # If we're in 12-hour mode if rtc.is12hour(): # Use rtc.pm() to read the AM/PM state of the hour @@ -83,50 +92,54 @@ def print_time(): print(" PM", end="") # Returns true if PM else: print(" AM", end="") - + print(" | ", end="") - + # Few options for printing the day, pick one: print(rtc.dayStr(), end="") # Print day string # print(rtc.dayChar(), end="") # Print day character # print(rtc.day(), end="") # Print day integer (1-7, Sun-Sat) - + print(" - ", end="") - + # Print date in USA format (MM/DD/YYYY) or international (DD/MM/YYYY) PRINT_USA_DATE = True # Change to False for international format - + if PRINT_USA_DATE: - print(str(rtc.month()) + "/" + str(rtc.date()) + "/", end="") # Print month/date + print( + str(rtc.month()) + "/" + str(rtc.date()) + "/", end="" + ) # Print month/date else: - print(str(rtc.date()) + "/" + str(rtc.month()) + "/", end="") # Print date/month - + print( + str(rtc.date()) + "/" + str(rtc.month()) + "/", end="" + ) # Print date/month + print(str(rtc.year())) # Print year + last_second = -1 - + while True: rtc.update() - + if rtc.second() != last_second: # If the second has changed print_time() # Print the new time last_second = rtc.second() # Update last_second value - + # Check for alarm interrupts # If using interrupt pin (uncomment if needed): # if interrupt_pin.value() == 0: # Interrupt pin is active-low # Check rtc.alarm1() to see if alarm 1 triggered the interrupt - + if rtc.alarm1(clear=True) == True: # Clear the alarm flag print("ALARM 1!") # Re-set the alarm for when s=30: rtc.setAlarm1(30) - + # Check rtc.alarm2() to see if alarm 2 triggered the interrupt if rtc.alarm2(clear=True) == True: # Clear the alarm flag print("ALARM 2!") # Re-set the alarm for when m increments by 1 rtc.setAlarm2(rtc.minute() + 1) - - + time.sleep(0.1) # Small delay to prevent busy waiting diff --git a/Actuators/DS3234/DS3234/Examples/ds3234-writeSRAM.py b/Actuators/DS3234/DS3234/Examples/ds3234-writeSRAM.py index 64c3e76..2b74515 100644 --- a/Actuators/DS3234/DS3234/Examples/ds3234-writeSRAM.py +++ b/Actuators/DS3234/DS3234/Examples/ds3234-writeSRAM.py @@ -1,9 +1,9 @@ -# FILE: DS3234-writeSRAM.py +# FILE: DS3234-writeSRAM.py # AUTHOR: Josip Šimun Kuči @ Soldered -# BRIEF: Example showing how to write and retrieve +# BRIEF: Example showing how to write and retrieve # data from RTC SRAM # WORKS WITH: DS3234 RTC Breakout: www.solde.red/333358 -# LAST UPDATED: 2025-09-15 +# LAST UPDATED: 2025-09-15 import machine import time @@ -15,8 +15,15 @@ # Initialize SPI and CS pin # Using VSPI (SPI ID 2) with default ESP32 pins -spi = machine.SPI(2, baudrate=1000000, polarity=1, phase=1, - sck=machine.Pin(18), mosi=machine.Pin(23), miso=machine.Pin(19)) +spi = machine.SPI( + 2, + baudrate=1000000, + polarity=1, + phase=1, + sck=machine.Pin(18), + mosi=machine.Pin(23), + miso=machine.Pin(19), +) cs_pin = machine.Pin(DS3234_CS_PIN, machine.Pin.OUT) # Create an instance of the RTC object @@ -36,7 +43,11 @@ # Choose some data to store in SRAM data_byte = 42 -print("Writing a byte with value", data_byte, "to memory address 0x{:02X}.".format(sram_address)) +print( + "Writing a byte with value", + data_byte, + "to memory address 0x{:02X}.".format(sram_address), +) # Do the writing here... # Note: If you have battery support on your RTC module, this value @@ -46,7 +57,11 @@ # Now we read the value back from SRAM read_back_byte = rtc.readFromSRAM(sram_address) -print("We have read back a byte with value", read_back_byte, "from memory address 0x{:02X}.".format(sram_address)) +print( + "We have read back a byte with value", + read_back_byte, + "from memory address 0x{:02X}.".format(sram_address), +) """ Write and read several bytes to/from an array """ @@ -56,7 +71,11 @@ # Get the length of the array to write length = len(write_buffer) -print("Writing", length, "byte(s) to SRAM, starting at address 0x{:02X}.".format(sram_address)) +print( + "Writing", + length, + "byte(s) to SRAM, starting at address 0x{:02X}.".format(sram_address), +) print("Values to write:", " ".join(str(b) for b in write_buffer)) # Now we do the writing @@ -68,8 +87,10 @@ # Now read back the stored data into read_buffer read_data = rtc.readFromSRAMBuffer(sram_address, length) -print("Data we have read back starting from SRAM address 0x{:02X}:".format(sram_address), - " ".join(str(b) for b in read_data)) +print( + "Data we have read back starting from SRAM address 0x{:02X}:".format(sram_address), + " ".join(str(b) for b in read_data), +) """ Write and read other data types directly to/from SRAM """ @@ -88,44 +109,55 @@ uint16_data = 32769 # write uint16_t to SRAM -print("Writing a uint16_t with value", uint16_data, "to memory address 0x{:02X}.".format(sram_address)) +print( + "Writing a uint16_t with value", + uint16_data, + "to memory address 0x{:02X}.".format(sram_address), +) -rtc.writeToSRAMValue(sram_address, uint16_data, 'uint16') +rtc.writeToSRAMValue(sram_address, uint16_data, "uint16") # And now read the value back: -read_back_uint16 = rtc.readFromSRAMValue(sram_address, 'uint16') +read_back_uint16 = rtc.readFromSRAMValue(sram_address, "uint16") -print("We have read back a uint16_t with value", read_back_uint16, "from memory address 0x{:02X}.".format(sram_address)) +print( + "We have read back a uint16_t with value", + read_back_uint16, + "from memory address 0x{:02X}.".format(sram_address), +) print("Written and read uint16_t values are equal:", uint16_data == read_back_uint16) # Example 2: Using a signed integer this time int32_data = -128653 -rtc.writeToSRAMValue(sram_address, int32_data, 'int32') +rtc.writeToSRAMValue(sram_address, int32_data, "int32") -read_back_int32 = rtc.readFromSRAMValue(sram_address, 'int32') +read_back_int32 = rtc.readFromSRAMValue(sram_address, "int32") print("Written and read int32_t values are equal:", int32_data == read_back_int32) # Example 3: Using a floating point float_data = 3.14159265358979 # the number pi -rtc.writeToSRAMValue(sram_address, float_data, 'float') +rtc.writeToSRAMValue(sram_address, float_data, "float") -read_back_float = rtc.readFromSRAMValue(sram_address, 'float') +read_back_float = rtc.readFromSRAMValue(sram_address, "float") -print("Written and read float values are equal:", abs(float_data - read_back_float) < 0.0001) +print( + "Written and read float values are equal:", + abs(float_data - read_back_float) < 0.0001, +) # Example 4: Using a string (custom implementation) string_data = "Hello RTC!" string_address = sram_address + 20 # Different address to avoid overlap # Convert string to bytes and write -string_bytes = string_data.encode('utf-8') +string_bytes = string_data.encode("utf-8") rtc.writeToSRAMBuffer(string_address, string_bytes) # Read back and convert to string read_string_bytes = rtc.readFromSRAMBuffer(string_address, len(string_bytes)) -read_string = read_string_bytes.decode('utf-8') +read_string = read_string_bytes.decode("utf-8") print("Original string:", string_data) print("Read back string:", read_string) diff --git a/Actuators/DS3234/DS3234/ds3234.py b/Actuators/DS3234/DS3234/ds3234.py index 926efe9..d29bd9a 100644 --- a/Actuators/DS3234/DS3234/ds3234.py +++ b/Actuators/DS3234/DS3234/ds3234.py @@ -1,7 +1,7 @@ -# FILE: ds3234.py +# FILE: ds3234.py # AUTHOR: Josip Šimun Kuči @ Soldered -# BRIEF: Module for the Soldered DS3234 RTC Breakout board -# LAST UPDATED: 2025-09-15 +# BRIEF: Module for the Soldered DS3234 RTC Breakout board +# LAST UPDATED: 2025-09-15 import machine import time @@ -79,10 +79,10 @@ "Wednesday", "Thursday", "Friday", - "Saturday" + "Saturday", ] -dayIntToChar = ['U', 'M', 'T', 'W', 'R', 'F', 'S'] +dayIntToChar = ["U", "M", "T", "W", "R", "F", "S"] class DS3234: @@ -91,11 +91,11 @@ def __init__(self, spi, cs_pin): self.cs_pin = cs_pin self._time = [0] * TIME_ARRAY_LENGTH self._pm = False - + # Initialize CS pin self.cs_pin.init(machine.Pin.OUT) self.cs_pin.value(1) - + def _spi_write_bytes(self, reg, values): """Write bytes to SPI device, incrementing from a register""" write_reg = reg | 0x80 @@ -103,14 +103,14 @@ def _spi_write_bytes(self, reg, values): self.spi.write(bytearray([write_reg])) self.spi.write(bytearray(values)) self.cs_pin.value(1) - + def _spi_write_byte(self, reg, value): """Write a byte to an SPI device's register""" write_reg = reg | 0x80 self.cs_pin.value(0) self.spi.write(bytearray([write_reg, value])) self.cs_pin.value(1) - + def _spi_read_byte(self, reg): """Read a byte from an SPI device's register""" self.cs_pin.value(0) @@ -118,7 +118,7 @@ def _spi_read_byte(self, reg): value = self.spi.read(1)[0] self.cs_pin.value(1) return value - + def _spi_read_bytes(self, reg, length): """Read bytes from an SPI device, incrementing from a register""" self.cs_pin.value(0) @@ -126,17 +126,17 @@ def _spi_read_bytes(self, reg, length): values = self.spi.read(length) self.cs_pin.value(1) return values - + @staticmethod def BCDtoDEC(val): """Convert binary-coded decimal (BCD) to decimal""" return ((val // 0x10) * 10) + (val % 0x10) - + @staticmethod def DECtoBCD(val): """Convert decimal to binary-coded decimal (BCD)""" return ((val // 10) * 0x10) + (val % 10) - + def setTime(self, sec, min, hour, day, date, month, year): """Set time and date/day registers of DS3234""" self._time[TIME_SECONDS] = self.DECtoBCD(sec) @@ -146,9 +146,9 @@ def setTime(self, sec, min, hour, day, date, month, year): self._time[TIME_DATE] = self.DECtoBCD(date) self._time[TIME_MONTH] = self.DECtoBCD(month) self._time[TIME_YEAR] = self.DECtoBCD(year) - + self._spi_write_bytes(DS3234_REGISTER_BASE, self._time) - + def setTime12h(self, sec, min, hour12, pm, day, date, month, year): """Set time and date/day registers of DS3234 in 12-hour format""" self._time[TIME_SECONDS] = self.DECtoBCD(sec) @@ -161,34 +161,36 @@ def setTime12h(self, sec, min, hour12, pm, day, date, month, year): self._time[TIME_DATE] = self.DECtoBCD(date) self._time[TIME_MONTH] = self.DECtoBCD(month) self._time[TIME_YEAR] = self.DECtoBCD(year) - + self._spi_write_bytes(DS3234_REGISTER_BASE, self._time) - + def setTimeArray(self, time_array): """Set time and date/day registers of DS3234 (using data array)""" if len(time_array) != TIME_ARRAY_LENGTH: return - + self._spi_write_bytes(DS3234_REGISTER_BASE, time_array) - + def autoTime(self): """Fill DS3234 time registers with current time/date""" # Get current time from MicroPython's RTC rtc = machine.RTC() now = rtc.datetime() - + # Format: (year, month, day, weekday, hour, minute, second, microsecond) year = now[0] % 100 # Convert to 2-digit year month = now[1] day = now[2] - weekday = now[3] + 1 # MicroPython weekday: 0-6 for Monday-Sunday, DS3234: 1-7 for Sunday-Saturday + weekday = ( + now[3] + 1 + ) # MicroPython weekday: 0-6 for Monday-Sunday, DS3234: 1-7 for Sunday-Saturday if weekday > 7: weekday = 1 - + hour = now[4] minute = now[5] second = now[6] - + # Convert to 12-hour format if needed if self.is12hour(): pm_bit = 0 @@ -199,13 +201,13 @@ def autoTime(self): pm_bit = TWELVE_HOUR_PM if hour >= 13: hour -= 12 - + hour = self.DECtoBCD(hour) hour |= pm_bit hour |= TWELVE_HOUR_MODE else: hour = self.DECtoBCD(hour) - + self._time[TIME_SECONDS] = self.DECtoBCD(second) self._time[TIME_MINUTES] = self.DECtoBCD(minute) self._time[TIME_HOURS] = hour @@ -213,146 +215,146 @@ def autoTime(self): self._time[TIME_DATE] = self.DECtoBCD(day) self._time[TIME_YEAR] = self.DECtoBCD(year) self._time[TIME_DAY] = self.DECtoBCD(weekday) - + self._spi_write_bytes(DS3234_REGISTER_BASE, self._time) - + def update(self): """Read all time/date registers and update the _time array""" rtc_reads = self._spi_read_bytes(DS3234_REGISTER_BASE, TIME_ARRAY_LENGTH) - + for i in range(TIME_ARRAY_LENGTH): self._time[i] = rtc_reads[i] - + if self._time[TIME_HOURS] & TWELVE_HOUR_MODE: if self._time[TIME_HOURS] & TWELVE_HOUR_PM: self._pm = True else: self._pm = False - + self._time[TIME_HOURS] &= 0x1F # Mask out 24-hour bit, am/pm from hours else: self._time[TIME_HOURS] &= 0x3F # Mask out 24-hour bit from hours - + def second(self): return self.BCDtoDEC(self._time[TIME_SECONDS]) - + def minute(self): return self.BCDtoDEC(self._time[TIME_MINUTES]) - + def hour(self): return self.BCDtoDEC(self._time[TIME_HOURS]) - + def day(self): return self.BCDtoDEC(self._time[TIME_DAY]) - + def dayChar(self): day_val = self.BCDtoDEC(self._time[TIME_DAY]) if 1 <= day_val <= 7: return dayIntToChar[day_val - 1] - return '?' # Or handle error appropriately + return "?" # Or handle error appropriately def dayStr(self): day_val = self.BCDtoDEC(self._time[TIME_DAY]) if 1 <= day_val <= 7: return dayIntToStr[day_val - 1] return "Unknown" # Or handle error appropriately - + def date(self): return self.BCDtoDEC(self._time[TIME_DATE]) - + def month(self): return self.BCDtoDEC(self._time[TIME_MONTH]) - + def year(self): return self.BCDtoDEC(self._time[TIME_YEAR]) - + def getSecond(self): self._time[TIME_SECONDS] = self._spi_read_byte(DS3234_REGISTER_SECONDS) return self.BCDtoDEC(self._time[TIME_SECONDS]) - + def getMinute(self): self._time[TIME_MINUTES] = self._spi_read_byte(DS3234_REGISTER_MINUTES) return self.BCDtoDEC(self._time[TIME_MINUTES]) - + def getHour(self): hour_register = self._spi_read_byte(DS3234_REGISTER_HOURS) - + if hour_register & TWELVE_HOUR_MODE: hour_register &= 0x1F # Mask out am/pm, 24-hour bit self._time[TIME_HOURS] = hour_register - + return self.BCDtoDEC(self._time[TIME_HOURS]) - + def getDay(self): self._time[TIME_DAY] = self._spi_read_byte(DS3234_REGISTER_DAY) return self.BCDtoDEC(self._time[TIME_DAY]) - + def getDate(self): self._time[TIME_DATE] = self._spi_read_byte(DS3234_REGISTER_DATE) return self.BCDtoDEC(self._time[TIME_DATE]) - + def getMonth(self): self._time[TIME_MONTH] = self._spi_read_byte(DS3234_REGISTER_MONTH) self._time[TIME_MONTH] &= 0x7F # Mask out century bit return self.BCDtoDEC(self._time[TIME_MONTH]) - + def getYear(self): self._time[TIME_YEAR] = self._spi_read_byte(DS3234_REGISTER_YEAR) return self.BCDtoDEC(self._time[TIME_YEAR]) - + def setSecond(self, s): if s <= 59: _s = self.DECtoBCD(s) self._spi_write_byte(DS3234_REGISTER_SECONDS, _s) - + def setMinute(self, m): if m <= 59: _m = self.DECtoBCD(m) self._spi_write_byte(DS3234_REGISTER_MINUTES, _m) - + def setHour(self, h): if h <= 23: _h = self.DECtoBCD(h) self._spi_write_byte(DS3234_REGISTER_HOURS, _h) - + def setDay(self, d): if 1 <= d <= 7: _d = self.DECtoBCD(d) self._spi_write_byte(DS3234_REGISTER_DAY, _d) - + def setDate(self, d): if d <= 31: _d = self.DECtoBCD(d) self._spi_write_byte(DS3234_REGISTER_DATE, _d) - + def setMonth(self, mo): if 1 <= mo <= 12: _mo = self.DECtoBCD(mo) self._spi_write_byte(DS3234_REGISTER_MONTH, _mo) - + def setYear(self, y): if y <= 99: _y = self.DECtoBCD(y) self._spi_write_byte(DS3234_REGISTER_YEAR, _y) - + def set12hour(self, enable12=True): if enable12: self.set24hour(False) else: self.set24hour(True) - + def set24hour(self, enable24=True): hour_register = self._spi_read_byte(DS3234_REGISTER_HOURS) - + hour12 = hour_register & TWELVE_HOUR_MODE if (hour12 and not enable24) or (not hour12 and enable24): return - + if enable24: old_hour = hour_register & 0x1F # Mask out am/pm and 12-hour mode old_hour = self.BCDtoDEC(old_hour) # Convert to decimal new_hour = old_hour - + hour_pm = hour_register & TWELVE_HOUR_PM if hour_pm and old_hour >= 1: new_hour += 12 @@ -363,62 +365,62 @@ def set24hour(self, enable24=True): old_hour = hour_register & 0x3F # Mask out am/pm and 12-hour mode old_hour = self.BCDtoDEC(old_hour) # Convert to decimal new_hour = old_hour - + if old_hour == 0: new_hour = 12 elif old_hour >= 13: new_hour -= 12 - + new_hour = self.DECtoBCD(new_hour) new_hour |= TWELVE_HOUR_MODE # Set bit 6 to set 12-hour mode if old_hour >= 12: new_hour |= TWELVE_HOUR_PM # Set PM bit if necessary - + self._spi_write_byte(DS3234_REGISTER_HOURS, new_hour) - + def is12hour(self): hour_register = self._spi_read_byte(DS3234_REGISTER_HOURS) return bool(hour_register & TWELVE_HOUR_MODE) - + def pm(self): hour_register = self._spi_read_byte(DS3234_REGISTER_HOURS) return bool(hour_register & TWELVE_HOUR_PM) - + def enable(self): control_register = self._spi_read_byte(DS3234_REGISTER_CONTROL) control_register &= ~(1 << 7) self._spi_write_byte(DS3234_REGISTER_CONTROL, control_register) - + def disable(self): control_register = self._spi_read_byte(DS3234_REGISTER_CONTROL) - control_register |= (1 << 7) + control_register |= 1 << 7 self._spi_write_byte(DS3234_REGISTER_CONTROL, control_register) - + def setAlarm1(self, second=255, minute=255, hour=255, date=255, day=False): # Read current alarm settings alarm_reg = list(self._spi_read_bytes(DS3234_REGISTER_A1SEC, 4)) - + # Set seconds if second == 255: alarm_reg[0] |= ALARM_MODE_BIT # Don't care about seconds else: alarm_reg[0] &= ~ALARM_MODE_BIT # Match specific seconds alarm_reg[0] = self.DECtoBCD(second) - + # Set minutes if minute == 255: alarm_reg[1] |= ALARM_MODE_BIT # Don't care about minutes else: alarm_reg[1] &= ~ALARM_MODE_BIT # Match specific minutes alarm_reg[1] = self.DECtoBCD(minute) - + # Set hours if hour == 255: alarm_reg[2] |= ALARM_MODE_BIT # Don't care about hours else: alarm_reg[2] &= ~ALARM_MODE_BIT # Match specific hours alarm_reg[2] = self.DECtoBCD(hour) - + # Set day/date if date == 255: alarm_reg[3] |= ALARM_MODE_BIT # Don't care about day/date @@ -429,27 +431,27 @@ def setAlarm1(self, second=255, minute=255, hour=255, date=255, day=False): else: alarm_reg[3] &= ~ALARM_DAY_BIT # Date of month (1-31) alarm_reg[3] = self.DECtoBCD(date) - + self._spi_write_bytes(DS3234_REGISTER_A1SEC, alarm_reg) def setAlarm2(self, minute=255, hour=255, date=255, day=False): # Read current alarm settings (Alarm2 has no seconds) alarm_reg = list(self._spi_read_bytes(DS3234_REGISTER_A2MIN, 3)) - + # Set minutes if minute == 255: alarm_reg[0] |= ALARM_MODE_BIT # Don't care about minutes else: alarm_reg[0] &= ~ALARM_MODE_BIT # Match specific minutes alarm_reg[0] = self.DECtoBCD(minute) - + # Set hours if hour == 255: alarm_reg[1] |= ALARM_MODE_BIT # Don't care about hours else: alarm_reg[1] &= ~ALARM_MODE_BIT # Match specific hours alarm_reg[1] = self.DECtoBCD(hour) - + # Set day/date if date == 255: alarm_reg[2] |= ALARM_MODE_BIT # Don't care about day/date @@ -460,10 +462,10 @@ def setAlarm2(self, minute=255, hour=255, date=255, day=False): else: alarm_reg[2] &= ~ALARM_DAY_BIT # Date of month (1-31) alarm_reg[2] = self.DECtoBCD(date) - + self._spi_write_bytes(DS3234_REGISTER_A2MIN, alarm_reg) - - def alarm1(self, clear:bool): + + def alarm1(self, clear: bool): statusRegister = self._spi_read_byte(DS3234_REGISTER_STATUS) if (statusRegister & ALARM_1_FLAG_BIT) != 0: # Fixed comparison if clear: @@ -473,7 +475,7 @@ def alarm1(self, clear:bool): return True return False - def alarm2(self, clear:bool): + def alarm2(self, clear: bool): statusRegister = self._spi_read_byte(DS3234_REGISTER_STATUS) if (statusRegister & ALARM_2_FLAG_BIT) != 0: # Fixed comparison if clear: @@ -486,43 +488,43 @@ def alarm2(self, clear:bool): def enableAlarmInterrupt(self, alarm1=True, alarm2=True): """Enable the SQW interrupt output on one, or both, alarms""" control_register = self._spi_read_byte(DS3234_REGISTER_CONTROL) - + # Set INTCN bit to enable alarm interrupts (SQW pin as interrupt) control_register |= ALARM_INTCN_BIT - + # Enable the specific alarms if alarm1: - control_register |= (1 << 0) # Set A1IE bit + control_register |= 1 << 0 # Set A1IE bit else: control_register &= ~(1 << 0) # Clear A1IE bit - + if alarm2: - control_register |= (1 << 1) # Set A2IE bit + control_register |= 1 << 1 # Set A2IE bit else: control_register &= ~(1 << 1) # Clear A2IE bit - + self._spi_write_byte(DS3234_REGISTER_CONTROL, control_register) def writeSQW(self, value): """Set the SQW pin high, low, or to one of the square wave frequencies""" control_register = self._spi_read_byte(DS3234_REGISTER_CONTROL) - + control_register &= SQW_CONTROL_MASK # Mask out RS1, RS2 bits (bits 3 and 4) - control_register |= (value << 3) # Add rate bits, shift left 3 - control_register &= ~SQW_ENABLE_BIT # Clear INTCN bit to enable SQW output + control_register |= value << 3 # Add rate bits, shift left 3 + control_register &= ~SQW_ENABLE_BIT # Clear INTCN bit to enable SQW output self._spi_write_byte(DS3234_REGISTER_CONTROL, control_register) def temperature(self): """Read the DS3234's die-temperature in degrees Celsius""" temp_register = self._spi_read_bytes(DS3234_REGISTER_TEMPM, 2) - + integer = temp_register[0] if integer > 127: # Handle negative temperatures (two's complement) integer = integer - 256 - + fractional = (temp_register[1] >> 6) * 0.25 return integer + fractional - + def writeToSRAM(self, address, data): """Write a single byte to SRAM""" self._spi_write_byte(DS3234_REGISTER_SRAMA, address) @@ -535,29 +537,29 @@ def writeToSRAMBuffer(self, address, values): def writeToSRAMValue(self, address, value, data_type): """Write a value of specified type to SRAM""" - if data_type == 'uint8': + if data_type == "uint8": buf = bytes([value & 0xFF]) - elif data_type == 'uint16': - buf = struct.pack('= max_length: break - - return data.decode('utf-8', 'ignore') if data else None - + + return data.decode("utf-8", "ignore") if data else None + def getUint64(self, hex_string): """Convert HEX string to 64-bit integer""" try: # Clean the hex string - remove any non-hex characters - hex_string = ''.join(c for c in hex_string if c in '0123456789ABCDEFabcdef') + hex_string = "".join(c for c in hex_string if c in "0123456789ABCDEFabcdef") hex_string = hex_string.upper() - + # Ensure we have exactly 16 characters for 64-bit # If shorter, pad with zeros; if longer, truncate if len(hex_string) < 16: - hex_string = hex_string + '0' * (16 - len(hex_string)) + hex_string = hex_string + "0" * (16 - len(hex_string)) else: hex_string = hex_string[:16] - + return int(hex_string, 16) except (ValueError, AttributeError): return 0 - + def printHex64(self, number): """Print 64-bit number in HEX format""" - hex_str = '{:016X}'.format(number & 0xFFFFFFFFFFFFFFFF) + hex_str = "{:016X}".format(number & 0xFFFFFFFFFFFFFFFF) print(hex_str) - + def hexToInt(self, char): """Convert HEX char to integer (0-15)""" char = char.upper() - if '0' <= char <= '9': - return ord(char) - ord('0') - elif 'A' <= char <= 'F': - return ord(char) - ord('A') + 10 + if "0" <= char <= "9": + return ord(char) - ord("0") + elif "A" <= char <= "F": + return ord(char) - ord("A") + 10 return 0 - + def intToHex(self, number): """Convert integer (0-15) to HEX char""" number &= 0x0F if 0 <= number <= 9: - return chr(ord('0') + number) + return chr(ord("0") + number) elif 10 <= number <= 15: - return chr(ord('A') + number - 10) - return '0' \ No newline at end of file + return chr(ord("A") + number - 10) + return "0" diff --git a/Sensors/TMP117/TMP117/Examples/tmp117-alarmPin.py b/Sensors/TMP117/TMP117/Examples/tmp117-alarmPin.py index 0a00cf2..ede7747 100644 --- a/Sensors/TMP117/TMP117/Examples/tmp117-alarmPin.py +++ b/Sensors/TMP117/TMP117/Examples/tmp117-alarmPin.py @@ -1,10 +1,10 @@ -# FILE: tmp117-simplePin.py +# FILE: tmp117-simplePin.py # AUTHOR: Josip Šimun Kuči @ Soldered # BRIEF: Example showing how to detect and configure an interrupt -# on the ALR (alarm) Pin when the LOW or HIGH temperature treshold +# on the ALR (alarm) Pin when the LOW or HIGH temperature treshold # is crossed # WORKS WITH: Temperature Sensor TMP117 Breakout: www.solde.red/333175 -# LAST UPDATED: 2025-09-22 +# LAST UPDATED: 2025-09-22 from machine import I2C, Pin import tmp117 import time @@ -19,6 +19,7 @@ # Initialize with default settings sensor.init() + # Set alert callback def alert_callback(pin): alert_type = sensor.getAlertType() @@ -27,6 +28,7 @@ def alert_callback(pin): elif alert_type == tmp117.TMP117_ALERT.LOWALERT: print("Low temperature alert!") + # Set at what pin the ALR pin of the Breakout is connected to, as well # as the function that will be called when an interrupt happens on that pin sensor.setAlertCallback(alert_callback, pin=43) @@ -42,4 +44,4 @@ def alert_callback(pin): temperature = sensor.getTemperature() print(f"Temperature: {temperature:.2f}°C") # Delay a bit until a new measurement can be made - time.sleep(0.2) \ No newline at end of file + time.sleep(0.2) diff --git a/Sensors/TMP117/TMP117/Examples/tmp117-customConfig.py b/Sensors/TMP117/TMP117/Examples/tmp117-customConfig.py index 6cf0143..6dbdc2a 100644 --- a/Sensors/TMP117/TMP117/Examples/tmp117-customConfig.py +++ b/Sensors/TMP117/TMP117/Examples/tmp117-customConfig.py @@ -1,9 +1,9 @@ -# FILE: tmp117-customConfig.py +# FILE: tmp117-customConfig.py # AUTHOR: Josip Šimun Kuči @ Soldered # BRIEF: Example showing how use custom configurations to # set different measurement modes as well as measurement times # WORKS WITH: Temperature Sensor TMP117 Breakout: www.solde.red/333175 -# LAST UPDATED: 2025-09-22 +# LAST UPDATED: 2025-09-22 from machine import I2C, Pin import time @@ -15,11 +15,13 @@ # Create TMP117 instance tmp = TMP117(i2c, addr=0x49) + def new_temperature(): """Callback function for new temperature data""" temperature = tmp.getTemperature() print("Temperature : {:.2f} °C".format(temperature)) + # Initialize with callback function tmp.init(new_temperature) @@ -33,25 +35,25 @@ def new_temperature(): tmp.setConvTime(TMP117_CONVT.C15mS5) tmp.setAveraging(TMP117_AVE.NOAVE) print("Setup 1: 15.5ms measurement time, no averaging") - + elif setup_nr == 2: # Setup 2: C125mS + AVE8 = 125 mS measurement time tmp.setConvTime(TMP117_CONVT.C125mS) tmp.setAveraging(TMP117_AVE.AVE8) print("Setup 2: 125ms measurement time, 8x averaging") - + elif setup_nr == 3: # Setup 3: C125mS + AVE32 = 500 mS measurement time tmp.setConvTime(TMP117_CONVT.C125mS) tmp.setAveraging(TMP117_AVE.AVE32) print("Setup 3: 500ms measurement time, 32x averaging") - + elif setup_nr == 4: # Setup 4: C4S + AVE64 = 4000 mS measurement time tmp.setConvTime(TMP117_CONVT.C4S) tmp.setAveraging(TMP117_AVE.AVE64) print("Setup 4: 4000ms measurement time, 64x averaging") - + else: # Default to setup 1 tmp.setConvTime(TMP117_CONVT.C15mS5) @@ -65,4 +67,4 @@ def new_temperature(): while True: """Infinite loop""" tmp.update() # Update the sensor/read configuration register - time.sleep(0.5) # Small delay to prevent busy waiting \ No newline at end of file + time.sleep(0.5) # Small delay to prevent busy waiting diff --git a/Sensors/TMP117/TMP117/Examples/tmp117-simpleRead.py b/Sensors/TMP117/TMP117/Examples/tmp117-simpleRead.py index 49902a7..cbcbf36 100644 --- a/Sensors/TMP117/TMP117/Examples/tmp117-simpleRead.py +++ b/Sensors/TMP117/TMP117/Examples/tmp117-simpleRead.py @@ -1,9 +1,9 @@ -# FILE: tmp117-simpleRead.py +# FILE: tmp117-simpleRead.py # AUTHOR: Josip Šimun Kuči @ Soldered # BRIEF: Example showing how to take a simple temperature # measurement every 100ms in Celsius # WORKS WITH: Temperature Sensor TMP117 Breakout: www.solde.red/333175 -# LAST UPDATED: 2025-09-22 +# LAST UPDATED: 2025-09-22 from machine import I2C, Pin import tmp117 @@ -25,4 +25,4 @@ # Read temperature temperature = sensor.getTemperature() print(f"Temperature: {temperature:.2f}°C") - time.sleep(0.2) \ No newline at end of file + time.sleep(0.2) diff --git a/Sensors/TMP117/TMP117/tmp117.py b/Sensors/TMP117/TMP117/tmp117.py index 7ec6035..837366f 100644 --- a/Sensors/TMP117/TMP117/tmp117.py +++ b/Sensors/TMP117/TMP117/tmp117.py @@ -1,7 +1,7 @@ -# FILE: TMP117.py +# FILE: TMP117.py # AUTHOR: Josip Šimun Kuči @ Soldered # BRIEF: A MicroPython module for the TMP117 Temperature Sensor Breakout -# LAST UPDATED: 2025-09-22 +# LAST UPDATED: 2025-09-22 from micropython import const import machine import time @@ -20,17 +20,20 @@ _TMP117_RESOLUTION = 0.0078125 + # Enums as classes class TMP117_PMODE: THERMAL = 0 ALERT = 1 DATA = 2 + class TMP117_CMODE: CONTINUOUS = 0 SHUTDOWN = 1 ONESHOT = 3 + class TMP117_CONVT: C15mS5 = 0 C125mS = 1 @@ -41,17 +44,20 @@ class TMP117_CONVT: C8S = 6 C16S = 7 + class TMP117_AVE: NOAVE = 0 AVE8 = 1 AVE32 = 2 AVE64 = 3 + class TMP117_ALERT: NOALERT = 0 HIGHALERT = 1 LOWALERT = 2 + class TMP117: def __init__(self, i2c, addr=0x49): self.i2c = i2c @@ -59,7 +65,7 @@ def __init__(self, i2c, addr=0x49): self.alert_pin = None self.alert_type = TMP117_ALERT.NOALERT self.newDataCallback = None - + def init(self, newDataCallback=None): """Initialize in default mode""" self.setConvMode(TMP117_CMODE.CONTINUOUS) @@ -67,49 +73,49 @@ def init(self, newDataCallback=None): self.setAveraging(TMP117_AVE.AVE8) self.setAlertMode(TMP117_PMODE.DATA) self.setOffsetTemperature(0) - + self.newDataCallback = newDataCallback - + def update(self): """Read configuration register and handle events""" return self.readConfig() - + def softReset(self): """Performs a soft reset""" reg_value = 1 << 1 self.writeConfig(reg_value) - + def setAlertMode(self, mode): """Set alert pin mode""" reg_value = self.readConfig() - + if mode == TMP117_PMODE.THERMAL: - reg_value |= 1 << 4 # change to thermal mode - reg_value &= ~(1 << 2) # set pin as alert flag - reg_value &= ~(1 << 3) # alert pin low active + reg_value |= 1 << 4 # change to thermal mode + reg_value &= ~(1 << 2) # set pin as alert flag + reg_value &= ~(1 << 3) # alert pin low active elif mode == TMP117_PMODE.ALERT: - reg_value &= ~(1 << 4) # change to alert mode - reg_value &= ~(1 << 2) # set pin as alert flag - reg_value &= ~(1 << 3) # alert pin low active + reg_value &= ~(1 << 4) # change to alert mode + reg_value &= ~(1 << 2) # set pin as alert flag + reg_value &= ~(1 << 3) # alert pin low active elif mode == TMP117_PMODE.DATA: - reg_value |= 1 << 2 # set pin as data ready flag - + reg_value |= 1 << 2 # set pin as data ready flag + self.writeConfig(reg_value) - + def setAlertCallback(self, alert_callback, pin): """Set alert callback function with proper configuration""" # Configure alert pin with strong pull-up self.alert_pin = machine.Pin(pin, machine.Pin.IN, machine.Pin.PULL_UP) - + # First, ensure we're in proper alert mode self.configureAlertMode() - + # Clear any existing alerts self.clearAlertFlags() - + # Wait for pin to stabilize time.sleep_ms(100) - + # Only set interrupt if pin is not currently active if self.alert_pin.value() == 1: # Should be high when no alert self.alert_pin.irq(trigger=machine.Pin.IRQ_FALLING, handler=alert_callback) @@ -120,23 +126,25 @@ def setAlertCallback(self, alert_callback, pin): self.clearAlertFlags() time.sleep_ms(50) if self.alert_pin.value() == 1: - self.alert_pin.irq(trigger=machine.Pin.IRQ_FALLING, handler=alert_callback) + self.alert_pin.irq( + trigger=machine.Pin.IRQ_FALLING, handler=alert_callback + ) else: print("Alert pin still active - check hardware") def configureAlertMode(self): """Ensure proper alert mode configuration""" reg_value = self.i2cRead2B(_TMP117_REG_CONFIGURATION) - + # Set to alert mode (not thermal mode) reg_value &= ~(1 << 4) # Clear T/nA bit (0 = Alert mode) - + # Set pin as alert flag (not data ready) reg_value &= ~(1 << 2) # Clear DR/Alert bit (0 = Alert mode) - + # Set active low polarity (typical for open-drain) reg_value &= ~(1 << 3) # Clear POL bit (0 = Active Low) - + self.writeConfig(reg_value) print("Alert mode configured") @@ -146,47 +154,47 @@ def clearAlertFlags(self): # Clear both alert flags clear_value = reg_value & ~((1 << 15) | (1 << 14)) self.i2cWrite2B(_TMP117_REG_CONFIGURATION, clear_value) - + def setAlertTemperature(self, lowtemp, hightemp): """Set alert temperature boundaries""" high_temp_value = int(hightemp / _TMP117_RESOLUTION) low_temp_value = int(lowtemp / _TMP117_RESOLUTION) - + self.i2cWrite2B(_TMP117_REG_TEMP_HIGH_LIMIT, high_temp_value) self.i2cWrite2B(_TMP117_REG_TEMP_LOW_LIMIT, low_temp_value) - + def setConvMode(self, cmode): """Set conversion mode""" reg_value = self.readConfig() reg_value &= ~((1 << 11) | (1 << 10)) # clear bits - reg_value |= (cmode & 0x03) << 10 # set bits + reg_value |= (cmode & 0x03) << 10 # set bits self.writeConfig(reg_value) - + def setConvTime(self, convtime): """Set conversion time""" reg_value = self.readConfig() reg_value &= ~((1 << 9) | (1 << 8) | (1 << 7)) # clear bits - reg_value |= (convtime & 0x07) << 7 # set bits + reg_value |= (convtime & 0x07) << 7 # set bits self.writeConfig(reg_value) - + def setAveraging(self, ave): """Set averaging mode""" reg_value = self.readConfig() reg_value &= ~((1 << 6) | (1 << 5)) # clear bits - reg_value |= (ave & 0x03) << 5 # set bits + reg_value |= (ave & 0x03) << 5 # set bits self.writeConfig(reg_value) - + def setOffsetTemperature(self, offset): """Set offset temperature""" offset_temp_value = int(offset / _TMP117_RESOLUTION) self.i2cWrite2B(_TMP117_REG_TEMPERATURE_OFFSET, offset_temp_value) - + def setTargetTemperature(self, target): """Set target temperature for calibration""" actual_temp = self.getTemperature() delta_temp = target - actual_temp self.setOffsetTemperature(delta_temp) - + def getTemperature(self): """Get temperature in °C""" temp = self.i2cRead2B(_TMP117_REG_TEMPERATURE) @@ -194,17 +202,17 @@ def getTemperature(self): if temp & 0x8000: temp = temp - 0x10000 return temp * _TMP117_RESOLUTION - + def getDeviceID(self): """Get Device ID (bits [11:0])""" raw = self.i2cRead2B(_TMP117_REG_DEVICE_ID) return raw & 0x0FFF - + def getDeviceRev(self): """Get Device Revision (bits [15:12])""" raw = self.i2cRead2B(_TMP117_REG_DEVICE_ID) return (raw >> 12) & 0x3 - + def getOffsetTemperature(self): """Get offset temperature in °C""" temp = self.i2cRead2B(_TMP117_REG_TEMPERATURE_OFFSET) @@ -212,11 +220,11 @@ def getOffsetTemperature(self): if temp & 0x8000: temp = temp - 0x10000 return temp * _TMP117_RESOLUTION - + def getAlertType(self): """Get alert type""" return self.alert_type - + def writeEEPROM(self, data, eeprom_nr): """Write data to EEPROM""" if not self.EEPROMisBusy(): @@ -232,7 +240,7 @@ def writeEEPROM(self, data, eeprom_nr): self.lockEEPROM() else: print("EEPROM is busy") - + def readEEPROM(self, eeprom_nr): """Read data from EEPROM""" if not self.EEPROMisBusy(): @@ -248,20 +256,20 @@ def readEEPROM(self, eeprom_nr): else: print("EEPROM is busy") return 0 - + def readConfig(self): """Read configuration register and clear alert flags""" reg_value = self.i2cRead2B(_TMP117_REG_CONFIGURATION) data_ready = (reg_value >> 13) & 0x1 - + # Handle data ready callback if data_ready and self.newDataCallback: self.newDataCallback() - + # Check for alert flags high_alert = (reg_value >> 15) & 0x1 low_alert = (reg_value >> 14) & 0x1 - + # Update alert type if high_alert: self.alert_type = TMP117_ALERT.HIGHALERT @@ -269,15 +277,15 @@ def readConfig(self): self.alert_type = TMP117_ALERT.LOWALERT else: self.alert_type = TMP117_ALERT.NOALERT - + # Clear alert flags if they are set if high_alert or low_alert: # Clear both alert flags while preserving other bits clear_value = reg_value & ~((1 << 15) | (1 << 14)) self.i2cWrite2B(_TMP117_REG_CONFIGURATION, clear_value) - + return reg_value - + def printConfig(self): """Print configuration in readable format""" reg_value = self.i2cRead2B(_TMP117_REG_CONFIGURATION) @@ -293,7 +301,7 @@ def printConfig(self): print(f"POL: {(reg_value >> 3) & 0x1}") print(f"DR/Alert: {(reg_value >> 2) & 0x1}") print(f"Soft_Reset: {(reg_value >> 1) & 0x1}") - + # Private methods def i2cWrite2B(self, reg, data): """Write two bytes to I2C device""" @@ -303,14 +311,14 @@ def i2cWrite2B(self, reg, data): buf[2] = data & 0xFF self.i2c.writeto(self.address, buf) time.sleep_ms(10) - + def i2cRead2B(self, reg): """Read two bytes from I2C device""" buf = bytearray(2) self.i2c.writeto(self.address, bytes([reg])) self.i2c.readfrom_into(self.address, buf) return (buf[0] << 8) | buf[1] - + def writeConfig(self, config_data): """Write configuration to config register""" if not self.EEPROMisBusy(): @@ -319,21 +327,21 @@ def writeConfig(self, config_data): self.lockEEPROM() else: print("EEPROM is busy") - + def lockEEPROM(self): """Lock EEPROM, write protection""" code = 0 code &= ~(1 << 15) self.i2cWrite2B(_TMP117_REG_EEPROM_UNLOCK, code) time.sleep_ms(100) - + def unlockEEPROM(self): """Unlock EEPROM, remove write protection""" code = 1 << 15 self.i2cWrite2B(_TMP117_REG_EEPROM_UNLOCK, code) time.sleep_ms(100) - + def EEPROMisBusy(self): """Check if EEPROM is busy""" code = self.i2cRead2B(_TMP117_REG_EEPROM_UNLOCK) - return bool((code >> 14) & 0x01) \ No newline at end of file + return bool((code >> 14) & 0x01)