Skip to content
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

Micropython support #7

Open
geoffklee opened this issue Nov 28, 2017 · 28 comments
Open

Micropython support #7

geoffklee opened this issue Nov 28, 2017 · 28 comments
Labels

Comments

@geoffklee
Copy link

I've made a small adapter class so that this can be used with micropython:

https://github.com/gkluoe/bme680/blob/master/library/bme680/i2c.py

@Gadgetoid would you consider a pull request to add this functionality?

@Gadgetoid
Copy link
Member

Nice work!

I would probably package this separately- as a general purpose way to adapt Python SMBus dependent libraries to MicroPython. While it's convenient, it has too much utility to be bundled with bme680.

Calling it something like micropython-smbus and having it supply an smbus importable module that you can use to shim up libraries that don't support supplying an alternate i2c object may be useful.

@geoffklee
Copy link
Author

Thanks - that's a good idea. Of course, if I do that I'll have to stop being lazy and implement the rest of the smbus methods ;-) Hopefully it'll work for @robmarkcole in the meantime.

@robmarkcole
Copy link

robmarkcole commented Nov 30, 2017

@gkluoe Hi Geoff, thanks for posting. I have a pycom Wipy 2.0 and have flashed the code from your repo. Do you have an example main.py where you init the i2c object as they do in the pycom docs? Have tried:

>>> sensor = bme680.BME680(i2c_addr=i2c.scan()[0])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/flash/lib/bme680.py", line 22, in __init__
ImportError: no module named 'smbus'

Happy to move this discussion to your repo but can't see an option to create an issue there.
Finally it appears you intend to add this sensor to a weather-station which is my project too :)

@geoffklee
Copy link
Author

Hi,

It's not quite a drop-in replacement for smbus (yet :) so you need to create an instance of the I2CAdapter and pass it to the BME680 constructor explicitly. OTOH, you probably don't need to set i2c_addr when you're initing the 680, unless you've changed it from the default, so this should work (I'm on a LoPy but I think the firmware is essentially the same):

from bme680.i2c import I2CAdapter

i2c_dev = I2CAdapter(1, I2C.MASTER, baudrate=100000)
bme680.BME680(i2c_device=i2c_dev) 

If you're not using the default pins, you can specify them explicitly:

i2c_dev = I2CAdapter(1, pins=('G15','G10'), baudrate=100000)
bme680.BME680(i2c_device=i2c_dev)

I've enabled issues on my fork now so if you're still having problems or have suggestions feel free to head over there! I'll tidy things up a bit and move the adapter into its own repo at some point soon.

@robmarkcole
Copy link

Thanks @gkluoe that solution works on my Wipy :-)

@Thosch42
Copy link

Hi!
I tried it also on a LoPy, but the line
i2c_dev = I2CAdapter(0, I2C.MASTER, baudrate=100000, pins=('P9', 'P10'))
give me an error: NameError: name 'I2C' is not defined.
Since I'm a 'C' guy, I'm pretty new in Python coding. As far as I understand, the I2C module is loaded, bacause I get no ImportError. But why is then I2C not defined?

Cheers,
Thomas

@geoffklee
Copy link
Author

Hi - I2C isn't defined because you haven't imported it, you've imported I2CAdapter instead :-)

So, I suspect

i2c_dev = I2CAdapter(0, I2CAdapter.MASTER, baudrate=100000, pins=('P9', 'P10'))

Would work for you in this particular case.

Fwiw, I've done a bit of work to generalise this out - it's a bit of a mess to use it at the moment but the readme should get you there:

https://github.com/gkluoe/micropython-smbus

@Thosch42
Copy link

Arghh, your'e right!!
Have you recognized any memory problems with your LoPy? During the burn in, I get an MemoryError: memory allocation failed, allocating 1024 bytes.

@robmarkcole
Copy link

Btw am writing this up https://github.com/robmarkcole/bme680-mqtt-micropython

@geoffklee
Copy link
Author

geoffklee commented Dec 15, 2017 via email

@papics
Copy link

papics commented Apr 15, 2018

Hi guys,

I am trying to use the i2c and bme680 libraries, but I am having some difficulties. I have quite some experience in python, but this is the first time I am working with sensors and micropython, so maybe you can help (even though this is not an actual Issue in your code on github).

