diff --git a/Actuators/DS3234/README.md b/Actuators/DS3234/README.md new file mode 100644 index 0000000..aeca5fe --- /dev/null +++ b/Actuators/DS3234/README.md @@ -0,0 +1,14 @@ +# How to install + +--- + +After [**installing the mpremote package**](https://docs.micropython.org/en/latest/reference/mpremote.html), flash a module to the board using the following command: + +```sh + mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Actuators/DS3234 +``` +Or, if you're running a Windows OS: + +```sh + python -m mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Actuators/DS3234 +``` diff --git a/Actuators/MCP23017/README.md b/Actuators/MCP23017/README.md new file mode 100644 index 0000000..f49b3fa --- /dev/null +++ b/Actuators/MCP23017/README.md @@ -0,0 +1,14 @@ +# How to install + +--- + +After [**installing the mpremote package**](https://docs.micropython.org/en/latest/reference/mpremote.html), flash a module to the board using the following command: + +```sh + mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Actuators/MCP23017 +``` +Or, if you're running a Windows OS: + +```sh + python -m mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Actuators/MCP23017 +``` diff --git a/Communication/RFID/README.md b/Communication/RFID/README.md new file mode 100644 index 0000000..e62c3df --- /dev/null +++ b/Communication/RFID/README.md @@ -0,0 +1,14 @@ +# How to install + +--- + +After [**installing the mpremote package**](https://docs.micropython.org/en/latest/reference/mpremote.html), flash a module to the board using the following command: + +```sh + mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Communication/RFID +``` +Or, if you're running a Windows OS: + +```sh + python -m mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Communication/RFID +``` diff --git a/README.md b/README.md index 1ff33e8..98ebd30 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,42 @@ Each module in the library is designed to be lightweight, readable, and compatib --- +## Currently available MicroPython modules + +### Sensors +- [AD8495](Sensors/AD8495/) +- [APDS9960](Sensors/APDS9960/) +- [BME280](Sensors/BME280/) +- [BME680](Sensors/BME680/) +- [BME688](Sensors/BME688/) +- [BMP180](Sensors/BMP180/) +- [BMP280](Sensors/BMP280/) +- [BMP388](Sensors/BMP388/) +- [HallEffect](Sensors/HallEffect/) +- [LaserDistanceSensor](Sensors/LaserDistanceSensor/) +- [LTR507](Sensors/LTR507/) +- [ObstacleSensor](Sensors/ObstacleSensor/) +- [PirSensor](Sensors/PirSensor/) +- [RotaryEncoder](Sensors/RotaryEncoder/) +- [SHTC3](Sensors/SHTC3/) +- [TMP117](Sensors/TMP117/) +- [UltrasonicSensor](Sensors/UltrasonicSensor/) + +### Actuators +- [DRV8825](Actuators/DRV8825/) +- [DS3234](Actuators/DS3234/) +- [MCP23017](Actuators/MCP23017/) +- [WS2812](Actuators/WS2812/) + +### Communication +- [RFID](Communication/RFID/) + +### Displays +- [LCD-I2C](Displays/LCD-I2C/) +- [SSD1306](Displays/SSD1306/) + +--- + ## Installation You can install a specific module using mpremote or manually downloading specific files onto the board using an IDE such as [Thonny](https://thonny.org/) diff --git a/Sensors/AD8495/README.md b/Sensors/AD8495/README.md new file mode 100644 index 0000000..8dc3f7c --- /dev/null +++ b/Sensors/AD8495/README.md @@ -0,0 +1,14 @@ +# How to install + +--- + +After [**installing the mpremote package**](https://docs.micropython.org/en/latest/reference/mpremote.html), flash a module to the board using the following command: + +```sh + mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/AD8495 +``` +Or, if you're running a Windows OS: + +```sh + python -m mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/AD8495 +``` diff --git a/Sensors/BME688/README.md b/Sensors/BME688/README.md new file mode 100644 index 0000000..ac78e39 --- /dev/null +++ b/Sensors/BME688/README.md @@ -0,0 +1,14 @@ +# How to install + +--- + +After [**installing the mpremote package**](https://docs.micropython.org/en/latest/reference/mpremote.html), flash a module to the board using the following command: + +```sh + mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/BME688 +``` +Or, if you're running a Windows OS: + +```sh + python -m mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/BME688 +``` diff --git a/Sensors/BMP280/BMP280/Examples/bmp280-forcedMode.py b/Sensors/BMP280/BMP280/Examples/bmp280-forcedMode.py new file mode 100644 index 0000000..f85771d --- /dev/null +++ b/Sensors/BMP280/BMP280/Examples/bmp280-forcedMode.py @@ -0,0 +1,35 @@ +# FILE: bmp280-forcedMode.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: One-shot measurements in forced mode with custom sampling +# WORKS WITH: Pressure & Temperature sensor BMP280 Breakout +# LAST UPDATED: 2025-01-15 + +from machine import Pin, I2C +from bmp280 import BMP280 + +# Import constants from separate file to keep examples stable if defaults change. +from bmp280_constants import ( + FORCED_MODE, + OVERSAMPLING_X8, + OVERSAMPLING_X2, + IIR_FILTER_2, +) +import time + +# If you aren't using the Qwiic connector, manually enter your I2C pins +# i2c = I2C(0, scl=Pin(22), sda=Pin(21)) +# bmp280 = BMP280(i2c) + +# Initialize sensor over Qwiic +bmp280 = BMP280() + +# Configure sampling rates and filter +bmp280.setOversampling( + presOversampling=OVERSAMPLING_X8, tempOversampling=OVERSAMPLING_X2 +) +bmp280.setIIRFilter(IIR_FILTER_2) +bmp280.setMode(FORCED_MODE) +while True: + temperature, pressure, altitude = bmp280.getMeasurements() + print("{:.2f}*C {:.2f}hPa {:.2f}m".format(temperature, pressure, altitude)) + time.sleep(1) diff --git a/Sensors/BMP280/BMP280/Examples/bmp280-normalMode.py b/Sensors/BMP280/BMP280/Examples/bmp280-normalMode.py new file mode 100644 index 0000000..55ba42d --- /dev/null +++ b/Sensors/BMP280/BMP280/Examples/bmp280-normalMode.py @@ -0,0 +1,38 @@ +# FILE: bmp280-normalMode.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: Continuous measurements in normal mode with custom sampling +# WORKS WITH: Pressure & Temperature sensor BMP280 Breakout +# LAST UPDATED: 2025-01-15 + +from machine import Pin, I2C +from bmp280 import BMP280 + +# Import constants from separate file to keep examples stable if defaults change. +from bmp280_constants import ( + NORMAL_MODE, + OVERSAMPLING_X4, + OVERSAMPLING_X2, + IIR_FILTER_4, + STANDBY_1000MS, +) +import time + +# If you aren't using the Qwiic connector, manually enter your I2C pins +# i2c = I2C(0, scl=Pin(22), sda=Pin(21)) +# bmp280 = BMP280(i2c) + +# Initialize sensor over Qwiic +bmp280 = BMP280() + +# Configure sampling rates and filter +bmp280.setOversampling( + presOversampling=OVERSAMPLING_X4, tempOversampling=OVERSAMPLING_X2 +) +bmp280.setIIRFilter(IIR_FILTER_4) +bmp280.setStandbyTime(STANDBY_1000MS) +bmp280.setMode(NORMAL_MODE) + +while True: + temperature, pressure, altitude = bmp280.getMeasurements() + print("{:.2f}*C {:.2f}hPa {:.2f}m".format(temperature, pressure, altitude)) + time.sleep(1) diff --git a/Sensors/BMP280/BMP280/Examples/bmp280-sleepMode.py b/Sensors/BMP280/BMP280/Examples/bmp280-sleepMode.py new file mode 100644 index 0000000..10b257d --- /dev/null +++ b/Sensors/BMP280/BMP280/Examples/bmp280-sleepMode.py @@ -0,0 +1,43 @@ +# FILE: bmp280-sleepMode.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: Sleep mode example with manual wake for a single measurement +# WORKS WITH: Pressure & Temperature sensor BMP280 Breakout +# LAST UPDATED: 2025-01-15 + +from machine import Pin, I2C +from bmp280 import BMP280 + +# Import constants from separate file to keep examples stable if defaults change. +from bmp280_constants import ( + SLEEP_MODE, + FORCED_MODE, + OVERSAMPLING_X1, + IIR_FILTER_OFF, +) +import time + +# If you aren't using the Qwiic connector, manually enter your I2C pins +# i2c = I2C(0, scl=Pin(22), sda=Pin(21)) +# bmp280 = BMP280(i2c) + +# Initialize sensor over Qwiic +bmp280 = BMP280() + +# Configure low-power sampling +bmp280.setOversampling( + presOversampling=OVERSAMPLING_X1, tempOversampling=OVERSAMPLING_X1 +) +bmp280.setIIRFilter(IIR_FILTER_OFF) +bmp280.setMode(SLEEP_MODE) + +while True: + # Wake up for a single measurement + bmp280.setMode(FORCED_MODE) + time.sleep(0.05) + + temperature, pressure, altitude = bmp280.getMeasurements() + print("{:.2f}*C {:.2f}hPa {:.2f}m".format(temperature, pressure, altitude)) + + # Back to sleep between samples + bmp280.setMode(SLEEP_MODE) + time.sleep(2) diff --git a/Sensors/BMP280/BMP280/bmp280.py b/Sensors/BMP280/BMP280/bmp280.py new file mode 100644 index 0000000..f093741 --- /dev/null +++ b/Sensors/BMP280/BMP280/bmp280.py @@ -0,0 +1,200 @@ +# FILE: bmp280.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: MicroPython library for the BMP280 temperature and pressure sensor +# LAST UPDATED: 2025-01-15 + +import time +from machine import I2C, Pin +from os import uname +from bmp280_constants import * + + +class BMP280: + """ + MicroPython class for the Bosch BMP280 temperature and pressure sensor. + Supports temperature, pressure, and altitude measurements. + """ + + def __init__(self, i2c=None, address=BMP280_I2C_ALT_ADDR): + """ + Initialize the BMP280 sensor. + + :param i2c: Initialized I2C object + :param address: I2C address of the sensor + """ + if i2c != None: + self.i2c = i2c + else: + if uname().sysname in ("esp32", "esp8266", "Soldered Dasduino CONNECTPLUS"): + self.i2c = I2C(0, scl=Pin(22), sda=Pin(21)) + else: + raise Exception("Board not recognized, enter I2C pins manually") + + self.address = address + self.seaLevelPressure = 1013.23 + self.tFine = 0.0 + self.ctrlMeas = 0x00 + self.config = 0x00 + + if not self.begin(): + raise Exception( + "BMP280 initialization failed! Check wiring and I2C address." + ) + + def _read8(self, register): + """Read a single byte from a register.""" + self.i2c.writeto(self.address, bytes([register])) + return self.i2c.readfrom(self.address, 1)[0] + + def _readBytes(self, register, length): + """Read multiple bytes from a register.""" + self.i2c.writeto(self.address, bytes([register])) + return self.i2c.readfrom(self.address, length) + + def _write8(self, register, value): + """Write a single byte to a register.""" + value = value & 0xFF + self.i2c.writeto(self.address, bytes([register, value])) + time.sleep_us(100) + + def begin( + self, + mode=SLEEP_MODE, + presOversampling=OVERSAMPLING_X16, + tempOversampling=OVERSAMPLING_X2, + iirFilter=IIR_FILTER_OFF, + standby=STANDBY_0_5MS, + ): + """Initialize the BMP280 with specified settings.""" + self.reset() + time.sleep_ms(10) + + chipId = self._read8(BMP280_CHIP_ID) + if chipId != BMP280_ID: + return False + + self._loadCalibrationParams() + self.setIIRFilter(iirFilter) + self.setStandbyTime(standby) + self.setOversampling(presOversampling, tempOversampling) + self.setMode(mode) + + return True + + def reset(self): + """Soft reset the BMP280 sensor.""" + self._write8(BMP280_RESET, RESET_CODE) + + def _loadCalibrationParams(self): + """Read calibration parameters from the sensor.""" + calib = self._readBytes(BMP280_CALIB, 24) + + self.digT1 = calib[1] << 8 | calib[0] + self.digT2 = self._toSigned(calib[3] << 8 | calib[2], 16) + self.digT3 = self._toSigned(calib[5] << 8 | calib[4], 16) + + self.digP1 = calib[7] << 8 | calib[6] + self.digP2 = self._toSigned(calib[9] << 8 | calib[8], 16) + self.digP3 = self._toSigned(calib[11] << 8 | calib[10], 16) + self.digP4 = self._toSigned(calib[13] << 8 | calib[12], 16) + self.digP5 = self._toSigned(calib[15] << 8 | calib[14], 16) + self.digP6 = self._toSigned(calib[17] << 8 | calib[16], 16) + self.digP7 = self._toSigned(calib[19] << 8 | calib[18], 16) + self.digP8 = self._toSigned(calib[21] << 8 | calib[20], 16) + self.digP9 = self._toSigned(calib[23] << 8 | calib[22], 16) + + def _toSigned(self, value, bits): + """Convert unsigned value to signed.""" + if value & (1 << (bits - 1)): + value -= 1 << bits + return value + + def setMode(self, mode): + """Set sensor operating mode.""" + self.ctrlMeas = (self.ctrlMeas & 0xFC) | (mode & 0x03) + self._write8(BMP280_CTRL_MEAS, self.ctrlMeas) + + def setOversampling(self, presOversampling, tempOversampling): + """Set oversampling for pressure and temperature.""" + self.ctrlMeas = ( + ((tempOversampling & 0x07) << 5) + | ((presOversampling & 0x07) << 2) + | (self.ctrlMeas & 0x03) + ) + self._write8(BMP280_CTRL_MEAS, self.ctrlMeas) + + def setIIRFilter(self, iirFilter): + """Set IIR filter setting.""" + self.config = (self.config & 0xE3) | ((iirFilter & 0x07) << 2) + self._write8(BMP280_CONFIG, self.config) + + def setStandbyTime(self, standby): + """Set standby time between measurements in normal mode.""" + self.config = (self.config & 0x1F) | ((standby & 0x07) << 5) + self._write8(BMP280_CONFIG, self.config) + + def setSeaLevelPressure(self, pressure): + """Set sea level pressure for altitude calculation.""" + self.seaLevelPressure = pressure + + def _readRawTemp(self): + data = self._readBytes(BMP280_TEMP_MSB, 3) + return (data[0] << 12) | (data[1] << 4) | (data[2] >> 4) + + def _readRawPress(self): + data = self._readBytes(BMP280_PRESS_MSB, 3) + return (data[0] << 12) | (data[1] << 4) | (data[2] >> 4) + + def _compensateTemp(self, adcTemp): + var1 = (adcTemp / 16384.0 - self.digT1 / 1024.0) * self.digT2 + var2 = ((adcTemp / 131072.0 - self.digT1 / 8192.0) ** 2) * self.digT3 + self.tFine = var1 + var2 + return self.tFine / 5120.0 + + def _compensatePress(self, adcPress): + var1 = self.tFine / 2.0 - 64000.0 + var2 = var1 * var1 * self.digP6 / 32768.0 + var2 = var2 + var1 * self.digP5 * 2.0 + var2 = var2 / 4.0 + self.digP4 * 65536.0 + var1 = (self.digP3 * var1 * var1 / 524288.0 + self.digP2 * var1) / 524288.0 + var1 = (1.0 + var1 / 32768.0) * self.digP1 + if var1 == 0: + return 0 + pressure = 1048576.0 - adcPress + pressure = (pressure - var2 / 4096.0) * 6250.0 / var1 + var1 = self.digP9 * pressure * pressure / 2147483648.0 + var2 = pressure * self.digP8 / 32768.0 + pressure = pressure + (var1 + var2 + self.digP7) / 16.0 + return pressure + + def getTemperature(self): + """Get temperature in Celsius.""" + adcTemp = self._readRawTemp() + return self._compensateTemp(adcTemp) + + def getPressure(self): + """Get pressure in hPa.""" + adcTemp = self._readRawTemp() + self._compensateTemp(adcTemp) + adcPress = self._readRawPress() + pressure = self._compensatePress(adcPress) + return pressure / 100.0 + + def getMeasurements(self): + """Get temperature, pressure, and altitude measurements.""" + temperature = self.getTemperature() + pressure = self.getPressure() + altitude = ( + (pow(self.seaLevelPressure / pressure, 0.190223) - 1.0) + * (temperature + 273.15) + / 0.0065 + ) + return (temperature, pressure, altitude) + + def getAltitude(self): + """Get altitude in meters.""" + return self.getMeasurements()[2] + + def readAllValues(self): + """Read temperature, pressure, and altitude.""" + return self.getMeasurements() diff --git a/Sensors/BMP280/BMP280/bmp280_constants.py b/Sensors/BMP280/BMP280/bmp280_constants.py new file mode 100644 index 0000000..ebf09e4 --- /dev/null +++ b/Sensors/BMP280/BMP280/bmp280_constants.py @@ -0,0 +1,52 @@ +# FILE: bmp280_constants.py +# AUTHOR: Josip Šimun Kuči @ Soldered +# BRIEF: Constants used in the BMP280 module +# LAST UPDATED: 2025-01-15 + +# BMP280 I2C addresses +BMP280_I2C_ADDR = 0x77 +BMP280_I2C_ALT_ADDR = 0x76 +BMP280_ID = 0x58 + +# Registers +BMP280_CHIP_ID = 0xD0 +BMP280_RESET = 0xE0 +BMP280_STATUS = 0xF3 +BMP280_CTRL_MEAS = 0xF4 +BMP280_CONFIG = 0xF5 +BMP280_PRESS_MSB = 0xF7 +BMP280_TEMP_MSB = 0xFA +BMP280_CALIB = 0x88 + +# Reset command +RESET_CODE = 0xB6 + +# Modes +SLEEP_MODE = 0x00 +FORCED_MODE = 0x01 +NORMAL_MODE = 0x03 + +# Oversampling +OVERSAMPLING_SKIP = 0x00 +OVERSAMPLING_X1 = 0x01 +OVERSAMPLING_X2 = 0x02 +OVERSAMPLING_X4 = 0x03 +OVERSAMPLING_X8 = 0x04 +OVERSAMPLING_X16 = 0x05 + +# Standby time +STANDBY_0_5MS = 0x00 +STANDBY_62_5MS = 0x01 +STANDBY_125MS = 0x02 +STANDBY_250MS = 0x03 +STANDBY_500MS = 0x04 +STANDBY_1000MS = 0x05 +STANDBY_2000MS = 0x06 +STANDBY_4000MS = 0x07 + +# IIR filter +IIR_FILTER_OFF = 0x00 +IIR_FILTER_2 = 0x01 +IIR_FILTER_4 = 0x02 +IIR_FILTER_8 = 0x03 +IIR_FILTER_16 = 0x04 diff --git a/Sensors/BMP280/README.md b/Sensors/BMP280/README.md new file mode 100644 index 0000000..40385df --- /dev/null +++ b/Sensors/BMP280/README.md @@ -0,0 +1,14 @@ +# How to install + +--- + +After [**installing the mpremote package**](https://docs.micropython.org/en/latest/reference/mpremote.html), flash a module to the board using the following command: + +```sh + mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/BMP280 +``` +Or, if you're running a Windows OS: + +```sh + python -m mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/BMP280 +``` diff --git a/Sensors/BMP280/package.json b/Sensors/BMP280/package.json new file mode 100644 index 0000000..576457c --- /dev/null +++ b/Sensors/BMP280/package.json @@ -0,0 +1,26 @@ +{ + "urls": [ + [ + "bmp280.py", + "github:SolderedElectronics/Soldered-MicroPython-Modules/Sensors/BMP280/BMP280/bmp280.py" + ], + [ + "bmp280_constants.py", + "github:SolderedElectronics/Soldered-MicroPython-Modules/Sensors/BMP280/BMP280/bmp280_constants.py" + ], + [ + "Examples/bmp280-normalMode.py", + "github:SolderedElectronics/Soldered-MicroPython-Modules/Sensors/BMP280/BMP280/Examples/bmp280-normalMode.py" + ], + [ + "Examples/bmp280-forcedMode.py", + "github:SolderedElectronics/Soldered-MicroPython-Modules/Sensors/BMP280/BMP280/Examples/bmp280-forcedMode.py" + ], + [ + "Examples/bmp280-sleepMode.py", + "github:SolderedElectronics/Soldered-MicroPython-Modules/Sensors/BMP280/BMP280/Examples/bmp280-sleepMode.py" + ] + ], + "deps": [], + "version": "1.0" +} diff --git a/Sensors/BMP388/BMP388/Examples/bmp388-forcedMeasurement.py b/Sensors/BMP388/BMP388/Examples/bmp388-forcedMeasurement.py index fab4457..c49c0a3 100644 --- a/Sensors/BMP388/BMP388/Examples/bmp388-forcedMeasurement.py +++ b/Sensors/BMP388/BMP388/Examples/bmp388-forcedMeasurement.py @@ -26,7 +26,9 @@ while True: temperature, pressure, altitude = bmp388.getMeasurements() if temperature is not None: - print("{:.2f}*C {:.2f}hPa {:.2f}m".format(temperature, pressure, altitude)) + print( + "{:.2f}*C {:.2f}hPa {:.2f}m".format(temperature, pressure, altitude) + ) break time.sleep(0.02) diff --git a/Sensors/BMP388/BMP388/Examples/bmp388-forcedModeWithInterrupt.py b/Sensors/BMP388/BMP388/Examples/bmp388-forcedModeWithInterrupt.py index 30ed9e4..69ba2b3 100644 --- a/Sensors/BMP388/BMP388/Examples/bmp388-forcedModeWithInterrupt.py +++ b/Sensors/BMP388/BMP388/Examples/bmp388-forcedModeWithInterrupt.py @@ -15,6 +15,7 @@ def interruptHandler(pin): global dataReady dataReady = True + # If you aren't using the Qwiic connector, manually enter your I2C pins # i2c = I2C(0, scl=Pin(22), sda=Pin(21)) # bmp388 = BMP388(i2c) diff --git a/Sensors/BMP388/BMP388/Examples/bmp388-normalMode.py b/Sensors/BMP388/BMP388/Examples/bmp388-normalMode.py new file mode 100644 index 0000000..8c8b68d --- /dev/null +++ b/Sensors/BMP388/BMP388/Examples/bmp388-normalMode.py @@ -0,0 +1,32 @@ +# FILE: bmp388-normalMode.py +# AUTHOR: Borna Biro for soldered.com +# BRIEF: Basic usage example for the BMP388 in normal mode +# WORKS WITH: Pressure & Temperature sensor BMP388 Breakout: www.solde.red/333316 +# LAST UPDATED: 2025-01-15 + +from machine import Pin, I2C +from bmp388 import BMP388 +from bmp388_constants import TIME_STANDBY_1280MS +import time + +# If you aren't using the Qwiic connector, manually enter your I2C pins +# i2c = I2C(0, scl=Pin(22), sda=Pin(21)) +# bmp388 = BMP388(i2c) + +# Initialize sensor over Qwiic +bmp388 = BMP388() + +# Set sea level pressure for accurate altitude readings +bmp388.setSeaLevelPressure(1025.0) + +# Set standby time to roughly 1.3 seconds +bmp388.setTimeStandby(TIME_STANDBY_1280MS) + +# Start continuous measurement in normal mode +bmp388.startNormalConversion() + +while True: + temperature, pressure, altitude = bmp388.getMeasurements() + if temperature is not None: + print("{:.2f}*C {:.2f}hPa {:.2f}m".format(temperature, pressure, altitude)) + time.sleep(0.25) diff --git a/Sensors/BMP388/BMP388/Examples/bmp388-normalModeWithFifo.py b/Sensors/BMP388/BMP388/Examples/bmp388-normalModeWithFifo.py index 6a4100d..aa04e80 100644 --- a/Sensors/BMP388/BMP388/Examples/bmp388-normalModeWithFifo.py +++ b/Sensors/BMP388/BMP388/Examples/bmp388-normalModeWithFifo.py @@ -39,9 +39,11 @@ if status == FIFO_DATA_READY: for i in range(len(temperatures)): altitude = altitudes[i] if i < len(altitudes) else 0.0 - print("{}: {:.2f}*C {:.2f}hPa {:.2f}m".format( - i + 1, temperatures[i], pressures[i], altitude - )) + print( + "{}: {:.2f}*C {:.2f}hPa {:.2f}m".format( + i + 1, temperatures[i], pressures[i], altitude + ) + ) print("Sensor Time: {} ms".format(sensorTime)) print() diff --git a/Sensors/BMP388/BMP388/Examples/bmp388-normalModeWithInterrupt.py b/Sensors/BMP388/BMP388/Examples/bmp388-normalModeWithInterrupt.py index f489557..236bd49 100644 --- a/Sensors/BMP388/BMP388/Examples/bmp388-normalModeWithInterrupt.py +++ b/Sensors/BMP388/BMP388/Examples/bmp388-normalModeWithInterrupt.py @@ -16,6 +16,7 @@ def interruptHandler(pin): global dataReady dataReady = True + # If you aren't using the Qwiic connector, manually enter your I2C pins # i2c = I2C(0, scl=Pin(22), sda=Pin(21)) # bmp388 = BMP388(i2c) @@ -43,6 +44,8 @@ def interruptHandler(pin): if dataReady: temperature, pressure, altitude = bmp388.getMeasurements() if temperature is not None: - print("{:.2f}*C {:.2f}hPa {:.2f}m".format(temperature, pressure, altitude)) + print( + "{:.2f}*C {:.2f}hPa {:.2f}m".format(temperature, pressure, altitude) + ) dataReady = False time.sleep(0.01) diff --git a/Sensors/BMP388/BMP388/bmp388.py b/Sensors/BMP388/BMP388/bmp388.py index a3b31ea..8361054 100644 --- a/Sensors/BMP388/BMP388/bmp388.py +++ b/Sensors/BMP388/BMP388/bmp388.py @@ -9,6 +9,7 @@ from os import uname from bmp388_constants import * + class BMP388: """ MicroPython class for the Bosch BMP388 temperature and pressure sensor. @@ -42,7 +43,9 @@ def __init__(self, i2c=None, address=BMP388_I2C_ALT_ADDR): self.ifConfig = 0x00 self.altEnable = False if not self.begin(): - raise Exception("BMP388 initialization failed! Check wiring and I2C address.") + raise Exception( + "BMP388 initialization failed! Check wiring and I2C address." + ) # Read 8-bit unsigned value from a register def _read8(self, register): @@ -54,7 +57,9 @@ def _read8(self, register): value = data[0] return value except OSError as e: - raise Exception(f"I2C read error at address 0x{self.address:02X}, register 0x{register:02X}: {e}") + raise Exception( + f"I2C read error at address 0x{self.address:02X}, register 0x{register:02X}: {e}" + ) # Read multiple bytes from a register def _readBytes(self, register, length): @@ -65,7 +70,9 @@ def _readBytes(self, register, length): data = self.i2c.readfrom(self.address, length) return data except OSError as e: - raise Exception(f"I2C read error at address 0x{self.address:02X}, register 0x{register:02X}: {e}") + raise Exception( + f"I2C read error at address 0x{self.address:02X}, register 0x{register:02X}: {e}" + ) # Write a byte to a register def _write8(self, register, value): @@ -77,7 +84,9 @@ def _write8(self, register, value): self.i2c.writeto(self.address, bytes([register, value])) time.sleep_us(100) # Small delay to ensure write completes except OSError as e: - raise Exception(f"I2C write error at address 0x{self.address:02X}, register 0x{register:02X}: {e}") + raise Exception( + f"I2C write error at address 0x{self.address:02X}, register 0x{register:02X}: {e}" + ) def _waitCmdReady(self, timeoutMs=100): """Wait until the sensor reports cmd_rdy in STATUS (bit 4).""" @@ -105,20 +114,24 @@ def _loadCalibrationParams(self): # Unpack parameters matching the datasheet layout # All values are little-endian - self.param_T1 = struct.unpack("> 4 if finalMode != mode: - print(f"Warning: Failed to set mode. Expected {mode}, got {finalMode}. PWR_CTRL=0x{finalPwrCtrl:02X}") + print( + f"Warning: Failed to set mode. Expected {mode}, got {finalMode}. PWR_CTRL=0x{finalPwrCtrl:02X}" + ) return True @@ -240,13 +255,15 @@ def setMode(self, mode): # Bits 2-3 are reserved and must be 0 (already cleared by masking with 0x03) # Clear mode bits (4-5), then set new mode self.pwrCtrl = (currentPwrCtrl & 0x03) | ((mode & 0x03) << 4) - + # Write the complete register value if not self._waitCmdReady(timeoutMs=100): pass self._write8(BMP388_PWR_CTRL, self.pwrCtrl) - time.sleep_ms(20) # Wait longer for register to update (sensor may need time to process mode change) - + time.sleep_ms( + 20 + ) # Wait longer for register to update (sensor may need time to process mode change) + # Verify the write succeeded verifyPwrCtrl = self._read8(BMP388_PWR_CTRL) if verifyPwrCtrl != self.pwrCtrl: @@ -267,7 +284,7 @@ def setMode(self, mode): verifyPwrCtrl = self._read8(BMP388_PWR_CTRL) if verifyPwrCtrl != self.pwrCtrl: pass - + # Update internal state to match what's actually in the register self.pwrCtrl = verifyPwrCtrl @@ -318,7 +335,9 @@ def _dataReady(self, debug=False): if debug: currentPwrCtrl = self._read8(BMP388_PWR_CTRL) - print(f"INT_STATUS: 0x{intStatus:02X} (drdy={drdy}), PWR_CTRL: 0x{currentPwrCtrl:02X} (mode={mode})") + print( + f"INT_STATUS: 0x{intStatus:02X} (drdy={drdy}), PWR_CTRL: 0x{currentPwrCtrl:02X} (mode={mode})" + ) if drdy: # If in FORCED_MODE, switch back to SLEEP_MODE @@ -469,10 +488,14 @@ def getMeasurements(self, debug=False): return (temperature, pressure, altitude) - def enableInterrupt(self, outputDrive=PUSH_PULL, activeLevel=ACTIVE_HIGH, latchConfig=UNLATCHED): + def enableInterrupt( + self, outputDrive=PUSH_PULL, activeLevel=ACTIVE_HIGH, latchConfig=UNLATCHED + ): """Enable data-ready interrupt on the INT pin.""" - self.intCtrl = (latchConfig & 0x01) << 2 | (activeLevel & 0x01) << 1 | (outputDrive & 0x01) - self.intCtrl |= (1 << 6) + self.intCtrl = ( + (latchConfig & 0x01) << 2 | (activeLevel & 0x01) << 1 | (outputDrive & 0x01) + ) + self.intCtrl |= 1 << 6 self._write8(BMP388_INT_CTRL, self.intCtrl) def disableInterrupt(self): @@ -495,11 +518,24 @@ def setIntLatchConfig(self, latchConfig): self.intCtrl = (self.intCtrl & ~0x04) | ((latchConfig & 0x01) << 2) self._write8(BMP388_INT_CTRL, self.intCtrl) - def enableFIFO(self, pressEnable=PRESS_ENABLED, altEnable=ALT_ENABLED, timeEnable=TIME_ENABLED, - subsampling=SUBSAMPLING_OFF, dataSelect=FILTERED, stopOnFull=STOP_ON_FULL_ENABLED): + def enableFIFO( + self, + pressEnable=PRESS_ENABLED, + altEnable=ALT_ENABLED, + timeEnable=TIME_ENABLED, + subsampling=SUBSAMPLING_OFF, + dataSelect=FILTERED, + stopOnFull=STOP_ON_FULL_ENABLED, + ): """Enable FIFO operation with the selected configuration.""" self.altEnable = altEnable == ALT_ENABLED - self.fifoConfig1 = (1 << 4) | ((pressEnable & 0x01) << 3) | ((timeEnable & 0x01) << 2) | ((stopOnFull & 0x01) << 1) | 0x01 + self.fifoConfig1 = ( + (1 << 4) + | ((pressEnable & 0x01) << 3) + | ((timeEnable & 0x01) << 2) + | ((stopOnFull & 0x01) << 1) + | 0x01 + ) self.fifoConfig2 = ((dataSelect & 0x07) << 3) | (subsampling & 0x07) self._write8(BMP388_FIFO_CONFIG_1, self.fifoConfig1) self._write8(BMP388_FIFO_CONFIG_2, self.fifoConfig2) @@ -513,7 +549,9 @@ def setFIFONoOfMeasurements(self, noOfMeasurements): """Set FIFO watermark based on number of measurements.""" fifoPress = (self.fifoConfig1 >> 3) & 0x01 fifoTemp = (self.fifoConfig1 >> 4) & 0x01 - fifoWatermark = noOfMeasurements * ((fifoPress | fifoTemp) + 3 * fifoPress + 3 * fifoTemp) + fifoWatermark = noOfMeasurements * ( + (fifoPress | fifoTemp) + 3 * fifoPress + 3 * fifoTemp + ) return self.setFIFOWatermark(fifoWatermark) def setFIFOWatermark(self, fifoWatermark): @@ -549,7 +587,9 @@ def setFIFOSubsampling(self, subsampling): def setFIFODataSelect(self, dataSelect): """Set FIFO data selection (filtered or unfiltered).""" - self.fifoConfig2 = (self.fifoConfig2 & ~(0x07 << 3)) | ((dataSelect & 0x07) << 3) + self.fifoConfig2 = (self.fifoConfig2 & ~(0x07 << 3)) | ( + (dataSelect & 0x07) << 3 + ) self._write8(BMP388_FIFO_CONFIG_2, self.fifoConfig2) def setFIFOStopOnFull(self, stopOnFull): @@ -619,9 +659,13 @@ def getFIFOData(self): status = FIFO_CONFIG_ERROR if configError else FIFO_DATA_READY return (status, temperatures, pressures, altitudes, sensorTime) - def enableFIFOInterrupt(self, outputDrive=PUSH_PULL, activeLevel=ACTIVE_HIGH, latchConfig=UNLATCHED): + def enableFIFOInterrupt( + self, outputDrive=PUSH_PULL, activeLevel=ACTIVE_HIGH, latchConfig=UNLATCHED + ): """Enable FIFO watermark/full interrupts on the INT pin.""" - self.intCtrl = (latchConfig & 0x01) << 2 | (activeLevel & 0x01) << 1 | (outputDrive & 0x01) + self.intCtrl = ( + (latchConfig & 0x01) << 2 | (activeLevel & 0x01) << 1 | (outputDrive & 0x01) + ) self.intCtrl |= (1 << 3) | (1 << 4) self._write8(BMP388_INT_CTRL, self.intCtrl) @@ -651,7 +695,9 @@ def disableI2CWatchdog(self): def setI2CWatchdogTimeout(self, watchdogTimeout): """Set I2C watchdog timeout.""" - self.ifConfig = (self._read8(BMP388_IF_CONFIG) & ~(1 << 2)) | ((watchdogTimeout & 0x01) << 2) + self.ifConfig = (self._read8(BMP388_IF_CONFIG) & ~(1 << 2)) | ( + (watchdogTimeout & 0x01) << 2 + ) self._write8(BMP388_IF_CONFIG, self.ifConfig) def fifoReady(self): @@ -666,7 +712,7 @@ def readAllValues(self): """ Read and return compensated temperature, pressure, and altitude values. - :return: (temperature °C, pressure hPa, altitude m) + :return: (temperature °C, pressure hPa, altitude m) """ return self.getMeasurements() diff --git a/Sensors/BMP388/BMP388/bmp388_constants.py b/Sensors/BMP388/BMP388/bmp388_constants.py index 4babe14..959950d 100644 --- a/Sensors/BMP388/BMP388/bmp388_constants.py +++ b/Sensors/BMP388/BMP388/bmp388_constants.py @@ -128,4 +128,4 @@ # I2C watchdog WATCHDOG_TIMEOUT_1MS = 0x00 -WATCHDOG_TIMEOUT_40MS = 0x01 \ No newline at end of file +WATCHDOG_TIMEOUT_40MS = 0x01 diff --git a/Sensors/BMP388/README.md b/Sensors/BMP388/README.md new file mode 100644 index 0000000..c2ffadf --- /dev/null +++ b/Sensors/BMP388/README.md @@ -0,0 +1,14 @@ +# How to install + +--- + +After [**installing the mpremote package**](https://docs.micropython.org/en/latest/reference/mpremote.html), flash a module to the board using the following command: + +```sh + mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/BMP388 +``` +Or, if you're running a Windows OS: + +```sh + python -m mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/BMP388 +``` diff --git a/Sensors/LaserDistanceSensor/README.md b/Sensors/LaserDistanceSensor/README.md new file mode 100644 index 0000000..44c01f4 --- /dev/null +++ b/Sensors/LaserDistanceSensor/README.md @@ -0,0 +1,14 @@ +# How to install + +--- + +After [**installing the mpremote package**](https://docs.micropython.org/en/latest/reference/mpremote.html), flash a module to the board using the following command: + +```sh + mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/LaserDistanceSensor +``` +Or, if you're running a Windows OS: + +```sh + python -m mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/LaserDistanceSensor +``` diff --git a/Sensors/ObstacleSensor/README.md b/Sensors/ObstacleSensor/README.md new file mode 100644 index 0000000..8deb707 --- /dev/null +++ b/Sensors/ObstacleSensor/README.md @@ -0,0 +1,14 @@ +# How to install + +--- + +After [**installing the mpremote package**](https://docs.micropython.org/en/latest/reference/mpremote.html), flash a module to the board using the following command: + +```sh + mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/ObstacleSensor +``` +Or, if you're running a Windows OS: + +```sh + python -m mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/ObstacleSensor +``` diff --git a/Sensors/RotaryEncoder/README.md b/Sensors/RotaryEncoder/README.md new file mode 100644 index 0000000..2a8aa49 --- /dev/null +++ b/Sensors/RotaryEncoder/README.md @@ -0,0 +1,14 @@ +# How to install + +--- + +After [**installing the mpremote package**](https://docs.micropython.org/en/latest/reference/mpremote.html), flash a module to the board using the following command: + +```sh + mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/RotaryEncoder +``` +Or, if you're running a Windows OS: + +```sh + python -m mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/RotaryEncoder +``` diff --git a/Sensors/TMP117/README.md b/Sensors/TMP117/README.md new file mode 100644 index 0000000..8cd090a --- /dev/null +++ b/Sensors/TMP117/README.md @@ -0,0 +1,14 @@ +# How to install + +--- + +After [**installing the mpremote package**](https://docs.micropython.org/en/latest/reference/mpremote.html), flash a module to the board using the following command: + +```sh + mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/TMP117 +``` +Or, if you're running a Windows OS: + +```sh + python -m mpremote mip install github:SolderedElectronics/Soldered-Micropython-modules/Sensors/TMP117 +```