-
Notifications
You must be signed in to change notification settings - Fork 223
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MLX90614: migrate to new I2C API #181
Conversation
abadfe1
to
1e8242d
Compare
This if for #182. With the changes, I can read from the sensor again. I'm not sure what to do about the baud rate change though. It's not strictly necessary. Here's how: from pslab import ScienceLab
from pslab.external.MLX90614 import MLX90614
from time import sleep
psl = ScienceLab() # This initializes the I2C bus.
sensor = MLX90614()
data = sensor.getAmbientTemperature()
print(data)
sleep(2)
data = sensor.getObjectTemperature()
print(data) yields
🎉 |
@bessman I took the i2c bus part from your patch to migrate the SSD1306 driver. Now I'm not sure where I got it from. For reference, see #184 Edit: found it, the branch is in here and was for some reason even targeted by the PR automatically; I looked at your fork only 😅 https://github.com/fossasia/pslab-python/commits/ssd1306 |
1e8242d
to
455f7d0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to the specific comments throughout the code, the module, class and its public methods need docstrings, and the methods should have type hints for input and return values.
Remember to add it to the lint whitelist in tox.ini.
class MLX90614(I2CSlave): | ||
_ADDRESS = 0x5A | ||
_OBJADDR = 0x07 | ||
_AMBADDR = 0x06 | ||
NUMPLOTS = 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like some class-level vars are unused (NUMPLOTS
, PLOTNAMES
, name
). Let's remove them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is also what I see in other sensors here.
For the other PR, I dropped a note on that:
#192 (comment)
It was a previous attempt to abstract the sensors in a way, and at the same time, it is tightly coupling them with the UI. I am seeing a very similar thing in sigrok. Let's discuss this in the nxt meeting a bit further in depth please, because that information is needed in some form.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm imagining some kind of SensorMetaDataMixin
class could be used for this.
|
||
self.source = self.OBJADDR | ||
self.source = self._OBJADDR | ||
|
||
self.name = 'Passive IR temperature sensor' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.name
also seems unused.
|
||
self.source = self.OBJADDR | ||
self.source = self._OBJADDR | ||
|
||
self.name = 'Passive IR temperature sensor' | ||
self.params = {'readReg': {'dataType': 'integer', 'min': 0, 'max': 0x20, 'prefix': 'Addr: '}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder what this is? It seems like it's related to the readReg
method, specifying its argument as an integer that should be between 0 and 0x20. From the rest of the code I gather that there are two register addresses of interest: _OBJADDR
(0x07) and _AMBADDR
(0x06). Does the sensor have other register addresses that could be interesting?
Either way, this variable is unused and should be removed. The information about register address range (0-0x20) should be moved to a docstring if it is relevant to the user.
|
||
self.name = 'Passive IR temperature sensor' | ||
self.params = {'readReg': {'dataType': 'integer', 'min': 0, 'max': 0x20, 'prefix': 'Addr: '}, | ||
'select_source': ['object temperature', 'ambient temperature']} | ||
|
||
try: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the sensor works with the default baudrate (125 kHz), there is no need for this configuration code.
Even if the sensor requires the baudrate to not exceed some maximum value, merely instantiating the sensor should not automatically reconfigure the I2C bus, I think. The user might have multiple sensors connected to the I2C bus and have manually set the baudrate to a desired speed.
Instead we should check if the baudrate is low enough for this sensor, and raise a warning or even an exception if it is too high. However, there is currently no way to read back the configured I2C baudrate (#195).
# print('switching baud to 100k') | ||
# self.I2C.configI2C(100e3) | ||
# except Exception as e: | ||
# print('FAILED TO CHANGE BAUD RATE', e.message) | ||
|
||
def select_source(self, source): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be a _private method, I think.
elif source == 'ambient temperature': | ||
self.source = self.AMBADDR | ||
self.source = self._AMBADDR | ||
|
||
def readReg(self, addr): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused? Seems like a leftover from debugging.
elif source == 'ambient temperature': | ||
self.source = self.AMBADDR | ||
self.source = self._AMBADDR | ||
|
||
def readReg(self, addr): | ||
x = self.getVals(addr, 2) | ||
print(hex(addr), hex(x[0] | (x[1] << 8))) | ||
|
||
def getVals(self, addr, numbytes): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be _private, name should be in snake_case.
|
||
def readReg(self, addr): | ||
x = self.getVals(addr, 2) | ||
print(hex(addr), hex(x[0] | (x[1] << 8))) | ||
|
||
def getVals(self, addr, numbytes): | ||
vals = self.I2C.readBulk(self.ADDRESS, addr, numbytes) | ||
vals = self.read(numbytes, addr) | ||
return vals | ||
|
||
def getRaw(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_private, snake_case.
The method itself could be clearer.
- It fetches three bytes from the sensor, but only uses two of them. Why?
- This:
[((((vals[1] & 0x007f) << 8) + vals[0]) * 0.02) - 0.01 - 273.15]
is some kind of conversion, but what exactly is going on?- The most significant bit of
val[1]
is discarded, why? - I think 0.02 is a resolution factor (0.02 degrees of resolution per bit), but what is 0.01? It looks like an offset, but if the resolution is 0.02 degC it makes no sense to have an offset of 0.01 degC.
- 273.15 is conversion from degK to degC.
- The most significant bit of
The method returns False if fewer than three bytes are received from the sensor. Can this happen in normal operation, or is it indicative of a hardware problem? If the latter, we should raise an exception instead.
The return type can be either list or bool. Multiple return types should be avoided; better return an empty list instead of False.
@@ -54,15 +48,15 @@ def getRaw(self): | |||
return False | |||
|
|||
def getObjectTemperature(self): | |||
self.source = self.OBJADDR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we could use the select_source
method for improved readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, thank you!
val = self.getRaw() | ||
if val: | ||
return val[0] | ||
else: | ||
return False | ||
|
||
def getAmbientTemperature(self): | ||
self.source = self.AMBADDR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
select_source
No description provided.