Trampoline Bounce Counter

Introduction

The Trampoline Bounce Counter was an idea I had been playing with for a while in my head. I had always wondered how many bounces I actually do in a training session, but was quite frankly far to lazy to try and count them myself! The perfect opportunity arose when the University Trampolining Society which I captained decided to host a 12 hour charity bounceathon! As part of the day we decided to aim for 100,000 bounces (spread over four trampolines) within the 12 hours.


Part 1

The first stage of the project required consideration of a method for actually identifying when a person was bouncing on the trampoline. The aim for me within this project was to develop a small system at minimal cost without sacrificing too much on reliability or functionality.

Within the professional trampoline circuit they utilise a laser trip wire system with two modules. One emits a laser beam and the other, located at the opposite side of the trampoline, receives it. When the performer land on the trampoline the bed is depressed and the beam is broken. As the trampoline rebounds, and the performer enter the air, the bed returns to it’s resting position and the beam is no longer broken. Now this system is very reliable and can even be used to time how long the performer is in the air for during a 10 bounce routine, however it requires two modules per trampoline.

I have some IR proximity sensors which I used for a small autonomous robot I had previously made. I considered using these in the same way as the laser system described above. The issue with this idea was that the very edges of the trampoline bed don’t move nearly as much as the centre and the measurement distance of the IR sensors limits it to this area.

The last idea I considered was using the small and very cheap ultrasonic HC-SR04 sensors. They have a measuring distance of 5cm to 200cm accurate to about +- 1 or 2 cm. This would allow one to be placed under the centre of the trampoline and measure the distance from the floor to the bed or hung of the edge of the trampoline and detect when the bed is depressed due to a bouncer. In the second scenario it would function by returning a time out error / reading of infinity when the bouncer is in the air and then a distance to the bed when the bouncer has landed and the bed has sunk.

Due to the low price and simple fact that I had a number of HC-SR04 modules lying around I decided to go ahead and try to develop a system using the following components:

  • Arduino Nano (small and cheap, with USB connectivity)
  • HC-SR04
  • Laptop (A processing script was used to receive the bounce counter data and display it)

Part 2

So I took baby steps with this project and part 2 was wiring up a HC-SR04 on a breadboard. The breakout I had was the 4 pin version (VSS, GND, TRIG, ECHO). I connected VSS to 5v, GND to GND, TRIG to pin 2 and ECHO to pin 3 (I think you can cope without a wiring diagram for that). There are two libraries (ping and newPing) out there which make using the HC-SR04 a doddle! I recommend the newPing one because it is much more versatile because you are able to set the timeout distance (the library converts this into the equivalent time for you). Anyway I went ahead and used the following code which worked first time!

#include <NewPing.h>

#define TRIGGER_PIN 2 // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN 3 // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void setup() {
// put your setup code here, to run once:
Serial.begin(115200);

}

void loop() {
// put your main code here, to run repeatedly:
int distance = getDistance();
Serial.print(distance);
Serial.println(” cm”);
}

unsigned int getDistance(){
unsigned int uS = sonar.ping(); // Send ping, get ping time in microseconds (uS).
unsigned int cm = uS / US_ROUNDTRIP_CM; // convert to
return cm;
}

I was very happy with the results. Generally speaking it was accurate to a cm or so!


Part 3

Time to move off the breadboard and onto some protoboard! I used some cheap 5cm by 5cm boards which are copper plated through the holes. This took no time at all given that all that was required was 4 simple connections because all communication with the Arduino (I chose the cheap and small nano for this project) would be done over USB. Below is the fritzing example and a picture of the finished boards!

TrampolineBounceCounter

TrampolineBounceCounterboard

This board was tested with the breadboard code from part 2 and worked a charm! At this point I realised that I was in fact going to need one of these modules per trampoline (4 for the event the system was designed for). This is why you can see the board being numbered as 1.


Part 4

The GUI was built using Processing because it is my favourite way to communicate with an Arduino running a script from a PC (It is just so versatile and easy to use!). The finished GUI shown below (actually a screenshot from the end of the 12 hour bounce – we achieved an amazing 105,263 bounces! in the 12 hours) inclaudes the university sports logo, the bluebell wood charity logo, a countdown timer (at zero in the image), a total of the bounces and the individual bounces per trampoline. The Timer updates once per second and the bounce counters update as and when a bounce was completed and the data sent to the laptop from the Arduino modules.

bounce Counter GUI

I’m not going to explain how I did everything within the GUI as most of it can be learnt from the Processing website and tutorials. As an overview I used a serial In event (void serialEvent(Serial thisPort)) which activates whenever new serial data is available on the serial port to handle the updating of an array which stored the number of bounces for each trampoline. I also used the coundown_timer library to facilitate the creation of the live timer.

Here is the code for anyone interested!

import com.dhchoi.*;
import processing.serial.*;

// create image variables
PImage sportSheffield;
PImage bluebell;

// Serial variables and objects
Serial[] myPorts = new Serial[4]; // Create a list of objects from Serial class
int[] dataIn = new int[4]; // a list to hold data from the serial ports

// timer variables and objects
CountdownTimer timer;
String timerCallbackInfo = “”;
long hours = 0;
long minutes = 00;
long seconds = 00;

// trampoline graphic sizing and positioning (update the values in setup)
int trampolineHeight;
int trampolineWidth;
int left;
int top;
int horizontalSpace;
int verticalSpace;
int padSize;
int deckWidth;
int totalWidth;
int totalHeight;
boolean sketchFullScreen() {
return true;
}

void setup() {
size(displayWidth, displayHeight);
// print a list of the serial ports:
printArray(Serial.list());

// get the ports’ names:
try{
String portOne = Serial.list()[0];
String portTwo = Serial.list()[1];
String portThree = Serial.list()[2];
//String portFour = Serial.list()[3];
// open the ports:
myPorts[0] = new Serial(this, portOne, 115200);
myPorts[1] = new Serial(this, portTwo, 115200);
myPorts[2] = new Serial(this, portThree, 115200);
//myPorts[3] = new Serial(this, portFour, 115200);
}
catch(Exception e){
println(“serial ports not found”);
}

// set up timer
timer = CountdownTimerService.getNewCountdownTimer(this).configure(1000, 7*60*60*1000+51*60*1000);

// set variables based on window size (update them here not in declerations)
trampolineHeight = height/4;
trampolineWidth = width/3;
horizontalSpace = 80;
verticalSpace = 0;
padSize = 20;
deckWidth = 100;
totalWidth = trampolineWidth*2 + horizontalSpace + deckWidth*2;
totalHeight = trampolineHeight*2;
left = width/2 – totalWidth/2;
top = height*2/3 – totalHeight/2;

// load images
sportSheffield = loadImage(“sportSheffield.png”);
bluebell = loadImage(“bluebell.jpg”);

dataIn[0] = 26313;
dataIn[1] = 26343;
dataIn[2] = 26353;
dataIn[3] = 22888;

}

void draw() {

// clear the screen:
background(0);

drawTrampolines();
updateText();
drawTimer();
drawLogos();

}

void drawLogos(){
image(sportSheffield, 10, 10, 250, 150);
image(bluebell, displayWidth – 300, 10,300,150);
}

// timer set up to ‘tick’ every second – update timer display
void onTickEvent(int timerId, long timeLeftUntilFinish) {
timerCallbackInfo = “[tick] – timeLeft: ” + timeLeftUntilFinish + “ms”;
hours = timeLeftUntilFinish / (60*60*1000);
minutes = (timeLeftUntilFinish % (60*60*1000)) / (60*1000);
seconds = ((timeLeftUntilFinish % (60*60*1000)) % (60*1000)) / 1000;
}

void drawTimer(){
textAlign(CENTER, CENTER);
textSize(100);
fill(255);

String timerText = hours + “:” + minutes + “:” + seconds;
text(timerText, width/2, 100);

}

void onFinishEvent(int timerId) {
timerCallbackInfo = “[finished]”;
println(timerCallbackInfo);
}

