0

ssd1306, pico, micropython
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

ssd1306, pico, micropython

by seasidemotors on Mon May 17, 2021 12:39 am

Hello all, just been playing with my Pi Pico and going through some tutorials and am struggling with getting my little
Adafruit SSD1306 OLED display working. Specifically 0.91" 128x32

I have looked through the code example that are listed here on the adafruit website and they are for circuit python or python3. Micropython is neither of these and does not appear to be cross compatible from my struggles. Lets just start with where I am at and what I have tried to do to get it to work.

I have the board wired up on a bread board to the Pi Pico using GPIO pin1 (GP0) for SDA, Pin2(GP1) for SCL, pin19(GP14) for reset, pin36 for 3.3v for Vin and pin38 is ground.
The panel board power led come on green, no display light up.
Used the following code example from page 119 of 'The Official Raspberry Pi Pico Guide'
Code: Select all | TOGGLE FULL SIZE
  1 import machine
  2
  3 sda=machine.Pin(0)
  4 scl=machine.Pin(1)
  5 i2c=machine.I2C(0,sda=sda, scl=scl, freq=400000)
  6
  7 print(i2c.scan())   


Using rshell I can upload the program, going to the repl and importing the program it returns a [60], so this tells me it is seeing the device and returning an address on the i2c bus.
but this is as far as I can go with the included code due to the examples they are using are specific to the SerLCD from SparkFun. I downloaded the Adafruit 1306 library for python but it downloads into my python3 directories and is of no use as a module for micropython.

Anyways this is where my show stops until I get some outside intervention.

Here is the rest of the example code that will pull the data from the onboard temp sensor and display it to the LCD, perhaps someone can help me navigate a way to accomplish the same outcome using the SSD1306 with micropython on Pico.

Code: Select all | TOGGLE FULL SIZE
import machine
import utime
sda=machine.Pin(0)
scl=machine.Pin(1)
i2c=machine.I2C(0,sda=sda, scl=scl, freq=400000)
adc = machine.ADC(4)
conversion_factor = 3.3 / (65535)
while True:
reading = adc.read_u16() * conversion_factor
temperature = 25 - (reading - 0.706)/0.001721
i2c.writeto(114, '\x7C')
i2c.writeto(114, '\x2D')
out_string = "Temp: " + str(temperature)
i2c.writeto([b]114, out_string)
utime.sleep(2)


The 114 in the i2c.writeto() lines refers to the address of the I2C device, in my case that would change to 60 as that is the address I was given when doing the i2c bus scan.

The next bits that may look a little odd are the \x7C and \x2D commands that are written. Each
I2C device requires data sent in a specific format. There’s no standard for this, so you’ll have to
refer to the documentation for whatever I2C device you’re setting up. The \x at the start of each
of these tells MicroPython that we’re sending a hexadecimal string (see ‘Hexadecimal’ box)
which is a common way of ensuring you’re sending the exact data you want. For our LCD, 7C
enters command mode and 2D blanks the LCD and sets the cursor to the beginning.


Now this is where things get tricky because obviously the SSD1306 has its own nomenclature requirements and these code example where specifically written for the Spark Fun SerLCD.

Anyways, here we are, hoping I can get some help getting this up and running.

seasidemotors
 
Posts: 20
Joined: Wed Mar 24, 2021 9:55 pm

Re: ssd1306, pico, micropython

by kevinjwalters on Mon May 17, 2021 12:50 pm

Instructables: SSD1306 With Raspberry Pi Pico covers MicroPython on Pi Pico with (128 x 64) SSD1306 and how to use the library/driver for the display..

kevinjwalters
 
Posts: 963
Joined: Sun Oct 01, 2017 3:15 pm

Re: ssd1306, pico, micropython

by seasidemotors on Mon May 17, 2021 9:38 pm

kevinjwalters wrote:Instructables: SSD1306 With Raspberry Pi Pico covers MicroPython on Pi Pico with (128 x 64) SSD1306 and how to use the library/driver for the display..


Thanks Kevin!

The link was broken but I was able to find the code here: https://www.instructables.com/SSD1306-W ... y-Pi-Pico/
It was for a 128x64 so I did my best to change things to 32. It works, for the most part, its has the lower half of the display cut off and it automatically shuts down after it goes through its routine which I think it is supposed to do.

