This is an ‘encore presentation’ of a post I originally wrote for my old blog — it was lost in the great blog fire of ’12 and (thank you Wayback Machine) is being edited and reposted here since it seemed to be pretty popular at the time. If you’re going to follow along with what I’ve done, give a quick look at Step 4 where I discover I’ve been using the wrong chip and have to change things up a bit.

The source code is hosted on Github here. There’s not really too much of it, but it’s worth making public for people.


I’ve long had an interest in experimental electronic music, so I’m excited that I have something to share in that arena.

In the past I’ve mentioned my wonderful wife (who is wonderful, if I didn’t say so), and for Christmas she doubly earned that distinction by buying me a Gakken SX-150 Analog Synthesizer. As far as a ‘kit’ goes, it isn’t much to speak of — just installing the pre-built board and speaker into the plastic case and wiring up the stylus controller — but it is such a simple design that it seems built to be hacked on, and that’s what I wanted to do.

I did find a number of cool SX-150 hacks, but often they were a bit more advanced than I’m ready for, so I figured I’d start with something simple and slowly build on it and make this a multi-part project. Since I’m really enjoying getting into Arduino programming, an Arduino-based sequencer seemed like a good candidate — so let’s get started!

STEP 1 – REPLACING THE STYLUS

The SX-150 has a simple but fun little synthesis circuit, but the input is a bit limited. There’s a stylus, which is a great alternative controller (especially for keyboard challenged people like me), and there’s a neat external input that I haven’t had a chance to play with but seems to really open up the potential for the SX-150, but I wanted to hack the SX-150 itself and not just feed in a new signal, so finding a way to reuse the stylus input circuitry was the way to go.

Analog synthesizers often use a control voltage to determine pitch, and a little playing around with a multimeter showed that that was exactly what was happening, with the voltage being scaled based on the position of the stylus. That sounded a lot like a potentiometer to me.

Testing this is pretty easy and doesn’t require soldering or altering the synth. The hookups to the stylus & strip are just screwed on, so I reused those screw connectors and ran some wires out from under those screws:

pad12

pad3

And then hooked it up to a spare pot I had lying around:

potsetup

And, just like that, I had dial-controlled pitch. I only had a 10K pot, which seems to provide only a small amount of range in the pitch, but it worked!

STEP 2 – CONTROLLING A DIGITAL POT THROUGH THE ARDUINO

Getting it hooked up to the pot was cool, but it’s not going to help much if I want to control it digitally, so I need to go from a regular analog potentiometer to a digipot. I’d never used one of these before, but I figured they had to exist. Just a little googling around and I found an Arduino.cc article about connecting to one via the Arduino using the SPI serial protocol. They used the Analog Devices AD5206 IC and after some questions to the guys on the arduino forums, I found that they could be purchased from Digi-Key. The AD5206 is a neat little chip — it has 6 separate potentiometer circuits, and each can independently set to one of 256 different resistance values.

I followed along with the arduino digipot article, but it was honestly more complex than I needed for an introduction. There were some basics that I was missing, so what follows here is based on the code from that article and a simplified schematic.

That article has 6 different LEDs (since the AD5206 gives you 6 different circuits), I need just one, so here’s what I did instead:

ad5206-1

You can see it’s a pretty simple circuit. On the left side of the chip we have a ground and 2 Voltage-ins (one positive, connected to +5V; one negative and connected to ground) and the 3 lines for the SPI interface. On the right there are the 3 lines that typically make up a potentiometer. The actual output from the pot is on pin 17, and here we’ve hooked it up to an LED just so you can see it working before we connect it to the SX-150.

When it’s all hooked up, it will look like this:

ledsetup

The source code is drawn primarily from the Arduino article except they are using all 6 pots, and I’m just using #1 (or #0 in code). I’ve changed some of the names and packaged it a little different, but I’m not taking much credit for it. :)

I’ll break the code down like this…first there’s the code that manages the SPI interface:

//--- SPI code

#define DATAOUT 11 //MOSI
#define DATAIN 12 //MISO - not used, but part of builtin SPI
#define SPICLOCK 13 //sck
#define SLAVESELECT 10 //ss

