#RaspberryPi Pico: A piano “synthesiser”

After having made an atrocious synthesiser, I thought it would be good to up my game and create a slightly less atrocious synthesiser. This time, instead of using the serial port, I thought I’d actually make a synthesiser. It is based on the Rolf Harris Stylophone from the early 70’s. I think my stylus piano actually sounds better, and at the very least it isn’t mired in as much controversy as anything associated with Rolf Harris.

One of the questions I pondered over was: how should I make a piano? I have a whole bunch of screws left over from when I’ve tinkered around with desktop computers. An actual keyboard would be too complicated to make, and would actually cost money. My engineering skills aren’t up to much. My craftsmanship is even worse.

I decided that the best approach would be to create a stylophone-type contraction. The keys are separated by resistors, and the stylus transmits a voltage to an ADC. That way, I have only 3 wires coming out the back of the stylus, rather than one wire per key. It means you can only play one note at a time, and there is the inconvenience of a stylus. But my desire to avoid a bunch of wiring made this a good trade-off. It’s only a toy, after all.

Here’s the schematic:

You touch the stylus, marked JS, against any one of the screw contacts J1-J11. There is actually a design fault. There should be a 220 ohm resistor between J11 and the 100K ohm resistor. It means that you can’t actually play the F5 key. I decided not to sweat the issue, as it was only a an experiment.

The actual “piano” itself looks like this:

Topside

When I say “stylus”, I of course mean “crocodile clip”. The wire running from the clip to the board is excessive for what we really need, but I just used a long piece of wire that I had available.

I used a piece of MDF as the base, and drilled holes in it through which I put the screws. I also gave names to the keys and their positions on the music stave, as I am hopeless at reading music. There are no black keys on my piano. You could add some, if you liked, but it would have made the board more complicated.

The underside of the board is sparse:

Underside

A big mistake I made was that I discovered that you can’t really solder to screws. So I had to tie some conducting wire around the screws, and solder the resistors to that. A better solution would be to use washers and trap the resistors between the washers and the board. Nuts could then keep the whole thing tight. I had neither screws that were long enough, washers or nuts, though. My original plan was to put the resistors on the underside of the board, but I quashed that idea when I discovered that you can’t solder resistors to screws.

Here’s what the complete assembly looks like, including the Pico and speaker:

Complete circuit

The 100k resistor is to create contact between the ADC input and ground. Otherwise you just have a floating ADC input, which gives spurious readings. 100k is large in relation to the other resistors, so that there is a much smaller resistance when the stylus connects with any of the key screws.

Video demonstration:

Here’s the code:

#include <stdio.h>
#include <vector>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/adc.h"
#include "hardware/pwm.h"
#include "hardware/clocks.h"

using namespace std;

using  u16 = uint16_t;
using  u32 = uint32_t;
using  u64 = uint64_t;

#define SPK 15

typedef struct {const char* name; u16 adc; u16 freq;} note_t;

const vector<note_t> notes = {
	{"C4",	11,	262},
	{"D4",	430,	294},
	{"E4",	834,	330},
	{"F4",	1252,	349},
	{"G4",	1650,	392},
	{"A4",	2060,	440},
	{"B4",	2480,	494},
	{"C5",	2880,	523},
	{"D5",	3303,	587},
	{"E5",	3701,	659},
	{"F5",	3900,	698} // F5 won't work properly
};

vector<u16> cutoffs; // for the midpoints in the notes table

//#define NO_NOTE_IDX  0xBAD;
uint slice_num; // of speaker SPK

void set_freq(u32 freq)
{
	u32 top, level;
	if(freq==0) {
		top = 0;
		level = 0;
	} else { 
		top = 1'000'000UL/freq-1;
		level = (top+1)/2-1; // 50% duty cycle
	}

	pwm_set_wrap(slice_num, top);
	pwm_set_chan_level(slice_num, pwm_gpio_to_channel(SPK), level);
}