But the all the code technically works, I will have to do some work on it myself, I got the driver that works and I have a demo program that gives me a bulk of working code to work with so that is huge :)

Thanks Again! :)

seasidemotors
 
Posts: 20
Joined: Wed Mar 24, 2021 9:55 pm

Re: ssd1306, pico, micropython

by seasidemotors on Wed May 19, 2021 2:10 am

Ok, so need a little more assistance....

I am not sure what all the numbers are corresponding to under 'Basic Stuff'??
Also not sure if the numbers under basic stuff are coordinated with the definitions for horiz, vert, box..and if they are I am not sure the relationship?
In addition if I fill the screen with white pixels it does not look like I am lighting up the entire 32 pixel vertical space?

Code: Select all | TOGGLE FULL SIZE
def horiz(l,t,r,c):  # left, right , top
    n = r-l+1        # Horizontal line
    for i in range(n):
        oled.pixel(l + i, t, c)

def vert(l,t,b,c):   # left, top, bottom
    n = b-t+1        # Vertical line
    for i in range(n):
        oled.pixel(l, t+i,c)

def box(l,t,r,b,c):  # left, top, right, bottom
    horiz(l,t,r,c)   # Hollow rectangle
    horiz(l,b,r,c)
    vert(l,t,b,c)
    vert(r,t,b,c)

# Clear the oled display in case it has junk on it.
oled.fill(0) # Black # I can change to 1 to light the pixels up white, does not look like full 32 pixel height??

# Basic stuff
oled.text("Raspberry Pi",5,10) #5 spaces out from left, 10 spaces down
oled.text("Pico",5,25) #5 spaces out, 25 down
oled.pixel(10,60,1) #Tried randomly changing these to 20, 30, 1 with no change in display output
oled.rect(5,32,20,10,1)
oled.fill_rect(40,40,20,10,1)
oled.line(77,45,120,60,1)
oled.rect(75,32,40,10,1)


seasidemotors
 
Posts: 20
Joined: Wed Mar 24, 2021 9:55 pm

Re: ssd1306, pico, micropython

by Tonygo on Wed May 19, 2021 4:02 am

Hi
I wrote the Instructable on using the SSD1306. I ported the graphics code from work I originally produced for the Pimoroni Pico Explorer display using MicroPython:

https://www.instructables.com/Raspberry ... r-Workout/

I wrote up the methods in Hackspace issue 41 which you can download here:

https://hackspace.raspberrypi.org/issues
Look on page 74

If you need any extra help get in touch via this forum.

Have you imported the SSD1306 library?

If you are using a 128x32 rather than a 128x64 this must be set correctly at the top of your code.

Post your whole screen fill code and I will have a look at it if it is still not working.

Best of luck

Tonygo
 
Posts: 95
Joined: Fri Apr 13, 2018 11:09 am

Re: ssd1306, pico, micropython

by seasidemotors on Wed May 19, 2021 12:38 pm

Hello Tony,

Thanks for responding, I appreciate the help. So I have the driver ssd1306.py in its entire original format, and when combined with the Demo it all works as expected although it would scroll the text off screen since it was set up for 128x64. I was able to change the sin wave value from 50 to 25 and it then scrolled just fine across the screen. It all works as expected without error.

Right now I have been trying to hack away at the demo program to be able to have a base to use for other things I might want to use the lcd for, such as pulling the temp from the temp sensor and displaying to the lcd. Thus my question of wanting to understand how the code was setup so that I might be able to manipulate the original code for my own projects. I have downloaded the datasheets for the display but honestly that is a little beyond my full technical comprehension, and thankfully you have already constructed a functional driver for the display.

So right now it simply displays the text for the given utime.sleep(8) and then goes blank. I want to be able to maybe change the size of the text, make a border or what not, but I cant do that if I do not understand how the parameters for the display work.

Code: Select all | TOGGLE FULL SIZE
# Display Image & text on I2C driven ssd1306 OLED display
# from machine import Pin, I2C
import machine
from ssd1306 import SSD1306_I2C
import utime
WIDTH  = 128                                            # oled display width
HEIGHT = 32                                             # oled display height