void SPIInitialize()
{
    byte clr;
    pinMode(DATAOUT, OUTPUT);
    pinMode(DATAIN, INPUT);
    pinMode(SPICLOCK,OUTPUT);
    pinMode(SLAVESELECT,OUTPUT);
    digitalWrite(SLAVESELECT,HIGH); //disable device

    SPCR = (1<<SPE)|(1<<MSTR);
    clr=SPSR;
    clr=SPDR;
    delay(10);
}

char SPITransfer(volatile char data)
{
    SPDR = data; // Start the transmission
    while (!(SPSR & (1<<SPIF))) // Wait the end of the transmission
    {
    };
    return SPDR; // return the received byte
}

Then there’s code that uses the SPI specifically for controlling the AD5206:

//--- AD5206 code

byte SetPot(int address, int value)
{
    digitalWrite(SLAVESELECT,LOW);

    //2 byte opcode
    SPITransfer(address);
    SPITransfer(value);

    digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
}

And finally there’s the (small) amount of code that actually does something with the digipot:

//--- Application code

void setup()
{
    SPIInitialize();

    SetPot(0,255);
}

byte resistance=0;
void loop()
{
    SetPot(0,resistance);
    delay(5);

    resistance++;
    if (resistance > 255)
    {
        resistance=0;
    }
}

Put it all together and compile it and push it to the Arduino and you should see the LED turn on and then fade out slowly until it’s out, and then it’ll go bright and fade out again, over and over.

STEP 3 – CONTROLLING THE SX-150 THROUGH THE ARDUINO

We’re finally here, and this is the easiest part of the whole project so far. You’ve already got a digipot hooked up, just pull the connections from A1, B1 & W1 and replace them with the wires hooked up to the SX-150. A1 & B1 go to Pad1 & Pad2 on the SX, and the stylus wire (Pad3) goes to W1.

Schematic:

ad5206-2

My setup looked like this:

sxsetup

Turn everything on and you should start to hear the aural equivalent of what you saw with the LED. The tone will jump up and then drift down, then jump up and drift down again. If you reverse Pad1 & Pad2, the tone will rise instead.

Try varying the value in the delay() statement in the code and you can control how fast the pitch changes.

And there you have it, my SX is being controlled not with the stylus but by code running on the Arduino!

Flush with this success, I picked up a cheap digital instrument tuner, then I changed the delay() value to 2000 — 2 seconds on each level.

Setting the tuner up close to the SX-150’s speaker, I was able to watch as the synth zoned in on a specific note. I changed the code a bit to send the current resistance level (0-255) to the serial port so I could watch it. When the synth had properly hit a note, I recorded the note & the resistance value. If I wanted to, I could set those resistance levels in order and play a musical scale…

…or at least part of one. Being the clever lad I am, I ordered the 10K resistance version of the AD5206 which leaves me in exactly the same place I was with the reduced pitch range (less than an octave) as with the 10K analog pot.

STEP 4 – GETTING THE CHIP RIGHT

Sigh. So, it turns out I used the wrong IC in the previous step, the AD5206-BN10 has only a 10K ohm range and that results in less than an octave range for the synth. They do make 50K & 100K versions, but for some reason they don’t seem to be available anywhere, at least not in the DIP form factor I needed for breadboarding. So, first things first, I had to swap out that digipot for one that was available and provided an expanded range.

I originally chose the AD5206 because there was already an article about how to use it — I had heard that the SPI interface used to control the resistance level could vary in significant ways from chip-to-chip, and since the article included working code, there was no sense messing with something that already worked.

Since the AD5206 was out of the question, however, I did some research and found that a few people had used the MCP42xxx series of ICs with the Ardunio. And, even more luckily, Digikey had the MCP42100 digipot — which provides 100K of range — available in the DIP form factor. And it’s only $2.40! It only has 2 circuits (not 6 like the AD5206), but honestly I only needed one circuit for what I’m doing so far; so I sent off an order for 3 of them (plus about 20 other things I suddenly decided I needed). I am going to write a blog post sometime about the electronics providers I use, but until then I have to say that I’m very pleased with DigiKey. They have a great selection, good prices, and they ship things out very quickly — I usually have stuff within 3-4 working days.

While I waited for the hardware to arrive, I checked out the datasheet for the MCP42100 to see how the pinouts and SPI interface changed.

In fact, I needed to make very few changes to my configuration. It took a bit of figuring because they call the pinouts something different (SCK rather than CLK, for instance), but the physical hookup really only required reversing two pins on the Arduino. In the code, it was just a matter of a different command byte.
The new code for controlling the pot now looks like this:

