Creating an analog "Xylophone" - need suggestions for input sensors

I’ve been wanting to create something very similar to what’s in this video. My initial plan was using piezo sensors to detect hits, and using the following code to deal with all the inputs.

//Xylophone
//Adapted for an ArduinoMega 
//from Jenna deBoisblanc and Spiekenzie Labs initial code

//*******************************************************************************************************************
// User settable variables
//*******************************************************************************************************************

int pinRead;
char pinAssignments[6] ={
  'A0','A1','A2','A3','A4','A5','A6','A7','A8','A9','A10','A11'};
byte PadNote[16] = {
  57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72};         // MIDI notes from 0 to 127 (Mid C = 60)
int PadCutOff[16] = 
{
  400,400,200,800,400,400,400,400,400,400,400,400,400,400,400,400};           // Minimum Analog value to cause a drum hit
int MaxPlayTime[16] = {
  90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90};               // Cycles before a 2nd hit is allowed
#define  midichannel 1;                              // MIDI channel from 0 to 15 (+1 in "real world")
boolean VelocityFlag  = true;                           // Velocity ON (true) or OFF (false)

//*******************************************************************************************************************
// Internal Use Variables
//*******************************************************************************************************************
boolean activePad[16] = {
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};                   // Array of flags of pad currently playing
int PinPlayTime[16] = {
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};                     // Counter since pad started to play
byte status1;

int pin = 0;     
int hitavg = 0;
//*******************************************************************************************************************
// Setup
//*******************************************************************************************************************
void setup() 
{
  Serial.begin(57600);                                  // connect to the serial port 115200

}
//*******************************************************************************************************************
// Main Program
//*******************************************************************************************************************
void loop() 
{
  for(int pin=0; pin < 16; pin++)                          //
  {
    //int pin = 3;
    //   for (pinRead=0; pinRead < 16, pin++){
    hitavg = analogRead(pinAssignments[pin]);  
    //Serial.println(hitavg);   
    // read the input pin

    if((hitavg > PadCutOff[pin]))
    {
      if((activePad[pin] == false))
      {
        if(VelocityFlag == true)
        {
          //          hitavg = 127 / ((1023 - PadCutOff[pin]) / (hitavg - PadCutOff[pin]));    // With full range (Too sensitive ?)
          hitavg = (hitavg / 8) -1 ;                                                 // Upper range
        }
        else
        {
          hitavg = 127;
        }
        MIDI_TX(144,PadNote[pin],hitavg); //note on

        PinPlayTime[pin] = 0;
        activePad[pin] = true;
      }
      else
      {
        PinPlayTime[pin] = PinPlayTime[pin] + 1;
      }
    }
    else if((activePad[pin] == true))
    {
      PinPlayTime[pin] = PinPlayTime[pin] + 1;
      if(PinPlayTime[pin] > MaxPlayTime[pin])
      {
        activePad[pin] = false;
        MIDI_TX(144,PadNote[pin],0); 
      }
    }
  } 
}

//*******************************************************************************************************************
// Transmit MIDI Message
//*******************************************************************************************************************
void MIDI_TX(byte MESSAGE, byte PITCH, byte VELOCITY) 
{
  status1 = MESSAGE + midichannel;
  Serial.write(status1);
  Serial.write(PITCH);
  Serial.write(VELOCITY);

}

Instead of MIDI, I added in a line of code that tells my WAV Trigger to play a certain note based on which piezo was hit.

The code only worked for the first 6 or 7 analog inputs. I had each piezo connected to a 1M Ohm resistor, a common ground, and the 5V lead to an analog port in an Arduino MEGA. While the notes did play the correct sound, there was often noise from other piezos that would randomly activate.

I have a few questions.

  1. Are piezo sensors optimal for this kind of work? If so, what’s the best way to get a signal from a piezo? I’ve heard that piezos work best on some sort of mesh, which I haven’t tried.

  2. What are other options for detecting hard and soft hits? FSRs would do the trick, but they’re $8 a piece; I need around 20 sensors. Piezos are nice and cheap, but they’re currently giving me trouble.

  3. Does the code have any inherent problems? I saw this person had a simila issue.

Any help is appreciated.

The FSR are indeed expensive, there is no way to find cheaper ones. Even at AliExpress they are 6.20 dollars.

There are many ways to detect a hit. For example metal contacts with a spacer in between. Have a look at this: http://www.kobakant.at/DIY/?p=913

I think there is no need to change the hardware. If a piezo is too sensitive, you can alter the sketch. If that doesn't help, you can lower the 1M parallel resistor to 470k.

