Haptic Feedback Example Applications: Rugged Piezoelectric Passcode System
These examples are provided as guides to using the Haptic Feedback Evaluation Kit for potential real-world applications. As discussed at the end, these are not full real-world solutions, but a useful starting step!
They outline the approach taken to solve the problem. They are not a guide to programming on the Arduino, therefore some prior knowledge of the Arduino language will be useful. Try the Arduino Learning or Reference pages for help with specific functions.
Piezoelectric materials produce a voltage when they are physically manipulated. Piezoelectric sensors use these materials and this effects to enable a range of measurements, such as changes in pressure or force. This makes them suitable inputs for a range of applications, they are commonly used to sense vibration. They can tell if a fan is operating correctly (picking up air movement) or if ball bearings are wearing down (excessive movement in the system).
Some of the more visible day-to-day applications include:
- Electronic dartboards (scoring)
- Musical instruments (pick-ups)
- Vending machines (item delivery)
- Strain gauges
- Inkjet printers (ejecting the ink)
In this example, we are suggesting their use as a switch or touch button. We have described it as ‘rugged’ because piezoelectric sensors are not compromised by the ingression of moisture or dust - like normal contact switches. This makes them a suitable low-cost alternative to capacitive touchscreens in products where the user interface is fixed, such as with white goods like microwaves.
However piezoelectric sensors are very flat and have no physical response, making it hard for the user to tell if they have pressed them. This is a perfect scenario for haptic feedback, where vibrating the surface can provide confirmation to the user.
To make pressing the buttons a bit more interesting, we’ve suggested their use in a passcode system. The user enters the correct code to gain access, again we can make use of the haptic feedback by providing information about the status of the attempt - e.g. one effect for successful unlocking and another for the incorrect code.
- 1 x Haptic Feedback Evaluation Kit
We’ve used spare pins on the Arduino to accept inputs from the sensors, which means the total amount is limited. We’re only using 3 buttons if you wanted to replicate something like a numeric keypad you could either use a different Arduino (like the Mega) or a second Arduino to handle the unlocking functionality and the kit to provide the haptics.
- 1 x Breadboard
The easiest way to connect the resistors and sensors.
- 3 x 1 MΩ Resistors
Or an equivalent selection of resistors, we used 9 x 330 kΩ resistors - 990 kΩ for each sensor.
- 3 x Piezoelectric Sensors
These can range quite significantly in price, the cheapest are sufficient for picking up button presses. If you intend to use them as a more advanced sensor, e.g. music pick-up, you may wish to order from a reputable supplier.
This software is built from the Development Mode. You can download the code used in this example in the top right of the page.
NOTE: Piezoelectric sensors don’t have much current, but can produce very high voltages to the microcontroller! They’re perfectly safe, but it is important to add in a parallel resistance as described or you run the risk of damaging your Arduino!
In order to adapt to as many applications as possible, we’ve kept the design very simple and open.
The user must press the correct button sequence to open the system, each button press receives the familiar short ‘click’ as confirmation when the correct code is entered a triple click communicates that the system is unlocked. In the event of the incorrect code being entered, the user receives an error ‘buzz’ so they know they were unsuccessful and to start again.
We are using three piezoelectric sensors to represent three different buttons. These behave exactly like a normal push switch - when the sensor is pressed, the piezoelectric material produces a voltage which we can read using the Arduino. This simulates a push switch connected to a ‘logic high’ voltage being closed, upon release the switch returns to its normal open state.
We can now remove a lot of the complexity from the sensing circuit. We are not concerned with factors such as the force of input or frequency detected. As mentioned earlier, the piezo sensors can be used in a range of applications because they can provide this information. However we are only concerned with the question ‘has the button been pressed?’, any reading on the sensor should trigger as an input.
We’ll play the haptic responses in the grip, in the real scenario the haptic actuator would be located in the same unit as the piezo sensors. If you wanted to simulate this, you could attach the sensors to the grip itself - simply electrical tape would suffice for testing.
To summarise, there are 3 instances where we will play a haptic effect (using the LRA C10-100, library 6):
- Every time a ‘button’ is pressed - effect 24, Sharp Tick 1 100%
- When the incorrect code is entered - effect 47, Buzz 1 100%
- When the correct code is entered - effect 12, Triple Click 100%
If you prefer to use different effects you can test them in Engineering Mode and look up all the different ones available in the Haptic Effect Table resource here.
First, as we need to access the Arduino’s spare pins, it is best to remove the haptic shield and connect the required pins manually. Refer to the Pin Mapping resource and connect the pins marked Red (DRV2605) and Blue (Haptic Grip).
Without the OLED screen or the touch driver connected, we have plenty of pins to choose from when connecting the piezo sensors. We used pins 2, 3 and 12 but you may wish to change these - just remember to edit the code accordingly.
Connect 1 MΩ resistance in parallel with each sensor. Piezo sensors are polarised, connect the black terminal to ground and the positive terminal to your chosen Arduino pin. If you’re following our wiring exactly, we connected the leftmost sensor to pin 2 and designated it the ‘first’ sensor, i.e. equal to a ‘1’ in the passcode.
Thankfully the sensors are extremely easy to implement. A simple
digitalRead(); of each of the pins (lines 70-72) will write a ‘1’ to the sensor value variable if the button is pressed. Otherwise, it remains low or ‘0’. From there a simple if() statement can be used to check the value of each sensor.
There are two caveats though. First, if more than one sensor is pressed at once (or more likely one of the sensors gives an erroneous reading) then the ‘lowest value’ sensor takes priority. For example, sensor 1 and sensor 3 are both high, the system records sensor 1 being pressed and ignores sensor 3.
The second issue is what is known as ‘switch debouncing’. This is where the system records multiple presses of the button when the user has only performed one action. Mechanical switches suffer from this problem too, and the Arduino IDE provides a debounce example tutorial. In this instance, adding in a simple delay between readings is sufficient, line 93 delay(100); can be adjusted if you still get multiple readings. Be careful, make this too long and the user could get frustrated - they expect efficiency!
The difficult part of the code comes from the passcode system. It is actually a fairly simple process and could be accomplished in a number of different ways, here’s how ours works:
The passcode is stored in an array (helpfully named
passcode) defined in line 38.
A second array called
userEntry will store the code the user enters.
After 4 digits are entered, we compare the two arrays to see if the user entered the correct code and act accordingly.
Throughout, the variable pos is used to keep track of our position in the
userEntry array. Once pos is equal to 3, we enter the last button pressed into the
userEntry array and then use pos to keep track of our position as we compare the
This means if you wish to extend the security of the system by lengthening the passcode, you must also update lines 123 and 129 in addition to adding the new values in the declared arrays on line 38 and 39. Alternatively, make use of the
sizeof(); function to reduce your coding efforts every time you change the passcode length.
The monitoring of the sensors is handled in the main
loop() function. Depending on which sensor is activated the
buttonPressed variable is set and passed to the
buttonEvent(); function. Go to line 121 to see the function.
When pos is less than 3 we simply place the
buttonPressed (which is received as button) into the
userEntry array. After we increase the value of pos so next time a button is pressed we don’t accidentally overwrite what the user is entering.
pos is equal to 3, we place
buttonPressed into the
userEntryarray for the last time before running our security check. The
for()loop sets our conditions, starting at the first entry we compare to see if the value in
userEntry[pos] is the same as
We’ve asked for information to be printed on the serial monitor to help with debugging, obviously you would want to remove this to increase security! You wouldn’t want to be telling a would-be hacker at which point they’d pressed the wrong button!
If the values in
passcode[pos] are not equal, the else statement line 143 prints error information on the serial monitor and plays the haptic error code. Importantly, it resets
pos to zero for the next user attempt and line
148 return; takes the program back to the main
The reason the
return; instruction is important is that if we complete the
for() loop with no errors (signalling the user entered the correct code) then we will play the ‘success’ haptic effect. In reality, this would also contain code, or call a function, to actually unlock the unit - normally via releasing an electromagnet or powering a gearmotor.
As mentioned at the beginning, we tried to keep the design very open to allow for flexible so it could be adapted to many applications. As a result, there’s plenty of work that would improve the performance of the system. Here are three suggestions that we found most prominent:
- Improved sensor measurement
We are using a very simple method for reading the sensors which could be improved. From testing, it is noticeable that some presses on the piezo sensor only register with the Arduino upon the release - i.e. as the user is removing their finger from the surface.
Obviously, this somewhat reduces the usefulness of a haptic response! The button monitoring could be improved in the code.
- Sensor location
For quick testing, we simply taped the sensors to a flat surface and played the haptic effect in the grip. In reality, you would want to play the haptic response on the same surface so that the user feels it as the press the button.
This can add complications as the piezo sensors could pick up the vibrations of the haptic effect. Increasing a delay between measurements may be enough to ignore any mistaken measurements on sensors after the ‘click’ is played on a button press. However, it is worth considering the length of the haptic effects you intend to play.
- Increasing security
This directly relates to the passcode system. At the moment the system is not overly secure, especially with the details printed on the serial monitor!
There are two ways to increase to increase the strength of the passcode:
A) Increase the length of the passcode
B) Increase the number of buttons
How well do you remember High School mathematics?! Will increasing the passcode by one number offer more security than an additional button? Which is easier to implement?