Adafruit Py Qt 2040 - Thonny - Piicodev

Hiya.

Got a wiring issue needing some assistance debugging.

Code

I’m using Thonny.
Running this code.

import neopixel
import machine

from PiicoDev_SSD1306 import *
from PiicoDev_Unified import sleep_ms


#Init Neopixel
np_pow = machine.Pin(11, machine.Pin.OUT)
np = machine.Pin(12, machine.Pin.OUT)
np_pow.value(1)
px = neopixel.NeoPixel(np, 1)

def pukPix(v):
    px[0] = (v, 0, v)
    px.write()
    sleep_ms(100)
    
def flickerNeopixel():
    for i in range(0,12):
        v = (i % 2) * 255
        pukPix(v)
    pukPix(0)

#Init Display
display = create_PiicoDev_SSD1306() #Onboard Neopixel flickers if you remove this line

while True:
    flickerNeopixel() #HERE I AM!
    sleep_ms(1000)

Files

I created and uploaded these files

Lib install

Lastly I made sure my PC running thonny had Piicodev library.

pip install piicodev
pip show piicodev
Name: piicodev
Version: 1.10.0
Summary: Drivers for the PiicoDev ecosystem of sensors and modules
Home-page: https://github.com/CoreElectronics/CE-PiicoDev-PyPI
Author: Core Electronics
Author-email: production.inbox@coreelectronics.com.au
License: UNKNOWN
Location: C:\Users\optim\AppData\Local\Programs\Python\Python311\Lib\site-packages
Requires: smbus2
Required-by:

Setup Pic

Error

I’m getting the following error.

PiicoDev could not communicate with module at address 0x3C, check wiring

On this thread I noticed to check the addr pins are unsoldered.
mine seem to have a connection?
What does that mean? could that be the source of my problem?

Any other sanity checks to try?
Thanks champs. :muscle:
Pix :heavy_heart_exclamation:

1 Like

Start by checking whether or not there is a device at address 0x3C.
Raspberry Pi | How to Scan and Detect I2C Addresses | Adafruit Learning System

Your device appears to have a switch for the address selection, not solder tabs. If you change the switch you should see the address change in the I2C bus scan. That would show that the device you are seeing in the scan is the SSD1306.

2 Likes

Thanks Jeff.

Doesn’t seem like there is a windows port of the i2c tools.
No worries, I’ll wait till I’m back in the studio with linux or OSX.
I’ll be in touch.

You don’t need any special Windows support for Pi code - it’s the same for any platform. For instance:
Raspberry Pi Pico: I2C Scanner (MicroPython) | Random Nerd Tutorials

Ah I see it’s a python library not a Linux package.

Thanks for the link :+1:
Based on the pinout of the qt py I have two i2c outs so I modified the code you linked to search both of them.

from machine import Pin, SoftI2C

def scani2c(i2c):
    devices = i2c.scan()
    if len(devices) == 0:
      print("No i2c device !")
    else:
      print('i2c devices found:', len(devices))
      for device in devices:
        print("I2C hexadecimal address: ", hex(device))

i2c_0 = SoftI2C(scl=Pin(25), sda=Pin(24))
i2c_1 = SoftI2C(scl=Pin(23), sda=Pin(22))

print('I2C_0 SCANNER')
scani2c(i2c_0)
print('i2c_1 SCANNER')
scani2c(i2c_1)

I2C_0 SCANNER
No i2c device !
i2c_1 SCANNER
i2c devices found: 1
I2C hexadecimal address: 0x3c

I was expecting the i2c_0 scanner to detect the qt py Audio BFF but I guess that’s a future problem.

i2c_1 did detect the OLED display which is good news.
The piicodev example code I found didn’t seem to require me to specify the address. I wonder if I’m supposed to modify the PiicoDev_SSD1306.py device module file. It’s from CE’s Raspberry Pi Pico Guide, which also uses an RP2040, but may not have the same SCL and SDA GPIO pinouts.
Happy to have a dig through that.
Any other thoughts to try while I’m at it? :slight_smile:

Quick followup.

The constructor of the Piicodev OLED display made it look like I might be able to pass in the SCL and SDA pin.

def create_PiicoDev_SSD1306(address=0x3C,bus=None, freq=None, sda=None, scl=None, asw=None):
   #...

However, the piicodev SSD1306 github README implies this is just for the Rpi Pico.

You detected something at that address. If you have more than one i2c device installed then you need to ensure that all are detected, otherwise you can’t be sure that the device you saw at 0x3C is the SSD1306. Or, unplug any other devices so that only the SSD1306 could be detected. Or, flip the address switch and run the scan again to confirm that it is detected at the alternate address (0X3D).