# Explicit Method
sda=machine.Pin(0)
scl=machine.Pin(1)
i2c=machine.I2C(0,sda=sda, scl=scl, freq=400000)
#  print(i2c.scan())
from ssd1306 import SSD1306_I2C
oled = SSD1306_I2C(128, 32, i2c)

def blk():
    oled.fill(0)
    oled.show()

def horiz(l,t,r,c):  # left, right , top
    n = r-l+1        # Horizontal line
    for i in range(n):
        oled.pixel(l + i, t, c)

def vert(l,t,b,c):   # left, top, bottom
    n = b-t+1        # Vertical line
    for i in range(n):
        oled.pixel(l, t+i,c)

def box(l,t,r,b,c):  # left, top, right, bottom
    horiz(l,t,r,c)   # Hollow rectangle
    horiz(l,b,r,c)
    vert(l,t,b,c)
    vert(r,t,b,c)

# Clear the oled display in case it has junk on it.
oled.fill(0) # Black

# Basic stuff
oled.text("Raspberry Pi",5,10)
oled.text("Pico",5,25)
oled.pixel(10,60,1)
oled.rect(5,32,20,10,1)
oled.fill_rect(40,40,20,10,1)
oled.line(77,45,120,60,1)
oled.rect(75,32,40,10,1)

# Finally update the oled display so the image & text is displayed
oled.show()
utime.sleep(8)

# Tidy up
blk()




I really appreciate your contribution to the community for writing and sharing the driver and demo program. I will check out your links this evening, I got to get off to work.

Thanks Again!
Robert

seasidemotors
 
Posts: 20
Joined: Wed Mar 24, 2021 9:55 pm

Re: ssd1306, pico, micropython

by seasidemotors on Thu May 20, 2021 1:37 am

I was able to download the Hackspace magazine and got to read through your article describing the code, very slick ;)

So I have a much better understanding of how the code works and was able to make some modifications that are to my liking. I can now move forward and try to port in the temperature reading from the pico.

Here is the snippet of code that I was able to modify to directly effect the display. I also wrote in comments so I can easily see what the numbers do and how they effect the screen display. I also put in a comment giving credit to the original author of the code (Tony) as is standard practice in open source software.

Code: Select all | TOGGLE FULL SIZE
# This code originally written by Tony Goodhew
# For code reference, hackspace #41, pg74

# Basic stuff
oled.text("Raspberry",2,2) #writes text, x,y
oled.text("Pico",2,16) #writes text, x,y
oled.pixel(100,25,0) #writes a single pixel, x,y,1=white, 0=Black
oled.rect(0,0,128,32,1) #draws a rectangle, x,y,width,height
oled.fill_rect(0,27,128,5,1) #fills in a box, x,y,width,height
oled.line(0,12,128,12,1) #draw a line, x,y,width,y-endpoint
Attachments
PXL_20210520_051820621.jpg
PXL_20210520_051820621.jpg (154.96 KiB) Viewed 303 times

seasidemotors
 
Posts: 20
Joined: Wed Mar 24, 2021 9:55 pm

Re: ssd1306, pico, micropython

by Tonygo on Thu May 20, 2021 5:54 am

You have to convert numbers to strings before you print.

Code: Select all | TOGGLE FULL SIZE
tempC = 24.1          # get from sensor
oled.text("Temperature: " + str(tempC),3,2)


This library does not support different sized characters but you could define the the few letters you need as icons and use them. Slow but it does the job.

Have fun

Tonygo
 
Posts: 95
Joined: Fri Apr 13, 2018 11:09 am

Re: ssd1306, pico, micropython

by seasidemotors on Thu May 20, 2021 11:52 pm

This project is complete, I have successfully queried the pico's internal temperature sensor and output the data to the SSD1306 OLED Display.

As a bonus I was able to convert the temp output to from Celsius to Fahrenheit. In the attached photo, I was able to lower the temp by placing a bag of frozen cherries on the chip :)

Final Code:

Code: Select all | TOGGLE FULL SIZE
robert@mountain-cabin:~/Code/1306LCD$ cat PicoTemp2LCD.py
# This code originally written by Tony Goodhew
# For code reference, hackspace #41, pg74

# Display Image & text on I2C driven ssd1306 OLED display
import machine
from ssd1306 import SSD1306_I2C
import utime

