Piezo Disc with sensitivity potentiometer

Hi All,

I wonder if anyone can help me. I've got a project that I'm currently building which requires the use of a piezo disc, Neopixel strip, arduino Nano. What I'm building is a touch sensor that responds to the slightest of touches of the piezo disc.

What I'm finding though at the moment is that the lights are flashing regardless of touching/not touching the piezo disc itself. I want to adjust the sensitivity of the disc so that I can dial in the sensitivity of the disc so that the lights only flash in the programmed sequence when the disc is touched.

Is it just a question of instead of using a fixed resistor between the wires on the piezo disc, use a potentiometer e.g 50K instead or still use a fixed value resistor e.g 47k and a potentiometer to make a divider to dial in the sensitivity?

Is this possible at all?

Thanks

Brett

Interesting problem, post both your annotated schematic that shows exactly how you have wired it and your code. Note on the schematic power sources and any lead over 10"/25cm in length.

Hi,

At the moment I'm using the example Knock sketch just to start with. However this is how I've got it wired up at the moment. All with short wires none over 10" in length.

I have tried different value resistors but still have have intermittent flashing on the neopixels regardless if the pizeo disc is touched our not.

When struck, piezos output high voltages at very tiny currents, so a series resistor won't make much difference. I suggest to try connecting a voltage divider in parallel with the piezo (e.g. 1 Megohm potentiometer), which works like a volume control to adjust the peak voltage presented to the ADC input.

Connect the piezo between the pot terminal marked "+5V" and GND.

The problem is simple assuming your schematic is correct. Vin needs about 7.5 volts. 3V8 is just about enough voltage to allow it to do goofy things and not run code properly. Go to an 8-12V source for Vin.

Please post the code.

Say how you are powering the neopixel strip, and how many actual pixels are on it.

a7

Hi,

I've been playing about today and managed to get the potentiometer to adjust the threshold value and I've changed the wiring of it slightly. It now uses different pins as can be seen from the code below:

/*
  Knock Sensor

  This sketch reads a piezo element to detect a knocking sound.
  It reads an analog pin and compares the result to a set threshold.
  If the result is greater than the threshold, it writes "knock" to the serial
  port, and toggles the LED on pin 13.

  The circuit:
	- positive connection of the piezo attached to analog in 0
	- negative connection of the piezo attached to ground
	- 1 megohm resistor attached from analog in 0 to ground

  created 25 Mar 2007
  by David Cuartielles <http://www.0j0.org>
  modified 30 Aug 2011
  by Tom Igoe

  This example code is in the public domain.

  https://www.arduino.cc/en/Tutorial/BuiltInExamples/Knock
*/

#include <Adafruit_NeoPixel.h>
#define PIXEL_PIN    6  // Digital IO pin connected to the NeoPixels.

#define PIXEL_COUNT 14  // Number of NeoPixels

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)

// these constants won't change:
// const int ledPin = 13;      // LED connected to digital pin 13
const int knockSensor = A5; // the piezo is connected to analog pin 0
const int threshold = A0;  // threshold value to decide when the detected sound is a knock or not


// these variables will change:
int sensorReading = 0;      // variable to store the value read from the sensor pin
int ledState = LOW;         // variable used to store the last LED status, to toggle the light
int thresholdReading = map(threshold, 0, 1023, 1, 255);

void setup() {
  pinMode(PIXEL_PIN, OUTPUT); // declare the ledPin as as OUTPUT
  Serial.begin(9600);       // use the serial port
  strip.begin(); // Initialize NeoPixel strip object (REQUIRED)
  strip.show();  // Initialize all pixels to 'off'
}