When you confirm that it is the SSD1306 that you have identified you need to change the constructor to use i2c_1. It looks like you can use the bus parameter (that worked for the scan): SDA/SCL parameters are listed as only for the Pico.

If that all works then it is likely that the same adjustment is needed for any other I2C device.

I ran those tests unplugging the audiobff and re-running. Confirmed that it really is the OLED. :+1:. For future I can distinguish the bus’s with the machine.Pin() objects I pass in to create my i2c_1 scanner. If the AudioBFF was found it would only be on the I2c_0 bus. The OLED is the only device on BUS 1.

i2c bus arguments…

A bit more digging and I did figure out what I was supposed to pass in.
The piicodev_ssd1306 lib constructor actually calls the create_unified_i2c() constructor. It tries to figure out if you’re on Linux or Esp32 etc. In my case it gives up and passes the call to an I2CUnifiedMachine().
Inside that constructor there is the if block below which just checks for… a bus. Could be anything.

if bus is not None and sda is not None and scl is not None:
            print('Using supplied bus, sda, and scl to create machine.I2C() with freq: {} Hz'.format(freq))
            self.i2c = I2C(bus, freq=freq, sda=sda, scl=scl)

So for example here is my first attempt at specifying the bus.

oled = create_PiicoDev_SSD1306(address=0x3C, bus=1, scl=Pin(23), sda=Pin(22))

And that got me this… PROGRESS!!

i2c devices found: 1
I2C hexadecimal address: 0x3c
Using supplied bus, sda, and scl to create machine.I2C() with freq: 400000 Hz

Unfortuantely, this also silences the error. :stuck_out_tongue:

oled = create_PiicoDev_SSD1306(address=0x3C, 
  bus="
  These beauteous forms, 
  Through a long absence, have not been to me 
  As is a landscape to a blind man’s eye: 
  But oft, in lonely rooms, and ’mid the din 
  Of towns and cities, I have owed to them, 
  In hours of weariness, sensations sweet", 
  scl=Pin(23), 
  sda=Pin(22))

Gotta love python :stuck_out_tongue:
@Jeff I think you’re right it is requiring me to specify the bus. I think It wants the integer 1 but I’m not sure why.

Ultimately, piicodev i2c is just a wrapper around machine.i2c().
If you read the docs on the machine.i2c github it says

I2C.init(scl, sda, *, freq=400000)
Initialise the I2C bus with the given arguments:
– scl is a pin object for the SCL line
– sda is a pin object for the SDA line
– freq is the SCL clock rate

I’m actually not sure why I need to specify the bus at all at this point or what it … does?

I found this article and I guess I’ll give it a read on the train home.
If anyone would love to save me the trouble I’d really appreciate it :sweat_smile:

TL;DR

The bottom line is that the CE Piicodev_ SSD1306 README does specify that the range is supposed to be (0,1). I assume that integers not floats.
I’m not certain how to figure out which one I am, or what it does. :stuck_out_tongue:

1 Like

The required values for bus are 0 or 1. If the type is not specified then you can assume integer, as integer can be promoted if required. Failing to specify the type for an argument is a common problem in Arduino library references and perhaps the habit is spreading. i2c0 and i2c1 are part of the Pi device definition. Using those terms means that code can be moved between different devices that may have different pin definitions for the same function, because the device definition maps the labels to the correct pins. Which one you use depends on how the bus is connected.

From my reading I would assume you don’t use SCL and SDA in the constructor (leave it as none) because bus #1 is already defined as using the alternate pins. You would supply SDA and SCL if you were using pins that are not defined for either bus #0 or bus #1, or if there was no usable bus definition for a particular device. However the code snippet you have quoted (if it applies to your device - you have to look at context for code snippets) suggests that if one of bus sda and scl is not none then all of them must be provided. Whether or not that is the case depends on whether sda and scl get assigned default values if they are passed as none. But you have code that works, so that’s not an issue.

The datasheet you reference is interesting reading (TI documentation is always very good) but probably not helpful for your case. The detail in that document is what the library is trying to simplify down to the minimum set of configuration and operation commands to make the bus devices usable. Having now got the constructor working you can move on with the debugging.

2 Likes

Because the Pico port of MicroPython does require a bus number: Hardware I2C

You’ve really hammered the edges of our PiicoDev docs @Pixmusix and I commend you! I’m sure we could have done a better job when putting them together… but it is an open source project :smiley: Pull requests welcomed.

I’m glad your project is on track :partying_face:

2 Likes

this is particularly unhinged. We did not include error handling for this case :sweat_smile:

2 Likes

I think you can have “too much documentation” though right?. :slight_smile:
If you document the piicodev ecosystem for every variation of micro-controller it will end up making the documentation inaccessible for beginners.
I figured it out in the end, and I had this forum as an alternate support network.

3 Likes