What a cutie pie! Or is it… a QT Py? This diminutive dev board comes with one of our new favorite chip, the RP2040. It’s been made famous in the new Raspberry Pi Pico and our Feather RP2040 and ItsyBitsy RP2040, but what if we wanted something really smol?
That leaves me with SDA, SCL, TX, and RX which I assume is used for serial interfacing and I2C.
However
From the CE product page.
-13 GPIO pins (11 breakout pads and two QT pads):
-Four 12 bit ADCs (one more than Pico)
-Two I2C ports (one on the QT connector, one on the breakout pads)
-SPI and UART peripherals, in standard QT Py locations,
-PWM outputs on every IO pin - for servos, LEDs, etc
-There are 6 GPIO in consecutive order for PIO compatibility
The way I read this: SDA+SCL is the first I2C and SDA1+SCL1 is the second.
SPI and UART peripherals is referring to TX and RX.
This makes me think that I will be able to daisy the piicodev display and the piicodev button to SDA1 and SCL1 without conflict with the audio bff. (assuming I’m happy to software bash… which I am).
You’ll just be initialising the daisy-chainable PiicoDev hardware on I2C1, and the Audio BFF will be initialised on the SPI bus. Since the GPIO23, and GPIO22 are not reused elsewhere on the board there should be no conflicts
Two things:
Don’t confuse I2S with I2C. I2S is an audio interface. I2C is a generic, low speed data interface.
Our PiicoDev libraries are only compatible with MicroPython - not CircuitPython, just be careful of that before you start.
No worries, I like the idea of using a higher level language like MicroPython for this project anyway.
Speed is not essential.
I’m choosing the RP2040 because apparently there is a micropython port for it.
Piicodev solution?
I’m choosing the adafruit board because it’s got this BFF component that looks convenient.
However I’d be open to a completely Piicodev solution, I’m just not sure how to handle the audio with piicodev. Is there a piicodev audio solution I should consider?
(i think I’d like to avoid coding up PWM from scratch on this occasion).
Just a PiicoDev Buzzer Module at the moment so probably not super useful for your project. This is not a bad idea for a module however and would fit well into the PiicoDev line-up.
I will pass this up and we may potentially do something like this in the future.
I have had to swap to a different SD card library since the code above no longer works.
Here is the wrapper around the guide above.
### THIS NEEDS TO BE A CLASS
from wavplayer import WavPlayer
import os
from machine import Pin, SPI, I2S
import sdcard
def play():
wp.play("hello.wav", loop=True)
return is_playing()
def pause():
wp.pause()
def resume():
wp.resume()
def is_playing():
return wp.isplaying()
# ======= SD CARD CONFIGURATION =======
#sd = SDCard(slot=1, sck=Pin(6), mosi=Pin(3), miso=Pin(4), cs=Pin(29))
sd_pin = Pin(29)
sd_spi = SPI(0)
sd = sdcard.SDCard(sd_spi, sd_pin)
os.mount(sd, "/sd")
# Read files from SD card
print("my list:")
for filename in os.listdir("/sd"):
print(filename)
# Init Audio
'''
A1 - This is used for audio data, or DAT.
A2 - This is used for wordselect clock, or LR.
A3 - This is used for bitclock, or BCLK.
'''
audio_clk_pin = Pin(26)
audio_word_pin = Pin(27)
audio_data_pin = Pin(28)
#Construct the wp
wp = WavPlayer(id=0, sck_pin=Pin(audio_clk_pin), ws_pin=Pin(audio_word_pin), sd_pin=Pin(audio_data_pin), ibuf=44100)
Here is the offending code.
play() #<----
while is_playing():
if button.is_pressed:
pause()
Goal
It’s all feeling like I’m on the wrong path entirely in terms of my build chain.
I will entertaining loading from SD onto flash first and reading from flash.
Nothing here is real time. 99% of the time this chip will be idle so I have plenty of time of read-write blocking code.
What a deep dive we are getting ourselves into. You managed to get some noise out so surely we can get the right noise out. I want to completely rule out hardware as an issue first. From what I’ve gathered the button and the display you have gotten working. This leaves the Audio BFF and the speaker.
If you haven’t already can you try either the CircuitPython or Arduino examples. The Arduino scripts are said to have been tested with the QT Py RP2040 and the library has been updated in the last year for those boards so hopefully that won’t be too difficult. It may be easier than the CircuitPython example.
Ah! I have also tried this.
I claim the Arduino script here doesn’t compile.
Here is a screen shot message.
I think I’ve got all the correct libraries installed.
Using library SdFat - Adafruit Fork at version 2.2.3 in folder: C:\Users\optim\Documents\Arduino\libraries\SdFat_-_Adafruit_Fork
Using library SPI at version 1.0 in folder: C:\Users\optim\AppData\Local\Arduino15\packages\adafruit\hardware\samd\1.7.16\libraries\SPI
Using library Adafruit Zero DMA Library at version 1.1.3 in folder: C:\Users\optim\AppData\Local\Arduino15\packages\adafruit\hardware\samd\1.7.16\libraries\Adafruit_ZeroDMA
Using library Adafruit WavePlayer Library at version 1.0.7 in folder: C:\Users\optim\Documents\Arduino\libraries\Adafruit_WavePlayer_Library
Using library I2S at version 1.0 in folder: C:\Users\optim\AppData\Local\Arduino15\packages\adafruit\hardware\samd\1.7.16\libraries\I2S
exit status 1
Compilation error: 'I2S' does not name a type
and I took the time to push out the verbose stack.
C:\Users\optim\Documents\Arduino\Flower_BFF\Flower_BFF.ino:10:1: error: 'I2S' does not name a type
10 | I2S i2s(OUTPUT); // I2S peripheral is...
| ^~~
C:\Users\optim\Documents\Arduino\Flower_BFF\Flower_BFF.ino: In function 'void setup1()':
C:\Users\optim\Documents\Arduino\Flower_BFF\Flower_BFF.ino:69:3: error: 'i2s' was not declared in this scope; did you mean 'I2s'?
69 | i2s.setDATA(pDOUT);
| ^~~
| I2s
C:\Users\optim\Documents\Arduino\Flower_BFF\Flower_BFF.ino: In function 'void loop1()':
C:\Users\optim\Documents\Arduino\Flower_BFF\Flower_BFF.ino:104:7: error: 'i2s' was not declared in this scope; did you mean 'I2s'?
104 | i2s.write((int32_t)sample.channel0 - 32768);
| ^~~
| I2s
C:\Users\optim\Documents\Arduino\Flower_BFF\Flower_BFF.ino: In function 'void play_i2s(File)':
C:\Users\optim\Documents\Arduino\Flower_BFF\Flower_BFF.ino:120:9: error: 'i2s' was not declared in this scope; did you mean 'I2s'?
120 | if (i2s.begin(rate)) {
| ^~~
| I2s
Multiple libraries were found for "Adafruit_ZeroDMA.h"
Used: C:\Users\optim\AppData\Local\Arduino15\packages\adafruit\hardware\samd\1.7.16\libraries\Adafruit_ZeroDMA
Not used: C:\Users\optim\Documents\Arduino\libraries\Adafruit_Zero_DMA_Library
This error makes sense to me.
This is such a weird line of c++ code. OUTPUT is as far as I can tell undefined. It does not appear anywhere else in the INO.
That would imply it’s in a header file or linker which is bizarre (but it’s c++ so… anything is possible). Also that comment is absolutely wild. People who put a trailing ellipse on a comment truly want to see the world burn.
I2S i2s(OUTPUT); // I2S peripheral is...
So I2S is apparently not a type at all which might suggest the linker can’t find the library header files, but if that was the case you would think the linker would throw an error as well.
#include <I2S.h> // << Why didn't this throw and error??
At this point I gave up, figuring one rabbit hole to focus on was enough
I’m open to debugging the Arduino code if you strongly suspect the hardware.
It would be nice to see it working. This is my first time playing with Is2 so maybe there is an obvious fix? Any thoughts?
No the code largely runs, the audio is just terrible. I find that unsurprising given that I’ve had to swap SD card library and then run surgery inside the library to get it to read.
While I don’t have the audio BFF I do have the QT Py. The board IDE is looking for should be the Adafruit QT Py RP2040. The I2S library may be board specific.
Give that a go and we’ll start tackling the MicroPython.
OK good call Jack.
I thought I had the right board installed but I it was just a look-alike.
Must have missed a step the first time.
I can’t upload the code.
Converting to uf2, output size: 178688, start address: 0x2000
Scanning for RP2040 devices
Unable to build drive list
Failed uploading: uploading error: exit status 1
I think it’s because I have already installed micro python.
Does thonny have an easy uninstall feature I could use?
It could be other things too. I’ll have a google around.
Maybe a PC thing? I’ll try some other computers tonight too tonight.
I am using a different boards manager all together.
It also includes all the QT ESP32 boards
We’ll be working backwards a bit if we keep pursuing this way I think. Adafruit has a page on UF2 which is the format the RP2040 uses. Bootloader mode may help. Hold the boot button when plugging it into the USB-C cable. The test board I have here wasn’t recognised until I did that.
I’m happy to spend the time making this work in C++.
A working example will be a breath of fresh air.
Who knows, It might be easy to get the Piicodev Button and the OLED working in C++ than it is to get the QT Py working in Micro-python.
Does the piicodev stuff work with adafruit libs?
Thanks for your support btw.
@Jack Thanks for giving me the encouragement to look back into the Arduino IDE route. It was a good idea and I gave up too quickly the first time.
A big part of my goal with this project was to learn micropython.
Now that we have ruled out hardware, Is there anything avenues I have to get audio working there?
Taking one step back from the Qt Py RP2040, its just a Pico!
The SAMD is a different processor entirely but also features in a QT Py form factor (Same as the Xiao)
Maybe, though drivers would have to be developed. But all of that is open source so should be convertible given enough time/knowledge/GPT tokens.
The OLED will being a SSD1306 but the button is running bespoke code and from the outside, interacts over I2C like any other device - but with a microcontroller between the main controller and end peripheral (button in this case).
Unfortunately without a driver for the specific chip on that module it will be hard and again a lot of porting.
Hope that answered at least some of your questions
Liam
@Pixmusix If you want to use the button with Arduino code, you can treat the PiicoDev button line any other a quirky I2C device. Truly a freaky little guy.
The register map is here in the README.md of the firmware repo.
You’re probably after the is_pressed and was_pressed registers which are 0x11 and 0x12
Why freaky?
You can ignore the second register number that is assigned to some registers - this is a quirk for writes to the register, which does not apply to the button state readonly registers.
For example
If you wanted to read the status of the LED, you would read from 0x05
If you wanted to write the state of the LED, you would write to 0x85
This isn’t at all how other i2c devices work, but it’s a quirk of how I’ve implemented an I2C device using the arduino i2c library. This strange behaviour would be completely concealed by the MicroPython driver that we intend the device to be used with, but if you’re braving doing this from scratch, this is useful to know
@Michael, so helpful as always! Seeing that table and reading this post made i2c protocol click for me.
Quick clarifying question.
The below code is feeding me data, but it the data is slow and doesn’t seem to correspond to button presses.
The bytes coming back are -0x1(-1) and 0x40(64).
64 is 0b1000000 and -1 (assuming two’s compliment) is 0b00000001. I’m starting to wonder if the piicodev is little endian but wire.h is expecting big endian? Are there any quirks in reading the bytes I need to be aware of?