# Define Hardware Parameters
sda=machine.Pin(0)
scl=machine.Pin(1)
i2c=machine.I2C(0,sda=sda, scl=scl, freq=400000)
oled=SSD1306_I2C(128, 32, i2c)

# Defining Display Parameters
def blk():
    oled.fill(0)
    oled.show()

def horiz(l,t,r,c):  # left, right , top
    n = r-l+1        # Horizontal line
    for i in range(n):
        oled.pixel(l + i, t, c)

def vert(l,t,b,c):   # left, top, bottom
    n = b-t+1        # Vertical line
    for i in range(n):
        oled.pixel(l, t+i,c)

def box(l,t,r,b,c):  # left, top, right, bottom
    horiz(l,t,r,c)   # Hollow rectangle
    horiz(l,b,r,c)
    vert(l,t,b,c)
    vert(r,t,b,c)

# Clear the oled display in case it has junk on it.
oled.fill(0) # Black

# Define Temperature Output and Display
adc=machine.ADC(4)
conversion_factor=3.3/(65535)
while True:
    reading=adc.read_u16()*conversion_factor
    tempF=80.6-(reading-0.706)/0.003098 #converted to *F
    oled.text("Temp: "+str(tempF),6,12)
    oled.rect(0,0,128,32,1)
    oled.show()
    utime.sleep(2)
    blk()



Actually I would like to know how to truncate the temp output to hundredth's place? ex. 78.52
Might also be nice to add an additional text output after the temp output? ex. 78.52*F
Attachments
pico_temp.jpg
pico_temp.jpg (136.1 KiB) Viewed 282 times

seasidemotors
 
Posts: 20
Joined: Wed Mar 24, 2021 9:55 pm

Re: ssd1306, pico, micropython

by Tonygo on Fri May 21, 2021 2:37 am

This rounds to the nearest 2 dp

Code: Select all | TOGGLE FULL SIZE
t= 21.126456
print(t)
t2 = float(round(t * 100)/100.0)
print(t2)


>>> %Run -c $EDITOR_CONTENT
21.12646
21.13
>>>

Tonygo
 
Posts: 95
Joined: Fri Apr 13, 2018 11:09 am

Re: ssd1306, pico, micropython

by seasidemotors on Wed May 26, 2021 12:15 am

Hello,

I am back, I figured I should keep working at this since I do need to figure this out as my future projects will rely heavily on showing sensor readings on displays. IE; I need to be able to display multiple outputs to a single screen such as Oil Pressure, Water Temp, Exhaust Gas Temp, Boost Pressure, etc....This screen I have now is obviously too small but the concept and programming would be the same just scaled to a larger display.

I went ahead and incorporated your code above to truncate the output to only 2 decimal places and for the most part it works.
The problem seems to be that it is a mathematical equation being applied to the output of the sensor reading as opposed to a absolute place holder per say.

As an example I might get something like, 78.06 but if the zero is after the 1st digit then I get something like 78.8001, other times I might just get 78.1 so not exactly always just 2 decimal places in the output.

Code: Select all | TOGGLE FULL SIZE
# Define Temperature Output and Display
adc=machine.ADC(4)
conversion_factor=3.3/(65535)
while True:
    reading=adc.read_u16()*conversion_factor
    temp=80.6-(reading-0.706)/0.003098
    tempF=float(round(temp*100)/100.0)
    oled.text("Temp: "+str(tempF),6,12)
    oled.rect(0,0,128,32,1)
    oled.show()
    utime.sleep(1)
    blk()



In addition I am also having a hard time figuring out how to refresh ONLY the temperature output. As can be seen from the loop, as it calls the blk() command that is always going to blank the entire screen every time the temp updates, in this case every second. Instead of writing this as a loop, should I be considering using an interrupt request?

I have been trying to find more information on programming the oled and I am missing something. I tried the adafruit tutorial but it only gives examples for circuit python or regular python. When I look up on youtube they are all basic hello world tutorials using thonny. As a note if it matters, I run Linux, write the code in Vim and upload via rshell.

seasidemotors
 
Posts: 20
Joined: Wed Mar 24, 2021 9:55 pm

Re: ssd1306, pico, micropython

by Tonygo on Wed May 26, 2021 6:46 am

