Sunday, October 28, 2012

Arduino Serial Servos - 20 Servos, 4 Pins, High Refresh Rates

In a recent post we looked at a very simple technique for controlling 10 servos with two Arduino pins using only one 30 cent chip.

I have since extended the library for two new functions -

1) Control upto 20 servos using only Four pins and two 30 cent chips
2) Variable refresh rate to maximise stability and holding torque

10 More Servos

The first addition - An additional 10 more servos has been achieved by duplicating the original libraries interaction with Timer1, OCR1A and the 4017 Chip onto OCR1B and a second 4017 chip. To put it another way each of the timer1 output compare registers is driving its own bank of ten servos through its own 4017 decade counter.

Refer to the original post here - http://rcarduino.blogspot.com/2012/08/arduino-serial-servos.html
For a description of the basic scheme.


Variable Refresh Rate
This second capability has always been a function of the library but is now more accessible.


The library blindly cycles through each bank of ten servos generating the required pulse. When the library gets to the last servo it points itself back to the first servo and starts again.



Remember, this is interrupt driven and is happening in the background, your main code is not effected.

A rough calculation suggests that driving 20 servos with this library will take a fraction more than half of one percent of Arduino processing power leaving you free to fill the other 99.5%


As the library continually cycles through the two banks of servos, a reduction in the number of servos will reduce the time taken for a cycle and therefore increase the servo refresh rate.

If we assume an average servo pulse of 1500ms, the following average refresh rates apply -
 
Servos In Each Bank Average Servo Position Time To Cycle Through Servos Average Refresh Rate
1 1500 1500 666.6666667
2 1500 3000 333.3333333
3 1500 4500 222.2222222
4 1500 6000 166.6666667
5 1500 7500 133.3333333
6 1500 9000 111.1111111
7 1500 10500 95.23809524
8 1500 12000 83.33333333
9 1500 13500 74.07407407
10 1500 15000 66.66666667


I have highlighted the values for 3 and 4 servos as 250Hz seems to be a common request for the refresh rates of ESCs used in quadcopters.

What advantage do we get by increasing the servo refresh rate ? in the case of quadcopters faster refresh can improve stability.

In the case of robotics, higher refresh rates can increase holding torque and again improve stability.

To drive 8 servos with a high refresh rate a fast and simple option is to use both banks of servos and drive only four servos on each bank.


You may already be using a variety of refresh rates without knowing, some of my RC Car transmitters operate at 50Hz, others at 60Hz and one at 91Hz - I have been mixing and matching equipment for years and only discovered the different refresh rates by plugging my receivers into an Arduino.