First you need to fix the sketch: Can you set the sensitivity to 400 for all of them. If they are too sensitive, raise all of them to 600 (or 800 or 1000 or so).

int PadCutOff[16] =
{
  400,400,200,800,400,400,400,400,400,400,400,400,400,400,400,400};           // Minimum Analog value to cause a drum hit

In the code above, there is a 200 and a 800 between the 400. Make them all the same to start with.

This is the main problem:

char pinAssignments[6] ={
  'A0','A1','A2','A3','A4','A5','A6','A7','A8','A9','A10','A11'};

The array size is only 6, but 12 values are declared, and declared in a wrong way. Does the compiler accept that ? That is so weird. Can you make it this:

char pinAssignments[16] = {
  A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11};

If you need 16 inputs, you can add them to that list.

In the original instructables, there is comment by user carter714 at 2014-05-30 telling about some changes. It is worth reading.

Thanks for the comprehensive reply! I hadn’t seen that comment by carter714 before, it looks very helpful. I’ll try out your suggestions and let you know how they worked.

Declaring those pin assignments as char looks just weird to me. It will work but I'd always use byte or uint8_t for that (or even short int).

Those single quotes are indeed also wrong. That's another thing. Normally a single quote goes around a single character, not two characters.

The assignments A0, A1, etc are actually macros and in code are replaced by a number designating the pin when you compile your code. That's another reason you should not use quotes around them, which makes the compiler interpret it as characters.

All in all indeed quite surprising this can compile, and that it even works a bit.

did anyone could resolve this? I get the same problem,


123123:11:64: error: too many initializers for 'char [6]' 'A0','A1','A2','A3','A4','A5','A6','A7','A8','A9','A10','A11'}; ^ exit status 1

too many initializers for 'char [6]'

I experimented with piezos as touch sensors with a very different circuit than yours. Along the way I found that diodes and BJTs can take the piezo spikes, they tend to flatten and lengthen them. LEDs (diodes) will flash without apparent harm.

I connected the piezo leads to 2 diodes each, one pointing Out and one pointing In. The In diodes connect to GND and the Out diodes I had go to 2 different BJT's to give me press and release on different pins. Each BJT collector got 5V through 2.2K ohms and the emitters went to INPUT pins with no pulldown.

When the piezo was hit or pressed the matching BJT filled the wire between emitter and pin with charge, even a very soft touch raised the pin to HIGH. A digital read eats 1 microamp, the code read those pins at about 50KHz (I use a loop() counter to know.) and how many reads to take the pin LOW was my analog measure of force. A very light touch would take 100 or less reads and a hard smash with a screwdriver might get 3000 or so. Releasing the piezo did the same.

You could wire the piezo to a full-wave rectifier (takes 4 diodes) and feed the output to an opto-isolator. The opto output is a switch you can wire between a pin set INPUT_PULLUP and GND, the Arduino pin will be safe and the circuit will have no bounce and the ON duration will be analog to hit force. Just don't use pulseIn() if you want to be able to read more than one piezo struck at the same time.

You could play such a thing with fingers instead of mallets.

The Youtube video named Ants, Amplified to see just how sensitive piezo disks are.

You could also use capacitance if strikes could make 2 connected metal plates get closer to each other.

If you have 2 unconnected metal plates with graphite in between, this is the basis of the Edison Carbon Microphone invented by Frank Dyer (Edison worker) and credit-heisted by the big vain dummy Edison himself. When the graphite is compressed -at all- the resistance through it drops, when it's loose the graphite has high resistance. It works well enough for telephone mics.

You could use IR-reflect ala industrial proximity sensor sensing through a hole in the surface to be struck.

There's more ways, be sure.

unodosis: did anyone could resolve this? I get the same problem,


123123:11:64: error: too many initializers for 'char [6]' 'A0','A1','A2','A3','A4','A5','A6','A7','A8','A9','A10','A11'}; ^ exit status 1

too many initializers for 'char [6]'

Start by fixing that number.

Secondly, that are 2-3 character strings. That doesn't fit in a single char, and then they require double quotes.

Thirdly, I don't see a variable name there.

Fourthly, I have the feeling you actually don't want char arrays here in the first place.

Finally, you should really read the thread you post to first. Carefully. The answer you're probably looking for is in #1.

By the way, there's no need to revive a necrothread for this, really. Better start your own and in it refer to this thread if you believe it's really the same topic. Of course after reading both this thread and the forum sticky, which you quite obviously also didn't read - or if you did, ignored the content of.