I think you are being far too optimistic about the readings from the temperature sensor built into the Pico. It was probably put there as a safety monitor to keep an eye on the core temperature and protect it from excessive overclocking and really heavy processor use. It was not really put there for accurate ambient temperature measurement by the user.

Expecting to read a temperature of the local air to 1/00 th of a degree F from such a device is not to be expected. The raw sensor reading is then passed through a rather ‘iffy’ ADC to produce a voltage then shifted to make it apparently 16-bit accurate. Compare the reading you get, once it has settled to a certified mercury thermometer. Usually any decimal part is fiction.

A couple of years ago I wrote a piece on reading temperatures, which you may find helpful:

https://www.instructables.com/Testing-T ... ne-for-Me/

Even specialist/expensive digital temperature sensors are only are only accurate to about + or – 1 degree C and there is a long ‘lag’ due to heat capacity.

How to stop screen flicker and messed up characters:

1. Only blank the whole screen if you really need to.
2. Write all the static items to the screen before the loop.
3. Overwrite dynamic items with a background rectangle before updating in foreground colour.


Code: Select all | TOGGLE FULL SIZE
# Display Image & text on I2C driven ssd1306 OLED display
# from machine import Pin, I2C
# Tony Goodhew 26th May 2021
import machine
from ssd1306 import SSD1306_I2C
import utime
WIDTH  = 128                                            # oled display width
HEIGHT = 32                                             # oled display height

# Explicit Method
sda=machine.Pin(0)
scl=machine.Pin(1)
i2c=machine.I2C(0,sda=sda, scl=scl, freq=400000)
#  print(i2c.scan())
from ssd1306 import SSD1306_I2C
oled = SSD1306_I2C(128, 32, i2c)

def blk():
    oled.fill(0)
    oled.show()

def horiz(l,t,r,c):  # left, right , top
    n = r-l+1        # Horizontal line
    for i in range(n):
        oled.pixel(l + i, t, c)

def vert(l,t,b,c):   # left, top, bottom
    n = b-t+1        # Vertical line
    for i in range(n):
        oled.pixel(l, t+i,c)

def box(l,t,r,b,c):  # left, top, right, bottom
    horiz(l,t,r,c)   # Hollow rectangle
    horiz(l,b,r,c)
    vert(l,t,b,c)
    vert(r,t,b,c)


# Define Temperature Output and Display
adc=machine.ADC(4)
conversion_factor=3.3/(65535)
blk()
oled.rect(0,0,128,32,1)
oled.text("Temp: ",6,12,1)
while True:
    reading=adc.read_u16()*conversion_factor
    temp=80.6-(reading-0.706)/0.003098
    tempF=float(round(temp*100)/100.0)
    oled.fill_rect(50,12,50,10,0) # Overwrite number
    oled.text(str(tempF),50,12)   # Update number
    oled.show()
    utime.sleep(1)


Look here for how to format printing. (I seldom do this.)

https://realpython.com/python-formatted-output/

Suggest you get a 128x64 display - so much more room!

Tonygo
 
Posts: 95
Joined: Fri Apr 13, 2018 11:09 am

Re: ssd1306, pico, micropython

by seasidemotors on Mon May 31, 2021 11:49 am

Tonygo wrote:I think you are being far too optimistic about the readings from the temperature sensor built into the Pico. It was probably put there as a safety monitor to keep an eye on the core temperature and protect it from excessive overclocking and really heavy processor use. It was not really put there for accurate ambient temperature measurement by the user.

Expecting to read a temperature of the local air to 1/00 th of a degree F from such a device is not to be expected. The raw sensor reading is then passed through a rather ‘iffy’ ADC to produce a voltage then shifted to make it apparently 16-bit accurate. Compare the reading you get, once it has settled to a certified mercury thermometer. Usually any decimal part is fiction.


Howdy,
Thanks again for the reply. I just wanted to respond to this real quick. I have no illusions regarding the internal temperature sensor in the pico, I understand what its function is intended for and aware of its limitations. I am only using it as a test bed to learn how to work with sensors and output devices. I also never expected nor do I particularly need a sensor to read to 1/100th of a degree, I am explicitly trying to lock in a decimal point for consistency of display as I may want to bring to market custom gauge displays and thus I need it to be rock solid consistent. Very much like the simple calculator in which you can select you decimal places from 0,1,2,3,4 or full/raw output. So if you pay me $$$ to provide you with a custom gauge solution, your not going to want to see a reading fluctuating from 1-5 decimal places. If the gauge reads 78.1 and I have it set to 2 decimal points then it needs to display 78.10 and vice versa, if its reading 78.08002 then it needs to output 78.08. In some cases I may not want decimal points at all, for instance if I am reading from a pyrometer with a limit of 2,500*f, I don't want any decimal points in the gauge display.