The refresh rate is determined by the total pulse width for each bank of servos - four servos set to 1 microsecond will be cycled through 250 (1/(4*0.001) times a second, if the same four servos are set to 2 microseconds we slow down to 125 (1/(4*.002) times a second.

Shouldn't I add code to fix a constant refresh rate ? 

My thinking is that if we can refresh faster and take advantage of the increased stability and hold, why would we introduce code and complexity to prevent this ? If you would prefer a fixed refresh rate, keep a few more servos than you need and use them to adjust the total.

The Code - 
 
Here is the new library and test sketch for you to try. The test sketch sets up the servos with increasing pulse widths, an interrupt service routine is provided which allows you to test the pulse width on any of the servo outputs.

To add or remove the second bank of servos driven by OCR1B, refer to the following line in the RCArduinoSerialServos.h file


// COMMENT OR UNCOMMENT THIS LINE TO ENABLE THE SECOND BANK OF SERVOS
#define MORE_SERVOS_PLEASE 1
 

 To change the number of servos driven by the library in order to increase the refresh rate refer to the following line in the RCArduinSerialServos.h file -

// THIS IS FOR ADVANCED USERS ONLY -
//
// Change the channel out count to adjust the frequency,
//
// BEFORE DOING THIS VERIFY THAT YOUR SERVOS AND ESCS ARE COMPATIBLE
// WITH HIGHER AND VARIABLE REFRESH RATES
//
// The library blindly pulses all ten servos one and after another
// If you change the RC_CHANNEL_OUT_COUNT to 4 servos, the library will pulse them more frequently than
// it can ten -
// 10 servos at 1500us = 15ms = 66Hz
// 4 Servos at 1500us = 6ms = 166Hz
// if you wanted to go even higher, run two servos on each 4017
// 2 Servos at 1500us = 3ms = 333Hz
#define RC_CHANNEL_OUT_COUNT 4

RCArduinoSerialServos.h

**************************************************************************************
// RCArduinoChannels by DuaneB is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
//
// http://rcarduino.blogspot.com
//
*****************************************************************************************************************************/


#include "Arduino.h"

// COMMENT OR UNCOMMENT THIS LINE TO ENABLE THE SECOND BANK OF SERVOS
#define MORE_SERVOS_PLEASE 1
// the second bank of servos uses pin 10 (clock) and 13 (reset) to drive an additional ten servos.
// the first bank uses pin 9 (clock) and 12 (reset)

// THIS IS FOR ADVANCED USERS ONLY -
//
// Change the channel out count to adjust the frequency,
//
// BEFORE DOING THIS VERIFY THAT YOUR SERVOS AND ESCS ARE COMPATIBLE
// WITH HIGHER AND VARIABLE REFRESH RATES
//
// The library blindly pulses all ten servos one and after another
// If you change the RC_CHANNEL_OUT_COUNT to 4 servos, the library will pulse them more frequently than
// it can ten -
// 10 servos at 1500us = 15ms = 66Hz
// 4 Servos at 1500us = 6ms = 166Hz
// if you wanted to go even higher, run two servos on each 4017
// 2 Servos at 1500us = 3ms = 333Hz
#define RC_CHANNEL_OUT_COUNT 4

#if defined (MORE_SERVOS_PLEASE)
#define RCARDUINO_MAX_SERVOS (RC_CHANNEL_OUT_COUNT*2)
#else
#define RCARDUINO_MAX_SERVOS (RC_CHANNEL_OUT_COUNT)
#endif

// Minimum and Maximum servo pulse widths, you could change these,
// Check the servo library and use that range if you prefer
#define RCARDUINO_SERIAL_SERVO_MIN 1000
#define RCARDUINO_SERIAL_SERVO_MAX 2000

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// CRCArduinoSerialServos
//
// A class for generating signals in combination with a 4017 Counter
//
// Output upto 10 Servo channels using just digital pins 9 and 12
// 9 generates the clock signal and must be connected to the clock pin of the 4017
// 12 generates the reset pulse and must be connected to the master reset pin of the 4017
//
// The class uses Timer1, as this prevents use with the servo library
// The class uses pins 9 and 12
// The class does not adjust the servo frame to account for variations in pulse width,
// on the basis that many RC transmitters and receivers designed specifically to operate with servos
// output signals between 50 and 100hz, this is the same range as the library
//
// Use of an additional pin would provide for error detection, however using pin 12 to pulse master reset
// at the end of every frame means that the system is essentially self correcting
//
// Note
// This is a simplified derivative of the Arduino Servo Library created by Michael Margolis
// The simplification has been possible by moving some of the flexibility provided by the Servo library
// from software to hardware.
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////

class CRCArduinoSerialServos
{
public:
    CRCArduinoSerialServos();

    // configures timer1
    static void begin();

    // called by the timer interrupt service routine, see the cpp file for details.
    static void OCR1A_ISR();
   
#if defined(MORE_SERVOS_PLEASE)
    static void OCR1B_ISR();
#endif

    // called to set the pulse width for a specific channel, pulse widths are in microseconds - degrees are for wimps !
    static void writeMicroseconds(uint8_t nChannel,uint16_t nMicroseconds);

protected:
    // this sets the value of the timer1 output compare register to a point in the future
    // based on the required pulse with for the current servo
    static void setOutputTimerForPulseDurationA();
   
    // Records the current output channel values in timer ticks
    // Manually set by calling writeChannel, the function adjusts from
    // user supplied micro seconds to timer ticks
    volatile static uint16_t m_unChannelOutA[RC_CHANNEL_OUT_COUNT];
    // current output channel, used by the timer ISR to track which channel is being generated
    static uint8_t m_sCurrentOutputChannelA;
   
#if defined(MORE_SERVOS_PLEASE)
    // Optional channel B for servo number 10 to 19
    volatile static uint16_t m_unChannelOutB[RC_CHANNEL_OUT_COUNT];
    static uint8_t m_sCurrentOutputChannelB;
    static void setOutputTimerForPulseDurationB();
#endif

    // two helper functions to convert between timer values and microseconds
    static uint16_t ticksToMicroseconds(uint16_t unTicks);
    static uint16_t microsecondsToTicks(uint16_t unMicroseconds);
};

RCArduinoSerialServos.cpp
#include "RCArduinoSerialServos.h"

/*----------------------------------------------------------------------------------------

This is essentially a derivative of the Arduino Servo Library created by Michael Margolis

As the technique is very similar to the Servo class, it can be useful to study in order
to understand the servo class.

What does the library do ? It uses a very inexpensive and common 4017 Counter IC
To generate pulses to independently drive up to 10 servos from two Arduino Pins

As previously mentioned, the library is based on the techniques used in the Arduino Servo
library created by Michael Margolis. This means that the library uses Timer1 and Timer1 output
compare register A.

OCR1A is linked to digital pin 9 and so we use digital pin 9 to generate the clock signal
for the 4017 counter.

Pin 12 is used as the reset pin.

*/

// Timer1 Output Compare A interrupt service routine
// call out class member function OCR1A_ISR so that we can
// access out member variables
ISR(TIMER1_COMPA_vect)
{
    CRCArduinoSerialServos::OCR1A_ISR();
}

void CRCArduinoSerialServos::OCR1A_ISR()
{
    // If the channel number is >= 10, we need to reset the counter
    // and start again from zero.
    // to do this we pulse the reset pin of the counter
    // this sets output 0 of the counter high, effectivley
    // starting the first pulse of our first channel
  if(m_sCurrentOutputChannelA >= RC_CHANNEL_OUT_COUNT)
  {
    // reset our current servo/output channel to 0
    m_sCurrentOutputChannelA = 0;

    // pulse reset on the counter - we set it high here
    PORTB|=16;

    // set the duration of the output pulse
    CRCArduinoSerialServos::setOutputTimerForPulseDurationA();

    // finish the reset pulse - we set it low here
    PORTB^=16;
 }
 else
 {
  // pulse the clock pin high
    PORTB|=2;

    // set the duration of the output pulse
    CRCArduinoSerialServos::setOutputTimerForPulseDurationA();

    // finish the clock pulse - set it back to low
    PORTB^=2;
  }
    // done with this channel so move on.
    m_sCurrentOutputChannelA++;
}
// After we set an output pin high, we need to set the timer to comeback for the end of the pulse
void CRCArduinoSerialServos::setOutputTimerForPulseDurationA()
{
  OCR1A = TCNT1 + m_unChannelOutA[m_sCurrentOutputChannelA];
}

#if defined(MORE_SERVOS_PLEASE)
// Timer1 Output Compare B interrupt service routine
// call out class member function OCR1B_ISR so that we can
// access out member variables
ISR(TIMER1_COMPB_vect)
{
    CRCArduinoSerialServos::OCR1B_ISR();
}

void CRCArduinoSerialServos::OCR1B_ISR()
{
    // If the channel number is >= 10, we need to reset the counter
    // and start again from zero.
    // to do this we pulse the reset pin of the counter
    // this sets output 0 of the counter high, effectivley
    // starting the first pulse of our first channel
  if(m_sCurrentOutputChannelB >= RC_CHANNEL_OUT_COUNT)
  {
    // reset our current servo/output channel to 10 -
    // Note that 10 is the first servo on output compare B
    m_sCurrentOutputChannelB = 0;

    // pulse reset on the counter - we set it high here
    PORTB|=32;

    // set the duration of the output pulse
    CRCArduinoSerialServos::setOutputTimerForPulseDurationB();

    // finish the reset pulse - we set it low here
    PORTB^=32;
 }
 else
 {
  // pulse the clock pin high
    PORTB|=4;

    // set the duration of the output pulse
    CRCArduinoSerialServos::setOutputTimerForPulseDurationB();

    // finish the clock pulse - set it back to low
    PORTB^=4;
  }
    // done with this channel so move on.
    m_sCurrentOutputChannelB++;
}

// After we set an output pin high, we need to set the timer to comeback for the end of the pulse
void CRCArduinoSerialServos::setOutputTimerForPulseDurationB()
{
  OCR1B = TCNT1 + m_unChannelOutB[m_sCurrentOutputChannelB];
}
#endif

// updates a channel to a new value, the class will continue to pulse the channel
// with this value for the lifetime of the sketch or until writeChannel is called
// again to update the value
void CRCArduinoSerialServos::writeMicroseconds(uint8_t nChannel,uint16_t unMicroseconds)
{
    // dont allow a write to a non existent channel
    if(nChannel > RCARDUINO_MAX_SERVOS)
        return;

  // constraint the value just in case
  unMicroseconds = constrain(unMicroseconds,RCARDUINO_SERIAL_SERVO_MIN,RCARDUINO_SERIAL_SERVO_MAX);

#if defined(MORE_SERVOS_PLEASE)
  if(nChannel >= RC_CHANNEL_OUT_COUNT)
  {
    // disable interrupts while we update the multi byte value output value
    uint8_t sreg = SREG;
    cli();
     
    m_unChannelOutB[nChannel-RC_CHANNEL_OUT_COUNT] = microsecondsToTicks(unMicroseconds);

    // enable interrupts
    SREG = sreg;
    return;
  }
#endif
 
  // disable interrupts while we update the multi byte value output value
  uint8_t sreg = SREG;
  cli();
 
  m_unChannelOutA[nChannel] = microsecondsToTicks(unMicroseconds);

  // enable interrupts
  SREG = sreg;
}

uint16_t CRCArduinoSerialServos::ticksToMicroseconds(uint16_t unTicks)
{
    return unTicks / 2;
}

uint16_t CRCArduinoSerialServos::microsecondsToTicks(uint16_t unMicroseconds)
{
 return unMicroseconds * 2;
}

void CRCArduinoSerialServos::begin()
{
    // set the pins we will use for channel A (OCR1A) as outputs
    pinMode(9,OUTPUT); // clock uses OCR1A and should not be changed
    pinMode(12,OUTPUT); // reset, if you really needed to, you could change this, but remember to change it in the ISR as well

    // pulse reset
    digitalWrite(12,HIGH);
    digitalWrite(12,LOW);

#if defined (MORE_SERVOS_PLEASE)

    // set the pins we will use for channel B (OCR1B) as outputs
    pinMode(10,OUTPUT); // clock uses OCR1B and should not be changed
    pinMode(13,OUTPUT); // reset, if you really needed to, you could change this, but remember to change it in the ISR as well

    // pulse reset
    digitalWrite(12,HIGH);
    digitalWrite(12,LOW);

#endif

    TCNT1 = 0;              // clear the timer count  

    // Initilialise Timer1
    TCCR1A = 0;             // normal counting mode
    TCCR1B = 2;     // set prescaler of 64 = 1 tick = 4us

    // ENABLE TIMER1 OCR1A INTERRUPT to enabled the first bank (A) of ten servos
    TIFR1 |= _BV(OCF1A);     // clear any pending interrupts;
    TIMSK1 |=  _BV(OCIE1A) ; // enable the output compare interrupt 

#if defined(MORE_SERVOS_PLEASE)

    // ENABLE TIMER1 OCR1B INTERRUPT to enable the second bank (B) of 10 servos
    TIFR1 |= _BV(OCF1B);     // clear any pending interrupts;
    TIMSK1 |=  _BV(OCIE1B) ; // enable the output compare interrupt 

#endif

    OCR1A = TCNT1 + 4000; // Start in two milli seconds
}

// See the .h file
volatile uint16_t CRCArduinoSerialServos::m_unChannelOutA[RC_CHANNEL_OUT_COUNT];
uint8_t CRCArduinoSerialServos::m_sCurrentOutputChannelA;

#if defined(MORE_SERVOS_PLEASE)

volatile uint16_t CRCArduinoSerialServos::m_unChannelOutB[RC_CHANNEL_OUT_COUNT];
uint8_t CRCArduinoSerialServos::m_sCurrentOutputChannelB;

#endif


 RCArduinoSerialServos sketch


// RCArduinoSerialServos by DuaneB is licensed under a Creative Commons Attribution-NonCommercial 3.0 Unported License.
// Based on a work at rcarduino.blogspot.com.
#include <RCArduinoSerialServos.h>

volatile uint32_t ulRiseTime;
volatile uint32_t ulPulseWidth;

void setup()
{
  Serial.begin(9600);
  Serial.println("RCArduinoSerialServos");
 
  // set the channels
  for(uint16_t nChannel = 0;nChannel < RCARDUINO_MAX_SERVOS;nChannel++)
  {
    CRCArduinoSerialServos::writeMicroseconds(nChannel,1000+(nChannel*50));
  }

  CRCArduinoSerialServos::begin();
 
  attachInterrupt(0,calcPulse,CHANGE);
}

void loop()
{
  delay(10);

  if(ulPulseWidth != 0)
  {
    // disable interrupts so that our pulse value does not get overwritten while we try and read it
    uint8_t sReg = SREG;
    cli();
   
    // take a local copy of the pulse witdth so that we can reenable interrupts as soon as possible
    uint32_t ulLocalPulseWidth = ulPulseWidth;
   
    // clear the pulse width so that we will only pick up new values written by calcPulse rather
    // than keep printing old values.
    ulPulseWidth = 0;
   
    // turn interrupts back on
    SREG = sReg;
   
    // print the pulse width
    Serial.println(ulLocalPulseWidth);
  }
 
}

// Read pulse width applied to digital pin 2 (interrupt 0)
void calcPulse()
{
  if(digitalRead(2))
  {
    ulRiseTime = micros();
  }
  else
  {
    ulPulseWidth = micros()- ulRiseTime;
  }
}










Tuesday, October 23, 2012

Five Dollar Synthesiser (post in progress)

What do you get out of interfacing Arduino to a low cost keyboard -

UPDATE - New synthesis mode added - Auduino style grain synthesis



UPDATE - Listen to the five dollar synthesiser here - 


Why hack a cheap childs keyboad -
1) Three octaves or more of ready made piano style keys
2) A large number of general purpose buttons for configuring you synth engine, adding effects or controlling a sequencer