I have an Adafruit HUZZAH32 - ESP32 Feather Board to which I installed micropython (following https://www.rototron.info/raspberry-pi-esp32-micropython-tutorial/ and actually I already made this work with a DHT22 sensor on another ESP32, so I know that this part should be all right, and I have also made use of the MQTT protocol there before, so that part works too), to which I have wired a BME680 using the default scl/22 and sda/23 pins.

Using only

...
import bme680
from i2c import I2CAdapter
...

...
i2c_dev = I2CAdapter()
sensor = bme680.BME680(i2c_device=i2c_dev)
...

my code gives an error: "TypeError: 'scl' argument required" on line "i2c_dev = I2CAdapter()"

So I tried with my basic understanding getting to the bottom of this, and I can get to the next line in the code at least if I do:

i2c_dev = I2CAdapter(scl=machine.Pin(22),sda=machine.Pin(23))

which results in an error: "OSError: [Errno 19] ENODEV" on line "sensor = bme680.BME680(i2c_device=i2c_dev)"

One more thing I tried is to see if this setting sees the connected sensor, by

i2c_dev.scan()
[119]

So I think that should be fine, but I have no idea how to progress from here...
I had no issues make this setup work with the DHT22 sensor, but I am pretty stuck with the BME680. I would appreciate any kind of suggestions about what I am doing wrong.

Best regards,
Peter

@geoffklee
Copy link
Author

Hi. Interesting.

If scan() is working, then I'm not sure where to go with this one.

I hacked together the I2CAdapter for a LoPy and that's all I have tested it on. We have reports of it working on a WiPy too, but it's possible the generic ESP32 micropython is sufficiently different that it's doing something I didn't anticipate.

Are you able to get any more of a traceback?

You could also try the more generic version of the adapter - it might be a bit more clear what it's doing: https://github.com/gkluoe/micropython-smbus

@geoffklee
Copy link
Author

Wait a sec! 119 == 0x77 which is the secondary address of the BME680.

By default the code looks on 0x76, hence ENODEV.

Try this:

bme680.BME680(i2c_device=i2c_dev, i2c_addr=I2C_ADDR_SECONDARY)

@papics
Copy link

papics commented Apr 17, 2018

This helped a bit, but unluckily still getting an error - maybe this can help further:

sensor = bme680.BME680(i2c_device=i2c_dev, i2c_addr=bme680.I2C_ADDR_SECONDARY)
Traceback (most recent call last):
File "", line 1, in
File "bme680.py", line 29, in init
File "bme680.py", line 56, in soft_reset
File "bme680.py", line 278, in _set_regs
File "i2c.py", line 35, in write_byte_data
TypeError: object with buffer protocol required

Line 35 is
return self.writeto_mem(addr, register, data)
in the
def write_byte_data(self, addr, register, data):
function

Any ideas? (And thanks for the help in any case, it is appreciated.)

@geoffklee
Copy link
Author

Hmm. I don't know why this would work differently on a LoPy, but this looks like a bug on my part.

I suspect that your I2C.writeto_mem() isn't liking data being an int and is looking for something it can treat like a buffer. Makes perfect sense :-)

Could you try changing line 35 in i2c.py to this:

self.writeto_mem(addr, register, bytes([data]))

This is just a hunch and could be completely wrong, but if that works I'll fix it more robustly.

@papics
Copy link

papics commented Apr 18, 2018

That was it! Wow, thanks! It is working :) I guess I can also make this change in write_i2c_block_data function and then everything should be fine. But of course if you make a robust fix, I am happy to test it on my ESP32. Again, thanks a lot for the help!

@geoffklee
Copy link
Author

Cool! Thanks for testing that and for highlighting the bug. I've opened an issue over on micropython-smbus where I'm maintaining a more general version of the I2CAdapter.

geoffklee/micropython-smbus#1

@Christian2306
Copy link

Hi,

I followed the instructions, but still gut the below shown error:

Traceback (most recent call last):
  File "<stdin>", line 18, in <module>
  File "bme680.py", line 29, in __init__
  File "bme680.py", line 56, in soft_reset
  File "bme680.py", line 278, in _set_regs
  File "i2c.py", line 35, in write_byte_data
TypeError: object with buffer protocol required

How can I solve this problem?

Thanks in advance for your help!

Christian

@Christian2306
Copy link

Christian2306 commented Jan 26, 2020

Hi again,