While I am new to writing code I am familiar with hardware, electrical circuits, computers, automotive, etc.... having both a A/A in Automotive Technology and an A/S in Computer Network Technology.


Tonygo wrote:A couple of years ago I wrote a piece on reading temperatures, which you may find helpful:

https://www.instructables.com/Testing-T ... ne-for-Me/

Even specialist/expensive digital temperature sensors are only are only accurate to about + or – 1 degree C and there is a long ‘lag’ due to heat capacity.

How to stop screen flicker and messed up characters:

1. Only blank the whole screen if you really need to.
2. Write all the static items to the screen before the loop.
3. Overwrite dynamic items with a background rectangle before updating in foreground colour.

Look here for how to format printing. (I seldom do this.)

https://realpython.com/python-formatted-output/

Suggest you get a 128x64 display - so much more room!


Thank You very much for these 2 links, I have only had a chance to do a cursory overview of the content but they are both very helpful for what I am looking to do. I also am much appreciative for the code example you also included. I haven't had a chance to try it just yet but look forward to, thank you.

In regards to the temperature and display options I did also just receive a PT100 RTD temp probe with amplifier and I also got a 16x2 character lcd to play with as well. I am also looking at a 128x128 oled screen but don't want to overwhelm myself just yet, still got plenty to chew on with what I got on deck at the moment.

Tony, thanks again for all your thoughtful replies and helpful instruction and guidance, it is very much appreciated.

seasidemotors
 
Posts: 20
Joined: Wed Mar 24, 2021 9:55 pm

Re: ssd1306, pico, micropython

by seasidemotors on Mon May 31, 2021 7:17 pm

Ok, I was able to use the .format command to format the decimal places to explicitly 2 decimal points by changing the oled.text formatting.

Code: Select all | TOGGLE FULL SIZE
#    oled.text("Temp: "+str(tempF),6,12) #old
      oled.text("Temp: {:.2f}".format(tempF),6,12) #new


As you can see I simply added in the {:.2f} in the text string to specify the exact decimal place holders. Instead of using the +str (string), I used the .format command.
Credit goes to Dave Hylands on the micropython forum. I just happened to stumble on the following thread that put the formatting command right in my hand.
https://forum.micropython.org/viewtopic.php?f=2&t=10497

Next up, let me see if I can use your code example to update just the number output.

seasidemotors
 
Posts: 20
Joined: Wed Mar 24, 2021 9:55 pm

Re: ssd1306, pico, micropython

by seasidemotors on Mon May 31, 2021 8:58 pm

Well we did it!
Flicker free display that only updates the sensor data with a consistent 2 decimal place display.

Code: Select all | TOGGLE FULL SIZE
# Display Image & text on I2C driven ssd1306 OLED display
import machine
from ssd1306 import SSD1306_I2C
import utime

# Define Hardware Parameters
sda=machine.Pin(0)
scl=machine.Pin(1)
i2c=machine.I2C(0,sda=sda, scl=scl, freq=400000)
oled=SSD1306_I2C(128,32, i2c)

# Defining Display Parameters
def blk():
    oled.fill(0)
    oled.show()

# Define Temperature Output and Display
adc=machine.ADC(4)
conversion_factor=3.3/(65535)
blk()
oled.rect(0,0,128,32,1)
oled.text("Temp: ",6,12,1)
while True:
    reading=adc.read_u16()*conversion_factor
    temp=80.6-(reading-0.706)/0.003098
    tempF=float(round(temp*100)/100.0)
    oled.fill_rect(50,12,50,10,0) # Overwrite number
    oled.text("{:.2f}".format(tempF),50,12)   # Update number
    oled.show()
    utime.sleep(1)

seasidemotors
 
Posts: 20
Joined: Wed Mar 24, 2021 9:55 pm

Please be positive and constructive with your questions and comments.