RaspPi Pico W data via OSC to Sonic Pi

Hello, I’m trying to connect Pico data output to Sonic Pi via OSC. Two days later, any help much appreciated! I’ve tried an MQTT, but it sounds as though OSC could work without it.
Also, Pico uses Micropython. I’m wondering whether to give up and migrate to a different microcontroller. It’s for a data sonification experiment.

What I’ve tried:
Robin’s local host OSC sample is working.
Touch OSC with an encoder block appears in the cue log and shows live data change in the
Temperature output from Pico to file and MQTT worked.
Subscribe/publish on the MQTT worked.
/Temperature file and loop triggers are appearing in Sonic Pi. However, there’s no data in the

Have tried both ways but just double checking:

  • the Sonic Pi code should reference the Pico IP/port? (have tried osc* also)
  • the Thonny/Micropython code should reference the Win laptop IP/port?

Hope I’m on track, v braincurdled as this is waaay out of comfort zone, too many parameters, but also happy to have learned a lot in past 2 days :slight_smile:

I saw a 2018 thread on OSC/Pico/Sonic being tricky, so if anyone has an update, that would be great.
Thank you!

I know nothing about this stuff …
But have You tried with midi ?
and You might try here …

1 Like

Thank you for the link and great Q!

The Pico it seems can handle midi and there’s also a pico-midi interface here. https://midimuso.co.uk/index.php/pico-midi/

Also found a good tutorial on pico-mqqt here: https://www.hivemq.com/article/iot-reading-sensor-data-raspberry-pi-pico-w-micropython-mqtt-node-red/

Node-red has been mentioned here too.

My partner does bat surveys and the pico is inexpensive so we’re experimenting… Sonic Pi looks like a brilliant way to do live data sonification?

Update:
Pico it seems can handle MIDI in, but not MIDI out yet, can’t find a workable library.
However, CircuitPlayground handles MIDI out, using adafruit_midi
CP can activate this online synth with MIDI: Viktor NV-1 Synthesizer (nicroto.github.io) and also shows up in Sonic Pi.

.
Both the Thonny and Sonic Pi need to be running for this interaction to work - hopefully a good sign that they’re speaking to each other… fingers x’d

Hm, just found webMIDI. Guess everyone here knows this already, but as a newb I found this v interesting!

I thought I’d dig out my pico W and have a go at doing this. I found a micro-pyrthon-osc library under development and the client side of this works with Sonic PI.

The program I used was this


#Pico W OSC client using uosc.client library
#download zip file of library from https://github.com/SpotlightKid/micropython-osc
#install using Thonny
#best to comment ALL print commands once you hacve things working to run it independently as main.py
import network
from machine import Pin, ADC
import usocket as socket
from time import sleep
import secrets
from uosc.client import  Client
import gc #garbage collection

adcpin = 4
sensor = machine.ADC(adcpin)

#set up onboard led to turn on when connected
led = machine.Pin('LED', machine.Pin.OUT)
led.on()
sleep(.3)
led.off()
sleep(.3)

# Wi-Fi configuration
ssid = secrets.SSID
password = secrets.PASSWORD

# Static IP address configuration (if required)
ip_address = secrets.PICOIP
subnet_mask = "255.255.255.0"
gateway = "192.168.1.1"  # Replace with your gateway IP

# OSC settings
sp_host = secrets.SPIP  # Replace with your Sonic Pi IP address
sp_port = secrets.SPPORT  # Replace with required  OSC port (4560 for Sonic PI)
sp_dest = socket.getaddrinfo(sp_host, sp_port)[0][-1]
#print(sp_dest) #turn off all prints for standalone operation
# GPIO pin configuration
gpio_pin = machine.Pin(7, machine.Pin.IN, machine.Pin.PULL_UP) #used to gtrigger OSC call

