diff --git a/Actuators/DS3234/DS3234/Examples/ds3234-deepSleep.py b/Actuators/DS3234/DS3234/Examples/ds3234-deepSleep.py new file mode 100644 index 0000000..83da831 --- /dev/null +++ b/Actuators/DS3234/DS3234/Examples/ds3234-deepSleep.py @@ -0,0 +1,110 @@ +# 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..9bee22a --- /dev/null +++ b/Actuators/DS3234/DS3234/Examples/ds3234-simpleRTC.py @@ -0,0 +1,145 @@ +# 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..2b74515 --- /dev/null +++ b/Actuators/DS3234/DS3234/Examples/ds3234-writeSRAM.py @@ -0,0 +1,177 @@ +# 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..d29bd9a --- /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("= 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" 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 diff --git a/Sensors/TMP117/TMP117/Examples/tmp117-alarmPin.py b/Sensors/TMP117/TMP117/Examples/tmp117-alarmPin.py new file mode 100644 index 0000000..ede7747 --- /dev/null +++ b/Sensors/TMP117/TMP117/Examples/tmp117-alarmPin.py @@ -0,0 +1,47 @@ +# 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) diff --git a/Sensors/TMP117/TMP117/Examples/tmp117-customConfig.py b/Sensors/TMP117/TMP117/Examples/tmp117-customConfig.py new file mode 100644 index 0000000..6dbdc2a --- /dev/null +++ b/Sensors/TMP117/TMP117/Examples/tmp117-customConfig.py @@ -0,0 +1,70 @@ +# 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 diff --git a/Sensors/TMP117/TMP117/Examples/tmp117-simpleRead.py b/Sensors/TMP117/TMP117/Examples/tmp117-simpleRead.py new file mode 100644 index 0000000..cbcbf36 --- /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) diff --git a/Sensors/TMP117/TMP117/tmp117.py b/Sensors/TMP117/TMP117/tmp117.py new file mode 100644 index 0000000..837366f --- /dev/null +++ b/Sensors/TMP117/TMP117/tmp117.py @@ -0,0 +1,347 @@ +# 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) diff --git a/Sensors/TMP117/package.json b/Sensors/TMP117/package.json new file mode 100644 index 0000000..91db9b1 --- /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": "1.0" +} \ No newline at end of file