diff --git a/Misc/Temp-Monitor/cs1237.py b/Misc/Temp-Monitor/cs1237.py new file mode 100644 index 0000000..cf23bbd --- /dev/null +++ b/Misc/Temp-Monitor/cs1237.py @@ -0,0 +1,254 @@ +# From: https://github.com/robert-hh/CS1237/blob/f442365eab14fb0b8e2df3858ee46e70bfb56536/cs1237.py + +# MIT License + +# Copyright (c) 2024 Robert Hammelrath + +# 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. + +from machine import Pin +import time +import micropython + +_CMD_READ = const(0x56) +_CMD_WRITE = const(0x65) + + +class CS1237: + _gain = {1: 0, 2: 1, 64: 2, 128: 3} + _rate = {10: 0, 40: 1, 640: 2, 1280: 3} + + def __init__(self, clock, data, gain=64, rate=10, channel=0): + self.clock = clock + self.data = data + self.data.init(mode=Pin.IN) + self.clock.init(mode=Pin.OUT) + self.clock(0) + # determine the number of attempts to find the trigger pulse + start = time.ticks_us() + for _ in range(10): + temp = data() + spent = time.ticks_diff(time.ticks_us(), start) + self.__wait_loop = 4_000_000 // spent + self.config(gain, rate, channel) + # pre-set some values for temperature calibration. + self.ref_value = 769000 + self.ref_temp = 20 + self.init = self.config + self.buffer_full = False + + def __repr__(self): + return "{}(gain={}, rate={}, channel={})".format(self.__qualname__, *self.get_config()) + + def __call__(self): + return self.read() + + def __write_bits(self, value, mask): + clock = self.clock + data = self.data + while mask != 0: + clock(1) + data(1 if (value & mask) != 0 else 0) + clock(0) + mask >>= 1 + + def __read_bits(self, bits=1): + # duplicate the clock high call to extend the positive pulse + clock = self.clock + data = self.data + value = 0 + for _ in range(bits): + clock(1) + clock(1) + clock(0) + value = (value << 1) | data() + return value + + def __write_status(self): + # get the config write status bits + return self.__read_bits(3) >> 1 + + def __write_cmd(self, cmd): + # clock bits 25 and 29, telling that a command follows + clock = self.clock + for _ in range(5): + clock(1) + clock(1) + clock(0) + # write the command word + 1 extra clock cycle + self.data.init(mode=Pin.OUT) + self.__write_bits(cmd << 1, 0x80) + + def __write_config(self, config): + value = self.read() # get the ADC value + self.__write_cmd(_CMD_WRITE) + # write the configuration byte + the 46th clock cycle + self.__write_bits(config << 1, 0x100) + self.data.init(mode=Pin.IN) + return value + + def __read_config(self): + value = self.read() # get the ADC value + self.__write_cmd(_CMD_READ) + self.data.init(mode=Pin.IN) + # read the configuration byte + 1 extra clock cycle + # And return both config and value + return self.__read_bits(9) >> 1, value + + def __drdy_cb(self, data): + self.data.irq(handler=None) + self.__drdy = True + + def read(self): + # Set up the trigger for conversion enable. + self.__drdy = False + self.data.irq(trigger=Pin.IRQ_FALLING, handler=self.__drdy_cb) + # Wait for the DRDY event + for _ in range(20000): + if self.__drdy is True: + break + time.sleep_us(50) + else: + self.__drdy = False + self.data.irq(handler=None) + raise OSError("Sensor does not respond") + # Get the data. + result = self.__read_bits(24) + # Check the sign. + if result > 0x7FFFFF: + result -= 0x1000000 + + return result + + def align_buffer(self, buffer): + for i in range(len(buffer)): + if buffer[i] > 0x7FFFFF: + buffer[i] -= 0x1000000 + self.data_acquired = True + + def __buffer_cb(self, data): + self.data.irq(handler=None) + # Check the sign later when it's time to do so + if self.buffer_index < self.buffer_size: + self.buffer[self.buffer_index] = self.__read_bits(24) + self.buffer_index += 1 + self.data.irq(trigger=Pin.IRQ_FALLING, handler=self.__buffer_cb) + else: + micropython.schedule(self.align_buffer, self.buffer) + + def read_buffered(self, buffer): + self.data_acquired = False + self.buffer = buffer + self.buffer_size = len(buffer) + self.buffer_index = 0 + self.data.irq(trigger=Pin.IRQ_FALLING, handler=self.__buffer_cb) + + def get_config(self): + config, _ = self.__read_config() + return ( + {value: key for key, value in self._gain.items()}[config >> 2 & 0x03], + {value: key for key, value in self._rate.items()}[config >> 4 & 0x03], + config & 0x03, + ) + + def config_status(self): + self.read() ## dummy read value + return self.__write_status() >> 1 + + def config(self, gain=None, rate=None, channel=None): + if gain is not None: + if gain not in self._gain.keys(): + raise ValueError("Invalid Gain") + self.gain = self._gain[gain] + if rate is not None: + if rate not in self._rate.keys(): + raise ValueError("Invalid rate") + self.rate = self._rate[rate] + if channel is not None: + if not 0 <= channel <= 3: + raise ValueError("Invalid channel") + self.channel = channel + config = self.rate << 4 | self.gain << 2 | self.channel + self.__write_config(config) + + def calibrate_temperature(self, temp, ref_value=None): + self.ref_temp = temp + if ref_value is None: + config, self.ref_value = self.__read_config() + if config != 0x02: + # Set gain=1, rate=10, channel=2 (temperature) + self.__write_config(0x02) + # Read the value and restore the previous configuration + self.ref_value = self.__write_config(config) + else: + self.ref_value = ref_value + + def temperature(self): + config, value = self.__read_config() + if config != 0x02: + # set gain=1, rate=10, channel=2 (temperature) + self.__write_config(0x02) + # Read the value and restore the previous configuration + value = self.__write_config(config) + return value / self.ref_value * (273.15 + self.ref_temp) - 273.15 + + def power_down(self): + self.clock(0) + self.clock(1) + + def power_up(self): + self.clock(0) + +class CS1238(CS1237): + pass + +class CS1237P(CS1237): + + def read(self): + data = self.data + # wait for the trigger pulse + start = time.ticks_ms() + for _ in range(self.__wait_loop): + if data(): + break + else: + raise OSError("No trigger pulse found") + for _ in range(5000): + if not data(): + break + time.sleep_us(50) + else: + raise OSError("Sensor does not respond") + result = self.__read_bits(24) + # check the sign. + if result > 0x7FFFFF: + result -= 0x1000000 + + return result + + def read_buffered(self, buffer): + self.data_acquired = False + self.buffer = buffer + self.buffer_size = len(buffer) + for i in range(self.buffer_size): + self.buffer[i] = self.read() + self.data_acquired = True + +class CS1238P(CS1237P): + pass diff --git a/Misc/Temp-Monitor/firmware.py b/Misc/Temp-Monitor/firmware.py index b0dca32..b5e2669 100644 --- a/Misc/Temp-Monitor/firmware.py +++ b/Misc/Temp-Monitor/firmware.py @@ -1,38 +1,20 @@ import time +from cs1237 import CS1237 from machine import Pin clock = Pin(2) data = Pin(4) -SLEEP_TIME = 1 - - -def read_adc(): - # Wait for DRDY - while data.high(): - pass - - value = 0 - for i in range(24): - clock.high() - time.sleep_us(SLEEP_TIME) - - value |= data.value() << (23 - i) - - clock.low() - time.sleep_us(SLEEP_TIME) - - # Shift 3 more bits - for i in range(3): - clock.high() - time.sleep_us(SLEEP_TIME) - clock.low() - time.sleep_us(SLEEP_TIME) - - return value - +cs1237 = CS1237(clock, data) +cs1237.config(gain=1) while True: - value = float(read_adc()) / (2**24-1) - print(f"{value:f}") - time.sleep(0.1) + raw_value = cs1237.read() + value = float(raw_value) / (2**23 - 1) + if value > 0.9999: + value = 0 + else: + value = 1 / (1 / value - 1) + temperature = 25 - value + if abs(temperature) < 50: + print(temperature)