From: jimmy Date: Tue, 25 Jun 2024 01:20:05 +0000 (-0500) Subject: sensors X-Git-Url: https://vault307.fbx.one/gitweb/SensorLib.git/commitdiff_plain/d6feefe30f3b10bcdcf68030f82970c06d1fe599?ds=inline sensors --- d6feefe30f3b10bcdcf68030f82970c06d1fe599 diff --git a/bh1750.py b/bh1750.py new file mode 100644 index 0000000..6564e94 --- /dev/null +++ b/bh1750.py @@ -0,0 +1,116 @@ +# https://github.com/flrrth/pico-bh1750 + +import math + +from micropython import const +from utime import sleep_ms + + +class BH1750: + """Class for the BH1750 digital Ambient Light Sensor + + The datasheet can be found at https://components101.com/sites/default/files/component_datasheet/BH1750.pdf + """ + + MEASUREMENT_MODE_CONTINUOUSLY = const(1) + MEASUREMENT_MODE_ONE_TIME = const(2) + + RESOLUTION_HIGH = const(0) + RESOLUTION_HIGH_2 = const(1) + RESOLUTION_LOW = const(2) + + MEASUREMENT_TIME_DEFAULT = const(69) + MEASUREMENT_TIME_MIN = const(31) + MEASUREMENT_TIME_MAX = const(254) + + def __init__(self, address, i2c): + self._address = address + self._i2c = i2c + self._measurement_mode = BH1750.MEASUREMENT_MODE_ONE_TIME + self._resolution = BH1750.RESOLUTION_HIGH + self._measurement_time = BH1750.MEASUREMENT_TIME_DEFAULT + + self._write_measurement_time() + self._write_measurement_mode() + + def configure(self, measurement_mode: int, resolution: int, measurement_time: int): + """Configures the BH1750. + + Keyword arguments: + measurement_mode -- measure either continuously or once + resolution -- return measurements in either high, high2 or low resolution + measurement_time -- the duration of a single measurement + """ + if measurement_time not in range(BH1750.MEASUREMENT_TIME_MIN, BH1750.MEASUREMENT_TIME_MAX + 1): + raise ValueError("measurement_time must be between {0} and {1}" + .format(BH1750.MEASUREMENT_TIME_MIN, BH1750.MEASUREMENT_TIME_MAX)) + + self._measurement_mode = measurement_mode + self._resolution = resolution + self._measurement_time = measurement_time + + self._write_measurement_time() + self._write_measurement_mode() + + def _write_measurement_time(self): + buffer = bytearray(1) + + high_bit = 1 << 6 | self._measurement_time >> 5 + low_bit = 3 << 5 | (self._measurement_time << 3) >> 3 + + buffer[0] = high_bit + self._i2c.writeto(self._address, buffer) + + buffer[0] = low_bit + self._i2c.writeto(self._address, buffer) + + def _write_measurement_mode(self): + buffer = bytearray(1) + + buffer[0] = self._measurement_mode << 4 | self._resolution + self._i2c.writeto(self._address, buffer) + sleep_ms(24 if self._measurement_time == BH1750.RESOLUTION_LOW else 180) + + def reset(self): + """Clear the illuminance data register.""" + self._i2c.writeto(self._address, bytearray(b'\x07')) + + def power_on(self): + """Powers on the BH1750.""" + self._i2c.writeto(self._address, bytearray(b'\x01')) + + def power_off(self): + """Powers off the BH1750.""" + self._i2c.writeto(self._address, bytearray(b'\x00')) + + @property + def measurement(self) -> float: + """Returns the latest measurement.""" + if self._measurement_mode == BH1750.MEASUREMENT_MODE_ONE_TIME: + self._write_measurement_mode() + + buffer = bytearray(2) + self._i2c.readfrom_into(self._address, buffer) + lux = (buffer[0] << 8 | buffer[1]) / (1.2 * (BH1750.MEASUREMENT_TIME_DEFAULT / self._measurement_time)) + + if self._resolution == BH1750.RESOLUTION_HIGH_2: + return lux / 2 + else: + return lux + + def measurements(self) -> float: + """This is a generator function that continues to provide the latest measurement. Because the measurement time + is greatly affected by resolution and the configured measurement time, this function attemts to calculate the + appropriate sleep time between measurements. + + Example usage: + + for measurement in bh1750.measurements(): # bh1750 is an instance of this class + print(measurement) + """ + while True: + yield self.measurement + + if self._measurement_mode == BH1750.MEASUREMENT_MODE_CONTINUOUSLY: + base_measurement_time = 16 if self._measurement_time == BH1750.RESOLUTION_LOW else 120 + sleep_ms(math.ceil(base_measurement_time * self._measurement_time / BH1750.MEASUREMENT_TIME_DEFAULT)) \ No newline at end of file diff --git a/si7021.py b/si7021.py new file mode 100644 index 0000000..a1bf02c --- /dev/null +++ b/si7021.py @@ -0,0 +1,167 @@ +'''This module implements a driver for the Si7021 humidity and temperature +sensor. + +Datasheet: +https://www.silabs.com/Support%20Documents%2FTechnicalDocs%2FSi7021-A20.pdf + +''' + +from time import sleep + +class CRCError(Exception): + 'Data failed a CRC check.' + pass + + +class Si7021(object): + 'Driver for the Si7021 temperature sensor.' + SI7021_DEFAULT_ADDRESS = 0x40 + SI7021_MEASTEMP_NOHOLD_CMD = bytearray([0xF3]) + SI7021_MEASRH_NOHOLD_CMD = bytearray([0xF5]) + SI7021_RESET_CMD = bytearray([0xFE]) + SI7021_ID1_CMD = bytearray([0xFA, 0x0F]) + SI7021_ID2_CMD = bytearray([0xFC, 0xC9]) + I2C_WAIT_TIME = 0.025 + + + def __init__(self, i2c, address=SI7021_DEFAULT_ADDRESS): + 'Initialize an Si7021 sensor object.' + self.i2c = i2c + self.address = address + self.serial, self.identifier = self._get_device_info() + + + @property + def temperature(self): + 'Return the temperature in Celcius.' + temperature = self._get_data(self.SI7021_MEASTEMP_NOHOLD_CMD) + celcius = temperature * 175.72 / 65536 - 46.85 + return celcius + + + @temperature.setter + def temperature(self, value): + raise AttributeError('can\'t set attribute') + + + @property + def relative_humidity(self): + 'Return the relative humidity as a percentage. i.e. 35.59927' + relative_humidity = self._get_data(self.SI7021_MEASRH_NOHOLD_CMD) + relative_humidity = relative_humidity * 125 / 65536 - 6 + return relative_humidity + + + @relative_humidity.setter + def relative_humidity(self, value): + raise AttributeError('can\'t set attribute') + + + def reset(self): + 'Reset the sensor.' + self.i2c.writeto(self.address, self.SI7021_RESET_CMD) + sleep(self.I2C_WAIT_TIME) + + + def _get_data(self, command): + 'Retrieve data from the sensor and verify it with a CRC check.' + data = bytearray(3) + self.i2c.writeto(self.address, command) + sleep(self.I2C_WAIT_TIME) + + self.i2c.readfrom_into(self.address, data) + value = self._convert_to_integer(data[:2]) + + verified = self._verify_checksum(data) + if not verified: + raise CRCError('Data read off i2c bus failed CRC check.', + data[:2], + data[-1]) + return value + + + def _get_device_info(self): + '''Get the serial number and the sensor identifier. The identifier is + part of the bytes returned for the serial number. + + ''' + # Serial 1st half + self.i2c.writeto(self.address, self.SI7021_ID1_CMD) + id1 = bytearray(8) + sleep(self.I2C_WAIT_TIME) + self.i2c.readfrom_into(self.address, id1) + + # Serial 2nd half + self.i2c.writeto(self.address, self.SI7021_ID2_CMD) + id2 = bytearray(6) + sleep(self.I2C_WAIT_TIME) + self.i2c.readfrom_into(self.address, id2) + + combined_id = bytearray([id1[0], id1[2], id1[4], id1[6], + id2[0], id2[1], id2[3], id2[4]]) + + serial = self._convert_to_integer(combined_id) + identifier = self._get_device_identifier(id2[0]) + + return serial, identifier + + def _convert_to_integer(self, bytes_to_convert): + 'Use bitwise operators to convert the bytes into integers.' + integer = None + for chunk in bytes_to_convert: + if not integer: + integer = chunk + else: + integer = integer << 8 + integer = integer | chunk + return integer + + + def _get_device_identifier(self, identifier_byte): + '''Convert the identifier byte to a device identifier. Values are based + on the information from page 24 of the datasheet. + + ''' + if identifier_byte == 0x00 or identifier_byte == 0xFF: + return 'engineering sample' + elif identifier_byte == 0x0D: + return 'Si7013' + elif identifier_byte == 0x14: + return 'Si7020' + elif identifier_byte == 0x15: + return 'Si7021' + else: + return 'unknown' + + + def _verify_checksum(self, data): + ''''Verify the checksum using the polynomial from page 19 of the + datasheet. + + x8 + x5 + x4 + 1 = 0x131 = 0b100110001 + + Valid Example: + byte1: 0x67 [01100111] + byte2: 0x8c [10001100] + byte3: 0xfc [11111100] (CRC byte) + + ''' + crc = 0 + values = data[:2] + checksum = int(data[-1]) + for value in values: + crc = crc ^ value + for _ in range(8, 0, -1): + if crc & 0x80: #10000000 + crc <<= 1 + crc ^= 0x131 #100110001 + else: + crc <<= 1 + if crc != checksum: + return False + else: + return True + +def convert_celcius_to_fahrenheit(celcius): + 'Convert a Celcius measurement into a Fahrenheit measurement.' + return celcius * 1.8 + 32 \ No newline at end of file diff --git a/us100.py b/us100.py new file mode 100644 index 0000000..56c8018 --- /dev/null +++ b/us100.py @@ -0,0 +1,36 @@ +import utime + +class US100UART: + """Read the US-100 sonar sensor in UART mode.""" + + def __init__(self, uart): + self.uart = uart + uart.init(baudrate=9600, bits=8, parity=None, stop=1) + utime.sleep_ms(100) + self.buf = bytearray(2) + self.t = 0 + + def distance(self): + """Read the temperature compensated distance im millimeters.""" + self.buf[0] = 0 + self.buf[1] = 0 + self.uart.write(b'\x55') + self.t = utime.ticks_ms() + while not self.uart.any(): + if utime.ticks_diff(utime.ticks_ms(), self.t) > 100: + raise Exception('Timeout while reading from US100 sensor!') + utime.sleep_us(100) + self.uart.readinto(self.buf, 2) + return (self.buf[0] * 256) + self.buf[1] + + def temperature(self): + """Read the temperature in degree celcius.""" + self.buf[0] = 0 + self.uart.write(b'\x50') + self.t = utime.ticks_ms() + while not self.uart.any(): + if utime.ticks_diff(utime.ticks_ms(), self.t) > 100: + raise Exception('Timeout while reading from US100 sensor!') + utime.sleep_us(100) + self.uart.readinto(self.buf, 1) + return self.buf[0] - 45 \ No newline at end of file diff --git a/veml7700.py b/veml7700.py new file mode 100644 index 0000000..7bbc044 --- /dev/null +++ b/veml7700.py @@ -0,0 +1,113 @@ +from machine import I2C, Pin +import time +from micropython import const + +#start const +# default address +addr = const(0x10) + +# Write registers +als_conf_0 = const(0x00) +als_WH = const(0x01) +als_WL = const(0x02) +pow_sav = const(0x03) + +# Read registers +als = const(0x04) +white = const(0x05) +interrupt = const(0x06) + +#gain 0.125 0.25 1 2 integration time +confValues = { 25: {1/8: bytearray([0x00, 0x13]), 1/4: bytearray([0x00,0x1B]), 1: bytearray([0x00, 0x01]), 2: bytearray([0x00, 0x0B])}, #25 + 50: {1/8: bytearray([0x00, 0x12]), 1/4: bytearray([0x00,0x1A]), 1: bytearray([0x00, 0x02]), 2: bytearray([0x00, 0x0A])}, #50 + 100:{1/8: bytearray([0x00, 0x10]), 1/4: bytearray([0x00,0x18]), 1: bytearray([0x00, 0x00]), 2: bytearray([0x00, 0x08])}, #100 + 200:{1/8: bytearray([0x40, 0x10]), 1/4: bytearray([0x40,0x18]), 1: bytearray([0x40, 0x00]), 2: bytearray([0x40, 0x08])}, #200 + 400:{1/8: bytearray([0x80, 0x10]), 1/4: bytearray([0x80,0x18]), 1: bytearray([0x80, 0x00]), 2: bytearray([0x80, 0x08])}, #400 + 800:{1/8: bytearray([0xC0, 0x10]), 1/4: bytearray([0xC0,0x18]), 1: bytearray([0xC0, 0x00]), 2: bytearray([0xC0, 0x08])}} #800 + +#gain 0.125, 0.25, 1, 2 integration time +gainValues = { 25: {1/8: 1.8432, 1/4: 0.9216, 1: 0.2304, 2: 0.1152}, #25 + 50: {1/8: 0.9216, 1/4: 0.4608, 1: 0.1152, 2: 0.0576}, #50 + 100:{1/8: 0.4608, 1/4: 0.2304, 1: 0.0288, 2: 0.0144}, #100 + 200:{1/8: 0.2304, 1/4: 0.1152, 1: 0.0288, 2: 0.0144}, #200 + 400:{1/8: 0.1152, 1/4: 0.0576, 1: 0.0144, 2: 0.0072}, #400 + 800:{1/8: 0.0876, 1/4: 0.0288, 1: 0.0072, 2: 0.0036}} #800 + + +# fin des constante + +# Reference data sheet Table 1 for configuration settings + +interrupt_high = bytearray([0x00, 0x00]) # Clear values +# Reference data sheet Table 2 for High Threshold + +interrupt_low = bytearray([0x00, 0x00]) # Clear values +# Reference data sheet Table 3 for Low Threshold + +power_save_mode= bytearray([0x00, 0x00]) # clear values +# Reference data sheet Table 4 for Power Saving Modes + + +class VEML7700: + + def __init__(self, + address=addr, + i2c=None, + it=25, + gain=1/8, + **kwargs): + + self.address = address + if i2c is None: + raise ValueError('An I2C object is required.') + self.i2c = i2c + + confValuesForIt = confValues.get(it) + gainValuesForIt = gainValues.get(it) + if confValuesForIt is not None and gainValuesForIt is not None: + confValueForGain = confValuesForIt.get(gain) + gainValueForGain = gainValuesForIt.get(gain) + if confValueForGain is not None and gainValueForGain is not None: + self.confValues = confValueForGain + self.gain = gainValueForGain + else: + raise ValueError('Wrong gain value. Use 1/8, 1/4, 1, 2') + else: + raise ValueError('Wrong integration time value. Use 25, 50, 100, 200, 400, 800') + + self.init() + + def init(self): + + # load calibration data + #self.i2c.writeto_mem(self.address, als_conf_0, bytearray([0x00,0x13]) ) + self.i2c.writeto_mem(self.address, als_conf_0, self.confValues ) + self.i2c.writeto_mem(self.address, als_WH, interrupt_high ) + self.i2c.writeto_mem(self.address, als_WL, interrupt_low) + self.i2c.writeto_mem(self.address, pow_sav, power_save_mode) + + def detect(self): + """ Functions is verified is module has detecedself. + this function not implemented for this time + """ + None + + def read_lux(self): + """ Reads the data from the sensor and returns the data. + + Returns: + the number of lux detect by this captor. + """ + #The frequency to read the sensor should be set greater than + # the integration time (and the power saving delay if set). + # Reading at a faster frequency will not cause an error, but + # will result in reading the previous data + + self.lux = bytearray(2) + + time.sleep(.04) # 40ms + + self.i2c.readfrom_mem_into(self.address, als, self.lux) + self.lux= self.lux[0]+self.lux[1]*256 + self.lux=self.lux*self.gain + return(int(round(self.lux,0))) \ No newline at end of file