void drawTrampolines(){

//outer rectangles
fill(0,0,90);
rect(left + deckWidth,top,trampolineWidth,trampolineHeight);
rect(left + deckWidth + trampolineWidth + horizontalSpace,top,trampolineWidth,trampolineHeight);
rect(left + deckWidth,top + trampolineHeight + verticalSpace,trampolineWidth,trampolineHeight);
rect(left + deckWidth + trampolineWidth + horizontalSpace,top + trampolineHeight + verticalSpace,trampolineWidth,trampolineHeight);

//inner rectangles
fill(220);
rect(left + deckWidth + padSize,top + padSize,trampolineWidth – padSize*2,trampolineHeight – padSize*2);
rect(left + deckWidth + trampolineWidth + horizontalSpace + padSize,top + padSize,trampolineWidth – padSize*2,trampolineHeight – padSize*2);
rect(left + deckWidth + padSize,top + trampolineHeight + verticalSpace + padSize,trampolineWidth – padSize*2,trampolineHeight – padSize*2);
rect(left + deckWidth + trampolineWidth + horizontalSpace + padSize,top + trampolineHeight + verticalSpace + padSize,trampolineWidth – padSize*2,trampolineHeight – padSize*2);

//middle mats
fill(0,0,150);
rect(left + deckWidth + trampolineWidth, top, horizontalSpace, trampolineHeight);
rect(left + deckWidth + trampolineWidth, top + trampolineHeight, horizontalSpace, trampolineHeight);

//end mats
fill(0,0,150);
rect(left, top, deckWidth, trampolineHeight);
rect(left + trampolineWidth*2 + deckWidth + horizontalSpace, top, deckWidth, trampolineHeight);
rect(left, top + trampolineHeight, deckWidth, trampolineHeight);
rect(left + trampolineWidth*2 + deckWidth + horizontalSpace, top + trampolineHeight, deckWidth, trampolineHeight);

}

void updateText(){
textAlign(CENTER, CENTER);
textSize(32);
fill(0);

// use the latest values from serial
text(dataIn[2], left + deckWidth + trampolineWidth/2, top + trampolineHeight/2);
text(dataIn[1], left + deckWidth + trampolineWidth*3/2 + horizontalSpace, top + trampolineHeight/2);
text(dataIn[2], left + deckWidth + trampolineWidth/2, top + trampolineHeight*3/2);
text(dataIn[0], left + deckWidth + trampolineWidth*3/2 + horizontalSpace, top + trampolineHeight*3/2);

// total number of bounces
fill (255);
int total = dataIn[0] + dataIn[1] + dataIn[2] + dataIn[2];
text(“Total Bounces : ” + total, width/2, 190);
}

// function that handles serial in events
void serialEvent(Serial thisPort) {
// variable to hold the number of the port:
int portNumber = -1;

// iterate over the list of ports opened, and match the
// one that generated this event:
for (int p = 0; p < myPorts.length; p++) {
if (thisPort == myPorts[p]) {
portNumber = p;
}
}
if (thisPort.available() > 0){
// read a byte from the port:
int inByte = thisPort.read();
//int inByte2 = thisPort.read();
// put it in the list that holds the latest data from each port:
if (inByte == 49){
dataIn[portNumber]++;
}
// tell us who sent what:
println(“Got ” + dataIn[portNumber] + ” from serial port ” + portNumber);
}
}

// key pressed
void keyPressed(){
switch(key){
case ‘s’:
timer.start();
// set bounces
dataIn[0] = 6429;
dataIn[1] = 5296;
dataIn[2] = 6429;
dataIn[3] = 6998;

break;
case ‘d’:
timer.stop(CountdownTimer.StopBehavior.STOP_AFTER_INTERVAL);
break;
}
}

 


 

Part 5

 

Shortly after the GUI was completed the big day arrived and the sensors were connected up (using very long USB wires – this had some issues but I will get to that) and away we went! 12 hours later we hit the total of 105,000 that Is shown in the picture in Part 4. Overall the event was a huge success and everyone loved the live bounce counter!

There were however some issues:

  • The resistance of the long usb wires meant that sometimes the modules dropped out and missed some bounce
    • Could be fixed by powering each module with a battery / making them wireless
    • Data transfer over the long wires seemed to be ok (even through a USB hub)
  • Very small bounces (such as those done by a child) and bounces way off from the centre of the trampoline often weren’t registered!
  • Larger (/heavier) people could be detected as bouncing when they were simply rocking the bed and not actually jumping!

In conclusion the method was a success! And as such will potentially be employed again next year!


 

UPDATE

The 12 hour charity bounce has been confirmed to go ahead in 2016 and as such the development of Bounce counter V2.0 is underway. Keep an eye out for updates on this new WiFi enabled system which has also features a customisable GUI.

3 thoughts on “Trampoline Bounce Counter

  1. Hi
    I’m really interested in your bounce counter, would you be able to make one for us. I’m trying to pull together a fundraising activity with the local trampoline park in conjunction with Barnados and it would be great if we could have a system that allows us to count the bounce

    Like

Leave a comment