This post concludes with a sketch which will provide your keyboard with some interesting capabilities to experiment with -

3) Four different waveforms - sine, square, ramp and dirty triangle
4) An adjustable speed arpeggio mode, low speed = arpeggios, high speed = chord like effects, very high speed = spacey distortion
5) Arpeggio record and play back
6) Transpose function, this shifts the three octave keyboard up and down by upto 7 octaves to give full access to the 128 midi note range - its also used in the video to creating the sweeping Arpeggios.

7) Delay ! - the code now features a delay function which you can here in action in the video.

Quick demonstration of using transpose with arpeggios -



Interfacing a low cost keyboard to Arduino -

The keyboard I am using is available for around 5 to 10 dollars, any similar keyboard in this price bracket is likely to work in the same way so pick one up and prepare to plug in.

So whats inside your typical five dollar keyboard ?

The main features are two PCBs which connect the many buttons and keys in a matrix.

The top PCB has been turned over to show the buttons and connections.

The entire matrix of buttons and keys is accessible from the17 solder pads visible in the center of the top PCB. Through these 17 pads we can read the state of any of the 36 keys and 33 buttons on our example keyboard.

Your keyboard may have a different number of keys and buttons, but the concept will be the same - all of the buttons are connected in a matrix pattern and this matrix is used to read the state of the individual buttons.

In the top right of the picture you might be able to make out a small amplifier. This amplifier uses a LM386 Chip which has previously been featured on RC Arduino in the post 'Adding Audio To Arduino Projects', this drives a small speaker fitted in the keyboard.

http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html


Zooming in for a closer look
While it might not be obvious from the picture the 17 solder connections represent 9 rows of 8 columns.

When a button is pressed it makes a unique connection between a row and a column.

By reading the state of these unique connections we know which keys are currently pressed. 


The great thing about using a ready made keyboard is that someone has already built the matrix for us. This many buttons and keys to plug into our Arduino is an excellent deal - its even better when you consider that we can read these buttons using only 10 or less Arduino pins.

How can we read the buttons using only 10 pins ?

The solution is multiplexing. To do this we switch all of the rows off and then switch each one on in turn. We record which row is currently on and then read the column to see if any keys are pressed.

If we press the center key of the keyboard, our column pins will not read anything until row 3 is the active (on) row. Then we can then do some simple maths to get the key number -

key number = (row * number of pins in a column) + column;

20 = (2*8) + 4

How do we do this with Arduino ?

One approach to this would be to dedicate a digital output pin for each row and a digital input pin for each column. This can be done but its a huge waste of resources, we would also need to write code to manage the switching on and off (multiplexing) of all of these pins.

A 4017 Decade Counter is a low cost widely available chip that will do this work for us using only 2 pins from the Arduino.


This chip has previously been featured on RC Arduino as a means to drive upto 10 Servos from just two digital output pins. It is a chip with all sorts of uses - LED Chasers, input and output multiplexing and more. If you don't have any, order a few, they are about 30 cents each and I can guarantee you will find a use for them.

http://rcarduino.blogspot.com/2012/08/arduino-serial-servos.html

The 4017 Decade counter does exactly what we want -

1) Has ten outputs
2) It only activates one output at a time
3) It automatically figures out which output to turn off and which one to turn on in a repeatable sequence
4) It does this switching across the 10 outputs and all we have to do is simply set an output HIGH then LOW to tell it when to switch to the next row.

Upgrading your keyboard
Your keyboard almost certainly has an existing micro controller inside it. In the example keyboard the micro controller is encased in a blob of white plastic so there is no chance to identify it or reprogram it.

To upgrade the keyboard we want to access the keyboard matrix using our Arduino, in the case of the example keyboard the microcontroller is on a separate board which can easily be removed by desoldering. 

Before - The microcontroller PCB is fitted sideways into the main key matrix PCB.

After - The original micro controller replaced with a set of wires that we can use to access the key matrix from Arduino.


Once the original micro controller is disconnected from the key matrix we can solder in a set of wires in its place, these wires will be used to connect the Arduino and 4017.

The Schematic

Parts List - 
8 * Current limiting resistors ( whatever you have between 500Ohms and 1K Ohms)
8 * 10K Pull down resistors
10 Diodes
1 * 4017 Decade Counter
Connecting wire



The arrangement of resistors shown for pin 4 should be duplicated for the 8 columns (pins 4 to 11). The 10K resistor pulls the input down to ground when the switch is open, without this, the switch would be floating leading to inconsistent readings. The 680 Resistors is in series with the pin and is there to limit the current entering the pin when a key is pressed.

In pictures
Columns
Starting at the bottom of the picture each column is connect to ground through a 10K pull down resistor.

The keyboard columns are connected through the thin white wires in the center of the picture.

These are then connected to the white jumper wires at the top of the screen through 680 Ohm current limiting resistors.

Thats it for the columns







Rows
Starting from the bottom we have the keyboard rows connected through the thin white wires.

Each of these is connected through a diode to an output of the 4017 Decade counter.










The full circuit - 72 Keys from 10 Arduino Pins using just one IC, 16 Resistors and 9 Diodes - 

Later in the series we can introduce a shift register to bring the number of Arduino pins down to 5 from the current 10.

Key Scanning Code


In order to ensure the code is portable between the Arduino Mega, Uno, Leonardo, Teensy and Due, the code makes use of the DigitalIO Library. This is not as fast as using direct port manipulation or the DigitalPin library but it is around four times faster than standard Arduino digitalRead and digitalWrite functions - a useful saving.

The DigitalIO and DigitalPin libraries are the work of Arduino forum user FatLib16 whose name comes from fast SD Card libraries that FatLib16 has also created.

The DigitalIO and DigitalPin Libraries can be downloaded from the following link -

http://code.google.com/p/beta-lib/downloads/detail?name=DigitalPinBeta20120804.zip&can=2&q=

For a complete list of FatLib16's libraries see here -
http://code.google.com/p/beta-lib/downloads/list

Some of these will be interesting to explore in the future as a way to add a sample and playback capability to the 5 dollar keyboard.

Key Scan using DigitalIO Library


 for(unsigned char sRow = 0;sRow <= (KEYS/COL_PINS);sRow++)
  {
    for(unsigned char sCol = 0;sCol < COL_PINS;sCol++)
    {

      // read each column pin using an array of DigitalIO objects, its faster than digital read
      // and portable to UNO, Mega, Leonardo, Teensy, Teensy 3 and Due in the future
      // Direct port manipulation is faster, but not portable.
      if(columnPins[sCol].read())
      {
        unsigned char sKey = (sRow<<3) + sCol;

        // Do something with this key
      }
     }
     clockKeyScanCounter(); // clock the 4017 to scan the next keypad row
   }


The Next Step


 The synth engine is provided by a single oscillator, using the library referenced in this post -


http://rcarduino.blogspot.com/2012/10/arduino-modular-synthesizer-part-one.html

The next version will include a simulated patch panel to configure the oscillator paths for a wider range of sounds and effects.

For a zip file including the complete sketch and modular synth library, message me Duane B on the Arduino Forum.

Duane B

Sunday, October 21, 2012

Arduino UNO - XBee Setup

There are many XBee tutorials out there, but I could not find one that I could apply to my specific Arduino/Shield/Xbee configuration. While this particular configuration may not apply to you, perhaps it will send you in the right direction, so please read on.
Please note, that your XBee shield may require you to perform extra steps, so please do further reading before jumping in head first. I am not an expert, so don't blame me if you fry your board.

The following set of steps worked for me:


Step 1: Download and Install X-CTU

The easiest way to setup your XBee module is via the X-CTU software from Digi. It can be downloaded from their site here:  http://www.digi.com/support/productdetail?pid=3257

  • Select  the Diagnostics, Utilities and MIBs section


  • Download and install the XCTU 32-bit ver 5.2.7.5 installer (do a virus check after download)
  • Please note that this is for Windows platforms only !

  • FYI: We will run the software later



Step 2: Upload the bare-minimum script on your Arduino controller

I perform this step to prevent any previous projects from interfering. This may not be necessary, but I tend to do this before wiring up any of my new projects. This is the script I load onto the Arduino:

Bare Minimum Arduino Sketch:
1
2
3
4
5
6
7
void setup() {

}

void loop() {

}




Step 3: Connect the Shield to Arduino, and XBee to Shield.


  • Disconnect the Arduino UNO from the computer. 

  • Insert the XBee module into the Shield.
  • This is the XBee Shield and XBee module side by side




    This is the XBee module on the XBee Shield


  • The pins from the XBee module should slot into the respective headers on the Shield.


  • Connect the XBee shield to the Arduino

  • The pins from the XBee shield should slot into the respective headers on the Arduino board.



  • Move the little white switch in the corner to USB.

    • There are 2 options - USB mode and XBee mode.
    • We want USB mode which will allow the computer to communicate with the XBee module



Step 4: Connect Arduino to computer, and run the X-CTU Software


  • With the XBee module and Shield connected to the Arduino, and the Shield's white switch in USB mode;  connect the Arduino to the computer using a USB cable.
  • This Arduino board appears as COM7 on my computer.
  • Here is what the X-CTU software looks like when it loads up




  • Press the Test / Query button to see if the computer can talk to the XBee module.
    • If successful, you should see something like this:





  • If you accidentally leave the XBee shield in XBee mode, or if your computer fails to communicate with the XBee module, you may encounter these screens.


If you get a successful Test / Query, then move onto the next step.


Step 5: Read XBee Firmware settings:

We can now try to read the XBee firmware settings by
  • Selecting the Modem Configuration Tab

  • Select the Read button to read the XBee firmware settings


Step 6: Download older version of firmware if necessary

  • If you did not have any issues with the previous step, then continue to step 7, otherwise read on.
  • In my case, the software could not find the firmware file necessary to read the firmware settings.
  • In step 4, I could see that I had the XB24 Modem type, and 10E8 firmware version,

  • I tried downloading new versions, but this did not seem to work, as the computer could not detect any newer updates available. The problem I had, was that I needed an "older version" of firmware to communicate with the XBee module in order to read/write new settings to it.
  • I could not select the firmware version from the drop down boxes, which indicated that this particular firmware version was not installed on my computer. So here is what I did.
    • I went back to the Digi site and selected the Firmware update section.
    • This provided a link to an FTP site with "previous versions" of firmware.





  • I then selected the relevant firmware version from the list, and downloaded the zip file to a logical folder on my hard-drive.  There is no need to unzip the file, because the X-CTU software will look for a zipped file.



  • In the X-CTU software, in the Modem Configurations tab, select the "Download new versions button", and select File. Navigate to the zip-file just downloaded and select it. 


  • The software should tell you that it has updated successfully or something to that effect.
  • The modem type and version should now be an available option in the drop down boxes, however, we will continue where we left off (in step 5) and try an read the firmware settings from the XBee module, by pressing the Read button in the Modem Configurations tab on the X-CTU software.
  • It should look something like this:

TO BE CONTINUED.......

Friday, October 19, 2012

Lap Timer Build Along Part 4 - Adding the IR Detector

The final part of the lap timer build along is also the easiest part, involving only the IR Detector and an optional LED with current limiting resistors.

The previous steps can be found in the project index section of RC Arduino -
http://rcarduino.blogspot.com/p/project-index.html

A good introduction to IR Detectors is provided on the Ada Fruit website here -

http://learn.adafruit.com/ir-sensor

To start our build we need a small section of strip board and a set of three wires for power, ground and IR Out.

