kopia lustrzana https://github.com/micropython/micropython-lib
Merge ce1e03fbef
into 68e0dfce0a
commit
85a7138626
|
@ -0,0 +1,9 @@
|
|||
metadata(
|
||||
description="Microchip MCP9808 temperature sensor driver",
|
||||
version="1.0.0",
|
||||
license="MIT",
|
||||
author="Marco Miano",
|
||||
)
|
||||
|
||||
# opt=2 so line numbers are preserved in case of exceptions
|
||||
module("mcp9808.py", opt=2)
|
|
@ -0,0 +1,715 @@
|
|||
"""MIT License
|
||||
|
||||
Copyright (c) 2024 Marco Miano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
|
||||
|
||||
Microchip MCP9808 driver for MicroPython
|
||||
|
||||
THE MCP9808 IS A COMPLEX SENSOR WITH MANY FEATURES. IS IT ADVISABLE TO READ THE DATASHEET.
|
||||
|
||||
DO NOT ACCESS REGISTERS WITH ADDRESSES HIGHER THAN 0x08 AS THEY CONTAIN CALIBRATION CODES.
|
||||
DOING SO MAY IRREPARABLY DAMAGE THE SENSOR.
|
||||
|
||||
This driver is a comprehensive implementation of the MCP9808 sensor's features. It is designed
|
||||
to be easy to use and offers a high level of abstraction from the sensor's registers.
|
||||
The driver includes built-in error checking (such as type validation and bounds checking
|
||||
for register access) and a debug mode to assist with development.
|
||||
|
||||
|
||||
|
||||
Example usage:
|
||||
|
||||
from mcp9808 import MCP9808, HYST_15, RES_0_125
|
||||
from machine import SoftI2C, Pin
|
||||
|
||||
i2c = SoftI2C(scl=Pin(17), sda=Pin(16), freq=400000)
|
||||
t_sensor = MCP9808(i2c)
|
||||
|
||||
# Get temeperature with deafult settings
|
||||
temperature: float = t_sensor.get_temperature()
|
||||
|
||||
# Various settings
|
||||
t_sensor.set_hysteresis_mode(hyst_mode=HYST_15)
|
||||
t_sensor.set_resolution(resolution=RES_0_125)
|
||||
t_sensor.set_alert_crit_limit(crit_limit=65.0)
|
||||
t_sensor.set_alert_upper_limit(upper_limit=50.0)
|
||||
t_sensor.set_alert_lower_limit(lower_limit=-10.0)
|
||||
t_sensor.enable_alert()
|
||||
|
||||
# Enable debug mode to get warnings
|
||||
t_sensor._debug = True
|
||||
|
||||
|
||||
# For more information, see the README file at
|
||||
https://github.com/MarcoMiano/mip-mcp9808
|
||||
"""
|
||||
|
||||
from machine import SoftI2C, I2C
|
||||
|
||||
# Handy Constants
|
||||
HYST_00 = 0b00 # Hysteresis 0°C (power-up default)
|
||||
HYST_15 = 0b01 # Hysteresis 1,5°C
|
||||
HYST_30 = 0b10 # Hysteresis 3,0°C
|
||||
HYST_60 = 0b11 # Hysteresis 6,0°C
|
||||
|
||||
RES_0_5 = 0b00 # Resolution 0.5°C
|
||||
RES_0_25 = 0b01 # Resolution 0.25°C
|
||||
RES_0_125 = 0b10 # Resolution 0.125°C
|
||||
RES_0_0625 = 0b11 # Resolution 0.0625°C (power-up default)
|
||||
|
||||
|
||||
class MCP9808(object):
|
||||
"""A class to interface with the Microchip MCP9808 temperature sensor over I2C.
|
||||
|
||||
Attributes:
|
||||
``BASE_ADDR`` (int): The base I2C address for the MCP9808 sensor.
|
||||
``REG_CFG`` (int): Address of the configuration register.
|
||||
``REG_ATU`` (int): Address of the alert temperature upper boundary trip register.
|
||||
``REG_ATL`` (int): Address of the alert temperature lower boundary trip register.
|
||||
``REG_ATC`` (int): Address of the critical temperature trip register.
|
||||
``REG_TEM`` (int): Address of the temperature register.
|
||||
``REG_MFR`` (int): Address of the manufacturer ID register.
|
||||
``REG_DEV`` (int): Address of the device ID register.
|
||||
``REG_RES`` (int): Address of the resolution register.
|
||||
"""
|
||||
|
||||
BASE_ADDR = 0x18
|
||||
#######################################################
|
||||
# DON'T ACCESS REGISTER WITH ADDRESS HIGHER THAN 0X08 #
|
||||
#######################################################
|
||||
REG_CFG = 0x01 # Config register
|
||||
REG_ATU = 0x02 # Alert Temperature Upper boundary trip register
|
||||
REG_ATL = 0x03 # Alert Temperature Lower boundary trip register
|
||||
REG_ATC = 0x04 # Critical Temperature Trip register
|
||||
REG_TEM = 0x05 # Temperature register
|
||||
REG_MFR = 0x06 # Manufacturer ID register
|
||||
REG_DEV = 0x07 # Device ID register
|
||||
REG_RES = 0x08 # Resolution register
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
i2c: SoftI2C | I2C,
|
||||
addr: int | None = None,
|
||||
A0: bool = False,
|
||||
A1: bool = False,
|
||||
A2: bool = False,
|
||||
debug: bool = False,
|
||||
) -> None:
|
||||
"""Initialize the sensor object instance.
|
||||
|
||||
Args:
|
||||
``i2c`` (SoftI2C | I2C): The I2C bus instance to use for communication.
|
||||
``addr`` (int | None, optional): The I2C address of the sensor. If not provided,
|
||||
the address will be calculated based on A0, A1, and A2. Defaults to None.
|
||||
``A0`` (bool, optional): The state of address pin A0. Defaults to False.
|
||||
``A1`` (bool, optional): The state of address pin A1. Defaults to False.
|
||||
``A2`` (bool, optional): The state of address pin A2. Defaults to False.
|
||||
``debug`` (bool, optional): Enable or disable debug mode. Defaults to False.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
|
||||
self._i2c: SoftI2C | I2C = i2c
|
||||
self._debug: bool = debug
|
||||
if addr:
|
||||
self._addr = addr
|
||||
else:
|
||||
self._addr: int = self.BASE_ADDR | (A2 << 2) | (A1 << 1) | A0
|
||||
self._check_device()
|
||||
self._get_config()
|
||||
|
||||
def _check_device(self) -> None:
|
||||
"""Checks the device's manufacturer ID and device ID to ensure it is the correct device.
|
||||
|
||||
Raises:
|
||||
``Exception``: If the manufacturer ID does not match the expected value.
|
||||
``Exception``: If the device ID does not match the expected value.
|
||||
Warns:
|
||||
If the hardware revision does not match the expected value and debug mode is enabled.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
|
||||
self._mfr_id: bytes = self._i2c.readfrom_mem(self._addr, self.REG_MFR, 2)
|
||||
if self._mfr_id != b"\x00\x54":
|
||||
raise Exception(f"Invalid manufacturer ID {self._mfr_id}")
|
||||
self._dev_id: bytes = self._i2c.readfrom_mem(self._addr, self.REG_DEV, 2)
|
||||
if self._dev_id[0] != 4:
|
||||
raise Exception(f"Invalid device ID {self._dev_id[0]}")
|
||||
if self._dev_id[1] != 0 and self._debug:
|
||||
print(
|
||||
f"[WARN] Module written for HW revision 0 but got {self._dev_id[1]}.",
|
||||
)
|
||||
|
||||
def _get_config(self) -> None:
|
||||
"""Private method to read the configuration register from the sensor.
|
||||
|
||||
This method reads 2 bytes from the configuration register of the sensor.
|
||||
It then parses the bytes to update the following instance attributes:
|
||||
``_hyst_mode``: Hysteresis mode (int)
|
||||
``_shdn``: Shutdown mode (bool)
|
||||
``_crit_lock``: Critical temperature register lock (bool)
|
||||
``_alerts_lock``: Alerts temperature registers lock (bool)
|
||||
``_irq_clear_bit``: Interrupt clear bit (bool)
|
||||
``_alert``: Alert output status (bool)
|
||||
``_alert_ctrl``: Alert control (bool)
|
||||
``_alert_sel``: Alert output select (bool)
|
||||
``_alert_pol``: Alert output polarity (bool)
|
||||
``_alert_mode``: Alert output mode (bool)
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
|
||||
buf: bytes = self._i2c.readfrom_mem(self._addr, self.REG_CFG, 2)
|
||||
self._hyst_mode: int = (buf[0] >> 1) & 0x03
|
||||
self._shdn = bool(buf[0] & 0x01)
|
||||
self._crit_lock = bool(buf[1] & 0x80)
|
||||
self._alerts_lock = bool(buf[1] & 0x40)
|
||||
self.irq_clear_bit = bool(buf[1] & 0x20)
|
||||
self._alert = bool(buf[1] & 0x10)
|
||||
self._alert_ctrl = bool(buf[1] & 0x08)
|
||||
self._alert_sel = bool(buf[1] & 0x04)
|
||||
self.alert_pol = bool(buf[1] & 0x02)
|
||||
self._alert_mode = bool(buf[1] & 0x01)
|
||||
|
||||
def _set_config(
|
||||
self,
|
||||
hyst_mode: int | None = None,
|
||||
shdn: bool | None = None,
|
||||
crit_lock: bool | None = None,
|
||||
alerts_lock: bool | None = None,
|
||||
irq_clear_bit: bool = False,
|
||||
alert_ctrl: bool | None = None,
|
||||
alert_sel: bool | None = None,
|
||||
alert_pol: bool | None = None,
|
||||
alert_mode: bool | None = None,
|
||||
) -> None:
|
||||
"""Private method to set the configuration of the sensor.
|
||||
|
||||
Parameters:
|
||||
``hyst_mode`` (int | None): Hysteresis mode. Valid values are HYST_00, HYST_15, HYST_30, HYST_60.
|
||||
``shdn (bool`` | None): Shutdown mode.
|
||||
``crit_lock`` (bool | None): Critical temperature register lock.
|
||||
``alerts_lock`` (bool | None): Alerts temperature registers lock.
|
||||
``irq_clear``_bit (bool): Interrupt clear bit.
|
||||
``alert_ctrl`` (bool | None): Alert output control.
|
||||
``alert_sel`` (bool | None): Alert output select.
|
||||
``alert_pol`` (bool | None): Alert output polarity.
|
||||
``alert_mode`` (bool | None): Alert output mode.
|
||||
Raises:
|
||||
``ValueError``: If hyst_mode is not one of the valid values.
|
||||
``TypeError``: If any of the boolean parameters are not of type bool.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
|
||||
if hyst_mode is None:
|
||||
hyst_mode = self._hyst_mode
|
||||
if shdn is None:
|
||||
shdn = self._shdn
|
||||
if crit_lock is None:
|
||||
crit_lock = self._crit_lock
|
||||
if alerts_lock is None:
|
||||
alerts_lock = self._alerts_lock
|
||||
if alert_ctrl is None:
|
||||
alert_ctrl = self._alert_ctrl
|
||||
if alert_sel is None:
|
||||
alert_sel = self._alert_sel
|
||||
if alert_pol is None:
|
||||
alert_pol = self.alert_pol
|
||||
if alert_mode is None:
|
||||
alert_mode = self._alert_mode
|
||||
|
||||
# Type/value check the parameters
|
||||
if hyst_mode not in [HYST_00, HYST_15, HYST_30, HYST_60]:
|
||||
raise ValueError(f"hyst_mode: {hyst_mode}. Value should be between 0 and 3 inclusive.")
|
||||
if shdn.__class__ is not bool:
|
||||
raise TypeError(
|
||||
f"shdn: {shdn} {shdn.__class__}. Expecting a bool.",
|
||||
)
|
||||
if crit_lock.__class__ is not bool:
|
||||
raise TypeError(
|
||||
f"crit_lock: {crit_lock} {crit_lock.__class__}. Expecting a bool.",
|
||||
)
|
||||
if alerts_lock.__class__ is not bool:
|
||||
raise TypeError(
|
||||
f"alerts_lock: {alerts_lock} {alerts_lock.__class__}. Expecting a bool.",
|
||||
)
|
||||
if irq_clear_bit.__class__ is not bool:
|
||||
raise TypeError(
|
||||
f"irq_clear_bit: {irq_clear_bit} {irq_clear_bit.__class__}. Expecting a bool.",
|
||||
)
|
||||
if alert_ctrl.__class__ is not bool:
|
||||
raise TypeError(
|
||||
f"alert_ctrl: {alert_ctrl} {alert_ctrl.__class__}. Expecting a bool.",
|
||||
)
|
||||
if alert_sel.__class__ is not bool:
|
||||
raise TypeError(
|
||||
f"alert_sel: {alert_sel} {alert_sel.__class__}. Expecting a bool.",
|
||||
)
|
||||
if alert_pol.__class__ is not bool:
|
||||
raise TypeError(
|
||||
f"alert_pol: {alert_pol} {alert_pol.__class__}. Expecting a bool.",
|
||||
)
|
||||
if alert_mode.__class__ is not bool:
|
||||
raise TypeError(
|
||||
f"alert_mode: {alert_mode} {alert_mode.__class__}. Expecting a bool.",
|
||||
)
|
||||
|
||||
# Build the send buffer
|
||||
buf = bytearray(b"\x00\x00")
|
||||
buf[0] = (hyst_mode << 1) | shdn
|
||||
buf[1] = (
|
||||
(crit_lock << 7)
|
||||
| (alerts_lock << 6)
|
||||
| (irq_clear_bit << 5)
|
||||
| (alert_ctrl << 3)
|
||||
| (alert_sel << 2)
|
||||
| (alert_pol << 1)
|
||||
| alert_mode
|
||||
)
|
||||
# Write the buffer to the sensor
|
||||
self._i2c.writeto_mem(self._addr, self.REG_CFG, buf)
|
||||
self._get_config()
|
||||
# Check if the configuration was set correctly id debug mode is enabled
|
||||
if self._debug:
|
||||
if self._hyst_mode != hyst_mode:
|
||||
print(
|
||||
f"[WARN] Failed to set hyst_mode. Set {hyst_mode} got {self._hyst_mode}",
|
||||
)
|
||||
if self._shdn != shdn:
|
||||
print(
|
||||
f"[WARN] Failed to set shdn. Set {shdn} got {self._shdn}",
|
||||
)
|
||||
if self._crit_lock != crit_lock:
|
||||
print(
|
||||
f"[WARN] Failed to set crit_lock. Set {crit_lock} got {self._crit_lock}",
|
||||
)
|
||||
if self.irq_clear_bit:
|
||||
print("[WARN] Something wrong with irq_clear_bit. Should always read False")
|
||||
if self._alerts_lock != alerts_lock:
|
||||
print(
|
||||
f"[WARN] Failed to set alerts_lock. Set {alerts_lock} got {self._alerts_lock}",
|
||||
)
|
||||
if self._alert_ctrl != alert_ctrl:
|
||||
print(
|
||||
f"[WARN] Failed to set alert_ctrl. Set {alert_ctrl} got {self._alert_ctrl}.",
|
||||
)
|
||||
if self._alert_sel != alert_sel:
|
||||
print(
|
||||
f"[WARN] Failed to set alert_sel. Set {alert_sel} got {self._alert_sel}.",
|
||||
)
|
||||
if self.alert_pol != alert_pol:
|
||||
print(
|
||||
f"[WARN] Failed to set alert_pol. Set {alert_pol} got {self.alert_pol}.",
|
||||
)
|
||||
if self._alert_mode != alert_mode:
|
||||
print(
|
||||
f"[WARN] Failed to set alert_mode. Set {alert_mode} got {self._alert_mode}.",
|
||||
)
|
||||
|
||||
def _set_alert_limit(self, limit: float, register: int) -> None:
|
||||
"""Private method to set the alert limit register.
|
||||
|
||||
Inteded to be used by the set_alert_XXXXX_limit wrapper methods.
|
||||
Args:
|
||||
``limit`` (float | int): The temperature limit to set. Must be between -128 and 127.
|
||||
``register`` (int): The register address to write the limit to.
|
||||
Raises:
|
||||
``TypeError``: If the limit is not a float or int.
|
||||
``ValueError``: If the limit is out of the range [-128, 127].
|
||||
``ValueError``: If the register address is not valid.
|
||||
Debug:
|
||||
- Issue a warning if the threshold is outside of the operational range.
|
||||
- Issue a warning if the alert limit was not set correctly.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
|
||||
if limit.__class__ not in [float, int]:
|
||||
raise TypeError(
|
||||
f"limit: {limit} {limit.__class__}. Expecting float|int.",
|
||||
)
|
||||
if limit < -128 or limit > 127:
|
||||
raise ValueError("Temperature out of range [-128, 127]")
|
||||
if (limit < -40 or limit > 125) and self._debug:
|
||||
print(
|
||||
"[WARN] Temperature outside of operational range, limit won't be ever reached.",
|
||||
)
|
||||
if register not in [self.REG_ATU, self.REG_ATL, self.REG_ATC]:
|
||||
raise ValueError(f"Invalid register address {register}")
|
||||
|
||||
buf = bytearray(b"\x00\x00")
|
||||
|
||||
# If limit is negative set sign fifth bit ON otherwise leave it OFF
|
||||
if limit < 0:
|
||||
sign = 0x10
|
||||
else:
|
||||
sign = 0x00
|
||||
|
||||
# If limit is between -1 and 0 (like -0.25) set integral to 0xFF (-0 in 2's complement)
|
||||
if -1 < limit < 0:
|
||||
integral: int = 0xFF
|
||||
# Otherwise truncate limit to a int and keep only the rightmost byte
|
||||
else:
|
||||
integral: int = int(limit) & 0xFF
|
||||
|
||||
# Calculate the fractional part by keeping the 2 rightmost bits of the integer division
|
||||
# of 0.25 (the sensitivity) and the remainder part of the decimal part of limit
|
||||
frac_normal: int = int((limit - integral) / 0.25) & 0x03
|
||||
|
||||
# Build the send buffer highest byte combining (bitwise-or) sign and the integral
|
||||
# right-shifted by 4
|
||||
buf[0] = sign | (integral >> 4)
|
||||
# Build the send buffer lowest byte combining (bitwise-or) the integral
|
||||
# left-shifted by 4 and the fractional part left shifted by 2 (last 2 bit are 0)
|
||||
buf[1] = (integral << 4) | (frac_normal << 2)
|
||||
|
||||
self._i2c.writeto_mem(self._addr, register, buf)
|
||||
|
||||
if self._debug:
|
||||
check: bytes = self._i2c.readfrom_mem(self._addr, register, 2)
|
||||
if check != buf:
|
||||
print(
|
||||
f"[WARN] Failed to set alert limit. Set {buf[0]:08b}-{buf[1]:08b}",
|
||||
f"but got {check[0]:08b}-{check[1]:08b}",
|
||||
)
|
||||
|
||||
def shutdown(self) -> None:
|
||||
"""Put the sensor in low power mode.
|
||||
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_config(shdn=True)
|
||||
|
||||
def wake(self) -> None:
|
||||
"""Wake the sensor from low power mode.
|
||||
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_config(shdn=False)
|
||||
|
||||
def lock_crit_limit(self) -> None:
|
||||
"""Locks the critical temperature limit.
|
||||
|
||||
When the critical temperature limit is locked, it cannot be changed
|
||||
until the sensor is power cycled.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_config(crit_lock=True)
|
||||
|
||||
def lock_alerts_limit(self) -> None:
|
||||
"""Locks the alerts limits.
|
||||
|
||||
When the alerts limits are locked, they cannot be changed
|
||||
until the sensor is power cycled.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_config(alerts_lock=True)
|
||||
|
||||
def irq_clear(self) -> None:
|
||||
"""Clears the interrupt output.
|
||||
|
||||
This method clears the interrupt output.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_config(irq_clear_bit=True)
|
||||
|
||||
def get_alert_status(self) -> bool:
|
||||
"""Get the alert status.
|
||||
|
||||
This method reads the alert status from the sensor.
|
||||
Returns:
|
||||
``bool``: The alert status.
|
||||
"""
|
||||
self._get_config()
|
||||
return self.alert
|
||||
|
||||
def enable_alert(self) -> None:
|
||||
"""Enable the alert output.
|
||||
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_config(alert_ctrl=True)
|
||||
|
||||
def disable_alert(self) -> None:
|
||||
"""Disable the alert output.
|
||||
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_config(alert_ctrl=False)
|
||||
|
||||
def set_alert_threshold(self, only_crit=False) -> None:
|
||||
"""Set the alert output select.
|
||||
|
||||
Select if the alert output should be activated only by the critical limit or both critical
|
||||
and upper/lower limits.
|
||||
Args:
|
||||
``only_crit`` (bool, optional): Set the alert output to only critical. Defaults to False.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_config(alert_sel=only_crit)
|
||||
|
||||
def set_alert_polarity(self, active_high=False) -> None:
|
||||
"""Set the alert output polarity.
|
||||
|
||||
Set the alert output polarity to active high or active low.
|
||||
Args:
|
||||
``active_high`` (bool, optional): Set the alert output polarity to active high.
|
||||
Defaults to False.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_config(alert_pol=active_high)
|
||||
|
||||
def set_alert_mode(self, irq=False) -> None:
|
||||
"""Set the alert output mode.
|
||||
|
||||
Set the alert output mode to interrupt or comparator.
|
||||
Args:
|
||||
``irq`` (bool, optional): Set the alert output mode to interrupt. Defaults to False.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_config(alert_mode=irq)
|
||||
|
||||
def set_alert_upper_limit(self, upper_limit: float) -> None:
|
||||
"""Set the alert upper limit.
|
||||
|
||||
Args:
|
||||
upper_limit (float | int): The upper limit to set.
|
||||
It will rounded to the nearest 0.25°C.
|
||||
Raises:
|
||||
``TypeError``: If the limit is not a float or int.
|
||||
``ValueError``: If the limit is out of the range [-128, 127].
|
||||
Debug:
|
||||
- Issue a warning if the threshold is outside of the operational range.
|
||||
- Issue a warning if the alert limit was not set correctly.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_alert_limit(upper_limit, self.REG_ATU)
|
||||
|
||||
def set_alert_lower_limit(self, lower_limit: float) -> None:
|
||||
"""Set the alert lower limit.
|
||||
|
||||
Args:
|
||||
lower_limit (float | int): The lower limit to set.
|
||||
Raises:
|
||||
``TypeError``: If the limit is not a float or int.
|
||||
``ValueError``: If the limit is out of the range [-128, 127].
|
||||
Debug:
|
||||
- Issue a warning if the threshold is outside of the operational range.
|
||||
- Issue a warning if the alert limit was not set correctly.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_alert_limit(lower_limit, self.REG_ATL)
|
||||
|
||||
def set_alert_crit_limit(self, crit_limit: float) -> None:
|
||||
"""Set the alert critical limit.
|
||||
|
||||
Args:
|
||||
crit_limit (float | int): The critical limit to set.
|
||||
Raises:
|
||||
``TypeError``: If the limit is not a float or int.
|
||||
``ValueError``: If the limit is out of the range [-128, 127].
|
||||
Debug:
|
||||
- Issue a warning if the threshold is outside of the operational range.
|
||||
- Issue a warning if the alert limit was not set correctly.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
self._set_alert_limit(crit_limit, self.REG_ATC)
|
||||
|
||||
def get_temperature(self) -> float:
|
||||
"""Get the temperature from the sensor.
|
||||
|
||||
Returns:
|
||||
``float``: The temperature in degrees Celsius.
|
||||
"""
|
||||
# Read temperature register from sensor
|
||||
buf: bytes = self._i2c.readfrom_mem(self._addr, self.REG_TEM, 2)
|
||||
# Extract the sign bit
|
||||
sign: int = buf[0] & 0x10
|
||||
# Calculate the 4 upper bit of the integral by left shifting the first byte by 4
|
||||
upper: int = (buf[0] << 4) & 0xFF
|
||||
# Calculate the 4 lower bit of the integral and the fractional part into a float
|
||||
# dividing by 16 the second buf byte
|
||||
lower: float = (buf[1] & 0xFF) / 16
|
||||
# Calculate the temperature as a float, adding the upper byte (leftmost 4 bit of integral)
|
||||
# and the lower byte (rightmost 4 bit of integral + fractional).
|
||||
# In case of negative value subtract 256 from the sum to convert from 2's complement 8+4bit
|
||||
# fractional value to negative float
|
||||
temp: float = (upper + lower) - 256 if sign else upper + lower
|
||||
return temp
|
||||
|
||||
def get_alert_triggers(self) -> tuple[bool, bool, bool]:
|
||||
"""Get the alert triggers.
|
||||
|
||||
Trigger bits are not influenced by the alert output mode (compare or interrupt) or by the
|
||||
alert polarity (active high or active low) or by the alert control (enable or disable).
|
||||
Returns:
|
||||
``tuple[bool, bool, bool]``: A tuple containing the alert triggers.
|
||||
The first element is True if the temperature is greater or equal to the critical
|
||||
limit.
|
||||
The second element is True if the temperature is greater than the upper limit.
|
||||
The third element is True if the temperature is less than the lower limit.
|
||||
"""
|
||||
# Read temperature register from sensor
|
||||
buf: bytes = self._i2c.readfrom_mem(self._addr, self.REG_TEM, 2)
|
||||
# Extract the 16th bit (last), Ta vs. Tcrit. False = Ta < Tcrit | True = Ta >= Tcrit
|
||||
ta_tcrit = bool(buf[0] & 0x80)
|
||||
# Extract the 15th bit, Ta vs. Tupper. False = Ta <= Tupper | True = Ta > Tupper
|
||||
ta_tupper = bool(buf[0] & 0x40)
|
||||
# Extract the 14th bit, Ta vs Tlower. False = Ta >= Tlower | True = Ta < Tlower
|
||||
ta_tlower = bool(buf[0] & 0x20)
|
||||
|
||||
return ta_tcrit, ta_tupper, ta_tlower
|
||||
|
||||
def set_resolution(self, resolution=RES_0_0625) -> None:
|
||||
"""Set the resolution of the sensor.
|
||||
|
||||
Args:
|
||||
resolution (int, optional): The resolution to set.
|
||||
Valid values are RES_0_5, RES_0_25, RES_0_125, RES_0_0625.
|
||||
Defaults to RES_0_0625.
|
||||
Raises:
|
||||
ValueError: If the resolution is not a valid value.
|
||||
Debug:
|
||||
- Issue a warning if the resolution was not set correctly.
|
||||
Returns:
|
||||
``None``
|
||||
"""
|
||||
# Check if resolution is a compatible value
|
||||
if resolution not in [RES_0_5, RES_0_25, RES_0_125, RES_0_0625]:
|
||||
raise ValueError(
|
||||
f"Invalid resolution: {resolution}. Value should be between 0 and 3 inclusive.",
|
||||
)
|
||||
|
||||
buf = bytearray(b"\x00")
|
||||
|
||||
buf[0] |= resolution & 0x03
|
||||
self._i2c.writeto_mem(self._addr, self.REG_RES, buf)
|
||||
if self._debug:
|
||||
check = self._i2c.readfrom_mem(self._addr, self.REG_RES, 1)
|
||||
if check != buf:
|
||||
print(f"[WARN] Failed to set resolution. Set {resolution} got {check[0]}")
|
||||
|
||||
@property
|
||||
def hyst_mode(self) -> int:
|
||||
"""Get the hysteresis mode.
|
||||
|
||||
Returns:
|
||||
``int``: The hysteresis mode.
|
||||
"""
|
||||
self._get_config()
|
||||
return self._hyst_mode
|
||||
|
||||
@hyst_mode.setter
|
||||
def hyst_mode(self, hyst_mode: int) -> None:
|
||||
"""Set the hysteresis mode.
|
||||
|
||||
Args:
|
||||
``hyst_mode`` (int): The hysteresis mode to set.
|
||||
Valid values are HYST_00, HYST_15, HYST_30, HYST_60.
|
||||
"""
|
||||
self._set_config(hyst_mode=hyst_mode)
|
||||
|
||||
@property
|
||||
def shdn(self) -> bool:
|
||||
"""Get the shutdown mode.
|
||||
|
||||
Returns:
|
||||
``bool``: The shutdown mode.
|
||||
"""
|
||||
self._get_config()
|
||||
return self._shdn
|
||||
|
||||
@property
|
||||
def crit_lock(self) -> bool:
|
||||
"""Get the critical temperature register lock.
|
||||
|
||||
Returns:
|
||||
``bool``: The critical temperature register lock.
|
||||
"""
|
||||
self._get_config()
|
||||
return self._crit_lock
|
||||
|
||||
@property
|
||||
def alerts_lock(self) -> bool:
|
||||
"""Get the alerts temperature registers lock.
|
||||
|
||||
Returns:
|
||||
``bool``: The alerts temperature registers lock.
|
||||
"""
|
||||
self._get_config()
|
||||
return self._alerts_lock
|
||||
|
||||
@property
|
||||
def alert(self) -> bool:
|
||||
"""Get the alert control.
|
||||
|
||||
Returns:
|
||||
``bool``: The alert control.
|
||||
"""
|
||||
self._get_config()
|
||||
return self._alert
|
||||
|
||||
@property
|
||||
def alert_ctrl(self) -> bool:
|
||||
"""Get the alert control.
|
||||
|
||||
Returns:
|
||||
``bool``: The alert control.
|
||||
"""
|
||||
self._get_config()
|
||||
return self._alert_ctrl
|
||||
|
||||
@property
|
||||
def alert_sel(self) -> bool:
|
||||
"""Get the alert output select.
|
||||
|
||||
Returns:
|
||||
``bool``: The alert output select.
|
||||
"""
|
||||
self._get_config()
|
||||
return self._alert_sel
|
||||
|
||||
@property
|
||||
def alert_mode(self) -> bool:
|
||||
"""Get the alert output mode.
|
||||
|
||||
Returns:
|
||||
``bool``: The alert output mode.
|
||||
"""
|
||||
self._get_config()
|
||||
return self._alert_mode
|
|
@ -0,0 +1,379 @@
|
|||
"""MIT License
|
||||
|
||||
Copyright (c) 2024 Marco Miano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
|
||||
|
||||
Microchip MCP9808 driver/sensor test suite for MicroPython
|
||||
|
||||
THE MCP9808 IS A COMPLEX SENSOR WITH MANY FEATURES. IS IT ADVISABLE TO READ THE DATASHEET.
|
||||
|
||||
DO NOT ACCESS REGISTERS WITH ADDRESSES HIGHER THAN 0x08 AS THEY CONTAIN CALIBRATION CODES.
|
||||
DOING SO MAY IRREPARABLY DAMAGE THE SENSOR.
|
||||
|
||||
This test suite is designed to check the correct operation of the MCP9808 sensor driver and the
|
||||
sensor itself. It is advisable to run this test suite if anything is changed in the driver code,
|
||||
or if the sensor is not behaving right.
|
||||
This test suite is written and tested on a Raspberry Pi Pico W Board with MicroPython v1.24.1.
|
||||
|
||||
|
||||
|
||||
Pin connections:
|
||||
- POWER: pin15
|
||||
The sensor is powered from a GPIO pin to be able to power cycle the sensor during the
|
||||
tests.
|
||||
CHECK THAT YOUR GPIO PIN CAN SUPPLY ENOUGH CURRENT AND VOLTAGE TO POWER THE SENSOR.
|
||||
(2.7V-5.5V AT 0.4mA)
|
||||
- SDA: pin16
|
||||
- SCL: pin17
|
||||
- ALERT: pin18
|
||||
The sensor alert pin is connected to a GPIO pin to check if the sensor is triggering
|
||||
alerts. The pin is pulled to VCC via the board internal pull-up resistor.
|
||||
The pin is active low. Wire an external pull-up resistor to VCC if needed.
|
||||
|
||||
Prerequisites:
|
||||
- Install the unittest module in the MicroPython device.
|
||||
- Install the MCP9808 driver in the MicroPython device.
|
||||
- Wire the sensor to the board as described above.
|
||||
- Run the test suite.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import mcp9808
|
||||
from mcp9808 import MCP9808
|
||||
from machine import SoftI2C, Pin
|
||||
from time import sleep_ms
|
||||
|
||||
##############################################
|
||||
# Change the pin numbers to match your setup #
|
||||
##############################################
|
||||
power_pin = Pin(15, Pin.OUT)
|
||||
alert_pin = Pin(18, Pin.IN, pull=Pin.PULL_UP)
|
||||
i2c_bus = SoftI2C(scl=Pin(17), sda=Pin(16), freq=400000)
|
||||
|
||||
|
||||
class TestMCP9808(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.power: Pin = power_pin
|
||||
cls.alert: Pin = alert_pin
|
||||
cls.i2c: SoftI2C = i2c_bus
|
||||
cls.sensor = MCP9808(cls.i2c)
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.power.off()
|
||||
sleep_ms(20)
|
||||
self.power.on()
|
||||
sleep_ms(2000)
|
||||
|
||||
def test_powerup_defaults(self) -> None:
|
||||
self.assertEqual(self.sensor.hyst_mode, mcp9808.HYST_00)
|
||||
self.assertFalse(self.sensor.shdn)
|
||||
self.assertFalse(self.sensor.crit_lock)
|
||||
self.assertFalse(self.sensor.alerts_lock)
|
||||
self.assertFalse(self.sensor.irq_clear_bit)
|
||||
self.assertFalse(self.sensor.alert)
|
||||
self.assertFalse(self.sensor.alert_ctrl)
|
||||
self.assertFalse(self.sensor.alert_sel)
|
||||
self.assertFalse(self.sensor.alert_pol)
|
||||
self.assertFalse(self.sensor.alert_mode)
|
||||
|
||||
def test_hysteresis_set(self) -> None:
|
||||
self.sensor.hyst_mode = mcp9808.HYST_15
|
||||
self.assertEqual(self.sensor.hyst_mode, mcp9808.HYST_15)
|
||||
self.sensor.hyst_mode = mcp9808.HYST_30
|
||||
self.assertEqual(self.sensor.hyst_mode, mcp9808.HYST_30)
|
||||
self.sensor.hyst_mode = mcp9808.HYST_60
|
||||
self.assertEqual(self.sensor.hyst_mode, mcp9808.HYST_60)
|
||||
self.sensor.hyst_mode = mcp9808.HYST_00
|
||||
self.assertEqual(self.sensor.hyst_mode, mcp9808.HYST_00)
|
||||
|
||||
def test_shutdown(self) -> None:
|
||||
self.sensor.shutdown()
|
||||
self.assertTrue(self.sensor.shdn)
|
||||
self.sensor.wake()
|
||||
self.assertFalse(self.sensor.shdn)
|
||||
|
||||
def test_crit_lock(self) -> None:
|
||||
# Lock critical limit register
|
||||
self.sensor.lock_crit_limit()
|
||||
# Check if the critical limit register is locked
|
||||
self.assertTrue(self.sensor.crit_lock)
|
||||
# Try to enable alerts
|
||||
self.sensor.enable_alert()
|
||||
# Alerts should not be enabled
|
||||
self.assertFalse(self.sensor.alert_ctrl)
|
||||
# Reset sensor
|
||||
self.setUp()
|
||||
# Check if the critical limit register is unlocked
|
||||
self.assertFalse(self.sensor.crit_lock)
|
||||
|
||||
def test_alerts_lock(self) -> None:
|
||||
# Lock alerts limit registers
|
||||
self.sensor.lock_alerts_limit()
|
||||
# Check if the alerts limit registers are locked
|
||||
self.assertTrue(self.sensor.alerts_lock)
|
||||
# Try to enable alerts
|
||||
self.sensor.enable_alert()
|
||||
# Alerts should not be enabled
|
||||
self.assertTrue(self.sensor.alerts_lock)
|
||||
# Reset sensor
|
||||
self.setUp()
|
||||
# Check if the alerts limit registers are unlocked
|
||||
self.assertFalse(self.sensor.alerts_lock)
|
||||
|
||||
def test_alert_control(self) -> None:
|
||||
# Get current temperature
|
||||
temp: float = self.sensor.get_temperature()
|
||||
# Enable alerts
|
||||
self.sensor.enable_alert()
|
||||
# Check if alerts are enabled
|
||||
self.assertTrue(self.sensor.alert_ctrl)
|
||||
# Set lower limit to current temperature + 10°C
|
||||
self.sensor.set_alert_lower_limit(temp + 10)
|
||||
sleep_ms(10)
|
||||
# Check if hardware alert is triggered
|
||||
self.assertEqual(self.alert.value(), 0)
|
||||
# Disable alerts
|
||||
self.sensor.disable_alert()
|
||||
# Check if alerts are disabled
|
||||
self.assertFalse(self.sensor.alert_ctrl)
|
||||
# Check if hardware alert is cleared
|
||||
self.assertEqual(self.alert.value(), 1)
|
||||
|
||||
def test_comp_lower_alerts(self) -> None:
|
||||
# Get current temperature
|
||||
temp: float = self.sensor.get_temperature()
|
||||
# Check if alert is disabled and in comparator mode
|
||||
self.assertFalse(self.sensor.alert_ctrl)
|
||||
self.assertFalse(self.sensor.alert_mode)
|
||||
# Set upper limit to 100°C
|
||||
# crit limit to 100°C
|
||||
# lower limit to current temperature + 10°C
|
||||
self.sensor.set_alert_upper_limit(100)
|
||||
self.sensor.set_alert_crit_limit(100)
|
||||
self.sensor.set_alert_lower_limit(temp + 10)
|
||||
# Enable alerts
|
||||
self.sensor.enable_alert()
|
||||
sleep_ms(10)
|
||||
# Check if hardware alert is triggered
|
||||
self.assertEqual(self.alert.value(), 0)
|
||||
# Check if correct alert trigger bit is set
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, False, True))
|
||||
# Set lower limit to current temperature - 10°C (simulating temperature increase)
|
||||
self.sensor.set_alert_lower_limit(temp - 10)
|
||||
sleep_ms(10)
|
||||
# Check if hardware alert is cleared
|
||||
self.assertEqual(self.alert.value(), 1)
|
||||
# Check if correct alert trigger bit is cleared
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, False, False))
|
||||
|
||||
def test_comp_upper_alerts(self) -> None:
|
||||
# Get current temperature
|
||||
temp: float = self.sensor.get_temperature()
|
||||
# Check if alert is disabled and in comparator mode
|
||||
self.assertFalse(self.sensor.alert_ctrl)
|
||||
self.assertFalse(self.sensor.alert_mode)
|
||||
# Set lower limit to 0°C
|
||||
# crit limit to 100°C
|
||||
# upper limit to current temperature - 10°C
|
||||
self.sensor.set_alert_lower_limit(0)
|
||||
self.sensor.set_alert_crit_limit(100)
|
||||
self.sensor.set_alert_upper_limit(temp - 10)
|
||||
# Enable alerts
|
||||
self.sensor.enable_alert()
|
||||
sleep_ms(10)
|
||||
# Check if hardware alert is triggered
|
||||
self.assertEqual(self.alert.value(), 0)
|
||||
# Check if correct alert trigger bit is set
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, True, False))
|
||||
# Set lower limit to current temperature + 10°C (simulating temperature drop)
|
||||
self.sensor.set_alert_upper_limit(temp + 10)
|
||||
sleep_ms(10)
|
||||
# Check if hardware alert is cleared
|
||||
self.assertEqual(self.alert.value(), 1)
|
||||
# Check if correct alert trigger bit is cleared
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, False, False))
|
||||
|
||||
def test_comp_crit_alerts(self) -> None:
|
||||
# Get current temperature
|
||||
temp: float = self.sensor.get_temperature()
|
||||
# Check if alert is disabled and in comparator mode
|
||||
self.assertFalse(self.sensor.alert_ctrl)
|
||||
self.assertFalse(self.sensor.alert_mode)
|
||||
# Set lower limit to 0°C
|
||||
# upper limit to 100°C
|
||||
# crit limit to current temperature - 10°C
|
||||
self.sensor.set_alert_lower_limit(0)
|
||||
self.sensor.set_alert_upper_limit(100)
|
||||
self.sensor.set_alert_crit_limit(temp - 10)
|
||||
# Enable alerts
|
||||
self.sensor.enable_alert()
|
||||
sleep_ms(10)
|
||||
# Check if hardware alert is triggered
|
||||
self.assertEqual(self.alert.value(), 0)
|
||||
# Check if correct alert trigger bit is set
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (True, False, False))
|
||||
# Set lower limit to current temperature + 10°C (simulating temperature drop)
|
||||
self.sensor.set_alert_crit_limit(temp + 10)
|
||||
sleep_ms(10)
|
||||
# Check if hardware alert is cleared
|
||||
self.assertEqual(self.alert.value(), 1)
|
||||
# Check if correct alert trigger bit is cleared
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, False, False))
|
||||
|
||||
def test_irq_lower_alerts(self) -> None:
|
||||
# Get current temperature
|
||||
temp: float = self.sensor.get_temperature()
|
||||
# Check if alert is disabled and in comparator mode
|
||||
self.assertFalse(self.sensor.alert_ctrl)
|
||||
self.assertFalse(self.sensor.alert_mode)
|
||||
# Set and check alert mode to IRQ
|
||||
self.sensor.set_alert_mode(irq=True)
|
||||
self.assertTrue(self.sensor.alert_mode)
|
||||
# Set lower limit to current temperature + 10°C
|
||||
# upper limit to 100°C
|
||||
# crit limit to 100°C
|
||||
self.sensor.set_alert_lower_limit(temp + 10)
|
||||
self.sensor.set_alert_upper_limit(100)
|
||||
self.sensor.set_alert_crit_limit(100)
|
||||
# Enable alerts
|
||||
self.sensor.enable_alert()
|
||||
sleep_ms(10)
|
||||
# Check if hardware IRQ is triggered
|
||||
self.assertEqual(self.alert.value(), 0)
|
||||
# Check if correct alert trigger bit is set
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, False, True))
|
||||
# Clear IRQ
|
||||
self.sensor.irq_clear()
|
||||
# Check if hardware IRQ is cleared
|
||||
self.assertEqual(self.alert.value(), 1)
|
||||
# Check if correct alert trigger bit is still set
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, False, True))
|
||||
# Set lower limit to current temperature - 10°C (simulating temperature increase)
|
||||
self.sensor.set_alert_lower_limit(temp - 10)
|
||||
sleep_ms(10)
|
||||
# Check if hardware IRQ is triggered
|
||||
self.assertEqual(self.alert.value(), 0)
|
||||
# Check if correct alert trigger bit is cleared
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, False, False))
|
||||
# Clear IRQ
|
||||
self.sensor.irq_clear()
|
||||
# Check if alert is cleared
|
||||
self.assertEqual(self.alert.value(), 1)
|
||||
# Check if correct alert trigger bit is cleared
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, False, False))
|
||||
|
||||
def test_irq_upper_alerts(self) -> None:
|
||||
# Get current temperature
|
||||
temp: float = self.sensor.get_temperature()
|
||||
# Check if alert is disabled and in comparator mode
|
||||
self.assertFalse(self.sensor.alert_ctrl)
|
||||
self.assertFalse(self.sensor.alert_mode)
|
||||
# Set and check alert mode to IRQ
|
||||
self.sensor.set_alert_mode(irq=True)
|
||||
self.assertTrue(self.sensor.alert_mode)
|
||||
# Set lower limit 0°C
|
||||
# upper limit to current temperature - 10°C
|
||||
# crit limit to 100°C
|
||||
self.sensor.set_alert_lower_limit(0)
|
||||
self.sensor.set_alert_upper_limit(temp - 10)
|
||||
self.sensor.set_alert_crit_limit(100)
|
||||
# Enable alerts
|
||||
self.sensor.enable_alert()
|
||||
sleep_ms(10)
|
||||
# Check if hardware IRQ is triggered
|
||||
self.assertEqual(self.alert.value(), 0)
|
||||
# Check if correct alert trigger bit is set
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, True, False))
|
||||
# Clear IRQ
|
||||
self.sensor.irq_clear()
|
||||
# Check if hardware IRQ is cleared
|
||||
self.assertEqual(self.alert.value(), 1)
|
||||
# Check if correct alert trigger bit is still set
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, True, False))
|
||||
# Set upper limit to current temperature + 10°C (simulating temperature drop)
|
||||
self.sensor.set_alert_upper_limit(temp + 10)
|
||||
sleep_ms(10)
|
||||
# Check if hardware IRQ is triggered
|
||||
self.assertEqual(self.alert.value(), 0)
|
||||
# Check if correct alert trigger bit is cleared
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, False, False))
|
||||
# Clear IRQ
|
||||
self.sensor.irq_clear()
|
||||
# Check if alert is cleared
|
||||
self.assertEqual(self.alert.value(), 1)
|
||||
# Check if correct alert trigger bit is cleared
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, False, False))
|
||||
|
||||
def test_irq_crit_alerts(self) -> None:
|
||||
# Get current temperature
|
||||
temp: float = self.sensor.get_temperature()
|
||||
# Check if alert is disabled and in comparator mode
|
||||
self.assertFalse(self.sensor.alert_ctrl)
|
||||
self.assertFalse(self.sensor.alert_mode)
|
||||
# Set and check alert mode to IRQ
|
||||
self.sensor.set_alert_mode(irq=True)
|
||||
self.assertTrue(self.sensor.alert_mode)
|
||||
# Set lower limit 0°C
|
||||
# upper limit to current temperature - 10°C
|
||||
# crit limit to 100°C
|
||||
self.sensor.set_alert_lower_limit(0)
|
||||
self.sensor.set_alert_upper_limit(temp - 10)
|
||||
self.sensor.set_alert_crit_limit(100)
|
||||
# Enable alerts (first IRQ from upper limit)
|
||||
self.sensor.enable_alert()
|
||||
sleep_ms(10)
|
||||
# Check if alert is triggered
|
||||
self.assertEqual(self.alert.value(), 0)
|
||||
# Check if correct alert trigger bit is set
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, True, False))
|
||||
# Clear IRQ
|
||||
self.sensor.irq_clear()
|
||||
# Check if alert is cleared
|
||||
self.assertEqual(self.alert.value(), 1)
|
||||
# Check if correct alert trigger bit is still set
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, True, False))
|
||||
# Set crit limit to current temperature - 5°C (to trigger second IRQ from crit limit)
|
||||
self.sensor.set_alert_crit_limit(temp - 5)
|
||||
sleep_ms(10)
|
||||
# Check if alert is triggered (second IRQ from crit limit)
|
||||
self.assertEqual(self.alert.value(), 0)
|
||||
# Check if correct alert trigger bit is set
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (True, True, False))
|
||||
# Clear IRQ
|
||||
self.sensor.irq_clear()
|
||||
# Check if alert is NOT cleard
|
||||
self.assertEqual(self.alert.value(), 0)
|
||||
# Check if correct alert trigger bit is still set
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (True, True, False))
|
||||
# Set crit limit to current temperature + 5°C (simulating temperature drop)
|
||||
self.sensor.set_alert_crit_limit(temp + 5)
|
||||
sleep_ms(10)
|
||||
# Check if alert is cleared
|
||||
self.assertEqual(self.alert.value(), 1)
|
||||
# Check if correct alert trigger bit is cleared
|
||||
self.assertEqual(self.sensor.get_alert_triggers(), (False, True, False))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
Ładowanie…
Reference in New Issue