void loop() {
  // read the sensor and store it in the variable sensorReading:
  sensorReading = analogRead(knockSensor);
  thresholdReading = analogRead(threshold);
  Serial.println(sensorReading);
  Serial.println(thresholdReading);

  // if the sensor reading is greater than the threshold:
  if (sensorReading >= thresholdReading) {
    colorWipe(strip.Color(255,   0,   0), 50);    // Red
    // toggle the status of the ledPin:
    ledState = !ledState;
    // update the LED pin itself:
    digitalWrite(PIXEL_PIN, ledState);
    // send the string "Knock!" back to the computer, followed by newline
    Serial.println("Knock!");
    //colorWipe();
  }
  delay(100);  // delay to avoid overloading the serial port buffer
  colorWipe(strip.Color(  0,   0,   0), 50);    // Black/off
}

// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
  for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
    delay(wait);                           //  Pause for a moment
  }
}

The code is based on the knock example code included with Arduino IDE. I have modified it with neopixel code from buttonCycler example included with the adafruit neopixel library. I am running everything from the USB providing 5v as the moment but my project is going to be using a 3.7v Li-ion cell with a TP4056 and a MT3068 Boost module to boost the voltage to 5v and this will power the Nano and the neopixels.

The problem I've got now is that the code is running but the piezo disc isn't really sensitive to touch. In order to get anything out of it at all i'm having to really tap on it to get any sort of reading out of it. I'm currently using 27mm discs. Would increasing the size of the disc help with this? I have noted that without the 1M resistor the value of the piezo disc increases over time, so I'm guessing it acts like a capacitor and when I tap on it that value decreases and starts to build up again. I have tried with different value resistors but to no effect really.

The idea of this is that the piezo disc will sit on the underneath of the lid of a 3d printed enclosure and the neopixels would light up if it is touched at all. This is why it needs to be sensitive to the slightest touch.

Hope this helps.

This

  delay(100);  // delay to avoid overloading the serial port buffer

is binding you to the brief signal, perhaps.

Good news: there is no such thing as overloading the serial buffer.

You could use a 21st century baud rate

  Serial.begin(115200);       // use the serial port

If you want spam control, print only when what would be printed is different since last time. or only if the value is over some threshold.

  static int lastSensorReadingPrinted = -1; // impossible initial value

  if (sensorReading != lastSensorReading) {
    Serial.print("sensor "); Serial.pritn(sensorReading);
    Serial.print("      threshold "); Serial.pritn(thresholdReading);
    Serial.println(thresholdReading);

    last SensorReadingPrinted = sensorReading;
  }

The idea is to make it so the sensor is being very rapidly and continuously read.

If that makes progress, there may be a better way to keep a close eye on the sensor. But all the time you do anything else, it is blinded. The delay(100) is a killer - you are blind 99.99 percent of the time. Or worse.

HTH

a7

A piezo disk is a capacitor, typically 1-2 nF, but proportional to the ceramic disk area.

When the sensor is struck, the voltage peaks produced are very narrow and fast, and to detect them your code must read the analog input as fast as possible. Report an event only when a threshold is exceeded.

Something along these lines, which will read the analog pin a few thousand times each second.

void loop() {
  sensorReading = analogRead(knockSensor);
  if (sensorReading >= threshold) {
     digitalWrite(LED_BUILTIN,1);
     delay(20); //just enough delay that the flash is visible
     }
  else digitalWrite(LED_BUILTIN,0);
}
1 Like

You also need a pull-down resistor to keep the input from floating to an unknown value. I suspect this is your root problem.

For a Piezo it should usually be in the range of 1M -10M and wired between the analog input and ground, or in parallel with the Piezo.

A potentiometer will serve the same purpose (plus allowing adjustment) and again 1-10M.

I've already got a 1M resistor between the Analog pin (A5 and GND). Im now not getting the constant rising of the value, its now staying stable at 0 with the 1M resistor across it.

Im not getting much reading out of the piezo now when tapped. Its not reading the tap all the time and when it does the signal is being sent back to serial monitor as between 1 and 10.

First time working with Piezo discs so am quite new to them.

I have taken the delay(100); line out completely so now there is no delay there.