//--- MCP42100 code

byte SetPot(int address, int value)
{
    // Slave Select set low to allow commands
    digitalWrite(SLAVESELECT, LOW);

    // 2 byte command
    SPITransfer(0x10 + address); // 0x10 = 'set pot' command
    SPITransfer(value); // Value to set pot

    // Release chip, signal end transfer
    digitalWrite(SLAVESELECT, HIGH); 
}

STEP 5 – FROM RESISTANCE TO PITCH

As I alluded to earlier, the next step was to take the 256 possible settings for the digipot and find out what pitch it played.

This was pretty much a manual procedure. I had picked up an inexpensive tuner that would show me what pitch was playing and how far out of tune it was, so I sat that near to the little SX-150 speaker and changed my code to hold each resistance value for 3 seconds at a time and output the resistance value to the serial monitor.

Then I just watched. As each pitch played, I watched the tuner. When it homed in on the right pitch, I made a note of it. A low ‘A’ was given at value 11, B-flat at 18, B at 25, and so on. As the resistance level climbed, so did the pitch, and by the time I was up around 240, I had registered 3 full octaves of range.
It turns out that there was about 7 steps between each semitone. Unfortunately, it wasn’t exactly 7 steps — which means that I don’t really get a perfectly pure pitch out of it…some notes are a little more out of tune than others. I’d love to find a digipot that allowed me maybe 250K ohm range with 1024 possible settings, I think I’d get more range and precision out of it — but for a cheap little analog synth this is actually pretty good. If anyone knows a chip with characteristics like that, I’d love to hear about it.

(Another thought: my SX-150 seems to have arrived with a really glitchy external output…I think I would have had better results if I could have tested against a better speaker, not the little one attached to the SX, but so be it).

STEP 6 – FROM PITCH TO NOTES

From that list that I made while I was testing the various resistance levels, I was able to make a simple array in my code that held the various pitch values. I changed the code a bit to loop not through the 255 different resistance levels, but instead through the 36 pitch values I noted. Playing each one for a second or so, it was quite clearly playing scales for me! Now we’re starting to get somewhere.

But one part is still missing. The pitch changes nicely, but we are in effect playing a single long continuous note as far as the synth is concerned. We aren’t firing off the envelope follower as the pitch changes — it’s the equivalent of holding the stylus against the little ribbon controller area and moving back and forth. The movement is much more precise, but we’re still just moving back and forth — not picking the stylus up and putting it back down.

We need the effect of ‘pressing a new key’ on a synthesizer. To do that we have to actually break the connection altogether and then connect it again. I was hopeful when I saw that the MCP42100 had a ‘shutdown’ mode, but that just sets the resistance to the lowest level, it doesn’t break the connection altogether.
So I needed a switch to gate the connection between the digipot and the SX-150, and although I haven’t actually worked with them that much since starting to hack at this stuff, I knew a transistor was the way to go. I won’t try to recap what transistors do in any detail — there are a ton of sites that do that (most of which, it seems, I visited while figuring stuff out) — but suffice it to say that there are 3 pins on the transistor. If you apply a voltage to the middle one (the base), it allows a different signal to pass from one of the other pins (the collector) to the third (the emitter). Take away the voltage from the base and the signal can’t pass — so that’s how I’m going to switch between ‘note on’ and ‘note off’ mode.

I used another Arduino pin (Pin 9 in my set up) to act as the ‘note on’/’note off’ director and hooked it up to the base of the transmitter. Then I fed the output from the MPC42100 into the transmitter at the collector, and connected the emitter to the SX-150. Here’s the schematic I came up with:

mcp-ard1

And here’s what it looks like in real life:

ardmcp2-1

ardmcp2-2

And, voila, that’s it.

Well, not quite it, we do need a bit of code to get it all working. As you saw in Part 1, I sort of layered my code. I had the layer that was responsible for dealing with the SPI commands (SPITransmit), then a layer for dealing with the digipot (SetPot), and now I’m going to add a layer for working with notes directly.