bool repeating_timer_callback(struct repeating_timer *t) {
	static int reading_number = 0;
	//printf("Reading %d ", reading_number++);
	static int idx = -1;
	int adc = adc_read();
	//printf("adc:%d\n", adc);

	// find cutoff
	int idx1 = -1;
	for(int i=0; i< cutoffs.size(); i++) {
		if(cutoffs[i]>adc) {idx1 = i; break;}
	}
	//printf("Cutoff index: %d\n", idx1);
	if(idx == idx1) return true;
	idx=idx1;	
	u16 freq = 0;
	if(idx>=0) freq = notes[idx].freq;

	set_freq(freq);


	return true;
}


int main() 
{
	stdio_init_all();
	puts("synth-1 started");
	adc_init();
	adc_gpio_init(26); // ADC0

	gpio_set_function(SPK, GPIO_FUNC_PWM);
	slice_num = pwm_gpio_to_slice_num(SPK);
	pwm_set_clkdiv(slice_num, clock_get_hz(clk_sys)/1'000'000UL);
	pwm_set_enabled(slice_num, true);

	//init cutoffs
	puts("Init cutoffs...");
	cutoffs.reserve(notes.size());
	for(int i = 0; i< notes.size()-1; i++) {
		cutoffs.push_back((notes[i].adc + notes[i+1].adc)/2);
		printf("Cutoff %d:adc %d\n", i, cutoffs[i]);
	}
	cutoffs.push_back(3900); // not really right
	puts("...OK");

	set_freq(0);

	struct repeating_timer timer;

	u32 ms = 50;
	//ms = 1000; // for debugging purposes
	add_repeating_timer_ms(-ms, repeating_timer_callback, NULL, &timer);


	for(;;);


	return 0;
}

You will probably need to tweak the ADC values (second field in note_t struct) in the notes vector. To obtain these values, you can put my adc project onto the Pico, and note down the ADC values as you work through each key on your keyboard.

From the notes, the Pico computes a vector of cutoffs, being the midpoint between ADC values. The function

repeating_timer_callback() 

is triggered every 50ms. It takes an ADC reading, and uses the cutoffs to determined an index into the notes array. From that it obtains a frequency for the note it should play.

Future directions? Well, I recently discovered that my local library does 3D printing. So the next logical step would be to try to see if I could actually 3D print keys, correct the current design flaws, and create a much more professional-looking product.

I am, however, half-inclined to abandon the keyboard idea. My keyboard skills are, shall we say, rather crude, so I might be better off going to a “tracker” type approach, where I enter keys into a program. This is bringing me back to using a Raspberry Pi 0.

Decisions decisions.

Anyway, you might like the above for a fun and doable project in any case.

Have fun.

Update 2021-02-19 Ice cream lid upgrade

I was fed up with the stylus idea and decided to upgrade it to a full keyboard (um, such as it is):

What I did was take an ice cream lid, cut it to size, and cut narrow slots into it so that it has a number of tabs, one for each screw, of the right spacing. The plastic lid gives flexibility to the keys. I used pritt stick to glue a sheet of aluminium foil to the underside of the lid.

Using a craft knife and some scissors, I split the aluminium along the slots to separate out the keys again. I cut a few strips of MDF to place under the lid, so that the keys separated from the screws below.

I drilled a couple of guide holes into the lid and base. I looped some thin wire around one of the holes, and used some sellotape to hold the wire in place. The other end of the wire was left protruding out of the side.

I then screwed the lid to the base. I removed the stylus. This left two wires sticking out: one from the stylus, and the other that was sandwiched to the underside of the aluminum foil. I joined the two ends together.

That way, when you press down a key, it creates a short in a similar way to the stylus. When you release the key, contact is broken.

It works pretty good, actually, even better than the stylus. I’d say.

Advertisement

About mcturra2000

Computer programmer living in Scotland.
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s