diff --git a/.classpath b/.classpath
index 7bc01d9..5176974 100644
--- a/.classpath
+++ b/.classpath
@@ -1,9 +1,9 @@
-
-
+
+
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0313e2c..223e3ad 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,27 +1,36 @@
+ package="com.vanderbie.heart_rate_monitor"
+ android:versionCode="1"
+ android:versionName="1.0" >
-
+
-
-
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/Processing/HeartRate_udp/HeartRate_udp.pde b/Processing/HeartRate_udp/HeartRate_udp.pde
new file mode 100644
index 0000000..a946dfe
--- /dev/null
+++ b/Processing/HeartRate_udp/HeartRate_udp.pde
@@ -0,0 +1,94 @@
+/**
+ * (./) udp.pde - how to use UDP library as unicast connection
+ * (cc) 2006, Cousot stephane for The Atelier Hypermedia
+ * (->) http://hypermedia.loeil.org/processing/
+ http://ubaa.net/shared/processing/udp/
+ *
+ * Extended by Joey van der Bie to display heart rate value send by Android phone
+ * https://github.com/joeyvanderbie/android-heart-rate-monitor
+ */
+
+// import UDP library
+import hypermedia.net.*;
+
+
+UDP udp; // define the UDP object
+int listenport = 6000;// this should be the same as the port in the Android app
+
+int textSize = 180;
+/**
+ * init
+ */
+void setup() {
+ size(600,600);
+ // create a new datagram connection on port 6000
+ // and wait for incomming message
+ udp = new UDP( this, listenport );
+ //udp.log( true ); // <-- printout the connection activity
+ udp.listen( true );
+
+
+ //Draw blank text
+ textSize(textSize);
+ background(255,0,0);
+ fill(255);
+ text("--", (width-textSize)/2, (height+textSize)/2);
+}
+
+//process events
+void draw() {;
+}
+
+void displayHeartRate(int heart_rate){
+ //display heart rate
+ background(255,0,0);
+ fill(255);
+ text(""+heart_rate, (width-textSize)/2, (height+textSize)/2);
+}
+
+/**
+ * To perform any action on datagram reception, you need to implement this
+ * handler in your code. This method will be automatically called by the UDP
+ * object each time he receive a nonnull message.
+ * By default, this method have just one argument (the received message as
+ * byte[] array), but in addition, two arguments (representing in order the
+ * sender IP address and his port) can be set like below.
+ */
+// void receive( byte[] data ) { // <-- default handler
+void receive( byte[] data, String ip, int port ) { // <-- extended handler
+
+
+ // get the "real" message =
+ // forget the ";\n" at the end <-- !!! only for a communication with Pd !!!
+ data = subset(data, 0, data.length-2);
+ String message = new String( data );
+
+ // print the result
+ println( "receive: \""+message+"\" from "+ip+" on port "+port );
+
+
+ String heartrate[] = split(message, ",");
+ displayHeartRate(int(heartrate[0]));
+
+}
+
+/**
+ * on key pressed event:
+ * send the current key value over the network
+ */
+void keyPressed() {
+
+ String message = str( key ); // the message to send
+ String ip = "localhost"; // the remote IP address
+ int port = 6100; // the destination port
+
+ // formats the message for Pd
+ message = message+";\n";
+ // send the message
+ udp.send( message, ip, port );
+}
+
+
+
+
+
diff --git a/Processing/HeartRate_udp/udp.pd b/Processing/HeartRate_udp/udp.pd
new file mode 100644
index 0000000..43773de
--- /dev/null
+++ b/Processing/HeartRate_udp/udp.pd
@@ -0,0 +1,30 @@
+#N canvas 103 333 764 337 10;
+#X obj 36 28 cnv 15 690 190 empty empty empty 20 12 0 12 -237178 -212212
+0;
+#X obj 36 221 cnv 15 690 70 empty empty empty 20 12 0 14 -146177 -66577
+0;
+#X obj 45 256 print receive;
+#X text 177 233 <-- Opens a socket for UDP network reception on port
+6100 and prints the received message.;
+#X obj 47 116 netsend 1;
+#X msg 47 94 disconnect;
+#X floatatom 47 164 5 0 0 0 - - -;
+#X text 95 164 <-- Reports whether the connection is open or not (0=close
+\, nonzero otherwise).;
+#X obj 242 70 cnv 15 480 60 empty empty empty 20 12 0 14 -259904 -66577
+0;
+#X msg 249 100 send \$1;
+#X floatatom 249 82 5 0 0 0 - - -;
+#X obj 45 234 netreceive 6100 1;
+#X msg 47 72 connect localhost 6000;
+#X text 314 81 [2] Send the current number to the remote machine (change
+the value by dragging the number box or type directly the new value).
+;
+#X text 47 40 [1] Connect to 'localhost' on port 6000 for sending UDP
+messages.;
+#X connect 4 0 6 0;
+#X connect 5 0 4 0;
+#X connect 9 0 4 0;
+#X connect 10 0 9 0;
+#X connect 11 0 2 0;
+#X connect 12 0 4 0;
diff --git a/Processing/udp/examples/udp/udp.pd b/Processing/udp/examples/udp/udp.pd
new file mode 100755
index 0000000..43773de
--- /dev/null
+++ b/Processing/udp/examples/udp/udp.pd
@@ -0,0 +1,30 @@
+#N canvas 103 333 764 337 10;
+#X obj 36 28 cnv 15 690 190 empty empty empty 20 12 0 12 -237178 -212212
+0;
+#X obj 36 221 cnv 15 690 70 empty empty empty 20 12 0 14 -146177 -66577
+0;
+#X obj 45 256 print receive;
+#X text 177 233 <-- Opens a socket for UDP network reception on port
+6100 and prints the received message.;
+#X obj 47 116 netsend 1;
+#X msg 47 94 disconnect;
+#X floatatom 47 164 5 0 0 0 - - -;
+#X text 95 164 <-- Reports whether the connection is open or not (0=close
+\, nonzero otherwise).;
+#X obj 242 70 cnv 15 480 60 empty empty empty 20 12 0 14 -259904 -66577
+0;
+#X msg 249 100 send \$1;
+#X floatatom 249 82 5 0 0 0 - - -;
+#X obj 45 234 netreceive 6100 1;
+#X msg 47 72 connect localhost 6000;
+#X text 314 81 [2] Send the current number to the remote machine (change
+the value by dragging the number box or type directly the new value).
+;
+#X text 47 40 [1] Connect to 'localhost' on port 6000 for sending UDP
+messages.;
+#X connect 4 0 6 0;
+#X connect 5 0 4 0;
+#X connect 9 0 4 0;
+#X connect 10 0 9 0;
+#X connect 11 0 2 0;
+#X connect 12 0 4 0;
diff --git a/Processing/udp/examples/udp/udp.pde b/Processing/udp/examples/udp/udp.pde
new file mode 100644
index 0000000..c740f52
--- /dev/null
+++ b/Processing/udp/examples/udp/udp.pde
@@ -0,0 +1,73 @@
+/**
+ * (./) udp.pde - how to use UDP library as unicast connection
+ * (cc) 2006, Cousot stephane for The Atelier Hypermedia
+ * (->) http://hypermedia.loeil.org/processing/
+ *
+ * Create a communication between Processing<->Pure Data @ http://puredata.info/
+ * This program also requires to run a small program on Pd to exchange data
+ * (hum!!! for a complete experimentation), you can find the related Pd patch
+ * at http://hypermedia.loeil.org/processing/udp.pd
+ *
+ * -- note that all Pd input/output messages are completed with the characters
+ * ";\n". Don't refer to this notation for a normal use. --
+ */
+
+// import UDP library
+import hypermedia.net.*;
+
+
+UDP udp; // define the UDP object
+int listenport = 6000;// this should be the same as the port in the Android app
+/**
+ * init
+ */
+void setup() {
+
+ // create a new datagram connection on port 6000
+ // and wait for incomming message
+ udp = new UDP( this, listenport );
+ //udp.log( true ); // <-- printout the connection activity
+ udp.listen( true );
+}
+
+//process events
+void draw() {;}
+
+/**
+ * on key pressed event:
+ * send the current key value over the network
+ */
+void keyPressed() {
+
+ String message = str( key ); // the message to send
+ String ip = "localhost"; // the remote IP address
+ int port = 6100; // the destination port
+
+ // formats the message for Pd
+ message = message+";\n";
+ // send the message
+ udp.send( message, ip, port );
+
+}
+
+/**
+ * To perform any action on datagram reception, you need to implement this
+ * handler in your code. This method will be automatically called by the UDP
+ * object each time he receive a nonnull message.
+ * By default, this method have just one argument (the received message as
+ * byte[] array), but in addition, two arguments (representing in order the
+ * sender IP address and his port) can be set like below.
+ */
+// void receive( byte[] data ) { // <-- default handler
+void receive( byte[] data, String ip, int port ) { // <-- extended handler
+
+
+ // get the "real" message =
+ // forget the ";\n" at the end <-- !!! only for a communication with Pd !!!
+ data = subset(data, 0, data.length-2);
+ String message = new String( data );
+
+ // print the result
+ println( "receive: \""+message+"\" from "+ip+" on port "+port );
+}
+
diff --git a/Processing/udp/examples/udp_multicast/udp_multicast.pde b/Processing/udp/examples/udp_multicast/udp_multicast.pde
new file mode 100755
index 0000000..596f237
--- /dev/null
+++ b/Processing/udp/examples/udp_multicast/udp_multicast.pde
@@ -0,0 +1,100 @@
+/**
+ * (./) udp_multicast.pde - how to use UDP library as multicast connection
+ * (cc) 2006, Cousot stephane for The Atelier Hypermedia
+ * (->) http://hypermedia.loeil.org/processing/
+ *
+ * Pass the mouse coordinates over the network to draw an "multi-user picture".
+ *
+ * --
+ *
+ * about multicasting:
+ * The only difference between unicast/broadcast and multicast address is that
+ * all interfaces identified by that address receive the same data. Multicasting
+ * provide additional options in the UDP object (see the documentation for
+ * more informations), but the usage is commonly the same: simply add the
+ * multicast group address in his initialization to reflect a multicast
+ * connection.
+ *
+ * (note: currently applets are not allowed to use multicast sockets)
+ */
+
+// import UDP library
+import hypermedia.net.*;
+
+
+UDP udp; // the UDP object
+
+
+/**
+ * init the frame and the UDP object.
+ */
+void setup() {
+
+ // to simplify the program, we use a byte[] array to pass the previous and
+ // the current mouse coordinates. The PApplet size must be defined with
+ // values <=255
+ size( 255, 255 );
+ background( 0 );
+
+ // create a multicast connection on port 6000
+ // and join the group at the address "224.0.0.1"
+ udp = new UDP( this, 6000, "224.0.0.1" );
+ // wait constantly for incomming data
+ udp.listen( true );
+ // ... well, just verifies if it's really a multicast socket and blablabla
+ println( "init as multicast socket ... "+udp.isMulticast() );
+ println( "joins a multicast group ... "+udp.isJoined() );
+
+}
+
+// process events
+void draw() {
+}
+
+
+/**
+ * on mouse move :
+ * send the mouse positions over the network
+ */
+void mouseMoved() {
+
+ byte[] data = new byte[4]; // the data to be send
+
+ // add the mouse positions
+ data[0] = byte(mouseX);
+ data[1] = byte(mouseY);
+ data[2] = byte(pmouseX);
+ data[3] = byte(pmouseY);
+
+ // by default if the ip address and the port number are not specified, UDP
+ // send the message to the joined group address and the current socket port.
+ udp.send( data ); // = send( data, group_ip, port );
+
+ // note: by creating a multicast program, you can also send a message to a
+ // specific address (i.e. send( "the messsage", "192.168.0.2", 7010 ); )
+}
+
+/**
+ * This is the program receive handler. To perform any action on datagram
+ * reception, you need to implement this method in your code. She will be
+ * automatically called by the UDP object each time he receive a nonnull
+ * message.
+ */
+void receive( byte[] data ) {
+
+ // retrieve the mouse coordonates
+ int x = int( data[0] );
+ int y = int( data[1] );
+ int px = int( data[2] );
+ int py = int( data[3] );
+
+ // slowly, clears the previous lines
+ noStroke();
+ fill( 0, 0, 0, 7 );
+ rect( 0, 0, width, height);
+
+ // and draw a single line with the given mouse positions
+ stroke( 255 );
+ line( x, y, px, py );
+
+}
diff --git a/Processing/udp/library.properties b/Processing/udp/library.properties
new file mode 100644
index 0000000..002506b
--- /dev/null
+++ b/Processing/udp/library.properties
@@ -0,0 +1,9 @@
+
+name = UDP
+authorList = [Stephane Cousot](http://ubaa.net/)
+url = http://ubaa.net/shared/processing/udp/
+category = Data
+sentence = Enables simple UDP communication, as well as multicast support.
+paragraph =
+version = 1
+prettyVersion = 0.1
diff --git a/Processing/udp/library/udp.jar b/Processing/udp/library/udp.jar
new file mode 100644
index 0000000..ef0e284
Binary files /dev/null and b/Processing/udp/library/udp.jar differ
diff --git a/Processing/udp/reference/index.htm b/Processing/udp/reference/index.htm
new file mode 100755
index 0000000..dc9e142
--- /dev/null
+++ b/Processing/udp/reference/index.htm
@@ -0,0 +1,23 @@
+
+
+
Create and manage unicast, broadcast or multicast socket to send and receive
+ datagram packets over the network.
+
+ The socket type is define at his initialyzation by the passed IP address.
+ To reach a host (interface) within a network, you need to specified the kind
+ of address:
+
+
An unicast address refer to a unique host within a subnet.
+
A broadcast address allow you to call every host within a subnet.
+
+
A multicast address allows to call a specific group of hosts within
+ the subnet. A multicast group is specified by a IP address in the range
+ 224.0.0.0 (reserved, should not be used) to
+ 239.255.255.255 inclusive, and by a standard UDP port number.
+
+ notes: the complete reference of special multicast addresses should be
+ found in the latest available version of the "Assigned Numbers RFC"
+
+
+ A packet sent to a unicast or broadcast address is only delivered to the
+ host identified by that address. To the contrary, when packet is send to a
+ multicast address, all interfaces identified by that address receive the data
+ .
+
+ To perform actions on receive and/or timeout events, you must implement
+ specific method(s) in your code. This method will be automatically called
+ when the socket receive incoming data or a timeout event. By default, the
+ "receive handler" is typically receive(byte[] data) but you can
+ retrieve more informations about the datagram packet, see
+ {@link UDP#setReceiveHandler(String name)} for more informations. In the same
+ logic, the default "timeout handler" is explicitly timeout().
+
+
+ note: currently applets are not allowed to use multicast sockets
+
Wait for incoming data, and call the appropriate handlers each time a
+ message is received. If the owner's class own the appropriate target
+ handler, this method send it the receive message as byte[], the sender
+ IP address and port.
+
+ This method force the current Thread to be ceased for a
+ temporary period. If you prefer listening without blocking the current
+ thread, use the {@link UDP#listen(int millis)} or
+ {@link UDP#listen(boolean on)} method instead.
Enable or disable the multicast socket loopback mode. By default loopback
+ is enable.
+
+ Setting loopback to false means this multicast socket does not want to
+ receive the data that it sends to the multicast group.
+ A null IP address will assign the packet address to the
+ local host. Use this method to send data to another application by
+ passing null as the destination address.
Set the maximum size of the packet that can be sent or receive on the
+ current socket. This value must be greater than 0, otherwise the buffer
+ size is set to the his default value.
+
+ return true if the new buffer value have been correctly set,
+ false otherwise.
+
+ note : this method has no effect if the socket is listening. To define
+ a new buffer size, call this method before implementing a new buffer in
+ memory. Explicitly before calling a receive methods.
+ By default, this method name is "receive" with one argument
+ representing the received data as byte[]. For more
+ flexibility, you can change this method with another useful method by
+ passing his name. You could get more informations by implementing two
+ additional arguments, a String representing the sender IP
+ address and a int representing the sender port :
+
+ void myCustomReceiveHandler(byte[] message, String ip, int port) {
+ // do something here...
+ }
+
Control the life-time of a datagram in the network for multicast packets
+ in order to indicates the scope of the multicasts (ie how far the packet
+ will travel).
+
+ The TTL value must be in range of 0 to 255 inclusive. The larger the
+ number, the farther the multicast packets will travel (by convention):
+
+ 0 -> restricted to the same host
+ 1 -> restricted to the same subnet (default)
+ <32 -> restricted to the same site
+ <64 -> restricted to the same region
+ <128 -> restricted to the same continent
+ <255 -> no restriction
+
+ The default value is 1, meaning that the datagram will not go beyond the
+ local subnet.
+
Enable or disable the multicast socket loopback mode. By default loopback
+ is enable.
+
+ Setting loopback to false means this multicast socket does not want to
+ receive the data that it sends to the multicast group.
Set the maximum size of the packet that can be sent or receive on the
+ current socket. This value must be greater than 0, otherwise the buffer
+ size is set to the his default value.
+
+ return true if the new buffer value have been correctly set,
+ false otherwise.
+
+ note : this method has no effect if the socket is listening. To define
+ a new buffer size, call this method before implementing a new buffer in
+ memory. Explicitly before calling a receive methods.
+ By default, this method name is "receive" with one argument
+ representing the received data as byte[]. For more
+ flexibility, you can change this method with another useful method by
+ passing his name. You could get more informations by implementing two
+ additional arguments, a String representing the sender IP
+ address and a int representing the sender port :
+
+ void myCustomReceiveHandler(byte[] message, String ip, int port) {
+ // do something here...
+ }
+
Control the life-time of a datagram in the network for multicast packets
+ in order to indicates the scope of the multicasts (ie how far the packet
+ will travel).
+
+ The TTL value must be in range of 0 to 255 inclusive. The larger the
+ number, the farther the multicast packets will travel (by convention):
+
+ 0 -> restricted to the same host
+ 1 -> restricted to the same subnet (default)
+ <32 -> restricted to the same site
+ <64 -> restricted to the same region
+ <128 -> restricted to the same continent
+ <255 -> no restriction
+
+ The default value is 1, meaning that the datagram will not go beyond the
+ local subnet.
+
+ * The socket type is define at his initialyzation by the passed IP address.
+ * To reach a host (interface) within a network, you need to specified the kind
+ * of address:
+ *
+ *
An unicast address refer to a unique host within a subnet.
+ *
A broadcast address allow you to call every host within a subnet.
+ *
+ *
A multicast address allows to call a specific group of hosts within
+ * the subnet. A multicast group is specified by a IP address in the range
+ * 224.0.0.0 (reserved, should not be used) to
+ * 239.255.255.255 inclusive, and by a standard UDP port number.
+ *
+ * notes: the complete reference of special multicast addresses should be
+ * found in the latest available version of the "Assigned Numbers RFC"
+ *
+ *
+ * A packet sent to a unicast or broadcast address is only delivered to the
+ * host identified by that address. To the contrary, when packet is send to a
+ * multicast address, all interfaces identified by that address receive the data
+ * .
+ *
+ * To perform actions on receive and/or timeout events, you must implement
+ * specific method(s) in your code. This method will be automatically called
+ * when the socket receive incoming data or a timeout event. By default, the
+ * "receive handler" is typically receive(byte[] data) but you can
+ * retrieve more informations about the datagram packet, see
+ * {@link UDP#setReceiveHandler(String name)} for more informations. In the same
+ * logic, the default "timeout handler" is explicitly timeout().
+ *
+ * Pass zero as port number, will let the system choose an
+ * available port.
+ *
+ * @param owner the target object to be call by the receive handler
+ * @param port local port to bind
+ */
+ public UDP( Object owner, int port ) {
+ this( owner, port, null );
+ }
+
+ /**
+ * Create a new datagram socket and binds it to the specified port on the
+ * specified local address or multicast group address.
+ *
+ * Pass zero as port number, will let the system choose an
+ * available port. The absence of an address, explicitly null
+ * as IP address will assign the socket to the Unspecified Address (Also
+ * called anylocal or wildcard address). To set up the socket as multicast
+ * socket, pass the group address to be joined. If this address is not a
+ * valid multicast address, a broadcast socket will be created by default.
+ *
+ * @param owner the target object to be call by the receive handler
+ * @param port local port to bind
+ * @param ip host address or group address
+ */
+ public UDP( Object owner, int port, String ip ) {
+
+ this.owner = owner;
+
+ // register this object to the PApplet,
+ // if it's used with Processing
+ try {
+ if ( owner instanceof PApplet ) ((PApplet)owner).registerMethod("dispose", this);
+ }
+ catch( NoClassDefFoundError e ) {;}
+
+ // open a new socket to the specified port/address
+ // and join the group if the multicast socket is required
+ try {
+
+ InetAddress addr = InetAddress.getByName(ip);
+ InetAddress host = (ip==null) ? (InetAddress)null: addr;
+
+ if ( !addr.isMulticastAddress() ) {
+ ucSocket = new DatagramSocket( port, host ); // as broadcast
+ log( "bound socket to host:"+address()+", port: "+port() );
+ }
+ else {
+ mcSocket = new MulticastSocket( port ); // as multicast
+ mcSocket.joinGroup( addr );
+ this.group = addr;
+ log( "bound multicast socket to host:"+address()+
+ ", port: "+port()+", group:"+group );
+ }
+ }
+ catch( IOException e ) {
+ // caught SocketException & UnknownHostException
+ error( "opening socket failed!"+
+ "\n\t> address:"+ip+", port:"+port+" [group:"+group+"]"+
+ "\n\t> "+e.getMessage()
+ );
+ }
+ catch( IllegalArgumentException e ) {
+ error( "opening socket failed!"+
+ "\n\t> bad arguments: "+e.getMessage()
+ );
+ }
+ catch( SecurityException e ) {
+ error( (isMulticast()?"could not joined the group":"warning")+
+ "\n\t> "+e.getMessage() );
+ }
+
+ }
+
+ /////////////////////////////// methods ///////////////////////////////
+
+ /**
+ * Close the socket. This method is automatically called by Processing when
+ * the PApplet shuts down.
+ *
+ * @see UDP#close()
+ */
+ public void dispose() {
+ close();
+ }
+
+ /**
+ * Close the actuel datagram socket and all associate resources.
+ */
+ public void close() {
+ if ( isClosed() ) return;
+
+ int port = port();
+ String ip = address();
+
+ // stop listening if needed
+ //listen( false );
+
+ // close the socket
+ try {
+ if ( isMulticast() ) {
+ if ( group!=null ) {
+ mcSocket.leaveGroup( group );
+ log( "leave group < address:"+group+" >" );
+ }
+ mcSocket.close();
+ mcSocket = null;
+ }
+ else {
+ ucSocket.close();
+ ucSocket = null;
+ }
+ }
+ catch( IOException e ) {
+ error( "Error while closing the socket!\n\t> " + e.getMessage() );
+ }
+ catch( SecurityException e ) {;}
+ finally {
+ log( "close socket < port:"+port+", address:"+ip+" >\n" );
+ }
+ }
+
+ /**
+ * Returns whether the current socket is closed or not.
+ * @return boolean
+ **/
+ public boolean isClosed() {
+ if ( isMulticast() ) return mcSocket==null ? true : mcSocket.isClosed();
+ return ucSocket==null ? true : ucSocket.isClosed();
+ }
+
+ /**
+ * Return the actual socket's local port, or -1 if the socket is closed.
+ * @return int
+ */
+ public int port() {
+ if ( isClosed() ) return -1;
+ return isMulticast()? mcSocket.getLocalPort() : ucSocket.getLocalPort();
+ }
+
+ /**
+ * Return the actual socket's local address, or null if the
+ * address correspond to any local address.
+ *
+ * @return String
+ */
+ public String address() {
+ if ( isClosed() ) return null;
+
+ InetAddress laddr = isMulticast() ? mcSocket.getLocalAddress():
+ ucSocket.getLocalAddress();
+ return laddr.isAnyLocalAddress() ? null : laddr.getHostAddress();
+ }
+
+ /**
+ * Send message to the current socket. Explicitly, send message to the
+ * multicast group/port or to itself.
+ *
+ * @param message the message to be send
+ *
+ * @see UDP#send(String message, String ip)
+ * @see UDP#send(String message, String ip, int port)
+ *
+ * @return boolean
+ */
+ public boolean send( String message ) {
+ return send( message.getBytes() );
+ }
+
+ /**
+ * Send data to the current socket. Explicitly, send data to the multicast
+ * group/port or to itself.
+ *
+ * @param buffer data to be send
+ *
+ * @see UDP#send(byte[] data, String ip)
+ * @see UDP#send(byte[] data, String ip, int port)
+ *
+ * @return boolean
+ */
+ public boolean send( byte[] buffer ) {
+ // probably if the group could not be joined for a security reason
+ if ( isMulticast() && group==null ) return false;
+
+ String ip = isMulticast() ? group.getHostAddress() : address();
+ return send( buffer, ip, port() );
+ }
+
+ /**
+ * Send message to the requested IP address, to the current socket port.
+ *
+ * @param message the message to be send
+ * @param ip the destination host's IP address
+ *
+ * @see UDP#send(String message)
+ * @see UDP#send(String message, String ip, int port)
+ *
+ * @return boolean
+ */
+ public boolean send( String message, String ip ) {
+ return send( message.getBytes(), ip );
+ }
+
+ /**
+ * Send data to the requested IP address, to the current socket port.
+ *
+ * @param buffer data to be send
+ * @param ip the destination host's IP address
+ *
+ * @see UDP#send(byte[] buffer)
+ * @see UDP#send(byte[] buffer, String ip, int port)
+ *
+ * @return boolean
+ */
+ public boolean send( byte[] buffer, String ip ) {
+ return send( buffer, ip, port() );
+ }
+
+ /**
+ * Send message to the requested IP address and port.
+ *
+ * A null IP address will assign the packet address to the
+ * local host. Use this method to send message to another application by
+ * passing null as the destination address.
+ *
+ * @param message the message to be send
+ * @param ip the destination host's IP address
+ * @param port the destination host's port
+ *
+ * @see UDP#send(String message)
+ * @see UDP#send(String message, String ip)
+ *
+ * @return boolean
+ */
+ public boolean send( String message, String ip, int port ) {
+ return send( message.getBytes(), ip, port );
+ }
+
+ /**
+ * Send data to the requested IP address and port.
+ *
+ * A null IP address will assign the packet address to the
+ * local host. Use this method to send data to another application by
+ * passing null as the destination address.
+ *
+ * @param buffer data to be send
+ * @param ip the destination host's IP address
+ * @param port the destination host's port
+ *
+ * @see UDP#send(byte[] buffer, String ip)
+ * @see UDP#send(byte[] buffer, String ip, int port)
+ *
+ * @return boolean
+ */
+ public boolean send( byte[] buffer, String ip, int port ) {
+
+ boolean success = false;
+ DatagramPacket pa = null;
+
+ try {
+
+ pa = new DatagramPacket( buffer, buffer.length, InetAddress.getByName(ip), port );
+
+ // send
+ if ( isMulticast() ) mcSocket.send( pa );
+ else ucSocket.send( pa );
+
+ success = true;
+ log( "send packet -> address:"+pa.getAddress()+
+ ", port:"+ pa.getPort() +
+ ", length: "+ pa.getLength()
+ );
+ }
+ catch( IOException e ) {
+ error( "could not send message!"+
+ "\t\n> port:"+port+
+ ", ip:"+ip+
+ ", buffer size: "+size+
+ ", packet length: "+pa.getLength()+
+ "\t\n> "+e.getMessage()
+ );
+ }
+ finally{ return success; }
+ }
+
+ /**
+ * Set the maximum size of the packet that can be sent or receive on the
+ * current socket. This value must be greater than 0, otherwise the buffer
+ * size is set to the his default value.
+ *
+ * return true if the new buffer value have been correctly set,
+ * false otherwise.
+ *
+ * note : this method has no effect if the socket is listening. To define
+ * a new buffer size, call this method before implementing a new buffer in
+ * memory. Explicitly before calling a receive methods.
+ *
+ * @param size the buffer size value in bytes or n<=0
+ * @return boolean
+ * @see UDP#getBuffer()
+ */
+ public boolean setBuffer( int size ) {
+ boolean done = false;
+
+ // do nothing if listening (block the thread otherwise)
+ if ( isListening() ) return done;
+
+ try {
+ // set the SO_SNDBUF and SO_RCVBUF value
+ if ( isMulticast() ) {
+ mcSocket.setSendBufferSize( size>0 ? size : BUFFER_SIZE );
+ mcSocket.setReceiveBufferSize( size>0 ? size : BUFFER_SIZE );
+ }
+ else {
+ ucSocket.setSendBufferSize( size>0 ? size : BUFFER_SIZE );
+ ucSocket.setReceiveBufferSize( size>0 ? size : BUFFER_SIZE );
+ }
+ // set the current buffer size
+ this.size = size>0 ? size : BUFFER_SIZE;
+ done = true;
+ }
+ catch( SocketException e ) {
+ error( "could not set the buffer!"+
+ "\n> "+e.getMessage()
+ );
+ }
+ finally{ return done; }
+ }
+
+ /**
+ * Return the actual socket buffer length
+ * @return int
+ * @see UDP#setBuffer(int size)
+ */
+ public int getBuffer() {
+ return size;
+ }
+
+ /**
+ * Returns whether the socket wait for incoming data or not.
+ * @return boolean
+ */
+ public boolean isListening() {
+ return listen;
+ }
+
+
+ /**
+ * Start/stop waiting constantly for incoming data.
+ *
+ * @param on the required listening status.
+ *
+ * @see UDP#listen()
+ * @see UDP#listen(int millis)
+ * @see UDP#setReceiveHandler(String name)
+ */
+ public void listen( boolean on ) {
+
+ listen = on;
+ timeout = 0;
+
+ // start
+ if ( on && thread==null && !isClosed() ) {
+ thread = new Thread( this );
+ thread.start();
+ }
+ // stop
+ if ( !on && thread!=null ) {
+ send( new byte[0] ); // unblock the thread with a dummy message
+ thread.interrupt();
+ thread = null;
+ }
+ }
+
+ /**
+ * Set the socket reception timeout and wait one time for incoming data.
+ * If the timeout period occured, the owner timeout() method is
+ * automatically called.
+ *
+ * @param millis the required timeout value in milliseconds.
+ *
+ * @see UDP#listen()
+ * @see UDP#listen(boolean on)
+ */
+ public void listen( int millis ) {
+ if ( isClosed() ) return;
+
+ listen = true;
+ timeout = millis;
+
+ // unblock the thread with a dummy message, if already listening
+ if ( thread!=null ) send( new byte[0] );
+ if ( thread==null ) {
+ thread = new Thread( this );
+ thread.start();
+ }
+ }
+
+ /**
+ * Wait for incoming data, and call the appropriate handlers each time a
+ * message is received. If the owner's class own the appropriate target
+ * handler, this method send it the receive message as byte[], the sender
+ * IP address and port.
+ *
+ * This method force the current Thread to be ceased for a
+ * temporary period. If you prefer listening without blocking the current
+ * thread, use the {@link UDP#listen(int millis)} or
+ * {@link UDP#listen(boolean on)} method instead.
+ *
+ * @see UDP#listen()
+ * @see UDP#listen(boolean on)
+ * @see UDP#setReceiveHandler(String name)
+ */
+ public void listen() {
+ try {
+
+ byte[] buffer = new byte[ size ];
+ DatagramPacket pa = new DatagramPacket(buffer,buffer.length);
+
+ // wait
+ if ( isMulticast() ) {
+ mcSocket.setSoTimeout( timeout );
+ mcSocket.receive( pa ); // <-- block the Thread
+ }
+ else {
+ ucSocket.setSoTimeout( timeout );
+ ucSocket.receive( pa ); // <-- block
+ }
+
+
+ log( "receive packet <- from "+pa.getAddress()+
+ ", port:"+ pa.getPort() +
+ ", length: "+ pa.getLength()
+ );
+
+
+ // get the required data only (not all the buffer)
+ // and send it to the appropriate target handler, if needed
+ if ( pa.getLength()!=0 ) {
+
+ byte[] data = new byte[ pa.getLength() ];
+ System.arraycopy( pa.getData(), 0, data, 0, data.length );
+
+ try {
+ // try with one argument first > byte[]
+ callReceiveHandler( data );
+ }
+ catch( NoSuchMethodException e ) {
+ // try with many argument > byte[], String, int
+ callReceiveHandler( data,
+ pa.getAddress().getHostAddress(),
+ pa.getPort()
+ );
+ }
+ }
+ }
+ catch( NullPointerException e ) {
+ // *socket=null from the close() method;
+ listen = false;
+ thread = null;
+ }
+ catch( IOException e ) {
+
+ listen = false;
+ thread = null;
+
+ if ( e instanceof SocketTimeoutException ) callTimeoutHandler();
+ else {
+ // do not print "Socket closed" error
+ // if the method close() has been called
+ if ( ucSocket!=null && mcSocket!=null )
+ error( "listen failed!\n\t> "+e.getMessage() );
+ }
+ }
+ }
+
+ /**
+ * Wait for incoming datagram packets. This method is called automaticlly,
+ * you do not need to call it.
+ */
+ public void run() {
+ while ( listen ) listen();
+ }
+
+ /**
+ * Register the target's receive handler.
+ *
+ * By default, this method name is "receive" with one argument
+ * representing the received data as byte[]. For more
+ * flexibility, you can change this method with another useful method by
+ * passing his name. You could get more informations by implementing two
+ * additional arguments, a String representing the sender IP
+ * address and a int representing the sender port :
+ *
+ *
+ * @param name the receive handler name
+ * @see UDP#setTimeoutHandler(String name)
+ */
+ public void setReceiveHandler( String name ) {
+ this.receiveHandler = name;
+ }
+
+ /**
+ * Call the default receive target handler method.
+ *
+ * @param data the data to be passed
+ * @throws NoSuchMethodException
+ */
+ private void callReceiveHandler( byte[] data )
+ throws NoSuchMethodException {
+
+ Class[] types; // arguments class types
+ Object[] values; // arguments values
+ Method method;
+
+ try {
+ types = new Class[]{ data.getClass() };
+ values = new Object[]{ data };
+ method = owner.getClass().getMethod(receiveHandler, types);
+ method.invoke( owner, values );
+ }
+ catch( IllegalAccessException e ) { error(e.getMessage()); }
+ catch( InvocationTargetException e ) { e.printStackTrace(); }
+ }
+
+ /**
+ * Call the receive target handler implemented with the optional arguments.
+ *
+ * @param data the data to be passed
+ * @param ip the IP adress to be passed
+ * @param port the port number to be passed
+ */
+ private void callReceiveHandler( byte[] data, String ip, int port ) {
+
+ Class[] types; // arguments class types
+ Object[] values; // arguments values
+ Method method;
+
+ try {
+ types = new Class[]{ data.getClass(),
+ ip.getClass(),
+ Integer.TYPE
+ };
+ values = new Object[]{ data,
+ ip,
+ new Integer(port)
+ };
+ method = owner.getClass().getMethod(receiveHandler, types);
+ method.invoke( owner, values );
+ }
+ catch( NoSuchMethodException e ) {;}
+ catch( IllegalAccessException e ) { error(e.getMessage()); }
+ catch( InvocationTargetException e ) { e.printStackTrace(); }
+ }
+
+ /**
+ * Register the target's timeout handler. By default, this method name is
+ * "timeout" with no argument.
+ *
+ * @param name the timeout handler name
+ * @see UDP#setReceiveHandler(String name)
+ */
+ public void setTimeoutHandler( String name ) {
+ this.timeoutHandler = name;
+ }
+
+ /**
+ * Call the timeout target method when the socket received a timeout event.
+ * The method name to be implemented in your code is timeout().
+ */
+ private void callTimeoutHandler() {
+ try {
+ Method m = owner.getClass().getDeclaredMethod(timeoutHandler, null);
+ m.invoke( owner, null );
+ }
+ catch( NoSuchMethodException e ) {;}
+ catch( IllegalAccessException e ) { error(e.getMessage()); }
+ catch( InvocationTargetException e ) { e.printStackTrace(); }
+ }
+
+ /**
+ * Returns whether the opened datagram socket is a multicast socket or not.
+ * @return boolean
+ */
+ public boolean isMulticast() {
+ return ( mcSocket!=null );
+ }
+
+ /**
+ * Returns whether the multicast socket is joined to a group address.
+ * @return boolean
+ */
+ public boolean isJoined() {
+ return ( group!=null );
+ }
+
+ /**
+ * Returns whether the opened socket send broadcast message socket or not.
+ * @return boolean
+ */
+ public boolean isBroadcast() {
+ boolean result = false;
+ try {
+ result = (ucSocket==null) ? false : ucSocket.getBroadcast();
+ }
+ catch( SocketException e ) { error( e.getMessage() ); }
+ finally { return result; }
+ }
+
+ /**
+ * Enables or disables the ability of the current process to send broadcast
+ * messages.
+ * @return boolean
+ */
+ public boolean broadcast( boolean on ) {
+ boolean done = false;
+ try {
+ if ( ucSocket!=null ) {
+ ucSocket.setBroadcast( on );
+ done = isBroadcast();
+ }
+ }
+ catch( SocketException e ) { error( e.getMessage() ); }
+ finally { return done; }
+ }
+
+ /**
+ * Enable or disable the multicast socket loopback mode. By default loopback
+ * is enable.
+ *
+ * Setting loopback to false means this multicast socket does not want to
+ * receive the data that it sends to the multicast group.
+ *
+ * @param on local loopback of multicast datagrams
+ */
+ public void loopback( boolean on ) {
+ try {
+ if ( isMulticast() ) mcSocket.setLoopbackMode( !on );
+ }
+ catch( SocketException e ) {
+ error( "could not set the loopback mode!\n\t>"+e.getMessage() );
+ }
+ }
+
+ /**
+ * Returns whether the multicast socket loopback mode is enable or not.
+ * @return boolean
+ */
+ public boolean isLoopback() {
+ try {
+ if ( isMulticast() && !isClosed() )
+ return !mcSocket.getLoopbackMode();
+ }
+ catch( SocketException e ) {
+ error( "could not get the loopback mode!\n\t> "+e.getMessage() );
+ }
+ return false;
+ }
+
+ /**
+ * Control the life-time of a datagram in the network for multicast packets
+ * in order to indicates the scope of the multicasts (ie how far the packet
+ * will travel).
+ *
+ * The TTL value must be in range of 0 to 255 inclusive. The larger the
+ * number, the farther the multicast packets will travel (by convention):
+ *
+ * 0 -> restricted to the same host
+ * 1 -> restricted to the same subnet (default)
+ * <32 -> restricted to the same site
+ * <64 -> restricted to the same region
+ * <128 -> restricted to the same continent
+ * <255 -> no restriction
+ *
+ * The default value is 1, meaning that the datagram will not go beyond the
+ * local subnet.
+ *
+ * return true if no error occured.
+ *
+ * @param ttl the "Time to Live" value
+ * @return boolean
+ * @see UDP#getTimeToLive()
+ */
+ public boolean setTimeToLive( int ttl ) {
+ try {
+ if ( isMulticast() && !isClosed() ) mcSocket.setTimeToLive( ttl );
+ return true;
+ }
+ catch( IOException e ) {
+ error( "setting the default \"Time to Live\" value failed!"+
+ "\n\t> "+e.getMessage() );
+ }
+ catch( IllegalArgumentException e ) {
+ error( "\"Time to Live\" value must be in the range of 0-255" );
+ }
+ return false;
+ }
+
+ /**
+ * Return the "Time to Live" value or -1 if an error occurred (or if
+ * the current socket is not a multicast socket).
+ *
+ * @return int
+ * @see UDP#setTimeToLive(int ttl)
+ */
+ public int getTimeToLive() {
+ try {
+ if ( isMulticast() && !isClosed() )
+ return mcSocket.getTimeToLive();
+ }
+ catch( IOException e ) {
+ error( "could not retrieve the current time-to-live value!"+
+ "\n\t> "+ e.getMessage() );
+ }
+ return -1;
+ }
+
+ /**
+ * Enable or disable output process log.
+ */
+ public void log( boolean on ) {
+ log = on;
+ }
+
+ /**
+ * Output message to the standard output stream.
+ * @param out the output message
+ */
+ private void log( String out ) {
+
+ Date date = new Date();
+
+ // define the "header" to retrieve at least the principal socket
+ // informations : the host/port where the socket is bound.
+ if ( !log && header.equals("") )
+ header = "-- UDP session started at "+date+" --\n-- "+out+" --\n";
+
+ // print out
+ if ( log ) {
+
+ String pattern = "yy-MM-dd HH:mm:ss.S Z";
+ String sdf = new SimpleDateFormat(pattern).format( date );
+ System.out.println( header+"["+sdf+"] "+out );
+ header = ""; // forget header
+ }
+ }
+
+ /**
+ * Output error messages to the standard error stream.
+ * @param err the error string
+ */
+ private void error( String err ) {
+ System.err.println( err );
+ }
+}
diff --git a/README.md b/README.md
index 311afeb..87fb7e1 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,20 @@
android-heart-rate-monitor
==========================
+
-Android heart rate monitor
+Android heart rate monitor and Processing heart rate receiver
## Introduction
-Android based heart rate monitor which uses the camera and its flash to determine the users heart rate in beats per minute.
+Android based heart rate monitor which uses the camera and its flash to determine the users heart rate in beats per minute and send via UDP.
+A Processing patch is provided to receive the data send.
+* Created by Joey van der Bie
+* Github: http://github.com/joeyvanderbie/android-heart-rate-monitor
+
+The original android-heart-rate-monitor info:
* Created by Justin Wetherell
-* Google: http://code.google.com/p/android-heart-rate-monitor
* Github: http://github.com/phishman3579/android-heart-rate-monitor
-* LinkedIn: http://www.linkedin.com/in/phishman3579
-* E-mail: phishman3579@gmail.com
-* Twitter: http://twitter.com/phishman3579
## Details
The App uses the PreviewCallback mechanism to grab the latest image from the preview frame. It then processes the YUV420SP data and pulls out all the red pixel values.
@@ -21,9 +23,23 @@ It uses data smoothing in a Integer array to figure out the average red pixel va
The App will collect data in ten second chunks and add the beets per minute to another Integer array which is used to smooth the beats per minute data.
+If you provide an ip adres and port the app will send your heart beat to the provided Processing patch.
+
## How To
-All you have to do is open the HeartRateMonitor App and then hold the tip of your index finger over the camera lens of your phone. The entire camera preview image should be red with a lighter area where the tip of your finger is touching. Do not press too hard or you will cut off circulation which will result in an inaccurate reading.
+Open the HeartRateMonitor App and hold the tip of your index finger over the camera lens of your phone. The entire camera preview image should be red with a lighter area where the tip of your finger is touching. Do not press too hard or you will cut off circulation which will result in an inaccurate reading.
+
+After ten seconds it will compute your heart rate. It'll take between ten and thirty seconds to get an accurate heart rate.
+
+If you provide an IP adres and port the app will send your heart beat to the provided Processing patch (default port is 6000)
-After a second or two, you should see the Android icon on the top of the screen start to flash red when it senses a heart beat. After ten seconds it will compute your heart rate and update the number next to the Android icon. It'll take between ten and thirty seconds to get an accurate heart rate.
+To use the Processing patch:
+- download Processing 2.0 from processing.org
+- copy the udp folder to the library folder of Processing
+- open de HeartRate_udp.pd patch in Processing
+Screenshots
+-----------------------------------------
+
+
+
diff --git a/ic_launcher-web.png b/ic_launcher-web.png
new file mode 100644
index 0000000..0f58431
Binary files /dev/null and b/ic_launcher-web.png differ
diff --git a/libs/GraphView-3.1.1.jar b/libs/GraphView-3.1.1.jar
deleted file mode 100644
index 94333ec..0000000
Binary files a/libs/GraphView-3.1.1.jar and /dev/null differ
diff --git a/libs/graphview-3.1.jar b/libs/graphview-3.1.jar
new file mode 100644
index 0000000..d03f1de
Binary files /dev/null and b/libs/graphview-3.1.jar differ
diff --git a/res/drawable-hdpi/ic_launcher.png b/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..9870684
Binary files /dev/null and b/res/drawable-hdpi/ic_launcher.png differ
diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..642184f
Binary files /dev/null and b/res/drawable-mdpi/ic_launcher.png differ
diff --git a/res/drawable-xhdpi/ic_launcher.png b/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..f3d8f71
Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/res/drawable-xxhdpi/ic_launcher.png b/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..df1ee33
Binary files /dev/null and b/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/res/layout/main.xml b/res/layout/main.xml
index 985fc99..627519e 100644
--- a/res/layout/main.xml
+++ b/res/layout/main.xml
@@ -1,46 +1,98 @@
-
+
+
+
+
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:orientation="vertical"
+ android:id="@+id/udp_panel" >
-
+
+
-
-
-
-
+ android:layout_height="wrap_content"
+ android:text="@string/label_data_to_target"
+ android:layout_marginTop="@dimen/activity_vertical_margin" />
+
+
+
+
+
+
+
+
+
+
-
-
+ android:layout_height="wrap_content" >
-
-
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
new file mode 100644
index 0000000..55c1e59
--- /dev/null
+++ b/res/values/dimens.xml
@@ -0,0 +1,7 @@
+
+
+
+ 16dp
+ 16dp
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8cb6e55..d244bc7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1,5 +1,15 @@
--
- HeartRateMonitor
+ Heart rate
+
+ Target IP
+ Target port
+ Stream data to IP
+
+
+ Invalid IP address
+ Unknown UDP network problem
+ Unknown IMU sensor problem
+ Warning: Not connected to a WIFI network.
diff --git a/screenshots/blank.png b/screenshots/blank.png
new file mode 100644
index 0000000..90604c6
Binary files /dev/null and b/screenshots/blank.png differ
diff --git a/screenshots/blank_small.png b/screenshots/blank_small.png
new file mode 100644
index 0000000..e92e619
Binary files /dev/null and b/screenshots/blank_small.png differ
diff --git a/screenshots/device-2014-06-07-130920.png b/screenshots/device-2014-06-07-130920.png
new file mode 100644
index 0000000..b838fef
Binary files /dev/null and b/screenshots/device-2014-06-07-130920.png differ
diff --git a/screenshots/featured.png b/screenshots/featured.png
new file mode 100644
index 0000000..55e5c29
Binary files /dev/null and b/screenshots/featured.png differ
diff --git a/screenshots/featured.svg b/screenshots/featured.svg
new file mode 100644
index 0000000..d17baeb
--- /dev/null
+++ b/screenshots/featured.svg
@@ -0,0 +1,1564 @@
+
+
+
+
diff --git a/screenshots/heartrate.png b/screenshots/heartrate.png
new file mode 100644
index 0000000..6c04b53
Binary files /dev/null and b/screenshots/heartrate.png differ
diff --git a/screenshots/heartrate_small.png b/screenshots/heartrate_small.png
new file mode 100644
index 0000000..ed75aec
Binary files /dev/null and b/screenshots/heartrate_small.png differ
diff --git a/screenshots/ic_launcher.png b/screenshots/ic_launcher.png
new file mode 100644
index 0000000..2b979fd
Binary files /dev/null and b/screenshots/ic_launcher.png differ
diff --git a/screenshots/icon.png b/screenshots/icon.png
new file mode 100644
index 0000000..edd3662
Binary files /dev/null and b/screenshots/icon.png differ
diff --git a/screenshots/icon.svg b/screenshots/icon.svg
new file mode 100644
index 0000000..2b206a3
--- /dev/null
+++ b/screenshots/icon.svg
@@ -0,0 +1,1554 @@
+
+
+
+
diff --git a/screenshots/promo.png b/screenshots/promo.png
new file mode 100644
index 0000000..1c9b68c
Binary files /dev/null and b/screenshots/promo.png differ
diff --git a/screenshots/promo.svg b/screenshots/promo.svg
new file mode 100644
index 0000000..3ffbb45
--- /dev/null
+++ b/screenshots/promo.svg
@@ -0,0 +1,1565 @@
+
+
+
+
diff --git a/screenshots/udp.png b/screenshots/udp.png
new file mode 100644
index 0000000..fe95c91
Binary files /dev/null and b/screenshots/udp.png differ
diff --git a/screenshots/udp_small.png b/screenshots/udp_small.png
new file mode 100644
index 0000000..a483ae6
Binary files /dev/null and b/screenshots/udp_small.png differ
diff --git a/src/com/jwetherell/heart_rate_monitor/HeartRateMonitor.java b/src/com/jwetherell/heart_rate_monitor/HeartRateMonitor.java
deleted file mode 100644
index cd48c36..0000000
--- a/src/com/jwetherell/heart_rate_monitor/HeartRateMonitor.java
+++ /dev/null
@@ -1,393 +0,0 @@
-package com.jwetherell.heart_rate_monitor;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.res.Configuration;
-import android.hardware.Camera;
-import android.hardware.Camera.PreviewCallback;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.util.Log;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import com.jjoe64.graphview.GraphView;
-import com.jjoe64.graphview.GraphViewSeries;
-import com.jjoe64.graphview.LineGraphView;
-import org.apache.commons.collections4.queue.CircularFifoQueue;
-import org.apache.commons.lang3.ArrayUtils;
-
-
-/**
- * This class extends Activity to handle a picture preview, process the preview
- * for a red values and determine a heart beat.
- *
- * @author Justin Wetherell
- */
-public class HeartRateMonitor extends Activity {
-
- public static final String TAG = "HeartRateMonitor";
- private static final AtomicBoolean processing = new AtomicBoolean(false);
-
- private static SurfaceView preview = null;
- private static SurfaceHolder previewHolder = null;
- private static Camera camera = null;
- private static View image = null;
- private static TextView text = null;
-
- private static WakeLock wakeLock = null;
-
- private static int averageIndex = 0;
- private static final int averageArraySize = 100;
- private static final int[] averageArray = new int[averageArraySize];
-
- public static enum TYPE {
- GREEN, RED
- };
-
- private static TYPE currentType = TYPE.GREEN;
-
- public static TYPE getCurrent() {
- return currentType;
- }
-
- private static int beatsIndex = 0;
- private static final int beatsArraySize = 14;
- private static final int[] beatsArray = new int[beatsArraySize];
- private static final long[] timesArray = new long[beatsArraySize];
- private static double beats = 0;
- private static long startTime = 0;
-
- private static GraphView graphView;
- private static GraphViewSeries exampleSeries;
-
- static int counter = 0;
-
- private static final int sampleSize = 256;
- private static final CircularFifoQueue sampleQueue = new CircularFifoQueue(sampleSize);
- private static final CircularFifoQueue timeQueue = new CircularFifoQueue(sampleSize);
-
- public static final CircularFifoQueue bpmQueue = new CircularFifoQueue(40);
-
- private static final FFT fft = new FFT(sampleSize);
-
- private Metronome metronome;
-
-
- static BufferedWriter out;
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- preview = (SurfaceView) findViewById(R.id.preview);
- previewHolder = preview.getHolder();
- previewHolder.addCallback(surfaceCallback);
- previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
-
- image = findViewById(R.id.image);
- text = (TextView) findViewById(R.id.text);
-
- PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
- wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "DoNotDimScreen");
-
-
- this.graphView = new LineGraphView(
- this // context
- , "GraphViewDemo" // heading
- );
- graphView.setScrollable(true);
-
- this.exampleSeries = new GraphViewSeries(new GraphView.GraphViewData[] {});
- this.graphView.addSeries(this.exampleSeries);
-// this.graphView.addSeries(new GraphViewSeries(new GraphView.GraphViewData[] {
-// new GraphView.GraphViewData(1, 2.0d)
-// , new GraphView.GraphViewData(2, 1.5d)
-// , new GraphView.GraphViewData(3, 2.5d)
-// , new GraphView.GraphViewData(4, 1.0d)
-// })); // data
- graphView.setViewPort(0,20);
-
- LinearLayout layout = (LinearLayout) findViewById(R.id.graph1);
- layout.addView(graphView);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onResume() {
- super.onResume();
-
- wakeLock.acquire();
-
- camera = Camera.open();
-
- startTime = System.currentTimeMillis();
-
- File file = new File(this.getFilesDir(), "data.txt");
- try {
- out = new BufferedWriter(new FileWriter(file));
- } catch (IOException e) {
- Log.e(TAG,"unexpected Error", e);
- e.printStackTrace();
- }
-
- metronome = new Metronome(this);
- metronome.start();
-
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onPause() {
- super.onPause();
-
- wakeLock.release();
-
- camera.setPreviewCallback(null);
- camera.stopPreview();
- camera.release();
- camera = null;
- try {
- out.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- bpm = -1;
- }
-
- public static int bpm;
- private static PreviewCallback previewCallback = new PreviewCallback() {
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onPreviewFrame(byte[] data, Camera cam) {
- if (data == null) throw new NullPointerException();
- Camera.Size size = cam.getParameters().getPreviewSize();
- if (size == null) throw new NullPointerException();
-
- if (!processing.compareAndSet(false, true)) return;
-
- int width = size.width;
- int height = size.height;
-
- int imgAvg = ImageProcessing.decodeYUV420SPtoRedAvg(data.clone(), height, width);
-
- try {
- out.write(imgAvg + ",");
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- sampleQueue.add((double)imgAvg);
- timeQueue.add(System.currentTimeMillis());
-
- double[] y = new double[sampleSize];
- double[] x = ArrayUtils.toPrimitive((Double[]) sampleQueue.toArray(new Double[0]));
- long[] time = ArrayUtils.toPrimitive((Long[]) timeQueue.toArray(new Long[0]));
-
- if (timeQueue.size() < sampleSize)
- {
- processing.set(false);
-
- return;
- }
-
- double Fs = ((double)timeQueue.size()) / (double)(time[timeQueue.size() - 1] - time[0]) * 1000;
-
- fft.fft(x,y);
-
- int low = Math.round((float)(sampleSize * 40 / 60 / Fs));
- int high = Math.round((float)(sampleSize * 160 / 60 / Fs));
-
- int bestI = 0;
- double bestV = 0;
- for (int i = low; i < high; i++){
- double value = Math.sqrt(x[i]*x[i] + y[i]*y[i]);
-
-
- if (value > bestV)
- {
- bestV = value;
- bestI = i;
- }
- }
-
- bpm = Math.round((float)(bestI * Fs * 60 / sampleSize));
- bpmQueue.add(bpm);
-
- text.setText(String.valueOf(bpm) + "," + String.valueOf(Math.round((float) Fs)));
-
- counter++;
- exampleSeries.appendData(new GraphView.GraphViewData(counter, imgAvg), true, 1000);
- processing.set(false);
-
-
-//
-//
-//
-//// try {
-//// out.write(imgAvg + ",");
-//// } catch (IOException e) {
-//// e.printStackTrace();
-//// }
-//
-//
-// // Log.i(TAG, "imgAvg="+imgAvg);
-// if (imgAvg == 0 || imgAvg == 255) {
-// processing.set(false);
-// return;
-// }
-//
-// int averageArrayAvg = 0;
-// int averageArrayCnt = 0;
-// // Loop over each element in avgArray and sum them up!
-// // averageArray holds the last [length] samples. This will be compared against imgAvg
-// // to determine if we're experiencing a beat.
-// for (int i = 0; i < averageArray.length; i++) {
-// if (averageArray[i] > 0) {
-// averageArrayAvg += averageArray[i];
-// averageArrayCnt++;
-// }
-// }
-//
-// if (averageIndex == averageArraySize) averageIndex = 0;
-// averageArray[averageIndex] = imgAvg;
-// averageIndex++;
-//
-// int rollingAverage = (averageArrayCnt > 0) ? (averageArrayAvg / averageArrayCnt) : 0;
-// TYPE newType = currentType;
-// if (imgAvg < rollingAverage) {
-// newType = TYPE.RED;
-//
-//
-// if (newType != currentType) {
-// beatsIndex++;
-// timesArray[beatsIndex % beatsArraySize] = System.currentTimeMillis();
-// beats++;
-// Log.d(TAG, "BEAT!!");
-// }
-// } else if (imgAvg > rollingAverage) {
-// newType = TYPE.GREEN;
-// }
-//
-//
-//
-// // Transitioned from one state to another to the same
-// if (newType != currentType) {
-// currentType = newType;
-// image.postInvalidate();
-// }
-//
-// long endTime = System.currentTimeMillis();
-// timesArray[beatsIndex % beatsArraySize] = System.currentTimeMillis();
-//
-//
-// counter++;
-//
-// exampleSeries.appendData(new GraphView.GraphViewData(counter, imgAvg), true, 1000);
-//
-// double totalTimeInSecs = (timesArray[beatsIndex % beatsArraySize] - timesArray[(beatsIndex + 1) % beatsArraySize]) / 1000d;
-// if (beatsIndex % beatsArraySize == 0)
-// {
-// Log.d(TAG, "time diff=" + totalTimeInSecs);
-// }
-//
-// double bps = ((beatsArraySize-1) / totalTimeInSecs);
-// int bpm = (int) (bps * 60d);
-//
-// text.setText(String.valueOf(bpm));
-//
-//
-// processing.set(false);
- }
- };
-
- private static SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- try {
- camera.setPreviewDisplay(previewHolder);
- camera.setPreviewCallback(previewCallback);
- } catch (Throwable t) {
- Log.e("PreviewDemo-surfaceCallback", "Exception in setPreviewDisplay()", t);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- Camera.Parameters parameters = camera.getParameters();
- parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
- Camera.Size size = getSmallestPreviewSize(width, height, parameters);
- if (size != null) {
- parameters.setPreviewSize(size.width, size.height);
- Log.d(TAG, "Using width=" + size.width + " height=" + size.height);
- }
- camera.setParameters(parameters);
- camera.startPreview();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- // Ignore
- }
- };
-
- private static Camera.Size getSmallestPreviewSize(int width, int height, Camera.Parameters parameters) {
- Camera.Size result = null;
-
- for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
- if (size.width <= width && size.height <= height) {
- if (result == null) {
- result = size;
- } else {
- int resultArea = result.width * result.height;
- int newArea = size.width * size.height;
-
- if (newArea < resultArea) result = size;
- }
- }
- }
-
- return result;
- }
-}
diff --git a/src/com/jwetherell/heart_rate_monitor/FFT.java b/src/com/vanderbie/heart_rate_monitor/FFT.java
similarity index 97%
rename from src/com/jwetherell/heart_rate_monitor/FFT.java
rename to src/com/vanderbie/heart_rate_monitor/FFT.java
index ec59baf..3b7596c 100644
--- a/src/com/jwetherell/heart_rate_monitor/FFT.java
+++ b/src/com/vanderbie/heart_rate_monitor/FFT.java
@@ -1,4 +1,4 @@
-package com.jwetherell.heart_rate_monitor;
+package com.vanderbie.heart_rate_monitor;
public class FFT {
diff --git a/src/com/vanderbie/heart_rate_monitor/HeartRateMonitor.java b/src/com/vanderbie/heart_rate_monitor/HeartRateMonitor.java
new file mode 100644
index 0000000..997a85f
--- /dev/null
+++ b/src/com/vanderbie/heart_rate_monitor/HeartRateMonitor.java
@@ -0,0 +1,526 @@
+package com.vanderbie.heart_rate_monitor;
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.commons.collections4.queue.CircularFifoQueue;
+import org.apache.commons.lang3.ArrayUtils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.hardware.Camera;
+import android.hardware.Camera.PreviewCallback;
+import android.net.ConnectivityManager;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import com.jjoe64.graphview.GraphView;
+import com.jjoe64.graphview.GraphViewSeries;
+import com.jjoe64.graphview.GraphViewSeries.GraphViewSeriesStyle;
+import com.jjoe64.graphview.LineGraphView;
+
+/**
+ * This class extends Activity to handle a picture preview, process the preview
+ * for a red values and determine a heart beat.
+ *
+ * @author Joey van der Bie
+ * @author Justin Wetherell
+ */
+public class HeartRateMonitor extends Activity {
+
+ public static final String TAG = "HeartRateMonitor";
+ private static final AtomicBoolean processing = new AtomicBoolean(false);
+
+ private static SurfaceView preview = null;
+ private static SurfaceHolder previewHolder = null;
+ private static Camera camera = null;
+ // private static View image = null;
+ private static TextView text = null;
+
+ private static WakeLock wakeLock = null;
+
+ private static int averageIndex = 0;
+ private static final int averageArraySize = 100;
+ private static final int[] averageArray = new int[averageArraySize];
+
+ public static enum TYPE {
+ GREEN, RED
+ };
+
+ private static TYPE currentType = TYPE.GREEN;
+
+ public static TYPE getCurrent() {
+ return currentType;
+ }
+
+ private static int beatsIndex = 0;
+ private static final int beatsArraySize = 14;
+ private static final int[] beatsArray = new int[beatsArraySize];
+ private static final long[] timesArray = new long[beatsArraySize];
+ private static double beats = 0;
+ private static long startTime = 0;
+
+ private static GraphView graphView;
+ private static GraphViewSeries exampleSeries;
+
+ static int counter = 0;
+
+ private static final int sampleSize = 256;
+ private static final CircularFifoQueue sampleQueue = new CircularFifoQueue(
+ sampleSize);
+ private static final CircularFifoQueue timeQueue = new CircularFifoQueue(
+ sampleSize);
+
+ public static final CircularFifoQueue bpmQueue = new CircularFifoQueue(40);
+
+ private static final FFT fft = new FFT(sampleSize);
+
+ private Metronome metronome;
+
+// static BufferedWriter out;
+
+ private boolean streamData = false;
+ public static DatagramSocket mSocket = null;
+ public static DatagramPacket mPacket = null;
+ TextView mIP_Adress;
+ TextView mPort;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ preview = (SurfaceView) findViewById(R.id.preview);
+ previewHolder = preview.getHolder();
+ previewHolder.addCallback(surfaceCallback);
+ previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+
+ // image = findViewById(R.id.image);
+ text = (TextView) findViewById(R.id.text);
+
+ PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ wakeLock = pm
+ .newWakeLock(PowerManager.FULL_WAKE_LOCK, "DoNotDimScreen");
+
+ this.graphView = new LineGraphView(this // context
+ , "Heart rate" // heading
+ );
+ graphView.setScrollable(true);
+
+ this.exampleSeries = new GraphViewSeries("Heart rate",
+ new GraphViewSeriesStyle(Color.RED, 8),
+ new GraphView.GraphViewData[] {});
+ this.graphView.addSeries(this.exampleSeries);
+ // this.graphView.addSeries(new GraphViewSeries(new
+ // GraphView.GraphViewData[] {
+ // new GraphView.GraphViewData(1, 2.0d)
+ // , new GraphView.GraphViewData(2, 1.5d)
+ // , new GraphView.GraphViewData(3, 2.5d)
+ // , new GraphView.GraphViewData(4, 1.0d)
+ // })); // data
+ graphView.setViewPort(0, 60);
+ graphView.setVerticalLabels(new String[] { "" });
+ graphView.setHorizontalLabels(new String[] { "" });
+ graphView.getGraphViewStyle().setVerticalLabelsWidth(1);
+ graphView.setBackgroundColor(Color.WHITE);
+
+ LinearLayout layout = (LinearLayout) findViewById(R.id.graph1);
+ layout.addView(graphView);
+
+ mIP_Adress = (TextView) findViewById(R.id.target_ip);
+ mPort = (TextView) findViewById(R.id.target_port);
+ ToggleButton streamDataButton = (ToggleButton) findViewById(R.id.stream_data_toggle);
+ streamDataButton.setChecked(streamData);
+ streamDataButton
+ .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ streamData = isChecked;
+ if (streamData) {
+ start_UDP_Stream();
+ } else {
+ stop_UDP_Stream();
+ }
+ }
+ });
+
+ Button toggle = (Button) findViewById(R.id.toggleUDPPanel);
+ toggle.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ LinearLayout udpPanel = (LinearLayout) findViewById(R.id.udp_panel);
+ if(udpPanel.getVisibility() == View.GONE){
+ udpPanel.setVisibility(View.VISIBLE);
+ }else{
+ udpPanel.setVisibility(View.GONE);
+
+ }
+
+ }
+ });
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ wakeLock.acquire();
+
+ camera = Camera.open();
+ startTime = System.currentTimeMillis();
+
+ // File file = new File(this.getFilesDir(), "data.txt");
+ // try {
+ // out = new BufferedWriter(new FileWriter(file));
+ // } catch (IOException e) {
+ // Log.e(TAG,"unexpected Error", e);
+ // e.printStackTrace();
+ // }
+
+ metronome = new Metronome(this);
+ metronome.start();
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ wakeLock.release();
+
+ camera.setPreviewCallback(null);
+ camera.stopPreview();
+ camera.release();
+ camera = null;
+ // try {
+ // out.close();
+ // } catch (IOException e) {
+ // e.printStackTrace();
+ // }
+
+ bpm = -1;
+ }
+
+ public static int bpm;
+ private static PreviewCallback previewCallback = new PreviewCallback() {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPreviewFrame(byte[] data, Camera cam) {
+ if (data == null)
+ throw new NullPointerException();
+ Camera.Size size = cam.getParameters().getPreviewSize();
+ if (size == null)
+ throw new NullPointerException();
+
+ if (!processing.compareAndSet(false, true))
+ return;
+
+ int width = size.width;
+ int height = size.height;
+
+ int imgAvg = ImageProcessing.decodeYUV420SPtoRedAvg(data.clone(),
+ height, width);
+
+ // try {
+ // out.write(imgAvg + ",");
+ // } catch (IOException e) {
+ // e.printStackTrace();
+ // }
+
+ sampleQueue.add((double) imgAvg);
+ timeQueue.add(System.currentTimeMillis());
+
+ double[] y = new double[sampleSize];
+ double[] x = ArrayUtils.toPrimitive((Double[]) sampleQueue
+ .toArray(new Double[0]));
+ long[] time = ArrayUtils.toPrimitive((Long[]) timeQueue
+ .toArray(new Long[0]));
+
+ if (timeQueue.size() < sampleSize) {
+ processing.set(false);
+
+ return;
+ }
+
+ double Fs = ((double) timeQueue.size())
+ / (double) (time[timeQueue.size() - 1] - time[0]) * 1000;
+
+ fft.fft(x, y);
+
+ int low = Math.round((float) (sampleSize * 40 / 60 / Fs));
+ int high = Math.round((float) (sampleSize * 160 / 60 / Fs));
+
+ int bestI = 0;
+ double bestV = 0;
+ for (int i = low; i < high; i++) {
+ double value = Math.sqrt(x[i] * x[i] + y[i] * y[i]);
+
+ if (value > bestV) {
+ bestV = value;
+ bestI = i;
+ }
+ }
+
+ bpm = Math.round((float) (bestI * Fs * 60 / sampleSize));
+ bpmQueue.add(bpm);
+
+ text.setText(String.valueOf(bpm));// + "," +
+ // String.valueOf(Math.round((float)
+ // Fs)));
+ new UDPThread()
+ .execute(bpm + ", " + System.currentTimeMillis());
+
+ counter++;
+ exampleSeries.appendData(new GraphView.GraphViewData(counter,
+ imgAvg), true, 1000);
+ processing.set(false);
+
+ //
+ //
+ //
+ // // try {
+ // // out.write(imgAvg + ",");
+ // // } catch (IOException e) {
+ // // e.printStackTrace();
+ // // }
+ //
+ //
+ // // Log.i(TAG, "imgAvg="+imgAvg);
+ // if (imgAvg == 0 || imgAvg == 255) {
+ // processing.set(false);
+ // return;
+ // }
+ //
+ // int averageArrayAvg = 0;
+ // int averageArrayCnt = 0;
+ // // Loop over each element in avgArray and sum them up!
+ // // averageArray holds the last [length] samples. This will be
+ // compared against imgAvg
+ // // to determine if we're experiencing a beat.
+ // for (int i = 0; i < averageArray.length; i++) {
+ // if (averageArray[i] > 0) {
+ // averageArrayAvg += averageArray[i];
+ // averageArrayCnt++;
+ // }
+ // }
+ //
+ // if (averageIndex == averageArraySize) averageIndex = 0;
+ // averageArray[averageIndex] = imgAvg;
+ // averageIndex++;
+ //
+ // int rollingAverage = (averageArrayCnt > 0) ? (averageArrayAvg /
+ // averageArrayCnt) : 0;
+ // TYPE newType = currentType;
+ // if (imgAvg < rollingAverage) {
+ // newType = TYPE.RED;
+ //
+ //
+ // if (newType != currentType) {
+ // beatsIndex++;
+ // timesArray[beatsIndex % beatsArraySize] =
+ // System.currentTimeMillis();
+ // beats++;
+ // Log.d(TAG, "BEAT!!");
+ // }
+ // } else if (imgAvg > rollingAverage) {
+ // newType = TYPE.GREEN;
+ // }
+ //
+ //
+ //
+ // // Transitioned from one state to another to the same
+ // if (newType != currentType) {
+ // currentType = newType;
+ // image.postInvalidate();
+ // }
+ //
+ // long endTime = System.currentTimeMillis();
+ // timesArray[beatsIndex % beatsArraySize] =
+ // System.currentTimeMillis();
+ //
+ //
+ // counter++;
+ //
+ // exampleSeries.appendData(new GraphView.GraphViewData(counter,
+ // imgAvg), true, 1000);
+ //
+ // double totalTimeInSecs = (timesArray[beatsIndex % beatsArraySize]
+ // - timesArray[(beatsIndex + 1) % beatsArraySize]) / 1000d;
+ // if (beatsIndex % beatsArraySize == 0)
+ // {
+ // Log.d(TAG, "time diff=" + totalTimeInSecs);
+ // }
+ //
+ // double bps = ((beatsArraySize-1) / totalTimeInSecs);
+ // int bpm = (int) (bps * 60d);
+ //
+ // text.setText(String.valueOf(bpm));
+ //
+ //
+ // processing.set(false);
+ }
+ };
+
+ private static SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ try {
+ camera.setPreviewDisplay(previewHolder);
+ camera.setPreviewCallback(previewCallback);
+ } catch (Throwable t) {
+ Log.e("PreviewDemo-surfaceCallback",
+ "Exception in setPreviewDisplay()", t);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width,
+ int height) {
+ Camera.Parameters parameters = camera.getParameters();
+ parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
+ Camera.Size size = getSmallestPreviewSize(width, height, parameters);
+ if (size != null) {
+ parameters.setPreviewSize(size.width, size.height);
+ Log.d(TAG, "Using width=" + size.width + " height="
+ + size.height);
+ }
+ camera.setParameters(parameters);
+ camera.startPreview();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // Ignore
+ }
+ };
+
+ private static Camera.Size getSmallestPreviewSize(int width, int height,
+ Camera.Parameters parameters) {
+ Camera.Size result = null;
+
+ for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
+ if (size.width <= width && size.height <= height) {
+ if (result == null) {
+ result = size;
+ } else {
+ int resultArea = result.width * result.height;
+ int newArea = size.width * size.height;
+
+ if (newArea < resultArea)
+ result = size;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private boolean start_UDP_Stream() {
+ if (streamData) {
+ boolean isOnWifi = isOnWifi();
+ if (isOnWifi == false) {
+ showDialog(R.string.error_warningwifi);
+ return false;
+ }
+
+ InetAddress client_adress = null;
+ try {
+ client_adress = InetAddress.getByName(mIP_Adress.getText().toString());
+ } catch (UnknownHostException e) {
+ showDialog(R.string.error_invalidaddr);
+ return false;
+ }
+ try {
+ mSocket = new DatagramSocket();
+ mSocket.setReuseAddress(true);
+ } catch (SocketException e) {
+ mSocket = null;
+ showDialog(R.string.error_neterror);
+ return false;
+ }
+
+ byte[] buf = new byte[256];
+ int port;
+ try {
+ port = Integer.parseInt(mPort.getText().toString());
+ mPacket = new DatagramPacket(buf, buf.length, client_adress,
+ port);
+ } catch (Exception e) {
+ mSocket.close();
+ mSocket = null;
+ showDialog(R.string.error_neterror);
+ return false;
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ private void stop_UDP_Stream() {
+ if (mSocket != null)
+ mSocket.close();
+ mSocket = null;
+ mPacket = null;
+
+ }
+
+ private boolean isOnWifi() {
+ ConnectivityManager conman = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
+ return conman.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
+ .isConnectedOrConnecting();
+ }
+
+}
diff --git a/src/com/jwetherell/heart_rate_monitor/HeartbeatView.java b/src/com/vanderbie/heart_rate_monitor/HeartbeatView.java
similarity index 98%
rename from src/com/jwetherell/heart_rate_monitor/HeartbeatView.java
rename to src/com/vanderbie/heart_rate_monitor/HeartbeatView.java
index 1d78383..d7be554 100644
--- a/src/com/jwetherell/heart_rate_monitor/HeartbeatView.java
+++ b/src/com/vanderbie/heart_rate_monitor/HeartbeatView.java
@@ -1,4 +1,4 @@
-package com.jwetherell.heart_rate_monitor;
+package com.vanderbie.heart_rate_monitor;
import android.content.Context;
import android.graphics.Bitmap;
diff --git a/src/com/jwetherell/heart_rate_monitor/ImageProcessing.java b/src/com/vanderbie/heart_rate_monitor/ImageProcessing.java
similarity index 98%
rename from src/com/jwetherell/heart_rate_monitor/ImageProcessing.java
rename to src/com/vanderbie/heart_rate_monitor/ImageProcessing.java
index d4770ea..5ca278f 100644
--- a/src/com/jwetherell/heart_rate_monitor/ImageProcessing.java
+++ b/src/com/vanderbie/heart_rate_monitor/ImageProcessing.java
@@ -1,4 +1,4 @@
-package com.jwetherell.heart_rate_monitor;
+package com.vanderbie.heart_rate_monitor;
/**
* This abstract class is used to process images.
diff --git a/src/com/jwetherell/heart_rate_monitor/Metronome.java b/src/com/vanderbie/heart_rate_monitor/Metronome.java
similarity index 94%
rename from src/com/jwetherell/heart_rate_monitor/Metronome.java
rename to src/com/vanderbie/heart_rate_monitor/Metronome.java
index a0c4010..569da83 100644
--- a/src/com/jwetherell/heart_rate_monitor/Metronome.java
+++ b/src/com/vanderbie/heart_rate_monitor/Metronome.java
@@ -1,13 +1,11 @@
-package com.jwetherell.heart_rate_monitor;
+package com.vanderbie.heart_rate_monitor;
+
+import org.apache.commons.lang3.ArrayUtils;
import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
import android.util.Log;
-import org.apache.commons.lang3.ArrayUtils;
-
-import java.util.Timer;
-import java.util.TimerTask;
/**
* Created by gdw on 2/8/14.
diff --git a/src/com/vanderbie/heart_rate_monitor/UDPThread.java b/src/com/vanderbie/heart_rate_monitor/UDPThread.java
new file mode 100755
index 0000000..315a645
--- /dev/null
+++ b/src/com/vanderbie/heart_rate_monitor/UDPThread.java
@@ -0,0 +1,44 @@
+package com.vanderbie.heart_rate_monitor;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+import android.os.AsyncTask;
+/**
+ * UDP streamer class
+ *
+ * @author Joey van der Bie
+ */
+public class UDPThread extends AsyncTask {
+
+ String msensordata;
+
+ @Override
+ protected Void doInBackground(String... params) {
+ byte bytes [] ;
+ msensordata = params[0];
+
+ try {
+ bytes = msensordata.getBytes("UTF-8");
+ if (HeartRateMonitor.mPacket == null || HeartRateMonitor.mSocket == null)
+ return null ;
+
+ HeartRateMonitor.mPacket.setData(bytes);
+ HeartRateMonitor.mPacket.setLength(bytes.length);
+
+
+ HeartRateMonitor.mSocket.send(HeartRateMonitor.mPacket);
+ } catch (UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ //Log.e("Error", "SendBlock");
+ return null;
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ //Log.e("Error", "SendBlock");
+ return null;
+ }
+ return null;
+ }
+ }
\ No newline at end of file