//--- Sequencer code
// A Bb B C C# D Eb E F Fb G G# A B Bb C C# D Eb E F F# G G# A Bb B C C# D Eb E F F# G G#
byte noteValues[] = { 11, 19, 26, 33, 40, 47, 54, 61, 68, 76, 83, 90, 98, 105, 112, 119, 126, 132, 139, 146, 153, 160, 167, 173, 180, 186, 193, 199, 206, 212, 218, 224, 231, 237, 243, 249 };

void NoteOn( int noteNum )
{
    SetPot(1, noteValues[noteNum]); // Set the resistance for the given note
    digitalWrite(NOTEON, HIGH); // Then turn on the note 
}

void NoteOff()
{
    digitalWrite(NOTEON, LOW); // Turn off the note
}

Yes, you could collapse those three layers together if you wanted, it would save a tiny bit of memory and speed things up a very very tiny bit, but I like having the reusability.

Now let’s change our main loop to play those notes in a rising scale:

void loop()
{
    // run through each of the notes in ascending pitch
    for (int noteNum = 0; noteNum < 35; noteNum++)
    {
        // play the note for 650 ms
        NoteOn(noteNum); 
        delay(650);

        // turn off the note and wait 100 ms to rest 
        NoteOff();
        delay(100);
    }
}

And now that’s it. We’ve abstracted away the whole notion of the stylus, or the potentiometer being there, and allowed ourselves to think just in terms of the notes we want to play on the SX, even though that synth doesn’t have that concept to start with. Right now we just call the notes 0-35 rather than “A”, “F#”, etc., but we could add those concepts into our code if we wanted — but since we’re not going to interact with them that way, we can just leave it as is for now.

You can see that we’re pretty close to having what we need to really get into building the sequencer now.

STEP 7 – CHANGING THE TEMPO

The code that I have right now just plays through the notes in a row: 0, 1, 2, 3, 4, etc., and at a fixed rate. If we wanted, we could certainly tell it to play a sequence of specific notes, that would be pretty trivial in fact. And if we wanted to change the tempo, we just have to change the delay() calls used to hold the note and provide a little break between them.

So that’s what I did. I grabbed a spare potentiometer and hooked the output up to a spare analog input pin on the Arduino, generating a number between 0 & 1023. Then on each pass through the loop, I check the analog value it is inputting and use that value to dictate the length of the note. Slow it down and you get about one note per second. Turn it the other way and the tempo increases until it all rushes together. I added an LED so I could see it flash as the notes played, and all it took was the pot and changing my loop to:

void loop()
{
    // determine the length of the note by the potentiometer value
    int notelen = map(analogRead(SEQUENCER_SPEED),0,1023, 5,100)*10;

    // run through each of the notes in ascending pitch
    for (int noteNum = 0; noteNum < 35; noteNum++)
    {
        // play the note for 9/10 of the note length
        NoteOn(noteNum); 
        delay(notelen*0.9);

        // turn off the note and wait for 1/10 the note length 
        NoteOff();
        delay(notelen*0.1);
    }
}

This is getting pretty cool!

STEP 8 – PULLING IT ALL TOGETHER

This is the big one. In previous steps we took control of the pitch of the SX-150 by using a digital potentiometer, and then added the concept of discrete notes by using a transistor, and the speed of the notes with another pot. On the software side we abstracted away all the hardware concepts and provided a “Note On/Note Off” way of interacting with the synth. Building a software-only sequencer would be pretty easy at this point — or at least it would just be a matter of software. But I’m trying to build some hardware here so let’s see what that’s going to look at:

To jump right into it, here’s the final schematic (click for full-size):

4stepschematic

You can see that there are several different sections to the circuit:

Pitch Control: This is what we spent the last two parts working on. It uses an SPI-controlled digital potentiometer to set the pitch that will be played on the SX-150, and a transistor to switch the note on and off.

Step Enable: 4 momentary switches that tell the Arduino “Hey, I want to change the pitch of this note” and 4 LEDs that indicate which note is being changed.

Each input has it’s own line to the Arduino, and I use a 5th to create a rudimentary “OR” gate and send it to an interrupt line. If any of the buttons is pressed, the interrupt goes high, and then the Arduino reads the 4 inputs to determine which one of them was pressed. Each note also has their output line to drive the LED.

Clearly this is where most of my Arduino inputs/outputs go and this limits me to 4 steps. I really wanted 8, but I will have to introduce shift registers to the equation to make that happen.

Pitch Select: I use a rotary encoder, which gives me a nice ‘clicky’ feel and more precision than using a standard pot. It’s a little tricky to program, but there’s a decent article on it. When the knob is turned, an interrupt is fired, both interrupts are read and compared, and — if a step enable is active — it changes the value of that note.

Speed Control: A simple pot that goes into an analog input, the Arduino speeds up the notes based on the analog value coming in.

When it all comes together on a breadboard, it looks pretty messy…this one is crying out for some better wiring, but this is just my first cut at it.

4stepphoto

Behind the scenes, we’ve got the source code running on the Arduino. Here are some of the highlights:

Sequence playing: The core of the sequencer, obviously, is going to be playing the sequence…

int stepVals[] = {18, 18, 18, 18}; //possible values 0-35, start in the middle.
int activeButton = -1;

void loop()
{
    // Each pass through the loop, play the full sequence of 4 steps
    for (int i = 0; i < 4; i++)
    {
        // Convert the potentiometer hooked to the Speed input into a value between 50ms & 1s 
        int speed = map(analogRead(SEQUENCER_SPEED),0,1023, 5,100)*10;

        // Turn on the given note for 90% of the full note period
        NoteOn(stepVals[i]);
        delay(speed*0.9);

        // Turn the note off for 10% of the full note period
        NoteOff();
        delay(speed*0.1);
    }
}

Changing the notes: There are two steps to changing the sequence. Note-enable — which indicates what step to change — and the actual changing of the note. There is an interrupt handler for each part:

// onButton() is an interrupt handler that responds when a note-enable button is pressed

void onButton()
{
    // Determine which button was pressed
    int selectedButton;

    if (analogRead(STEP1_ENABLE) > 128) selectedButton = 1; 
    if (analogRead(STEP2_ENABLE) > 128) selectedButton = 2; 
    if (analogRead(STEP3_ENABLE) > 128) selectedButton = 3; 
    if (analogRead(STEP4_ENABLE) > 128) selectedButton = 4;

    // if that button was already selected, turn it off, otherwise make the 
    // new button the current one
    if (selectedButton == activeButton)
        activeButton = -1;
    else
        activeButton = selectedButton;

    // activate the LED associated with the selected step 
    digitalWrite(STEP1_ENABLEDISPLAY, (activeButton == 1)); 
    digitalWrite(STEP2_ENABLEDISPLAY, (activeButton == 2)); 
    digitalWrite(STEP3_ENABLEDISPLAY, (activeButton == 3)); 
    digitalWrite(STEP4_ENABLEDISPLAY, (activeButton == 4)); 
}


// onTurn() is an interrupt handler that fires when the rotary encoder is turned
void onTurn()
{
    // If no button selected, just discard
    if (activeButton == -1)
        return;

    // Get the current value for this step, update it, and write it back
    int currentVal = stepVals[activeButton-1];

    int a = digitalRead(NOTE_SELECT_INTERUPT);
    int b = digitalRead(NOTE_SELECT_B);

    if (a == HIGH) // found a low-to-high on channel A
    { 
        if (b == LOW) // check channel B to see which way encoder is turning
            currentVal = constrain(currentVal-1,0,35); // CCW
        else 
            currentVal = constrain(currentVal+1,0,35); // CW
    }
    else // found a high-to-low on channel A
    { 
        if (b == LOW) // check channel B to see which way encoder is turning 
            currentVal = constrain(currentVal+1,0,35); // CW
        else 
            currentVal = constrain(currentVal-1,0,35); // CCW
    } 

    stepVals[activeButton-1] = currentVal;
}

And that about wraps it up. Once you have more precise control over the SX-150, it starts to feel less like a toy and more like a viable sound-making tool.

If you want the full source & schematics, you can find them hosted on Github here.

Have fun!

3 thoughts on “Hacking the SX-150 – Building an Arduino-based Sequencer

  1. Hi! Great tutorial! I have two Gakken SX150 MkII (got one for free) and will be controlling them from an Arduino (together with some other stuff). I found this IC, the ADN2850BCPZ250, which has 250kOhm resistance, and offers 10 bits of resolution. I think I will try that one out and report my findings! :)

  2. Hmmm…the ADN2850BCPZ250 costed like 25 dollars to Sweden. So I’ll start with the MCP42100, and if it all works, switch to the ADN. Great guide!

Leave a Reply

Your email address will not be published. Required fields are marked *