Archive for the ‘ USB – Control Voltage Interface ’ Category

Tips for a Successful Open-source Hardware Project – Raising Funding

I decided to fund the two projects (Octomod and tabulaRasa, if anyone missed part one) in slightly different ways.


The Octomod was funded through word-of-mouth, Twitter, Facebook, and various online forums.

This had the advantage of allowing me to interact with almost everyone who decided to buy a circuit board. I really like the forum method, because I received a lot of valuable feedback and many great suggestions on the designs. On the other hand, these methods forced me to buy the materials up front. I also had to deal with taking orders and collecting money, which can get complicated – making a free, functional storefront through WordPress also seems to be nearly impossible.

For the tabulaRasa, in addition to the social networks and forums, I decided to apply to Kickstarter.

Kickstarter is a way to raise funds for a project, by allowing people to commit a certain amount of money in exchange for a “reward” if the full funding amount is met. If full funding isn’t reached, no money exchanges hands. This has two advantages – 1) the project owner doesn’t have to invest money in the project upfront (although I did, and I’m sure many others do as well), and 2) the people who are funding only end up paying if the project is fully funded.

A couple of things I’ve learned after the Kickstarter experience:

I’m not sure that the video Kickstarter requires really made a difference in my case – but it can’t hurt.

Make sure to set a good financial goal – not too high and not too low.

Likewise, I realized I allowed for too many different funding tiers – this complicated things when it came time to provide the rewards. In the future, I’d go with only 3 or 4 funding levels, and minimize the number of different reward types.

You definitely want to be responsive to questions and comments – both on Kickstarter and on email.

Also, I found it important to remain flexible and willing to change the project. The music I make with my electronics doesn’t require a volt per octave tuning scale, so I designed the tabulaRasa without any sort of calibrated tuning. Many people contacted me asking about v/octave capabilities, so I spent some time doing the math and implementing a tuning algorithm. I made a video demonstrating it working, and had a bunch of people kick-in funding.

The last step of the process was ordering and assembling the boards, which I’ll cover in the next post.

Tips for a Successful Open-source Hardware Project – Design Considerations

Here are some things I’ve observed while developing the tabulaRasa and the USB-Octomod. Both projects are open-source hardware/software packages for use in modular synthesizers.

For me, the process divides up into three stages: design, funding, and production. I’ll cover each stage in a separate post.

Design


Make sure to spend plenty of time brainstorming, prototyping, scrapping, and remaking your project.

You want to explore as many angles as possible. Often a redesign or code rewrite will make implementing new features easier, or allow you to optimize some aspect of the design. Both the Octomod and the tabulaRasa went through multiple stages of sketching, prototyping, pcb design (and fabrication) before I decided they were done.

Aim to use common, versatile components.

You don’t want to use a single-supplier component in a design, unless there is no other option. The Octomod uses a Teensy 2.0 microcontroller unit which is available from only one source. Within a few days of announcing the availability of PCBs, the Teensy 2.0 went out of stock, and stayed that way for months. Besides limiting the usefulness of the PCBs, this left me fielding questions about the availability of a product over which I had no control. Some people are experiencing a similar issue with the SD card socket used in the tabulaRasa. Since the part is available from Sparkfun, I assumed it would be relatively simple for people to get. However, I didn’t consider that shipping to European countries from Sparkfun is either expensive or impossible. (Not sure which it is, I just know I got some questions about alternate sources.)

Using a minimal amount of components is also an advantage.

My initial designs for the tabulaRasa used a separate DAC chip, interfaced to the mcu through SPI. Although this theoretically provides for much higher bit resolution, most DACs require 16 or more bits to be transmitted per sample. The additional chip also added significant cost to the project. I instead used a simple PWM DAC, using an RC lowpass filter to smooth out the built-in PWM output of the ATmega. Simple, cheap, and still of a high-enough quality for my needs.

Open-source is an asset.

Many of the technical questions I received (usually related to compilation or misunderstanding of OSC) were easily solved because users could send me the exact error messages or line numbers they received. (On the other hand, asking users to compile their own code also led to confusion. I had a few people try to compile Arduino code in Processing, and vice-versa.)