There is way, way too much other completely unnecessary code in that loop, and at the glacial serial Baud rate of 9600, it takes 1 millisecond to print a single character. So there are in fact lots of "delays".

Try the minimal loop code posted in #10.

Here is my code now

/*
  Knock Sensor

  This sketch reads a piezo element to detect a knocking sound.
  It reads an analog pin and compares the result to a set threshold.
  If the result is greater than the threshold, it writes "knock" to the serial
  port, and toggles the LED on pin 13.

  The circuit:
	- positive connection of the piezo attached to analog in 0
	- negative connection of the piezo attached to ground
	- 1 megohm resistor attached from analog in 0 to ground

  created 25 Mar 2007
  by David Cuartielles <http://www.0j0.org>
  modified 30 Aug 2011
  by Tom Igoe

  This example code is in the public domain.

  https://www.arduino.cc/en/Tutorial/BuiltInExamples/Knock
*/

#include <Adafruit_NeoPixel.h>
#define PIXEL_PIN    6  // Digital IO pin connected to the NeoPixels.

#define PIXEL_COUNT 14  // Number of NeoPixels

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)

// these constants won't change:
// const int ledPin = 13;      // LED connected to digital pin 13
const int knockSensor = A5; // the piezo is connected to analog pin 0
const int threshold = A0;  // threshold value to decide when the detected sound is a knock or not


// these variables will change:
int sensorReading = 0;      // variable to store the value read from the sensor pin
int ledState = LOW;         // variable used to store the last LED status, to toggle the light
int thresholdReading = map(threshold, 0, 1023, 1, 255);

void setup() {
  pinMode(PIXEL_PIN, OUTPUT); // declare the ledPin as as OUTPUT
  Serial.begin(115200);       // use the serial port
  strip.begin(); // Initialize NeoPixel strip object (REQUIRED)
  strip.show();  // Initialize all pixels to 'off'
}

void loop() {

  static int lastSensorReading = -1; // impossible initial value

  if (sensorReading != lastSensorReading) {
    Serial.print("sensor "); Serial.print(sensorReading);
    Serial.print("      threshold "); Serial.print(thresholdReading);
    Serial.println("");

    lastSensorReading = sensorReading;
  }
  // read the sensor and store it in the variable sensorReading:
  sensorReading = analogRead(knockSensor);
  thresholdReading = analogRead(threshold);
  //Serial.println(sensorReading);
  //Serial.println(thresholdReading);

  // if the sensor reading is greater than the threshold:
  if (sensorReading >= thresholdReading) {
    colorWipe(strip.Color(255,   0,   0), 50);    // Red
    // toggle the status of the ledPin:
    ledState = !ledState;
    // update the LED pin itself:
    digitalWrite(PIXEL_PIN, ledState);
    // send the string "Knock!" back to the computer, followed by newline
    Serial.println("Knock!");
    //colorWipe();
  }
  //delay(100);  // delay to avoid overloading the serial port buffer
  colorWipe(strip.Color(  0,   0,   0), 50);    // Black/off
}

// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
  for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
    delay(wait);                           //  Pause for a moment
  }
}

Would there be anything else there that you can suggest I remove. I have used the baud rate of 115200 for the serial. When The project is finished I'll remove the serial statements as they are only being used for debugging at the moment.

Thanks for the help.

Brett

Does the setup work to your satisfaction?

It works in principle. When the disc is tapped with a little bit of force the lights come on as per the program. Maybe I'm wanting to much out of it and expecting the impossible but Instead of a forceful tap I want the lights to come on with the slightest of touches.

As mentioned the finished project will be a touch sensor enclosed in a 3d printed enclosure with this piezo disc stuck the base of the lid inside and reaction to the slightest of touches to the lid will turn the lights on.

Why not use a touch sensor?

a7

For a piezo disk to respond to the slightest of touches, you will need an amplifier.

+1 for touch sensor.