These can be soldered to the strip board so each leg of the IR Detector is connected through the copper strips of the strip board to one strand of the cable. In the picture I have used a section of ribbon cable, the cable should be long enough to suite your application, for example long enough to mount the detector on the steering column support of your Kart and allow you to attach the main unit to the steering wheel - don't make the cable longer than necessary it will only get in the way and may pick up interference.

Notice that the IR Detector is facing away from the connecting wires and that there is room for some additional components between the detector and the connecting wires, this is to allow us to add an indicator ID.

Reverse Side View

Next we add a 10K current limiting resistor between the output of the IR Detector and the wire we will be connecting to our Arduino interrupt pin.

For this resistor to have any effect we need to cut the copper track underneath the resistor so that the current has to pass through the resistor, a 3mm or 3.5mm drill bit will do this nicely.

Reverse view showing the cut in the copper track beneath the 10K resistor

Next we need to add the current limiting resistor for our indicator LED, I am using 560, but anything from 500 to 800 Ohms should be fine. This resistor connects from the row with the VCC Pin of the detector to the row below the Vout pin. From here we can also add two short lengths of connecting wire for our indicator LED, on wire should come from the VOut track and another from the track below VOut where we have just added the resistor.

This is to allow use to add an indicator LED which will light whenever the unit receives an IR Signal, this is useful as it will let you know if there is environmental interference such as reflected sunlight or fluorescent lighting.

At this point you should have the following circuit -
You can now add an indicator LED of whatever colour will be most visible in your application. To connect the LED, the long leg should be connected to the length of wire which is soldered to the same row as the 560 Ohm resistor, the shorted leg should be connected to same the same row as the Vout pin of the IR Detector.

You can test this set up immediately by connecting 4 to 6 volts to the circuit, the positive voltage should be applied to the top row, the ground should be connected to the middle row. If you use a TV Remote or the Transponder from part 3 of the build along, you should see the LED Light, if not, try swapping the LED around incase it was soldered in reverse.

This is essentially the same circuit as shown in the Lady Ada tutorial -

http://learn.adafruit.com/ir-sensor/testing-an-ir-sensor

Assuming that you have tested your detector correctly, we can now connect it to the Lap Timer.

To do this we need to connect the Vcc wire (top pin/wire in the picture assuming the detector is facing away from the connecting wires) to the 5V supply of the Arduino. Next we need to connect the center wire to the ground of the Arduino. Finally connect the bottom wire to digital pin 2 of your Arduino.

Congratulations, you have now finished the electronics however to be able to use the lap timer you need to build a small enclosure for the detector, without this sunlight and many type of indoor lighting will saturate your detector so that it is unable to detect signals. As I am based in Dubai where the sun is always fierce I have gone as far as spraying the inside of my enclosure with ultra matt camouflage paint. You can see the enclosures I have used in the following clips of the timer in action, not that the indicator LED is on the outside of the enclosure where we can see it - you knew that already right ?

Build Along Lap Timer in action complete with IR Detector as built in this post


I have recently added a few extensions to the project including a count down mode and support for external audio, you can see the new menu options and see them in action at the track in the following two clips -


New Menu Options
At the track with external audio enabled


The external audio option uses an LM386 based amplifier to drive external speakers. You can use this IC to add big sound to any Arduino project, here is a link to the circuit as used in the Lap Timer -

http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html

If you would like the latest code, contact me through the arduino forum for a zip file containing the full project.

Future Developments - I am considering adding support for three additional transponder types -

1) Magnetic - I am told that many Kart Tracks use a magnetic strip under the track which lapping Karts detect using a window sensor such as you would use for home security.
2) Commercial Beacons - The commercial beacons used at many auto racing tracks use a well know pattern of pulses, it would make sense to support this pattern allowing users to 'arrive and drive' without having to place your own transponder around the track.
3) User selected pulse length - Allow the user to choose a pulse length, this would allow two or three systems to be used alongside each other. All of the calculations needed to build your own unique transponder are linked in previous parts of build along.

Stay tuned.

Duane B

Saturday, October 13, 2012

Algorithmic Music Visualizer

This is a short follow up to a previous post 'Algorithmic Music On Arduino'

http://rcarduino.blogspot.com/2012/09/algorithmic-music-on-arduino.html

The music generation algorithms take advantage of the repeating structure within binary numbers, an obvious development is a visualizer that would reveal some of the structure within the music.

This quick project uses the value of the algorithm output to drive the sound and visuals directly. The visualizer is simply a scrolling buffer of the 8 most recent output values.


While it would be possible to use FFT to visualize the music, in the case of algorithmic music it is more interesting to use the visualizer as a means to explore what is happening within the algorithm rather than in the frequency spectrum.



The short video shows that even with no additional processing, the visuals are very different for each of the algorithms and reveal some of the structure within the music.

The first algorithm running from 0:11 to 0:38 produces a cascade effect reminiscent of the matrix - unfortunately this effect is subtle and needs development for the best effect. The second section of the video provides a more obvious demonstration of how the sound is being developed within the algorithm.

Some other ideas to explore in the future -

1) Bit rate - for each bit in the 8 bit output, increment a counter if it has changed from the last sample. Use the values for the 8-bits to drive the display

2) Divide by 2 - Use the current output for row 0, update row 1 every second output, row 2 every fourth output, row 3 every 8 etc upto 128

For now, here is a scrolling buffer of the 8 most recent output values (and a little reaction from the family)


Duane B

Wednesday, October 10, 2012

New Arduino Products and Video Tutorials

UK Based electronics supplier RS-Online have added a new Arduino section to the home page of thier site.

Clicking on the links will take you to this section with an overview of the new starter kit, some video demonstrations and datasheets.

These are the 15 projects included in the new official Arduino starter kit -

01 GET TO KNOW YOUR OWN TOOL an introduction to the concepts you'll need to use this kit
02 SPACESHIP INTERFACE design to control panel for your starship
03 LOVE-O-METER measure how hot-blooded you are
04 COLOR MIXING LAMP produce any color with a lamp that uses light as an input
05 MOOD CUE clue people in to how you're doing
06 LIGHT THERIM create a musical instrument you play by waving your hands
07 KEYBOARD INSTRUMENT play music and make some noise with this keyboard
08 DIGITAL HOURGLASS a light-up hourglass that can stop you from working too much
09 MOTORIZED PINWHEEL a color wheel that will have your head spinning
10 ZOETROPE create a mechanical animation you can play forward or reverse
11 CRYSTAL BALL a mystical tour to answer all your tough question
12 KNOCK LOCK tap out the secret code to open the door
13 TOUCHY-FEEL LAMP a lamp that responds to your touch
14 TWEAK THE ARDUINO LOGO control your personal computer from your Arduino
15 HACKING BUTTONS create a master control for all your devices!

More information including the video tutorials can be found here -

http://uk.rs-online.com/web/generalDisplay.html?id=arduino

Arduino Due

While the site does not mention the upcoming Arduino Due, there is a 'stay tuned for Arduino' email notification which you can subscribe to in the link above. If RS have any marketting sense I am sure they will use this mailing list to announce the availability of Due or pre orders.

So if you are UK based Stay Tuned.

UPDATE: The Arduino team have started a new thread on the Arduino forum for the newly released started kit - http://arduino.cc/forum/index.php/topic,125444.0/topicseen.html


Duane B

Sunday, October 7, 2012

Arduino Modular Synthesizer - Part One

The RC Arduino site has featured a number of Audio projects recently, and while they all produce very different sounds they all use the same basic technique to generate their sound.

With this in mind an obvious development of these projects would be a modular synthesizer which allows the user to easily combine the techniques used by individual projects to produce new instruments.