i also had a look at the smbus implementation shown at the linked issue ([https://github.com/gkluoe/micropython-smbus]), but I'm using a ESP32 and it seems like I have no clue how to change the code appropriately to get this running.

I mean especially the code line:
bus = SMBus(SMBus.MASTER, pins=('G15','G10'), baudrate=100000)

I tried using:

import usmbus
bus = usmbus.SMBus(1,scl=Pin(27), sda=Pin(26),baudrate=100000)

But the I get this error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: extra keyword arguments given

Is this the better way? If so, can anyone help me on this?

Thanks in advance for your help!

Christian

@arminblum
Copy link

Hi Christian2306

I encountered the same TypeError problem with a Raspberry Pi Pico in file "I2C.py", and I tried the solution from gkluoe (see obove), commented on 18 Apr 2018.

Change line 35 from I2C.py to:
self.writeto_mem(addr, register, bytes([data]))
this solves the issue.

Just use the default I2c :

i2c_dev = I2CAdapter(0) # defaults on Pico to SCL=Pin(9), SDA>=Pin(8), freq=400000

bme = bme680.BME680(i2c_device=i2c_dev)

while True:
if bme.get_sensor_data():
output = '{0:.2f} C,{1:.2f} hPa,{2:.3f} %RH'.format(
bme.data.temperature,
bme.data.pressure,
bme.data.humidity)
print(output)
sleep(1)

@captn3m0
Copy link

@arminblum Can you check if the heat_stable parameter is getting set correctly? For me, it always returns False. print(bme.data.head_stable) ought to do it.

@arminblum
Copy link

@captn3m0 The "bme.data.head_stable" in my case becomes correctly true after the "burn_in_time = 120".
Here my log of the bme680-all.py script with the "print(bme.data.head_stable)" line which returns true after 120s, I changed the output to json format:

{"gas_ohms": 239831.2} {"gas_ohms": 239831.2} {"gas_ohms": 239831.2} {"gas_ohms": 239654.2} {"gas_ohms": 240008.4} {"gas_ohms": 240126.6} {"gas_baseline": 250000, "humidity_baseline": 40.0} True {"temperature_C": 23.8, "pressure_hPa": 966.0, "gas_ohms_data": 240422.8, "air_quality": 91.8, "humidity": 31.5} {"temperature_C": 23.8, "pressure_hPa": 966.0, "gas_ohms_data": 240422.8, "air_quality": 91.8, "humidity": 31.5} {"temperature_C": 23.8, "pressure_hPa": 966.0, "gas_ohms_data": 240719.8, "air_quality": 91.90001, "humidity": 31.5}

Check in your script the value of your "burn_in_time" .

@captn3m0
Copy link

Not sure what the issue was, but I noticed after a while that I wasn't running the latest version (1.0.5). Updating to that fixed it.

@elschopi
Copy link

elschopi commented Apr 27, 2021

Hello everyone,
I'm trying to use this library on an ESP32 with Micropython V1.15. Using the I2CAdapter (I define it using i2c_dev = I2CAdapter(0) for the hardware I2C bus) I can scan for I2C devices, and get a result (in my case, just address 119 because the BME680 is the only thing attached at the moment).
However, when I try the examples from the library (indoor-air-quality.py in my case, since that's what I'm interested in), I get an error: AttributeError: 'module' object has no attribute 'OS_2X'
I have of course modified the definitions at the beginning to adapt to the use of the I2CAdapter. However it seems that the constants dont get imported correctly?
I've put constants.py and init.py into a folder bme680 on my ESP32, and use
import bme680 from i2c import * i2c_dev = I2CAdapter(0) sensor = bme680.BME680(i2c_device=i2c_dev)
I am quite sure I'm making stupid mistakes here, but I can't find them myself, so I'd be happy about any help there :D

edit:
On a Raspberry Pi 4, the unmodified code works flawlessly with Python3 and having been installed from PyPi.

@arminblum
Copy link

arminblum commented Apr 30, 2021

@elschopi
I tried to use the bme680 driver in conjunction with the I2CAdaper as described in this post with MicroPython v1.14 on 2021-02-02; ESP module with ESP8266. Importing the BME680 raised an MemoryError. the garbage collector did not help (gc.collect()). ESP8266 has not enought memory to run the BME680 driver.
Indeed, I used this I2CAdapter successfully with MicroPython v1.14 on 2021-02-08; Raspberry Pi Pico with RP2040. My script works fine. All imported modules (BME680.py, constants,py, i2c.py an my script bmetest68-all.py are all in on th esame level, there is no file structure on the pico.

During my numerous tests it's highly probable that I encoutered the same AttributeError: as you mention in your post.

I used BME680.py from: (https://github.com/robmarkcole/bme680-mqtt-micropython/tree/master/lib)

my script: bmetest68-all.py:
could not import the code ..

@elschopi
Copy link

@arminblum I did get it to work after a bit. I'm now using a flat file structure (no folders) too, but have also replaced the "bme680" part in the configuration data with "constants". So now it looks like this:
sensor.set_humidity_oversample(constants.OS_2X) (example)
instead of this:
sensor.set_humidity_oversample(bme680.OS_2X)

I did however run into memory allocation problems too, which was quite an issue as I want to collect other sensor data as well (want the BME680 as air quality sensor, and measure temperature, humidity and pressure with an BME280 as well as light levels with a TSL2561). As I'm using the ESP32 just as a client to a Raspberry Pi 4 station, I'm now sending gas resistance data over MQTT and let the RPi compute and control everything else.

I'm currently successfully and as far as I can tell stable running the pimoroni library, but some work on my end is still need to be done :P

@garykuipers
Copy link

@gkluoe Thanks for this, I am trying to implement Atlas-Scientific Sensors. See: https://github.com/Atlas-Scientific/python_AtlasOEM_lib

Traceback (most recent call last):
File "main.py", line 40, in
File "main.py", line 11, in main
File "AtlasOEM.py", line 82, in write_active_hibernate
File "AtlasOEM.py", line 43, in write_byte
File "smbus.py", line 42, in write_byte_data
OSError: [Errno 19] ENODEV

Any idea what I can do to fix this?
Thanks!

@geoffklee
Copy link
Author

Hi @garykuipers it looks like your sensor isn't being detected on the I2C bus. I'd suggest trying some simple code like this to scan the I2C bus to make sure that your hardware is being detected and you know which ID and pins to use.

https://gist.github.com/projetsdiy/f4330be62589ab9b3da1a4eacc6b6b1c

That will make troubleshooting the Atlas code a lot simpler:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants