OLED display SSD1306 in MicroPython, example with a I2C digital barometer BME280 (or BMP180)

I propose today to learn how to detect the devices connected to the bus i2c in MicroPython. For this tutorial, we will retrieve the measurements returned by a BME280 environment sensor that measures temperature, atmospheric pressure and humidity. The data will then be displayed on a very classic 128×64 pixel monochrome SSD1360 OLED display in Arduino and DIY projects. I advise you to use the uPiCraft code editor to develop in MicroPython if you are on Windows. On Linux / Raspberry Pi or macOS, follow this tutorial to learn how to use rshell.

Hardware and circuit for an ESP8266 or ESP32 project

For this tutorial, I used a Wemos d1 mini based ESP8266. The SCL pin is on pin D1 (GPIO5) and the pin SDA on D2 (GPIO4). The OLED screen and the BME280 must be powered by 3V3.

ESP32 project

The i2c bus is on pins GPIO22 (SCL) and GPIO21 (SDA). However, pin 21 is missing on the Wemos LoLin32 Lite I am currently using. Fortunately, the library I2C allows to choose other pins as we will see later.

Scan devices connected to the i2c bus in MicroPython

The i2c bus is natively supported by MicroPython. All methods are detailed on the online documentation here. The following methods are available for connecting to a sensor and reading data in the registers. The methods are accessible from the machine class.

  • I2C.init (scl, sda, freq = 400000), initializes the i2c bus. The sda ​​and scl pins must be indicated. It is also possible to change the frequency of the bus.
  • I2C.deinit (), stop the i2c bus
  • I2C.scan (), scan the i2c bus and return the addresses of the devices found
  • I2C.start (), generates a Start condition on the bus
  • I2C.stop (), or a Stop
  • I2C.readinto () reads the bytes on the bus and stores in a buffer
  • I2C.write (), write the buffer on the bus
  • I2C.readfrom (addr, nbytes, stop = True), reads nbytes on the slave. Return an object
  • I2C.readfrom_into (addr, buf, stop = True), same but stores in a buffer the bytes returned
  • I2C.writeto (addr, buf, stop = True), allows to write the contents of a buffer to the slave at the indicated address
  • I2C.readfrom_mem (addr, memaddr, nbytes, *, addrsize = 8), read at the memory address of the slave nbytes
  • I2C.readfrom_mem_into (addr, memaddr, buf, *, addrsize = 8), same but stores in a buffer the nbytes returned by the slave
  • I2C.writeto_mem (addr, memaddr, buf, *, addrsize = 8), writes the buffer to the specified slave’s memory

By default, MicroPython automatically converts hex and decimal values. In general, manufacturers indicate the i2c address in hex. This little bit of code does the opposite conversion. You will be able to more easily identify connected devices. Then in the code, you can use either the address in hex or decimal, MicroPython does the automatic conversion.

Create a new script and paste the code below. Save the script as scanneri2c.py, for example, and start the execution by pressing the F5 key.

uPicraft will upload it and launch the script. If the wiring is correct, the scanner must detect the OLED screen (0x3c typically) and the BME280 sensor (0x76 in general). You can also use a BME180 that will not return the humidity level.

Now let’s take a closer look at how we use the i2c bus in MicroPython. First, we need to create an I2C object that will connect to the i2c bus and communicate with the connected devices. On an ESP8266, the SCL pin is on pin D1 (GPIO5) and the pin SDA on D2 (GPIO4). However, the library does not require the use of official pins. As with Arduino code, we can assign other pins. Here, I connected the i2c bus on board pins 22 and 18 the ESP32 Wemos Lolin32 Lite.

Then the i2c method. scan () retrieves device addresses as a hex array.

Read the measurements of a BMP180 / BME280 in MicroPython

There are some functional drivers on GitHub or internet. No need to re-invent the wheel. For this tutorial, I used the driver adapted by Catdog2. This driver is based on Adafruit Adafruit_I2C.py library. Go to GitHub to get the BME280 drivers code and paste it into a new script. Save the script as bme280.py. Do F5 to upload it (nothing will run on the map).

I have a weakness for the BME280 because it saves a DHT11 / DHT21 or DHT22 if you want to measure the temperature and humidity. We also gain in compactness and energy saving, essential conditions for projects of objects connected on battery.

Create a new script and paste the following code. The beginning is identical. We start by initializing an I2C object by indicating the bus pins and then create a bme280 object. It is passed in parameter the object i2c. It is also possible to pass the BME280 address as a parameter if it is different from the default address 0x76 (with the previous scanner, it is easy to find it). The values ​​() method retrieves the measures directly formatted with the unit.

Embed an OLED SSD1306 display into MicroPython

Now that we have concrete measures, we will display them on a small monochrome OLED screen very classic and already seen in several tutorials. uPiCraft already embeds a driver for SSD1306 displays. It is in the Upi_lib section. Copy the ssd1306.py library to the map. If you do not use uPiCraft, you can recover the original drivers on GitHub.

You will need 6 lines of code to display text on an OLED screen in MicroPython. After creating an ssd1306 object that requires horizontal resolution in pixels, the vertical resolution and the i2c object. It can also be passed the screen address if it is different from 0x3c. Compared to the Arduino library, we can pass the screen resolution at initialization without having to go modify the library.

We then have some methods to manage the display:

  • poweroff(), turns off the screen. Convenient for battery operation.
  • contrast(), to adjust the contrast
  • invert(), invert the colors of the screen (finally white and black!)
  • show(), to refresh the view
  • fill(), to fill the screen in black (1) or white (0)
  • pixel(), to turn on a particular pixel
  • scroll(), scroll the screen.
  • text(), to display on text at the indicated x, y position
  • Draw lines hline ), vline() or any line line()
  • Draw a rect rect rectangle() or rectangle filled fill_rect()

It is a very powerful library that has nothing to envy to Arduino libraries (Adafruit GFX among others). It fully supports the Frame Buffer class of MicroPython. Whatever the resolution of the screen used, just indicate the horizontal and vertical resolution to have a correct display.

Full project code

Here we come to the end of this tutorial. We know how to do a lot of extra things in MicroPython now:

  • Scan the i2c bus
  • Retrieve data on an i2c sensor
  • Display text or simple geometric shapes on a monochrome OLED display SSD1306

Create a new script and paste the following code. Modify the following parameters at the beginning of the code according to your configuration:

  • pinScl = 22 #ESP8266 GPIO5 (D1)
  • pinSda = 18 #ESP8266 GPIO4 (D2)
  • addrOled = 60 #0x3c
  • addrBME280 = 118 #0x76
  • hSize = 64 # Hauteur ecran en pixels | display heigh in pixels
  • wSize = 128 # Largeur ecran en pixels | display width in pixels

Save and send the code with the F5 key.

I have tested with several screens (124×64, 124×32, 64×48), everything works perfectly the first time!

oled ssd1306 micropython esp8266 esp32 bme280 bmp180 64x48

64×48 pixels

oled ssd1306 micropython esp8266 esp32 bme280 bmp180 124x32

124×32 pixels

I was really surprised how easy it is to manage an OLED display with MicroPython code. As the code is not compiled, we gain a lot of time for the development. In the next tutorial, we’ll see how to handle multiple Dallas DS18B20 probes.

Subscribe to the weekly newsletter

No spam and no other use will be made of your email. You can unsubscribe anytime.

DIY Projects