Wooden sensor box w/ 2 rotary disks

A homebuilt wooden sensor box i made, mainly for controlling PureData.

Similar projects worth following
A homebuilt wooden sensor box using different kinds of sensors and the Teensy 3.1 microcontroller.
For sensing the disk movement, I'm making use of IR LEDs and phototransistors and a technique called "Quadrature Encoding". I hacked two old hard drives for the motors and the hard drive platters.
I will have to decide wether i want it to work as a USB MIDI device, or OSC, or just raw data sent into Pure Data.
Currently I'm receiving raw data. I'm writing PD patches to find reasonable applications for music. I use Processing for any kind of visual representation. It's not meant for DJing in the first place, but I am interested in using jog wheels as sensors, for whatever case, I'll see.
To distinguish it a little bit from other DJ controllers via Design, i placed both jog wheels on the right side. Of course, it may be used for whatever makes sense.

  • 2 × Hard Disk w/ motor
  • 4 × IR reflective sensors consisting of an IR LED and a phototransistor
  • 2 × Op-Amp LM-358 used as comparators with hysteresis
  • 1 × Teensy 3.1 Microcontroller
  • 12 × Nail as a capacitive sensor

View all 11 components

  • Schematics and wiring

    Jan Godde11/10/2015 at 12:00 1 comment

    This is how one IR sensor and one op-amp are connected to the Teensy. I use some variable resistors for finetuning the thresholds. Keep in mind that operating a LM358 op-amp with only 3.3V will not give you the full output range. The output range will be missing 2V, so it will be only 1.3V (HIGH ~2.2V, LOW ~0.9V).

    Two IR sensors are needed to sense direction and speed of one hard disk platter. When using only one, there is no chance for calculating the direction. When using two sensors with a 90° angle offset, you can make use of the Quadrature Encoding technique.

    ITR9904 IR sensor datasheet

    LM358 datasheet

    To connect the ribbon and pressure sensors is easy. See, for example:

    Connect a pressure (FSR) sensor

    SoftPot ribbon sensor product page

    On the SoftPot product page, it's said not to connect Ground and Power directly. So make sure to put a 10kOhm resistor in series to connect the ground and the power pin. The center pin can be read directly.

    The faders and potentiometers are connected to the Teensy like this.

    For the switches, i used the <Bounce.h> library. On this page, you can find detailled information on using the switches. It's nice that you only need one digital pin and a ground connection for each switch.

    The LEDs are connected with a resistor in series to make sure that there is not too much current flowing. The longer leg is connected to a digital pin, the shorter with a resistor to ground. As i used really bright white LEDs, i chose a bigger resistor than you would normally do to limit the light intensity.

    The touch buttons are connected to the Teensy's touch pins directly.

  • The touch sensing and touchRead()

    Jan Godde11/09/2015 at 19:14 0 comments

    For reading the touch sensors, i simply use the "touchRead()" operator that you get with Teensyduino (Teensy 3.0 and higher). To be honest, i don't know precisely what the Teensy is doing here. It's some advanced capacitive sensing technology that is implemented with the new Teensy boards. I read something about a reference capacitor equalling out changing conditions. It's said to be faster and giving more reproducible results than when using the old technique with 2 pins, a resistor and the CapSense library (Arduino CapSense article).

    Now i may have made a mistake in the design as i was just reading some articles about capacitive touch sensing and it seems that you have improved readings using a grounding plate surrounding the sensor. It seems that the better touch sensing is with an electrical field between two capacitor plates that you change while bringing your finger closer to this system. This would mean there is a metal sensor object that is connected to the touch pins of the Teensy and a grounding plate (connected to ground), surrounding this object. In my design, i just soldered cables to the nails and connect the cables to Teensy's touch pins, making use of touchRead().

    This is working quite good, but I have to tell you, that the pin readings change drastically when i touch, for example, the housing of my laptop computer. I will try to make some changes and improve my design.

    See The art of capacitive touch sensing for more information on the topic, though this may not be the same technology as the one that touchRead() is working with in the new Teensy boards. Also have a look at the PRJC (Teensy) forum. There are some posts on this topic.

  • Processing sketch

    Jan Godde11/09/2015 at 19:05 0 comments

    This is the Processing code i used in the second demo video demonstrating the rotary disks.

    It is receiving the Teensy data from the serial port (for communication syntax, see Arduino code in the other project log entry).

    The wheel resolution (see LEDMode in Arduino code) is being controlled here.

    All the values are being scaled.

    // ***** wheelController Software *****
    //  ******** Jan Godde, 2015 ********
    //     USING PHASE VOCODER IN PD IN soundWheel.pd SKETCH
    import processing.serial.*;
    import org.puredata.processing.PureData;
    PureData pd; // don't forget: expr operator seems not to be working with pdp5!
    final int REVOL = 308; // resolution optimized: 4 x (77 black bars)
    Serial myPort;
    String thisString = "0";
    int[] splitString = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
    int LEDMode;
    int wheel2Mode;
    float pos1, pos2;
    float ribbon1, ribbon2, fader1, fader2, fsr1, fsr2;
    float knob1, knob2, knob3, knob4;
    float lefttouch, righttouch, mono, wheeltouch1, wheeltouch2;
    float[] inputValue = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //fader, pressure, knobs, 2 wheels
    float[] touch = {0, 0, 0, 0, 0}; //2 touch ducking - 1 touch mono (middle) - 2 wheel touch
    float[] ribbon = {0, 0};
    void setup()
      pd = new PureData(this, 44100, 0, 2);
      LEDMode = 0;
      wheel2Mode = 0;
      String portName = Serial.list()[4]; // N° 4
      myPort = new Serial(this, portName, 57600);
    void draw()
      lefttouch = constrain( map(touch[0], 1000, 5700, 0, 1000), 0, 1000 );
      righttouch = constrain( map(touch[1], 1050, 5700, 0, 1000), 0, 1000 );
      mono = constrain( map(touch[2], 1000, 5700, 0, 1000), 0, 1000 );
      wheeltouch1 = constrain( map(touch[3], 1500, 6500, 0, 1000), 0, 1000 );
      wheeltouch2 = constrain( map(touch[4], 1500, 6500, 0, 1000), 0, 1000 );
      fader1 = constrain( map(inputValue[0], 0, 1023, 0, 1000), 0, 1000 ); //fader1
      fader2= constrain( map(inputValue[1], 0, 1023, 0, 1000), 0, 1000 ); //fader2
      fsr1 = constrain( map(inputValue[2], 0, 700, 0, 1000), 0, 1000 );  //fsr1
      fsr2 = constrain( map(inputValue[3], 200, 700, 0, 1000), 0, 1000 );//fsr2
      knob1 = constrain( map(inputValue[4], 0, 1023, 0, 1000), 0, 1000 ); //knob1
      knob2 = constrain( map(inputValue[5], 0, 1023, 0, 1000), 0, 1000 ); //knob2
      knob3 = constrain( map(inputValue[6], 0, 1023, 0, 1000), 0, 1000 ); //knob3
      knob4 = constrain( map(inputValue[7], 0, 1023, 0, 1000), 0, 1000 ); //knob4
      ribbon1 = constrain( map(ribbon[0], 450, 750, 0, 1000), 0, 1000 );
      ribbon2 = constrain( map(ribbon[1], 450, 750, 0, 1000), 0, 1000 );
      drawWheels(inputValue[8], inputValue[9], wheeltouch1, wheeltouch2);
      pos1 = constrain( map(inputValue[8], 0, ((LEDMode+1)*REVOL), 0, 1000), 0, 1000 );
      pos2 = constrain( map(inputValue[9], 0, ((LEDMode+1)*REVOL), 0, 1000), 0, 1000 );
      // send all values from 0...1000
      pd.sendFloat("vol1", (float)fader1); //fader1
      pd.sendFloat("vol2", (float)fader2); //fader2
      pd.sendFloat("pvrev1", (float)knob2); //knob2
      pd.sendFloat("pvrev2", (float)knob3); //knob3
      pd.sendFloat("drywet1", (float)knob1); //knob1
      pd.sendFloat("drywet2", (float)knob4); //knob4
      pd.sendFloat("pos1", (float)pos1); // wheel 1 position
      pd.sendFloat("pos2", (float)pos2); // wheel 2 position
      pd.sendFloat("transpose1", (float)fsr1); //fsr1
      pd.sendFloat("transpose2", (float)fsr2); //fsr2
      pd.sendFloat("touch7", (float)lefttouch); 
      pd.sendFloat("touch9", (float)righttouch); 
      pd.sendFloat("touch8", (float)mono); // "mono" channel cross-talk
      pd.sendFloat("wheeltouch1", (float)wheeltouch1); // wheel touch 1 & 2
      pd.sendFloat("wheeltouch2", (float)wheeltouch2);
      pd.sendFloat("wheel2mode", (float)wheel2Mode); // scratch mode on/off on wheel 2
      pd.sendFloat("ribbon1", (float)ribbon1);
      pd.sendFloat("ribbon2", (float)ribbon2);
    void drawWheels(float pos1, float pos2, float touch1, float touch2)...
    Read more »

  • Arduino (Teensyduino) Code

    Jan Godde11/09/2015 at 19:01 0 comments

    This is the code that i am using in the second demo video, demonstrating the rotary disks. This code may be still a mess, and there may be some stupid solutions, but so far it works.

    I am not reading in all the touch sensors as for this example i only need a few.

    For reading the touch sensors, i simply use the "touchRead()" operator that is working with Teensyduino (Teensy 3.0 and higher). This is working quite good, but I have to tell you, that the pin readings change drastically when i touch, for example, the housing of my laptop computer. I will try to make some changes and improve my design. I posted another project log entry on this topic.

    The two switches change a mode that is represented by the first 4 LEDs. The higher the mode (LEDMode: 0, 1, 2, 3, 4), the slower the values change when disks are spun. So it decreases the sensitivity of the wheels, making the wheel movements more precise.

    When both switches are pressed, a second mode (LED5Mode) is changed (0, 1), displayed by LED number 5.

    If you wonder about all the Serial.print() operators at the end of the sketch, this is how the data is being sent to Processing (see Processing code).

    // ***** wheelController Software *****
    //  ******** Jan Godde, 2015 ********
    // ARDUINO SETTINGS FOR processingReceiveWheels SKETCH
    //     USING PHASE VOCODER IN PD IN soundWheel.pd SKETCH
    #include <Encoder.h>
    #include <Bounce.h>
    #define REVOL 308 // resolution optimized: 4 x (77 black bars)
    int LEDMode = 0;
    int LED5Mode = 0;
    int switch1last, switch2last;
    int LEDState[5][5] = { {LOW, LOW, LOW, LOW, LOW}, {HIGH, LOW, LOW, LOW, LOW}, 
                           {LOW, HIGH, LOW, LOW, LOW},
                           {LOW, LOW, HIGH, LOW, LOW}, {LOW, LOW, LOW, HIGH, LOW} };
    Encoder knobLeft(9, 10);
    Encoder knobRight(11, 12); 
    Bounce switch1 = Bounce(2, 5); // (pin number, 5 ms debounce time)
    Bounce switch2 = Bounce(3, 5); // switch 1: D2, switch2: D3
    // int sensorPin[20] = { A7, A6, A14, A20, A10, A11, A15, A16, //fader - fsr - knobs
    //                     0, 1, 15, 16, 17, 18, 19, 22, 23, 25, 32, 33 }; //touchPins
    int sensorPin[13] = { A7, A6, A14, A20, A10, A11, A15, A16, //fader - fsr - knobs
                 0, 33, 32, 1, 25}; // 3 touchPins & 2 wheelTouch
    int ribbonPin[2] = {A0, A19};
    int ribbonValue[2] = {0, 0};
    int ribbonTouched[2] = {0, 0};
    int LEDPin[5] = {4, 5, 6, 7, 8};
    int outputValue[15]; //with wheel encoder at last two positions
    int positionLeft  = 0;
    int positionRight = 0;
    //int newLeft, newRight;
    void setup() {  
      pinMode(A14, INPUT_PULLUP);//fsr1
      pinMode(A20, INPUT_PULLUP);//fsr2
      pinMode(A0, INPUT_PULLUP);//ribbon1
      pinMode(A19, INPUT_PULLUP);//ribbon2
      pinMode(2, INPUT_PULLUP);  //switch1
      pinMode(3, INPUT_PULLUP);  //switch2
      pinMode(4, OUTPUT);
      pinMode(5, OUTPUT);
      pinMode(6, OUTPUT);
      pinMode(7, OUTPUT);
      pinMode(8, OUTPUT);
    void loop() {
      if (switch1.update()){
        if(switch1.fallingEdge()) {
          switch1last = 1;
          LEDMode = mod((LEDMode-1),5);
          if (switch2last == 1){
        if(switch1.risingEdge()) {
          switch1last = 0;
      if (switch2.update()){
        if(switch2.fallingEdge()) {
          switch2last = 1;
          LEDMode = mod((LEDMode+1),5);
          if (switch1last == 1){
        if(switch2.risingEdge()) {
          switch2last = 0;
      //Serial.print(" ");
      for(int i = 0; i < 8; i++){
        outputValue[i] = analogRead(sensorPin[i]);
      for(int i = 8; i < 13; i++){
        outputValue[i] = touchRead(sensorPin[i]);
      int newLeft, newRight;
      newLeft =;
      newRight =;
      //newLeft = newLeft + 1;
      //newRight = newRight + 2;
      if (newLeft != positionLeft || newRight != positionRight) {
        newLeft = mod(newLeft, (LEDMode+1)*REVOL);
        newRight = mod(newRight, (LEDMode+1)*REVOL);
        positionLeft = newLeft;
        positionRight = newRight;
      for(int i = 0; i < 13; i++){
    Read more »

View all 4 project logs

  • 1
    Step 1

    I was designing this box with SketchUp. I knew the dimensions, and what sensors I'd like to use, but hardly more. Here is how i ended up with the 3D model:

  • 2
    Step 2

    For sensing the movement of the rotary disks (which i hacked out of two old hard drives i had lying around) i was thinking about different approaches. If you look around in the web, there are a couple of DIY maker projects hacking hard disk drives. I could have hacked any optical mice and have the camera look at the disk movement. I could have measured the voltage on the hard disk connectors that appears while spinning the hard drive (amplifying necessary, resolution questionable). I think by measuring the voltages on the hard disk contact pins you can still achieve pretty good results.There's a number of other hard disk jog wheel projects out there. I decided to use reflective optoelectronics. Using Quadrature Encoding, i could go with a good resolution (with 77 black bars, i get 4x77 = 308 ticks per revolution). This is the disk with the pattern for the IR sensor:

    The IR sensors i used are named "ITR9904". You can use other sensors as well. There are many IR sensors like this, in one housing, with both one IR LED and one phototransistor. With this design, they have to make use of IR reflection, not transmission.

    With the IR sensors, i can measure a high voltage if IR light is reflected, and a low voltage if not. Very important was the exact position of the two sensors. They have to be in an angle of 90 degrees in order to make the Quadrature Encoding possible with this approach. This works if we have an odd number of black (absorbing) stripes on the disk. Here you can see how i placed the sensors below the disk (90°) in the 3D model:

  • 3
    Step 3

    The top panel was built out of two laser cut wood layers that i glued together. The red bars indicate the positions at which i added extra wood blocks that i could put screws into for connceting the top panel to the rest of the box. The screws are hidden by case feet.

View all 7 instructions

Enjoy this project?



glenn.szymanski wrote 04/13/2020 at 00:55 point

I'm looking to add some extra expressiveness to my electronic drum set. I'd like to add semi-circular shaped ribbon controllers underneath the cymbals or maybe an XY pad. And some sort of IR sensor underneath the mesh drum heads, so that pushing down on them sends a MIDI CC #. Could all of these extra controllers be wired into standard cable type connectors to all go to a central "brain" type module that would house the Teensy board?

  Are you sure? yes | no

Martin wrote 01/09/2017 at 09:30 point

Looks very good!

One comment to the 90° positioning of the sensors. They do not to be 90° mechanically, only electrically, and this is independent of the number of strips (even or odd). They can be side by side and have just a distance (midpoints) of (2n+1)*w/2, where w is the width of a strip or gap. So they can be on the same small PCB and easier to mount.

How did you make the black strips on the backside of the disks? Toner transfer or PCB- photo resist could be possible.

  Are you sure? yes | no

Jason Prescher wrote 02/06/2016 at 09:09 point

Very Nice! Good work. Classy look too.

  Are you sure? yes | no

Elliot Williams wrote 11/13/2015 at 21:16 point

Wow!  Thanks for adding in all the detail!  That's fantastic.

I'm going to read up on the Teensy cap sensing this weekend. :)

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates