Pong
To paraphrase a Berkeley professor, "No platform is successful until it can run pong." Pong, for you young ones, is widely considered the first video game, and if not the absolute first, than the first to make people think seriously about video games. This app demonstrates some key concepts on both the mote side and client side, including
- Sensor Reading
- Digital Interfaces
- Sending and Receiving Objects
- Sending and Receiving Byte Buffers
We'll also highlight the utility of our Java implementation by combining the power and flexibility of the Sentilla Perk Kit with the Java wunkderkind of visualization, processing.
Mote application
In order to play pong, we'll need the motes, at a minimum, to send data fairly frequently to the host server. We'll use the accelerometers built into the JCreates as tilt sensors.
Rather than creating our own ADC-level interface to the accelerometer, we'll implement the example accelerometer code provided in the Sentilla Example Mote Applications.
1 2 3 | // Instantiate the accelerometer // Let the Accelerometer know that we're only interested in Y-axis readings Accelerometer accel = new Accelerometer(false, true, false); |
To minimize latency for the player, we'll use ByteBuffers to transmit the tilt data rather than objects by doing this we bypass the overhead for serialization, the trade off being that we need to manually deserialize the data in the client application.
1 2 | ByteSender bsender = ByteSenderDriver.create(1000); ByteBuffer buf = new ByteBuffer(12); |
As it stands, our application will simply send data to the host-server, and the host server must have the moteID hard-mapped to the left and right hand players. It would be more elegant to let the players dictate this and have the program map it on the fly. To do this we'll use another example library, the "AccelerometerButton" interface. This function allows the accelerometer to block as an interupt until it is shaken with sufficient vigor. Upon start up, we'll have the motes wait to be shaken, and the order in which they are shaken will determine which mote maps to player 1 and player 2.
1 2 3 4 5 6 7 8 9 10 11 12 | //Wake up for game AcceleratorButton button = new AcceleratorButton(); button.shake(); // Block until the mote shakes buf.writeLong(moteID); //Write Mote ID buf.writeFloat(1234); // Write Code to let the client know this is the write messag bsender.send(buf); // Write out a few times to make sure the computer gets the message Thread.sleep(500); bsender.send(buf); Thread.sleep(500); bsender.send(buf); buf.clear(); ledPort.writePort((byte) 0xFF, (byte) 126); //Let the player know we're waitng via a unique led array |
We'll implement a receive function to let the motes wait until the client has both player before we start flooding with accelerometer data packets. To implement a reciever in the mote we..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | Receiver recv = ReceiverDriver.create(PongMoteApp.DataMessage.class); while (!ready) { if (recv.isDone()) { DataMessage dmsg = (DataMessage) recv.getData(); if (dmsg.status == 186) { ready = true; } else if (dmsg.status == 187) { ready = true; restart = true; } recv.setReceive().submit(); } } |
We'll use the LEDS on the mote to keep score, and implement a non-blocking receiver to do so. We'll send out the accelerometer data as fast as it comes in. Finally, we'll look for a reset cammand so the client can restart the game without having to reboot the motes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | while(!restart) { timeout++; if (timeout > 1000000) { restart = true; //reset motes if nothing has happened for a while } // Read the accelerometer accel.getMultipleReadings(readings); // Extract only the Y axis axisOfInterestReading = readings[0]; // Pack and send message buf.writeLong(moteID); buf.writeFloat(axisOfInterestReading); bsender.send(buf); buf.clear(); // Watch out for score updates if (recv.isDone()) { timeout = 0; DataMessage dmsg = (DataMessage) recv.getData(); if ((dmsg.status == 180) && (dmsg.moteId == moteID)) { ledPort.writePort((byte)0xFF, (byte)((1 << (dmsg.score))-1)); } if (dmsg.status == 187) { restart = true; ready = false; } recv.setReceive().submit(); } } |
The complete code can be found here
Client Application
We'll be using processing to ease the mechanics of our gameplay. I've modified the pong code generously shared by the students at the Freie Universitat in Berlin here. Processing is an applet library which allows animations and interactive media to be written in real Java with a fraction of the coding overhead. By convention it uses two functions,
- a "void setup()" where the program is initialized and
- a "void draw()" where the logic for the program loops at a rate of 30 Hz unless otherwise specified in the setup method
First, we'll import the processing library by copying core.jar into our project folder and linking it in our project preferences in the Sentilla Work.
Then we'll create a client application, import the core processing components, and have our application extend "PApplet"
1 | import processing.core.*; |
When the program starts, it needs to initialize a few basic parameters to set up the game
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | Object obj = new Object(); Receiver receiver; Sender sender; PongMoteApp.DataMessage dmsg = new PongMoteApp.DataMessage(); public void setup() { // Set up the playing field size(500, 500); stroke(255); frameRate(30); background(0); PFont font = createFont("FFScala", 32); textFont(font, 44); redraw(); /* create the host server connection */ HostClient host = new HostClient(); try { host.connect(); } catch (Exception e){} //Create Sender, send reset code to motes sender = SenderDriver.create("local"); dmsg.status = 187; sender.send(dmsg); delay(100); sender.send(dmsg); delay(100); sender.send(dmsg); delay(100); sender.send(dmsg); delay(4000); //Start receiver receiver = ReceiverDriver.create(ByteBuffer.class); receiver.cancel(); receiver.setReceive().submit(); } |
It then needs to tell the players to shake their respective motes. We'll set up a receiver to do so, mapping the moteID to a player variable. After we have two motes checked in the computer will send a signal to start the game.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | if (waitforone) { background(0); text("Player One Shake",0,height/2); if (receiver.isDone()) { try { ByteBuffer rbuf = (ByteBuffer) receiver.getData(); long tempmote = rbuf.readLong(); float tempcheck = rbuf.readFloat(); if (tempcheck == 1234) { waitforone = false; waitfortwo = true; background(0); mote1 = tempmote; rbuf.clear(); } } catch (Exception e){} receiver.setReceive().submit(); } } // Wait for player 2 else if (waitfortwo) { background(0); text("Player Two Shake",0,height/2); if (receiver.isDone()) { try{ ByteBuffer rbuf = (ByteBuffer) receiver.getData(); background(0); mote2 = rbuf.readLong(); float tempcheck = rbuf.readFloat(); rbuf.clear(); if ((mote2 != mote1) && (tempcheck == 1234)) { waitfortwo = false; dmsg.status = 186; sender.send(dmsg); delay(1000); sender.send(dmsg); delay(1000); sender.send(dmsg); } } catch (Exception e){} receiver.setReceive().submit(); } } |
Now we can use the bulk of Pong source. We need to repeatedly listen for packets to arrive, determine which mote sent the packet, and then have the game react accordingly. We'll take +1 g orientation to map the paddle on-screen to the top of the motion range, and -1 g to the bottom. We'll them linearly interpolate the position of the mote between these extremes. Finally, we'll smooth out the motion by doing a bit of linear interpolation between frames. When a player scores, their accomplishment should be noted by their mote, so we'll send a packet out. Finally, after 8 points, the game should reset both itself and the motes.
The source can be found here
Conclusion
So now you built an application that shows off pervasive computing with an old-school flair. Be sure to consider the basic concepts we covered here as you go forth developing new apps, including:
- Blocking on physical interrupt
- Sending and receiving ByteBuffers
- Sending and receiving objects
- Basic sensing
- Basic digital I/O
#Pong
To paraphrase a Berkeley professor, "No platform is successful until it can run pong." Pong, for you young ones, is widely considered the first video game, and if not the absolute first, than the first to make people think seriously about video games. This app demonstrates some key concepts on both the mote side and client side, including
- Sensor Reading
- Digital Interfaces
- Sending and Receiving Objects
- Sending and Receiving Byte Buffers
We'll also highlight the utility of our Java implementation by combining the power and flexibility of the Sentilla Perk Kit with the Java wunkderkind of visualization, [processing](http://processing.org).
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/CY0JXAwgl24&hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><embed src="http://www.youtube.com/v/CY0JXAwgl24&hl=en&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"></embed></object>
!{300,300}[Image Alt Text][[pong]]
##Mote application
In order to play pong, we'll need the motes, at a minimum, to send data fairly frequently to the host server. We'll use the accelerometers built into the JCreates as tilt sensors.
Rather than creating our own ADC-level interface to the accelerometer, we'll implement the example accelerometer code provided in the Sentilla Example Mote Applications.
<code>
#!java
// Instantiate the accelerometer
// Let the Accelerometer know that we're only interested in Y-axis readings
Accelerometer accel = new Accelerometer(false, true, false);
</code>
To minimize latency for the player, we'll use ByteBuffers to transmit the tilt data rather than objects by doing this we bypass the overhead for serialization, the trade off being that we need to manually deserialize the data in the client application.
<code>
#!java
ByteSender bsender = ByteSenderDriver.create(1000);
ByteBuffer buf = new ByteBuffer(12);
</code>
As it stands, our application will simply send data to the host-server, and the host server must have the moteID hard-mapped to the left and right hand players. It would be more elegant to let the players dictate this and have the program map it on the fly. To do this we'll use another example library, the "AccelerometerButton" interface. This function allows the accelerometer to block as an interupt until it is shaken with sufficient vigor. Upon start up, we'll have the motes wait to be shaken, and the order in which they are shaken will determine which mote maps to player 1 and player 2.
<code>
#!java
//Wake up for game
AcceleratorButton button = new AcceleratorButton();
button.shake(); // Block until the mote shakes
buf.writeLong(moteID); //Write Mote ID
buf.writeFloat(1234); // Write Code to let the client know this is the write messag
bsender.send(buf); // Write out a few times to make sure the computer gets the message
Thread.sleep(500);
bsender.send(buf);
Thread.sleep(500);
bsender.send(buf);
buf.clear();
ledPort.writePort((byte) 0xFF, (byte) 126); //Let the player know we're waitng via a unique led array
</code>
We'll implement a receive function to let the motes wait until the client has both player before we start flooding with accelerometer data packets. To implement a reciever in the mote we..
<code>
#!java
Receiver recv = ReceiverDriver.create(PongMoteApp.DataMessage.class);
while (!ready)
{
if (recv.isDone())
{
DataMessage dmsg = (DataMessage) recv.getData();
if (dmsg.status == 186)
{
ready = true;
}
else if (dmsg.status == 187)
{
ready = true;
restart = true;
}
recv.setReceive().submit();
}
}
</code>
We'll use the LEDS on the mote to keep score, and implement a non-blocking receiver to do so. We'll send out the accelerometer data as fast as it comes in. Finally, we'll look for a reset cammand so the client can restart the game without having to reboot the motes
<code>
#!java
while(!restart)
{
timeout++;
if (timeout > 1000000)
{
restart = true; //reset motes if nothing has happened for a while
}
// Read the accelerometer
accel.getMultipleReadings(readings);
// Extract only the Y axis
axisOfInterestReading = readings[0];
// Pack and send message
buf.writeLong(moteID);
buf.writeFloat(axisOfInterestReading);
bsender.send(buf);
buf.clear();
// Watch out for score updates
if (recv.isDone())
{
timeout = 0;
DataMessage dmsg = (DataMessage) recv.getData();
if ((dmsg.status == 180) && (dmsg.moteId == moteID))
{
ledPort.writePort((byte)0xFF, (byte)((1 << (dmsg.score))-1));
}
if (dmsg.status == 187)
{
restart = true;
ready = false;
}
recv.setReceive().submit();
}
}
</code>
The complete code can be found @[here][[PongMoteApp.java]]
##Client Application
We'll be using processing to ease the mechanics of our gameplay. I've modified the pong code generously shared by the students at the Freie Universitat in Berlin [here](http://www.inf.fu-berlin.de/inst/ag-se/teaching/K-BKI-2006/Pong/). Processing is an applet library which allows animations and interactive media to be written in real Java with a fraction of the coding overhead. By convention it uses two functions,
- a "void setup()" where the program is initialized and
- a "void draw()" where the logic for the program loops at a rate of 30 Hz unless otherwise specified in the setup method
First, we'll import the processing library by copying core.jar into our project folder and linking it in our project preferences in the Sentilla Work.
Then we'll create a client application, import the core processing components, and have our application extend "PApplet"
<code>
#!java
import processing.core.*;
</code>
When the program starts, it needs to initialize a few basic parameters to set up the game
<code>
#!java
Object obj = new Object();
Receiver receiver;
Sender sender;
PongMoteApp.DataMessage dmsg = new PongMoteApp.DataMessage();
public void setup()
{
// Set up the playing field
size(500, 500);
stroke(255);
frameRate(30);
background(0);
PFont font = createFont("FFScala", 32);
textFont(font, 44);
redraw();
/* create the host server connection */
HostClient host = new HostClient();
try
{
host.connect();
}
catch (Exception e){}
//Create Sender, send reset code to motes
sender = SenderDriver.create("local");
dmsg.status = 187;
sender.send(dmsg);
delay(100);
sender.send(dmsg);
delay(100);
sender.send(dmsg);
delay(100);
sender.send(dmsg);
delay(4000);
//Start receiver
receiver = ReceiverDriver.create(ByteBuffer.class);
receiver.cancel();
receiver.setReceive().submit();
}
</code>
It then needs to tell the players to shake their respective motes. We'll set up a receiver to do so, mapping the moteID to a player variable. After we have two motes checked in the computer will send a signal to start the game.
<code>
#!java
if (waitforone)
{
background(0);
text("Player One Shake",0,height/2);
if (receiver.isDone())
{
try
{
ByteBuffer rbuf = (ByteBuffer) receiver.getData();
long tempmote = rbuf.readLong();
float tempcheck = rbuf.readFloat();
if (tempcheck == 1234)
{
waitforone = false;
waitfortwo = true;
background(0);
mote1 = tempmote;
rbuf.clear();
}
}
catch (Exception e){}
receiver.setReceive().submit();
}
}
// Wait for player 2
else if (waitfortwo)
{
background(0);
text("Player Two Shake",0,height/2);
if (receiver.isDone())
{
try{
ByteBuffer rbuf = (ByteBuffer) receiver.getData();
background(0);
mote2 = rbuf.readLong();
float tempcheck = rbuf.readFloat();
rbuf.clear();
if ((mote2 != mote1) && (tempcheck == 1234))
{
waitfortwo = false;
dmsg.status = 186;
sender.send(dmsg);
delay(1000);
sender.send(dmsg);
delay(1000);
sender.send(dmsg);
}
}
catch (Exception e){}
receiver.setReceive().submit();
}
}
</code>
Now we can use the bulk of Pong source. We need to repeatedly listen for packets to arrive, determine which mote sent the packet, and then have the game react accordingly. We'll take +1 g orientation to map the paddle on-screen to the top of the motion range, and -1 g to the bottom. We'll them linearly interpolate the position of the mote between these extremes. Finally, we'll smooth out the motion by doing a bit of linear interpolation between frames. When a player scores, their accomplishment should be noted by their mote, so we'll send a packet out. Finally, after 8 points, the game should reset both itself and the motes.
The source can be found @[here][[PongClientApp.java]]
##Conclusion
So now you built an application that shows off pervasive computing with an old-school flair. Be sure to consider the basic concepts we covered here as you go forth developing new apps, including:
- Blocking on physical interrupt
- Sending and receiving ByteBuffers
- Sending and receiving objects
- Basic sensing
- Basic digital I/O