Modular Synthesizer
A modular synthesizer is traditionally a large analogue device often including a mix and match of vintage components. Early models were often enormous DIY machines that could easily take up a whole wall of a room.

Current models are still large in comparison to digital synthesizers this is partly due to the physical user interface which is also what gives these machines their character.

A typical modular synthesizer user interface - Analog inputs to control the module oscillators and a patchwork of wires to direct the signals between different modules.
Original photo by Nina Richards


Arduino as a Digital Modular Synth ?

While the Arduino is a digital device, the Auduino and Illutron B projects both demonstrate the appeal of using analog inputs to control an Arduino based digital synthesizer.

AuduinoIllutron B

Auduino Features - 
A grain synthesizer which uses two triangle waveforms with independently controlled frequency and decay to create a more complex waveform. A fifth control is then used to set the pitch by controlling the repetition rate of the complex waveform.

The tone of the waveform can be dramatically altered by adjusting the frequencies of the two component waveforms.


Illutron B Features
A wavetable synthesizer which uses pre calculated waveforms stored within the program memory. These waveforms are played back at different speeds to produce sound at the required pitch. To further increase the musical quality of the waveforms, they are combined with an envelope, this describes how the sound developes over time and can provide an approximation of many musical instruments.




More information and examples of both of these projects can be found here -

http://rcarduino.blogspot.com/2012/08/the-must-build-arduino-project-illutron.html
http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html

Taking it further
 
The goal of this series of posts is to extend these ideas by -

1) Building a set of software components that will provide digital versions of the most common modules found in a modular synthesizer.


2) Building a simple, low cost simulated patch panel to direct how the digital sound will be built and processed by the software running on the Arduino.

Progress so far

Below are two examples of vastly different Arduino synthesisers built for this project with the same four oscillators connected in different ways - in effect this is a demonstration of the concept of an Arduino modular synthesiser.

XLT2 - eXtremely Limited Techno Toy


 
The eXtremeley Limited Techno Toy or XLT2

As an initial experiment I wanted to build something that could be built from a few common components and that would be able to generate a reasonable range of new sounds.

The first result is the XLT2 - eXtemeley Limited Techno Toy.

An added bonus is that this synth and the Gated Grain Synth below are both directly compatible with the Auduino synth circuit, so if you have never built an Auduino before, now you have two more reasons to build one.

The XLT2 features two audio oscillators that are able to generate square, ramp or sine waveforms at audio frequencies.

These audio waveforms are then combined with individual low frequency oscillators ( LFOs ) which again generate square, ramp or sine waves but at much lower frequencies. These low frequency waveforms may not be audible on their own, but when we direct them to control the amplitude (volume) of an audio signal they can add a rythm, pulse or vibrato effect.

Each LFO also has an associated LED which reflects the LFO output both in frequency and profile - i.e. a squarewave will switch the LED hard on, then hard off whereas a sinewave will fade on and fade off reflecting the effect the LFO has on the audio signal.

A final control is provided to mix the two channels together according to a user selected ratio.

The Gated Grain Synthesizer

Once the XLT2 was up and running I wanted to try creating a similar sound to the Auduino using the same four oscillators as the XLT2, the grain synthesizer is the result.

This synth can produce a sound similar to the Auduino or even a passable violin if a sinewave is used as the output gate.

The main feature of the Gated Grain synth is that it sounds very different from the XLT2 but is built from the same four oscillators driving different aspects of the audio signal.

Proof of concept

The difference in sound between these two devices demonstrates the potential for an Arduino based synthesiser which allows the end user to configure the sound generation path using a simulated patch pannel. A follow up post will explore some of the options for implementing the patch pannel and how the relevant routing can be implemented at run time in software.

Getting Serious and moving to the Arduino Due

While this project has lots of potential for development the limited processing power of the 8-bit 16Mhz Arduino UNO will only get us so far.

The soon to be release Arduino Due platform however will allow the idea to be taken much further.

The Due has a built in analog to digital converter giving a true analog output rather than the UNOs PWM output, this together with higher resolution should provide far better sound quality.

The higher processing speeds of the Arduino Due will also allow for more oscillators and more importantly - complex relationships between the oscillators. It should also be possible to implement some software filters.

Perhaps the most exciting possibility is the high sampling rate offered by the Due, it may be possible to capture samples directly with the due and use them there and then as part of the synthesizer.




As the project stands, the user must select the waveforms at compile time however an obvious development would be to add a button for each oscillator allowing the user to cycle through the available waveforms. As my intention is to provide this function and others through a simulated patch panel, I will leave it to the user to add these buttons.


Note : The Audio output in the video is through a PC Speaker driven by the simple amplifier outlined in this post -

http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html



#define SAMPLE_MAX (65535.0)
#define SAMPLE_FREQUENCY (8000.0)
#define TIMER1_FREQUENCY 2000000
#define UPDATE_RATE 8000

#define MIDI_NOTE_MAX 127

#define MIX_MIDDLE 127

#define GRAIN1_FREQUENCY_INPUT A0
#define GRAIN1_AMPLITUDE_INPUT A1

#define GRAIN2_FREQUENCY_INPUT A2
#define GRAIN2_AMPLITUDE_INPUT A3

#define LFO_FREQUENCY_INPUT A4
#define LFO_AMPLITUDE_INPUT A5

#define GRAIN1_SHAPE_INPUT 4
#define GRAIN2_SHAPE_INPUT 7
#define LFO_SHAPE_INPUT 8

#define PWM_OUT OCR0A