# Connect to Wi-Fi
def connect_to_wifi(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    if not wlan.isconnected():
        print("Connecting to WiFi...")
        wlan.active(True)
        led.off()
        #uncomment next line for static address. Otherwise uses dhcp
        #wlan.ifconfig((ip_address, subnet_mask, gateway, "8.8.8.8"))  # Set static IP
        wlan.connect(ssid, password)
        while not wlan.isconnected():
            #print(".",end=".")
            sleep(1)  # Add a delay of 1 seconds
    #print("Connected to WiFi")
    #print("IP Address:", wlan.ifconfig()[0])
    # LED on for wifi connected
    led.on()

def ReadTemperature():
 	adc_value = sensor.read_u16()
 	volt = (3.3/65535) * adc_value
 	temperature = 27 - (volt - 0.706)/0.001721
 	return round(temperature, 1)

def ReadPot(): #originally I used a potentiometer rather than the LDR to vary the input signal
    pot = ADC(26) #gp26 physical pin 31
    pot_value = pot.read_u16()
    return round(pot_value*120/65535,1) #range 0-220

def sm(state): #send OSC message
        osc = Client(sp_dest)
        osc.send('/pico/pot',state )
        gc.collect() #necessary or runs out of memory

# Main program loop

connect_to_wifi(ssid, password)
while True:
    p=ReadPot()
    sm(p)
    sleep(0.1)

It has an associated secrets.py file which is this:

SSID = "yourSSIDhere"
PASSWORD = 'yoiurPasswordhere'
SPIP = 'ipo address of Sonic Pi computer'
PICOIP = 'fixed IP for Pico'
SPPORT = 4560

The library is by SpotLightKid and is on github here
Download it from the Code button as a zip file. You can then install it using Thonny to your pico W using Tools menu → Manage Packages…-> Install from local file click here

I made a small demo video showing the program in action. I set it up to measure the input to ADC0 (actual pin 31, or GP26) Initially I used a 10K potentiometer connected to ground and 3v3 with the “slider” connected to GP26, but in the video I changed this for an Light Dependent Resistor I found with a resistor in series, and measured the voltage at the junction. I scaled the readings in range 0-120 which covered more or less the midi audio range.

One or two “gotcha” I found out. I found it necessary to include a garbage collection call after the OSC sm function was called, or the program stopped with a memory complaint. Goggling led to the solution which seems to work OK.
Secondly, I put in various print statements as the program was being developed. In order to run it automatically without being connected to Thonny, it is a good idea to comment all of these out so that the pico doesn’t try and send serial data along the usb cable and hang because there isn’t a suitable serial receiver active.

The program has the option to use a static IP address for the pico, or you can just use a dhcp allocated address. It’s just a matter of including an extra line or otherwise.

I haven’t tried the server side of things, so you can only send from the pico to SP at the moment.

EDIT ADDITONAL NOTES
1
the program I used on Sonic Pi to receive the OSC call and produce a sound related to the data is shwon below. A long 100 second tone is started. A referenced to the tone is stored in :y This is then used to control the pitch value of the tone in the live loop, with changes smoothed out by the note_slide: parameter

use_synth :sine
y=play 50,sustain: 1000,amp: 0.5
set :y,y
live_loop :temp do
  use_real_time
  t=sync "/osc*/pico/pot"
  puts t[0].to_i
  control get(:y), note: t[0].to_i,note_slide: 0.1
  
end

2 The pico program contains a function def ReadTemperature():
This is not actually used. It reads the temperature of the internal sensor in the pico processor chip. you can if you wish adjust the program to send the temperature reading instead of the adc reading in the OSC call.

  1. You can change the OSC address from /pico/pot to anything you like: eg /pico/temp or /pico/lightlevel Just make sure you use the appropriate address on the Sonic Pi side. Note this has /osc* prepended to the address.
2 Likes

Robin, wow! Thanks so much! Pints on me if you’re passing this way :smiley:

I’ll give it a go. A year ago I wouldn’t have remotely understood this code, but kindof roughly get what it’s doing, thanks to your v clear notes. Would never have got there in 1m years :sweat_smile:

The video is great too and am excited to make a baby light theremin.

I’m teaching some numeracy creative tech taster workshops in the new year (incl SP) - this way to show interaction and data behind the scenes will be v helpful.

Will report back!

Update success:

  • everything installed
  • Wifi speaking to Pico
  • LED on
  • SP showing OSC/Pico sync message and pot/temp directories
  • SP sine sound

I don’t have an LDR yet, so I’m trying with the inbuilt Pico temperature sensor (ADC)
Currently can’t get the sound to change by heating.
In SP, there’s no data appearing in liveloop_temp [ ] . Data shows for other inputs from other devices, so I’m wondering if it’s getting through.

Checks done:

  • Thonny - ‘print’ is showing the temperature data, under ‘def’.
temperature = ReadTemperature()
print(temperature)

But this might be the wrong place to put it?

  • SP - incoming OSC switched on
  • Pico IP - have obrained this from CMD ipconfig - ending .254
  • tried different gateways - ending .140 (local), .254 (Pico) and the one appearing in Thonny log after connection .199
  • multiple print checks

Probably doing something obvious wrong but can’t spot it!

Encouraging screengrab 1:


2:

I did wonder about the gateways, and also ‘ReadPot’ having 26 rather than ‘adcpin’, tried swopping this.

Am going to do a full switch on-off of everything next… getting there!

EDIT:
PS using Windows 11

Hello again.
A fresh Pico install and reboot later…
Think I need a sensor to test the pot option? Will meanwhile get an LRD. I have a capacitive touch breakout, not sure if this will work, trying the obvious first!

So far, I can’t get live_loop/pot and /temp showing in SP, no [temperature data] in the brackets. Should I be expecting this?

I’ll paste in the code I’ve used to test /temp /pot variants. Please forgive if faulty - am beginner! Wifi is connected. LED on. OSC incoming ticked.

Thonny Micropython:

#Pico W OSC client using uosc.client library
#download zip file of library from https://github.com/SpotlightKid/micropython-osc
#install using Thonny
#best to comment ALL print commands once you hacve things working to run it independently as main.py
import network
from machine import Pin, ADC
import usocket as socket
from time import sleep
import secrets
from uosc.client import  Client
import gc #garbage collection

adcpin = 4
sensor = machine.ADC(adcpin)

#set up onboard led to turn on when connected
led = machine.Pin('LED', machine.Pin.OUT)
led.on()
sleep(.3)
led.off()
sleep(.3)

# Wi-Fi configuration
ssid = secrets.SSID
password = secrets.PASSWORD

# Static IP address configuration (if required)
ip_address = secrets.PICOIP
subnet_mask = "255.255.255.0"
gateway = "192.168.1.140"  # Replace with your gateway IP

# OSC settings
sp_host = secrets.SPIP  # Replace with your Sonic Pi IP address
sp_port = secrets.SPPORT  # Replace with required  OSC port (4560 for Sonic PI)
sp_dest = socket.getaddrinfo(sp_host, sp_port)[0][-1]
#print(sp_dest) #turn off all prints for standalone operation
# GPIO pin configuration
gpio_pin = machine.Pin(7, machine.Pin.IN, machine.Pin.PULL_UP) #used to gtrigger OSC call

# Connect to Wi-Fi
def connect_to_wifi(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    if not wlan.isconnected():
        print("Connecting to WiFi...")
        wlan.active(True)
        led.off()
        #uncomment next line for static address. Otherwise uses dhcp
        #wlan.ifconfig((ip_address, subnet_mask, gateway, "8.8.8.8"))  # Set static IP
        wlan.connect(ssid, password)
        while not wlan.isconnected():
            #print(".",end=".")
            sleep(1)  # Add a delay of 1 seconds
    print("Connected to WiFi")
    print("IP Address:", wlan.ifconfig()[0])
    # LED on for wifi connected
    led.on()

def ReadTemperature():
 	adc_value = sensor.read_u16()
 	volt = (3.3/65535) * adc_value
 	temperature = 27 - (volt - 0.706)/0.001721
 	return round(temperature, 1)

def ReadPot(): #originally I used a potentiometer rather than the LDR to vary the input signal
    pot = ADC(26) #gp26 physical pin 31
    pot_value = pot.read_u16()
    return round(pot_value*120/65535,1) #range 0-220

def sm(state): #send OSC message
        osc = Client(sp_dest)
        osc.send('/pico/pot',state )
        gc.collect() #necessary or runs out of memory

# Main program loop

connect_to_wifi(ssid, password)
while True:
    p=ReadPot()
    sm(p)
    sleep(0.1)
    
#while True:
#    t=ReadTemperature()
#    sm(t)
#    sleep(0.5)

Sonic Pi:

use_synth :sine
y=play 50,sustain: 1000,amp: 0.5
set :y,y
live_loop :temp do
  use_real_time
  t=sync "/osc*/pico/temp"
  puts t[0].to_i
  control get(:y), note: t[0].to_i,note_slide: 0.1
  
end

live_loop :pot do
  use_real_time
  t=sync "/osc*/pico/pot"
  puts t[0].to_i
  control get(:y), note: t[0].to_i,note_slide: 0.1
  
end

Will try the LRD in a few days, but if you spot anything, please let’s know!

Am still wondering about this from Thonny:

Connected to WiFi
IP Address: 192.168.1.199

when the local host and Pico have different endings.

Thanks so much.


valiant attempt to recreate your video :sweat_smile:

Needs one change. You have to match the OSC address /pico/pot or /pici/temp in both the pico program AND the Sonic Pi one. You changed the Sonic Pi one to /osc*/pico/temp but didn’t change the line osc.send('/pico/pot',state ) to osc.send('/pico/temp',state) to match
You can always leacve it as /pico/pot if you wnt regardless of what you are sending. It is just nicer to indicate what you are measruing in the Sonic Pi program that’s all.

I put in the program you send and uncommented the temp lines at the end commenting out the ones above and it worked fine. I lewft everthing as pico/pot for simplcity.
You dont get mutch temp change but if you blow on it or puff hot air on it it does change a bit.

Thanks, Robin. I changed the /temp issue in the code; frozen soup as well as logburner, no change to tone.
However, when I change the sound output source, it gives a tiny bzzz at each change, like a contact buzz. My jump leads and resistor are pretty spindly, I think they’re in the right place but… a clue?
Oddly when I connected to a TV sound source, I noticed different buffers’ sound coming from different places :

  • touchosc_bridge midi >> coming from telly
  • sonic pi internal midi and OSC sine from Pico >> from laptop speaker
    Any other suggestions gratefully received. Will have a go with Circ Playground another time.