More to come on how I raised money for construction on Kickstarter, and my thoughts on the manufacturing process.

USB-Octomod MIDI Support

Screen shot 2010-11-25 at 9.01.04 AM

The new USB-Octomod Software GUI.

The USB-Octomod software now supports MIDI input. Using MIDI controller numbers 20 – 27, you can convert MIDI to control voltage. The new version is a free download here.

USB-Octomod Demo Video

Octomod PD-Extended object, and modified NRCI library.

Hi,
I’ve made an Octomod object in PD-extended and bundled it with a modified version of the NRCI library I co-developed a few years ago. It’s got a great set of control data and rhythm generators, which make it really easy to get some interesting patterns up and running quickly – and now sent out over OSC to the Octomod Processing app.
Here’s the PD-Object and library:
http://gregsurges.com/wp-content/uploads/2010/08/nrci-octomod.zip
(open the -workspace.pd file)
And for more info on the use of NRCI:
https://ccrma.stanford.edu/~cburns/NRCI/
Check it out, and let me know what you think!
- Greg

Screen shot 2010-10-19 at 4.27.05 PM

I’ve made an Octomod object in PD-Extended and bundled it with a modified version of the NRCI library I co-developed a few years ago. It’s got a great set of control data and rhythm generators, which make it really easy to get some interesting patterns up and running quickly – and now sent out over OSC to the Octomod Processing app.

Here’s the PD-Object and library. (open the -workspace.pd file)

And for more info on the use of NRCI:

https://ccrma.stanford.edu/~cburns/NRCI/

Breadboard Octomod

George Macklin wrote about his breadboard version of the Octomod here.

Here’s his (super-clean) version:

Breadboard Octomod

USB-Octomod Feature

The Octomod is now featured in the Projects section of PJRC.com.

Updates and Arduino Wavetable Oscillator

Here’s my finished Octomod:

My Octomod Enclosure

My Octomod Enclosure

The project continues:

  • Second batch of PCBs has arrived.
  • A small number of component sets has been ordered for kits and assembled boards.
  • Teensy 2.0s should be back in stock within a week.

My new project is a voltage-controlled wavetable oscillator, with SD card storage, voltage control over waveform blending, and in an Arduino shield format. I’m currently breadboarding the prototype, and really pushing the Arduino platform. It’s going to be awesome.

Arduino Modification for USB-Octomod

Spent the better part of Sunday afternoon making the USB-Octomod work with an Arduino. There is a bundle containing code and instructions linked here.

Using OSC, Processing, and a Microcontroller to connect Max/MSP and a DAC IC.

My most recent project, the USB-Octomod, uses Processing to create an OpenSoundControl (OSC) interface between any OSC-ready software and a hardware DAC device I built. I’m going to break down the connections between different pieces of software and hardware, in order to explain how the system works and to provide the basis for a future tutorial on how one might use the device.

You can read more about the Octomod here, but it essentially allows computer control over the analog control voltages commonly used in analog synthesizers. Input a number 0 – 1023, and the device will output an analog voltage from -5V to +5V.

The OSC interface presents the inputs to the device, in the form of 8 numbered channels. The user sends an OSC message from their software of choice, and the interface program receives, processes, and communicates the data to the microcontroller in the device. I used a Teensy 2.0, which is very similar to Arduino, and any of this information should easily translate to the Arduino.

The OSC Interface

The OSC interface is simple. In your host program, you need to create a message formatted as follows:

/dac chanOne chanTwo chanThree chanFour chanFive chanSix chanSeven chanEight

For example:

/dac 256 273 50 1020 756 902 840 111

The trick here is that instead of sending an individual message whenever a channel changes, you can reduce network traffic by packaging all of the channels in one message, and updating that message at the rate of the most rapidly changing channel.

Sending OSC from Max/MSP

Here’s what it looks like in Max:

Screen shot 2010-08-25 at 8.55.57 AM

What is this code doing?

  1. The pak object outputs all eight of its inputs as a list whenever any one of the inputs changes.
  2. The message box below appends the /dac prefix to the list. Now the OSC message is formatted correctly.
  3. We don’t want the message to send automatically whenever a channel updates, so we buffer it with the second message box. This is done by sending the first message to the right inlet of the second message box.
  4. Finally, the metro object triggers the full OSC message to be sent once every 10ms.

The OSC interface application is expecting data on port 9999, and we’re going to be using the software locally, so we use the localhost address: 127.0.0.1. The Max udpsend object takes those two numbers as arguments, and transmits the OSC message.

Receiving OSC in Processing

The OSC interface program is written in Processing. OSC is easy to use in Processing as well. With a couple of lines of code, we’re ready to go:

import oscP5.*; // import the oscP5 library
import netP5.*; // the netP5 library is also required for the osc library
OscP5 oscP5;
oscP5 = new OscP5(this, 9999); // all you need to start oscp5 listening on port 9999

Now all we have to do is tell our program what to do when an OSC message is received. This is done by defining the oscEvent function.

After parsing out each of the eight input numbers, we check if a given channel needs to update its state. If so, we pass it to the writeValue() function. If not, we ignore it and don’t have to waste processor time sending the redundant data over the serial port. In my experience, this allows update rates of up to (possibly beyond) 1ms.

void oscEvent(OscMessage theOscMessage){
 if(theOscMessage.checkAddrPattern("/dac")==true){
   for(int i = 0; i < 8; i++){
     data[i] = theOscMessage.get(i).intValue();
     channelData[i] = data[i];
   }
   for(int i = 0; i < 8; i++){
     writeValue(i, data[i]);
   }
 }
}

Writing Serial Data to the Teensy

Serial teensy;
teensy = new Serial(this, Serial.list()[0], 19200);

The above lines are used in Processing to initialize a Serial object, allowing both read and write operations. The Serial.list()[0] argument indicates which actual serial port we want to write to. On my system, the Teensy always shows up as port 0 – this might be different on yours. Finally, the baud rate of 19200 is specified. Baud rate is the number of distinct signal events per second, and is a measure of data transfer speed.

Below is our writeValue() function, which was referenced above. The function is called repeatedly, once for each new sample to be written. First, we have to choose which of our two DAC chips should receive the data. Channels 0 – 3 go to chip A, 4 – 7 to chip B.

The MAX5250 is expecting a two byte word, which is assembled in the next section of code.

The SPI data expected by the MAX5250 DAC is as follows:
Screen shot 2010-08-27 at 10.48.25 AM

The first two bits select which of the four-per-chip channels to use, the second two bits allow us to write data with or without updating the actual voltage outputs, the next 10 bits are the actual data to be assigned, and the last two bits are unused. So, to write a data value of 512 to channel 3 and immediately output a voltage, we would send 1011001110110100.

As you can see, it’s a bit involved, and that’s why we want to avoid running all of this code unless the data has actually changed. We end up with three bytes to send to the Teensy 2.0, a one byte digit to indicate which DAC we want to write to, and the two additional SPI bytes. These are put into a buffer (really just an array) which is only transmitted when the buffer is full. This is to circumvent some timing weirdness in the USB to Serial conversion hardware.

void writeValue(int _channel, int _data){
 if(_channel > 3) { // assign one of two dac chips to respond
    dacChip = 1;
  } else {
    dacChip = 0;
  }

  /* bit shifting and masking to assemble proper list of bits for the DAC */
  _channel = _channel << 14;
  updateBits = 3 << 12;
  _channel = _channel | updateBits;
  _data = _data << 2;   spiWord = _channel | _data;
  binaryString = binary(spiWord, 16); // at this point, we've assembled our proper list of 16 bits
  outputData.add(byte(dacChip)); // so we'll throw them into an array, to facilitate transfer over serial
  outputData.add(byte(unbinary(binaryString.substring(0, 8))));
  outputData.add(byte(unbinary(binaryString.substring(8, 16))));
  if(outputData.size() >= 24){
    outputBytes = new byte[outputData.size()];
    for(int i = 0; i < outputData.size(); i++){
      outputBytes[i] = outputData.get(i);
    }
    teensy.write(outputBytes);
    dataIndex = 0;
    outputData = new ArrayList();
    previousUpdate = currentTime;
  }
}

Initializing SPI on the Teensy 2.0

Here’s an explanation of SPI from Wikipedia:

The SPI bus specifies four logic signals.

  • SCLK — Serial Clock (output from master)
  • MOSI/SIMO — Master Output, Slave Input (output from master)
  • MISO/SOMI — Master Input, Slave Output (output from slave)
  • SS — Slave Select (active low; output from master)

Essentially, the Master (Teensy 2.0 here) triggers the Slave chip by setting the SS pin low. Then the SCLK pin outputs a periodic clock pulse while the MOSI pin transmits the data (holding the SS pin low for the entire transfer). Here’s an image of the transmission from the MAX5250 datasheet – note that they use DIN (Data In) instead of MOSI, but it’s the same thing.

Screen shot 2010-08-27 at 10.24.55 AM

The first bit of code here is just a couple of statements to simplify our SPI communication. The DACs have a “Slave Select” pin, which allows them to either receive or ignore incoming data. This allows for easier wiring, you can connect all of the SPI lines to each chip, and just select which chip should respond at a given moment. Our DAC select byte (from above, in the writeValue() function) interfaces with the Slave Select code on the Teensy, and allows us to route data to the appropriate chip.Below, in the setup() function, we set the SS pins to output and set them both HIGH, so that no data is accidentally received by the DACs.

Finally, we call the setup_spi() function, found in Andrew Smallbone’s SPI library. These settings define how the Teensy should handle SPI, whether the DACs read the data on the rising or falling edge of the clock pulse, the SPI transmission rate as related to the Teensy clock, and a couple of other settings. You might notice that the serial interface is being initialized with a baud rate of 9600. The Teensy 2.0 actually ignores any baud rate argument and runs at full USB 2.0 speed.

#define SELECT_DAC_ONE digitalWrite(PORTB0, LOW);
#define DESELECT_DAC_ONE digitalWrite(PORTB0, HIGH);
#define SELECT_DAC_TWO digitalWrite(PORTD0, LOW);
#define DESELECT_DAC_TWO digitalWrite(PORTD0, HIGH);

void setup(){
  CPU_PRESCALE(CPU_4MHz);
  pinMode(PORTB0, OUTPUT);
  pinMode(PORTD0, OUTPUT);
  Serial.begin(9600);
  DESELECT_DAC_ONE;
  DESELECT_DAC_TWO;
  setup_spi(SPI_MODE_0, SPI_MSB, SPI_NO_INTERRUPT, SPI_MSTR_CLK2);
}

The last bit of code here reads incoming serial data, and immediately sends it out to the proper DAC. The serial buffering on the Teensy is a little bit different than the Arduino, in that it receives an entire USB packet at a time. The timing of the calls to Serial.read() can then be an issue. We want to make sure that we’re reading our three bytes in the proper order, and not getting out of phase with the host app, so we check that our first byte is either a 1 or a 0. Since the SPI interface packs data into the first and last bits of our data word (the second two bytes), a byte with the value of 1 or 0 will only appear as the first byte in the series. Timing is also important here, we need to introduce some brief delays so that we’re not reading or writing data too quickly.

void loop(){
  pollAndWrite();
}

void pollAndWrite(){
 data = false;
 while(!data){
  if(Serial.available()) { // look into the receive buffering - not receiving from Max properly
    firstByte = Serial.read();
    delayMicroseconds(100);
    if(firstByte == B00000000) {
      secondByte = Serial.read();
      delayMicroseconds(100);
      thirdByte = Serial.read();
      SELECT_DAC_ONE;
      send_spi(secondByte);
      send_spi(thirdByte);
      delayMicroseconds(10);
      DESELECT_DAC_ONE;
      data = true;
    }
      if(firstByte == B00000001){
        secondByte = Serial.read();
        delayMicroseconds(100);
        thirdByte = Serial.read();
        SELECT_DAC_TWO;
        send_spi(secondByte);
        send_spi(thirdByte);
        delayMicroseconds(10);
        DESELECT_DAC_TWO;
        data = true;
    }
  }
 }
}

So that’s the software side of the USB-Octomod. Although it’s fairly involved, there are only a few tricky spots, and the OSC interface greatly simplifies what the end-user actually has to think about during composition or performance.  Once the Processing and Teeny code is compiled and loaded, it becomes a plug-and-play device.