// Will put this in progmem - eventually
/*PROGMEM*/
unsigned char sineWave[256] =
{
127    ,    //0
130    ,    //1
133    ,    //2
136    ,    //3
139    ,    //4
142    ,    //5
145    ,    //6
148    ,    //7
151    ,    //8
154    ,    //9
157    ,    //10
160    ,    //11
163    ,    //12
166    ,    //13
169    ,    //14
172    ,    //15
175    ,    //16
178    ,    //17
181    ,    //18
184    ,    //19
186    ,    //20
189    ,    //21
192    ,    //22
194    ,    //23
197    ,    //24
200    ,    //25
202    ,    //26
205    ,    //27
207    ,    //28
209    ,    //29
212    ,    //30
214    ,    //31
216    ,    //32
218    ,    //33
221    ,    //34
223    ,    //35
225    ,    //36
227    ,    //37
229    ,    //38
230    ,    //39
232    ,    //40
234    ,    //41
235    ,    //42
237    ,    //43
239    ,    //44
240    ,    //45
241    ,    //46
243    ,    //47
244    ,    //48
245    ,    //49
246    ,    //50
247    ,    //51
248    ,    //52
249    ,    //53
250    ,    //54
250    ,    //55
251    ,    //56
252    ,    //57
252    ,    //58
253    ,    //59
253    ,    //60
253    ,    //61
253    ,    //62
253    ,    //63
254    ,    //64
253    ,    //65
253    ,    //66
253    ,    //67
253    ,    //68
253    ,    //69
252    ,    //70
252    ,    //71
251    ,    //72
250    ,    //73
250    ,    //74
249    ,    //75
248    ,    //76
247    ,    //77
246    ,    //78
245    ,    //79
244    ,    //80
243    ,    //81
241    ,    //82
240    ,    //83
239    ,    //84
237    ,    //85
235    ,    //86
234    ,    //87
232    ,    //88
230    ,    //89
229    ,    //90
227    ,    //91
225    ,    //92
223    ,    //93
221    ,    //94
218    ,    //95
216    ,    //96
214    ,    //97
212    ,    //98
209    ,    //99
207    ,    //100
205    ,    //101
202    ,    //102
200    ,    //103
197    ,    //104
194    ,    //105
192    ,    //106
189    ,    //107
186    ,    //108
184    ,    //109
181    ,    //110
178    ,    //111
175    ,    //112
172    ,    //113
169    ,    //114
166    ,    //115
163    ,    //116
160    ,    //117
157    ,    //118
154    ,    //119
151    ,    //120
148    ,    //121
145    ,    //122
142    ,    //123
139    ,    //124
136    ,    //125
133    ,    //126
130    ,    //127
127    ,    //128
124    ,    //129
121    ,    //130
118    ,    //131
115    ,    //132
112    ,    //133
109    ,    //134
106    ,    //135
103    ,    //136
100    ,    //137
97    ,    //138
94    ,    //139
91    ,    //140
88    ,    //141
85    ,    //142
82    ,    //143
79    ,    //144
76    ,    //145
73    ,    //146
70    ,    //147
68    ,    //148
65    ,    //149
62    ,    //150
60    ,    //151
57    ,    //152
54    ,    //153
52    ,    //154
49    ,    //155
47    ,    //156
45    ,    //157
42    ,    //158
40    ,    //159
38    ,    //160
36    ,    //161
33    ,    //162
31    ,    //163
29    ,    //164
27    ,    //165
25    ,    //166
24    ,    //167
22    ,    //168
20    ,    //169
19    ,    //170
17    ,    //171
15    ,    //172
14    ,    //173
13    ,    //174
11    ,    //175
10    ,    //176
9    ,    //177
8    ,    //178
7    ,    //179
6    ,    //180
5    ,    //181
4    ,    //182
4    ,    //183
3    ,    //184
2    ,    //185
2    ,    //186
1    ,    //187
1    ,    //188
1    ,    //189
1    ,    //190
1    ,    //191
0    ,    //192
1    ,    //193
1    ,    //194
1    ,    //195
1    ,    //196
1    ,    //197
2    ,    //198
2    ,    //199
3    ,    //200
4    ,    //201
4    ,    //202
5    ,    //203
6    ,    //204
7    ,    //205
8    ,    //206
9    ,    //207
10    ,    //208
11    ,    //209
13    ,    //210
14    ,    //211
15    ,    //212
17    ,    //213
19    ,    //214
20    ,    //215
22    ,    //216
24    ,    //217
25    ,    //218
27    ,    //219
29    ,    //220
31    ,    //221
33    ,    //222
36    ,    //223
38    ,    //224
40    ,    //225
42    ,    //226
45    ,    //227
47    ,    //228
49    ,    //229
52    ,    //230
54    ,    //231
57    ,    //232
60    ,    //233
62    ,    //234
65    ,    //235
68    ,    //236
70    ,    //237
73    ,    //238
76    ,    //239
79    ,    //240
82    ,    //241
85    ,    //242
88    ,    //243
91    ,    //244
94    ,    //245
97    ,    //246
100    ,    //247
103    ,    //248
106    ,    //249
109    ,    //250
112    ,    //251
115    ,    //252
118    ,    //253
121    ,    //254
123        //255
};

// should pre calculate these and store them in progmem to save memory
// for prototyping, lets calculate them and use ram
unsigned char rampWave[256];
unsigned char squareWave[256];

// used to convert midi note numbers into the increments required to generate the note in the ISR
PROGMEM unsigned int midiNoteToWavePhaseIncrement[128] =
{
 66 // 0,8.18,66.98,66
,70 // 1,8.66,70.96,70
,75 // 2,9.18,75.18,75
,79 // 3,9.72,79.65,79
,84 // 4,10.30,84.38,84
,89 // 5,10.91,89.40,89
,94 // 6,11.56,94.72,94
,100 // 7,12.25,100.35,100
,106 // 8,12.98,106.32,106
,112 // 9,13.75,112.64,112
,119 // 10,14.57,119.34,119
,126 // 11,15.43,126.43,126
,133 // 12,16.35,133.95,133
,141 // 13,17.32,141.92,141
,150 // 14,18.35,150.35,150
,159 // 15,19.45,159.29,159
,168 // 16,20.60,168.77,168
,178 // 17,21.83,178.80,178
,189 // 18,23.12,189.43,189
,200 // 19,24.50,200.70,200
,212 // 20,25.96,212.63,212
,225 // 21,27.50,225.28,225
,238 // 22,29.14,238.67,238
,252 // 23,30.87,252.86,252
,267 // 24,32.70,267.90,267
,283 // 25,34.65,283.83,283
,300 // 26,36.71,300.71,300
,318 // 27,38.89,318.59,318
,337 // 28,41.20,337.53,337
,357 // 29,43.65,357.60,357
,378 // 30,46.25,378.87,378
,401 // 31,49.00,401.40,401
,425 // 32,51.91,425.27,425
,450 // 33,55.00,450.55,450
,477 // 34,58.27,477.34,477
,505 // 35,61.74,505.73,505
,535 // 36,65.41,535.80,535
,567 // 37,69.30,567.66,567
,601 // 38,73.42,601.42,601
,637 // 39,77.78,637.18,637
,675 // 40,82.41,675.07,675
,715 // 41,87.31,715.21,715
,757 // 42,92.50,757.74,757
,802 // 43,98.00,802.79,802
,850 // 44,103.83,850.53,850
,901 // 45,110.00,901.11,901
,954 // 46,116.54,954.69,954
,1011 // 47,123.47,1011.46,1011
,1071 // 48,130.81,1071.60,1071
,1135 // 49,138.59,1135.32,1135
,1202 // 50,146.83,1202.83,1202
,1274 // 51,155.56,1274.36,1274
,1350 // 52,164.81,1350.13,1350
,1430 // 53,174.61,1430.42,1430
,1515 // 54,185.00,1515.47,1515
,1605 // 55,196.00,1605.59,1605
,1701 // 56,207.65,1701.06,1701
,1802 // 57,220.00,1802.21,1802
,1909 // 58,233.08,1909.38,1909
,2022 // 59,246.94,2022.92,2022
,2143 // 60,261.63,2143.20,2143
,2270 // 61,277.18,2270.64,2270
,2405 // 62,293.66,2405.66,2405
,2548 // 63,311.13,2548.71,2548
,2700 // 64,329.63,2700.27,2700
,2860 // 65,349.23,2860.83,2860
,3030 // 66,369.99,3030.95,3030
,3211 // 67,392.00,3211.18,3211
,3402 // 68,415.30,3402.12,3402
,3604 // 69,440.00,3604.42,3604
,3818 // 70,466.16,3818.75,3818
,4045 // 71,493.88,4045.83,4045
,4286 // 72,523.25,4286.41,4286
,4541 // 73,554.37,4541.29,4541
,4811 // 74,587.33,4811.33,4811
,5097 // 75,622.25,5097.42,5097
,5400 // 76,659.26,5400.53,5400
,5721 // 77,698.46,5721.67,5721
,6061 // 78,739.99,6061.89,6061
,6422 // 79,783.99,6422.36,6422
,6804 // 80,830.61,6804.25,6804
,7208 // 81,880.00,7208.85,7208
,7637 // 82,932.33,7637.51,7637
,8091 // 83,987.77,8091.66,8091
,8572 // 84,1046.50,8572.82,8572
,9082 // 85,1108.73,9082.58,9082
,9622 // 86,1174.66,9622.66,9622
,10194 // 87,1244.51,10194.85,10194
,10801 // 88,1318.51,10801.07,10801
,11443 // 89,1396.91,11443.33,11443
,12123 // 90,1479.98,12123.79,12123
,12844 // 91,1567.98,12844.71,12844
,13608 // 92,1661.22,13608.50,13608
,14417 // 93,1760.00,14417.70,14417
,15275 // 94,1864.65,15275.02,15275
,16183 // 95,1975.53,16183.31,16183
,17145 // 96,2093.00,17145.63,17145
,18165 // 97,2217.46,18165.16,18165
,19245 // 98,2349.32,19245.31,19245
,20389 // 99,2489.01,20389.70,20389
,21602 // 100,2637.02,21602.14,21602
,22886 // 101,2793.83,22886.67,22886
,24247 // 102,2959.95,24247.58,24247
,25689 // 103,3135.96,25689.42,25689
,27216 // 104,3322.44,27216.99,27216
,28835 // 105,3520.00,28835.39,28835
,30550 // 106,3729.31,30550.04,30550
,32366 // 107,3951.06,32366.63,32366
,34291 // 108,4186.01,34291.26,34291
,36330 // 109,4434.92,36330.32,36330
,38490 // 110,4698.64,38490.65,38490
,40779 // 111,4978.03,40779.41,40779
,43204 // 112,5274.04,43204.25,43204
,45773 // 113,5587.65,45773.32,45773
,48495 // 114,5919.91,48495.14,48495
,51378 // 115,6271.92,51378.79,51378
,54433 // 116,6644.87,54433.96,54433
,57670 // 117,7040.00,57670.76,57670
,61100 // 118,7458.62,61100.07,61100
,64733 // 119,7902.13,64733.26,64733
,3046 // 120,8372.02,68582.53,3046
,7124 // 121,8869.84,72660.64,7124
,11445 // 122,9397.27,76981.30,11445
,16022 // 123,9956.06,81558.77,16022
,20872 // 124,10548.07,86408.50,20872
,26010 // 125,11175.30,91546.65,26010
,31454 // 126,11839.81,96990.28,31454
};

class CSynth
{
public:
  volatile static unsigned char* m_pWaveForm1;
  volatile static unsigned char* m_pWaveForm2;
  volatile static unsigned char* m_pLFOShape1;
  volatile static unsigned char* m_pLFOShape2;
  volatile static unsigned int m_nWaveForm1Accumulator;
  volatile static unsigned int m_nWaveForm1Increment;
  volatile static unsigned int m_nWaveForm2Accumulator;
  volatile static unsigned int m_nWaveForm2Increment;
  volatile static unsigned int m_nLFOAccumulator1;
  volatile static unsigned int m_nLFOIncrement1;
  volatile static unsigned int m_nLFOAccumulator2;
  volatile static unsigned int m_nLFOIncrement2;
  volatile static unsigned char m_sLFOType;
  volatile static unsigned char m_sMix;
  static void startAudio()
  {
     TCCR1A=0x0;          // set the timer prescaler to 8 = 16/8 = 2MHz
     TCCR1B=0x02;          // set the timer prescaler to 8 = 16/8 = 2MHz
     TIMSK1 |= (1<<OCIE1A);   // Enable output compare match interrupt on OCR1A
    
     TCCR0A=0B10110011;                                    //-8 bit audio PWM
    //TCCR0A=0x83;          // Set timer waveform generation mode to FAST PWM, clear OC0A On match, set at bottom - OC0A = digital pin 6.
    TCCR0B=0x01;          // Set to clock frequency, no prescaler
    OCR0A=127;            // set in the middle - do we need this ? probably not.
    DDRD|=1<<6;          // Set digital pin 6 to output - channels 2 and 3
  }
  static void triggerMidiNote(unsigned char sNote)
  {
    if(sNote < MIDI_NOTE_MAX)
    {
      m_nWaveForm1Increment = (pgm_read_word(midiNoteToWavePhaseIncrement + (sNote)));
    }
  }
};

volatile unsigned char* CSynth::m_pWaveForm1;
volatile unsigned char* CSynth::m_pWaveForm2;
volatile unsigned char* CSynth::m_pLFOShape1;
volatile unsigned char* CSynth::m_pLFOShape2;
volatile unsigned int CSynth::m_nWaveForm1Accumulator;
volatile unsigned int CSynth::m_nWaveForm1Increment;
volatile unsigned int CSynth::m_nWaveForm2Accumulator;
volatile unsigned int CSynth::m_nWaveForm2Increment;
volatile unsigned int CSynth::m_nLFOAccumulator1;
volatile unsigned int CSynth::m_nLFOIncrement1;
volatile unsigned int CSynth::m_nLFOAccumulator2;
volatile unsigned int CSynth::m_nLFOIncrement2;
volatile unsigned char CSynth::m_sLFOType;
volatile unsigned char CSynth::m_sMix;
 
CSynth synth;

// iterate the grains and LFO
SIGNAL (TIMER1_COMPA_vect)
{
  OCR1A += (TIMER1_FREQUENCY/UPDATE_RATE);

   // cross fade between the two channels
  unsigned char sAmplitude1 = 0;
  unsigned char sAmplitude2 = 0;
 
  if(synth.m_sMix < 127)
  {
    sAmplitude1 = synth.m_sMix << 1;
    sAmplitude2 = 255;
  }
  else if(synth.m_sMix > 127)
  {
    sAmplitude2 = (255-synth.m_sMix) << 1;
    sAmplitude1 = 255;
  }
 
  OCR0B = OCR0A =
  (((((
    (*(synth.m_pWaveForm1 + ((synth.m_nWaveForm1Accumulator+=synth.m_nWaveForm1Increment)>>8))) *
    (*(synth.m_pLFOShape1 + ((synth.m_nLFOAccumulator1+=synth.m_nLFOIncrement1) >> 8)))) >> 8) *
    sAmplitude1) >> 8) +
   ((((
    (*(synth.m_pWaveForm2 + ((synth.m_nWaveForm2Accumulator+=synth.m_nWaveForm2Increment)>>8))) *
    (*(synth.m_pLFOShape2 + ((synth.m_nLFOAccumulator2+=synth.m_nLFOIncrement2) >> 8)))) >> 8) *
    sAmplitude2) >> 8)) >> 1;   

}

void setup()
{
 pinMode(GRAIN1_SHAPE_INPUT,INPUT);
 pinMode(GRAIN2_SHAPE_INPUT,INPUT);
 pinMode(LFO_SHAPE_INPUT,INPUT);

 digitalWrite(GRAIN1_SHAPE_INPUT,HIGH);
 digitalWrite(GRAIN2_SHAPE_INPUT,HIGH);
 digitalWrite(LFO_SHAPE_INPUT,HIGH);

 Serial.begin(9600);

  for(int nIndex = 0;nIndex < 256;nIndex ++)
 {
   rampWave[nIndex] = 255 - nIndex;
   squareWave[nIndex] = (nIndex < 127) ? 0 : 255;
 }

 synth.m_pWaveForm1 = sineWave;
 synth.m_pWaveForm2 = sineWave;
 synth.m_pLFOShape1 = rampWave;
 synth.m_pLFOShape2 = squareWave;
 synth.m_sMix = MIX_MIDDLE;
 synth.startAudio();
}
void loop()
{
 synth.triggerMidiNote(analogRead(1)>>3);
 synth.m_nWaveForm2Increment = analogRead(2)<<5;

 // the LFO Oscilators control the volume of the two waveforms, a low value
 synth.m_nLFOIncrement1 = analogRead(3) >> 2;
 synth.m_nLFOIncrement2 = analogRead(4) >> 2;

 // this mixes the two channels a low value will shut off one channel, a high value will shut off the other
 // anywhere in between will generate a proportional mix of the two channels
 synth.m_sMix = analogRead(5) >> 2;
}