The Ketai Bluetooth Class method onBluetoothDataEvent() does not work on the latest Android modes. Also, it has no readStringUntil() method which makes it difficult to read fast incoming data. So I wrote a native Android Bluetooth code to make this possible. I tested it on a MultiLaser Lollipop 5.1 device, as well as on a Samsung Android 10, using an HC-05 module set to a baud rate of 115200, without losing a single reading byte. The first code is the simplest reading code, it will display a single sine wave sent by the Arduino… The second, the simplest sending which will send a sine wave and can be seen in the serial plotter window of the Arduino IDE, The third is a combined code with two sliders that sent a frequency value to the Arduino which will generate two sine waves to be displayed on the device. You will need to give permissions “ACCESS_FINE_LOCATION” and “BLUETOOTH”.
If you use this code, please comment if it’s working or not.
Receiver
- Arduino code
#include <SoftwareSerial.h>
#include <Wire.h>
SoftwareSerial mySerial(11, 12); // RX, TX
int data;
boolean toggle = false;
void setup() {
mySerial.begin(115200); // Set the bluetooth model baudrate
Serial.begin(115200); // Remember to set this plotter baudrate
}
void loop() {
if (mySerial.available()) {
data = mySerial.read();
if(data > 254) toggle = true;
if(data < 1) toggle = false;
if (toggle)Serial.println(data);
else Serial.println(data+255);
}
}
- Processing code
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import processing.core.PConstants;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;
BluetoothAdapter mBluetoothAdapter;
BluetoothSocket mmSocket;
BluetoothDevice mmDevice;
OutputStream mmOutputStream;
InputStream mmInputStream;
Activity activity;
Permission bt, afl;
Thread runThread;
byte[] readBuffer;
int buffer_index;
int counter;
boolean stop_thread, plotting = false, init = true;
String msg = " ";
String device_name = "HC-05";
String r_str = " ";
String t_str;
float px, py, x, y;
void setup() {
fullScreen();
orientation(LANDSCAPE);
bt = new Permission(this, "BLUETOOTH");
afl = new Permission(this, "ACCESS_FINE_LOCATION");
textAlign(CENTER);
textSize(70);
t_str = "Click to connect.";
textSize(12); // To create a "text width factor",
float twf = width/textWidth(t_str);
int f = 4; // Empericaly found
textSize(f*twf); // Making it proportional for other screen resolutions
activity = this.getActivity();
}
void draw() {
if (plotting == false && init) {
background(150);
text(t_str, width/2, height/2);
strokeWeight(10);
noFill();
stroke(50);
rect(0, 0, width, height);
}
}
void mousePressed () {
if (init) {
try {
init = false;
findBluetooth();
connect();
plotting = true;
fill(0, 0, 90);
rect(0, 0, width, height);
}
catch (IOException ex) {
println(ex);
}
}
}
void plot(String Str) {
if (Str.length() < 3 || Str == null) Str = "0#0";
if (plotting) {
stroke(0, 200, 255);
strokeWeight(2);
y = int(Str);
int h = height/2;
line(px, py+h, x, y+h);
py = y;
px = x;
x++;
if (x > width) {
x = y = px = py = 0;
background(0, 0, 90);
}
}
}
void findBluetooth() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
println("No bluetooth adapter available");
}
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enableBluetooth, 0);
}
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
print("Paired devices MAC Adress:");
printArray(pairedDevices);
for (BluetoothDevice device : pairedDevices) {
print("Paired devices by name:");
println(device.getName());
if (device.getName().equals(device_name)) {
mmDevice = device;
println("Bluetooth device name found");
break;
}
}
}
}
void connect() throws IOException {
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //Standard SerialPortService ID
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
mmSocket.connect();
mmOutputStream = mmSocket.getOutputStream();
mmInputStream = mmSocket.getInputStream();
println("Bluetooth connected");
final byte delimiter = 33; //This is the ASCII code for a ! character
stop_thread = false;
buffer_index = 0;
readBuffer = new byte[1024];
runThread = new Thread(new Runnable() {
public void run() {
while (!Thread.currentThread().isInterrupted() && !stop_thread) {
try {
int bytesAvailable = mmInputStream.available();
if (bytesAvailable > 0) {
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for (int i = 0; i < bytesAvailable; i++) {
byte b = packetBytes[i];
if (b == delimiter) {
byte[] encodedBytes = new byte[buffer_index];
System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
final String data = new String(encodedBytes, "US-ASCII");
plot(data);
buffer_index = 0;
} else {
readBuffer[buffer_index++] = b;
}
}
}
}
catch (IOException ex)
{
stop_thread = true;
}
}
}
}
);
runThread.start();
}
void closeBluetooth() throws IOException {
stop_thread = true;
mmOutputStream.close();
mmInputStream.close();
mmSocket.close();
println("Bluetooth stopped");
}
class Permission {
PApplet parent;
boolean requestedPortraitImage = false;
Permission(PApplet pParent, String permissionName) {
parent = pParent;
parent.requestPermission("android.permission."+permissionName, "onPermissionResult", this);
}
void onPermissionResult(boolean granted) {
if (granted) println("User did grant permission.");
else println("User did NOT grant permission.");
}
}
void onPause() {
try {
closeBluetooth();
}
catch (Exception ex) {
}
super.onPause();
}
void onStop () {
try {
closeBluetooth();
}
catch (Exception ex) {
}
super.onStop();
}
void onRestart() {
try {
findBluetooth();
connect();
}
catch (IOException ex) {
println(ex);
}
}
Sender
- Arduino code
#include <SoftwareSerial.h>
#include <Wire.h>
SoftwareSerial mySerial(11, 12); // RX, TX
float a = 0;
int y = 0;
void setup() {
mySerial.begin(115200); // Set the baudrate equal to HC06 setting
}
void loop() {
y = int(80*sin(a));
a += 0.08;
if (a > TWO_PI) a = 0;
mySerial.print(String(y) + '!');
}
- Processing code
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import processing.core.PConstants;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;
BluetoothAdapter mBluetoothAdapter;
BluetoothSocket mmSocket;
BluetoothDevice mmDevice;
OutputStream mmOutputStream;
InputStream mmInputStream;
Activity activity;
Permission bp, afl;
Thread runThread;
byte[] readBuffer;
int buffer_index;
boolean stop_thread, init = true;
String device_name = "HC-05";
boolean permissions_granted;
String Str = " ";
String t_str;
float a, increment = 0.04;
int y;
void setup() {
fullScreen();
orientation(LANDSCAPE);
bp = new Permission(this, "BLUETOOTH");
afl = new Permission(this, "ACCESS_FINE_LOCATION");
textAlign(CENTER);
textSize(70);
t_str = "Click to connect.";
textSize(12); // To create a "text width factor",
float twf = width/textWidth(t_str);
int f = 4; // Empericaly found
textSize(f*twf); // Making it proportional for other screen resolutions
}
void draw() {
if (init) {
background(150);
text(t_str, width/2, height/2);
strokeWeight(10);
noFill();
stroke(50);
rect(0, 0, width, height);
} else {
y = int(80 * sin(a));
a += increment;
if (a > TWO_PI) a = 0;
try {
sendData();
}
catch (IOException ex) {
}
}
}
void mousePressed () {
if (init) {
try {
init = false;
findBluetooth();
connect();
background(80);
text("Connecting to send.", width/2, height/2);
}
catch (IOException ex) {
println(ex);
}
}
}
void sendData() throws IOException {
mmOutputStream.write(y);
}
void findBluetooth() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
println("No bluetooth adapter available");
}
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enableBluetooth, 0);
}
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
print("Paired devices MAC Adress:");
printArray(pairedDevices);
for (BluetoothDevice device : pairedDevices) {
print("Paired devices by name:");
println(device.getName());
if (device.getName().equals(device_name)) {
mmDevice = device;
println("Bluetooth device name found");
break;
}
}
}
}
void connect() throws IOException {
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //Standard SerialPortService ID
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
mmSocket.connect();
mmOutputStream = mmSocket.getOutputStream();
mmInputStream = mmSocket.getInputStream();
println("Bluetooth connected");
final byte delimiter = 33; //This is the ASCII code for a ! character
stop_thread = false;
buffer_index = 0;
readBuffer = new byte[1024];
runThread = new Thread(new Runnable() {
public void run() {
while (!Thread.currentThread().isInterrupted() && !stop_thread) {
try {
int bytesAvailable = mmInputStream.available();
if (bytesAvailable > 0) {
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for (int i = 0; i < bytesAvailable; i++) {
byte b = packetBytes[i];
if (b == delimiter) {
byte[] encodedBytes = new byte[buffer_index];
System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
final String data = new String(encodedBytes, "US-ASCII");
//plot(data);
buffer_index = 0;
} else {
readBuffer[buffer_index++] = b;
}
}
}
}
catch (IOException ex)
{
stop_thread = true;
}
}
}
}
);
runThread.start();
}
void closeBluetooth() throws IOException {
stop_thread = true;
mmOutputStream.close();
mmInputStream.close();
mmSocket.close();
println("Bluetooth stopped");
}
class Permission {
PApplet parent;
boolean requestedPortraitImage = false;
Permission(PApplet pParent, String permissionName) {
parent = pParent;
parent.requestPermission("android.permission."+permissionName, "onPermissionResult", this);
}
void onPermissionResult(boolean granted) {
if (granted) println("User did grant permission.");
else println("User did NOT grant permission.");
}
}
void onPause() {
try {
closeBluetooth();
}
catch (Exception ex) {
}
super.onPause();
}
void onStop () {
try {
closeBluetooth();
}
catch (Exception ex) {
}
super.onStop();
}
void onRestart() {
try {
findBluetooth();
connect();
}
catch (IOException ex) {
println(ex);
}
}
Combined
- Arduino code
#include <SoftwareSerial.h>
#include <Wire.h>
SoftwareSerial mySerial(11, 12); // RX, TX
unsigned long previous_millis;
boolean new_data = false;
float a = 0, b = 0;
float i_1 = 0, i_2 = 0;
int udi_1 = 20, udi_2 = 1;
int y_1 = 0, y_2 = 0;
const byte number_of_chars = 32;
char receivedChars[number_of_chars];
void setup() {
mySerial.begin(115200); // Set the baudrate equal to HC06 setting
Serial.begin(9600);
}
void loop() {
receivePacket();
if (new_data == true) {
parseData();
new_data = false;
}
while (mySerial.available() <= 0) {
y_1 = int(70 * sin(a));
a += i_1;
if (a > TWO_PI) a = 0;
mySerial.print(String(y_1) + '!');
y_2 = int(70 * sin(b));
b += i_2;
if (b > TWO_PI) b = 0;
mySerial.print(String(y_2) + '#');
}
}
void receivePacket() {
static boolean receiving = false;
static byte index = 0;
char start_mark = '<';
char end_mark = '>';
char rc;
while (mySerial.available() > 0 && new_data == false) {
rc = mySerial.read();
if (receiving == true) {
if (rc != end_mark) {
receivedChars[index] = rc;
index++;
if (index >= number_of_chars) {
index = number_of_chars - 1;
}
} else {
receivedChars[index] = '\0';
receiving = false;
index = 0;
new_data = true;
}
} else if (rc == start_mark) {
receiving = true;
}
}
}
void parseData() {
char * split;
split = strtok(receivedChars, ",");
udi_1 = atoi(split);
split = strtok(NULL, ",");
udi_2 = atoi(split);
i_1 = udi_1/100.0;
i_2 = udi_2/100.0;
Serial.println(i_1);
Serial.println(i_2);
}
- Processing code
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import processing.core.PConstants;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;
BluetoothAdapter mBluetoothAdapter;
BluetoothSocket mmSocket;
BluetoothDevice mmDevice;
OutputStream mmOutputStream;
InputStream mmInputStream;
Activity activity;
Permission bp, afl;
Thread runThread;
byte[] readBuffer;
int buffer_index;
int counter;
boolean stop_thread, plotting = false, init = true;
boolean drag_just_finished;
String msg = "<0,0>";
String device_name = "HC-05";
String r_str, t_str;
boolean permissions_granted;
float a, b;
float px_1, py_1, x_1, y_1, px_2, py_2, x_2, y_2;
float loc_1, loc_2, num_1 = 0, num_2 = 0;
String[] y_values = {"0#0"};
void setup() {
fullScreen();
orientation(LANDSCAPE);
bp = new Permission(this, "BLUETOOTH");
afl = new Permission(this, "ACCESS_FINE_LOCATION");
textAlign(CENTER);
textSize(70);
t_str = "Click to connect.";
textSize(12); // To create a "text width factor",
float twf = width/textWidth(t_str);
int f = 4; // Empericaly found
textSize(f*twf); // Making it proportional for other screen resolutions
loc_1 = width/50;
loc_2 = width/50;
}
void draw() {
if (plotting == false && init) {
background(150);
text(t_str, width/2, height/2);
strokeWeight(10);
noFill();
stroke(50);
rect(0, 0, width, height);
} else if (plotting == false) {
drawSliderOne();
drawSliderTwo();
}
}
void mousePressed () {
if (init) {
try {
init = false;
findBluetooth();
connect();
plotting = true;
fill(0, 0, 90);
rect(0, 0, width, height);
drawSliderOne();
drawSliderTwo();
}
catch (IOException ex) {
println(ex);
}
}
}
void mouseDragged() {
plotting = false;
if (mouseY > height-height/8) {
loc_1 = mouseX;
if (loc_1 < width/50) loc_1 = width/50;
if (loc_1 > width-width/50) loc_1 = width-width/50;
try {
sendData();
}
catch (IOException ex) {
}
}
if (mouseY > height-height/3 && mouseY < height-height/3+height/8) {
loc_2 = mouseX;
if (loc_2 < width/50) loc_2 = width/50;
if (loc_2 > width-width/50) loc_2 = width-width/50;
try {
sendData();
}
catch (IOException ex) {
}
}
}
void mouseReleased() {
plotting = true;
}
void drawSliderOne() {
pushStyle();
noStroke();
num_1 = map(loc_1, 0.0, width, 2, 10);
fill(100, 180, 200);
rect(0, height-height/8, width, height/8);
fill(200, 200, 255);
rect(width/50, height-height/16, width-width/25, height/100);
fill(100, 100, 255);
ellipse(loc_1, height-height/17, height/20, height/20);
popStyle();
}
void drawSliderTwo() {
pushStyle();
noStroke();
num_2 = map(loc_2, 0.0, width, 2, 10);
fill(100, 180, 200);
rect(0, height-height/3, width, height/8);
fill(200, 200, 255);
rect(width/50, height-9*height/32, width-width/25, height/100);
fill(100, 100, 255);
ellipse(loc_2, height-9*height/32, height/20, height/20);
popStyle();
}
void plot(String r_str) {
if (r_str.length() < 3 || r_str == null) r_str = "0#0";
if (plotting) {
y_values = split(r_str, "#");
stroke(0, 200, 255);
strokeWeight(2);
y_1 = int(y_values[0])+height/6;
line(px_1, py_1, x_1, y_1);
py_1 = y_1;
px_1 = x_1;
x_1++;
if (x_1 > width) {
x_1 = y_1 = px_1 = py_1 = x_2 = y_2 = px_2 = py_2 = 0;
fill(0, 0, 90);
rect(0, 0, width, height-height/3);
}
y_2 = int(y_values[1])+height/2;
line(px_2, py_2, x_2, y_2);
py_2 = y_2;
px_2 = x_2;
x_2++;
}
}
void sendData() throws IOException {
msg = "<"+int(num_1)+","+int(num_2)+">";
mmOutputStream.write(msg.getBytes());
}
void findBluetooth() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
println("No bluetooth adapter available");
}
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enableBluetooth, 0);
}
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
print("Paired devices MAC Adress:");
printArray(pairedDevices);
for (BluetoothDevice device : pairedDevices) {
print("Paired devices by name:");
println(device.getName());
if (device.getName().equals(device_name)) {
mmDevice = device;
println("Bluetooth device name found");
break;
}
}
}
}
void connect() throws IOException {
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //Standard SerialPortService ID
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
mmSocket.connect();
mmOutputStream = mmSocket.getOutputStream();
mmInputStream = mmSocket.getInputStream();
println("Bluetooth connected");
//final Handler handler = new Handler();
final byte delimiter = 33; //This is the ASCII code for a ! character
stop_thread = false;
buffer_index = 0;
readBuffer = new byte[1024];
runThread = new Thread(new Runnable() {
public void run() {
while (!Thread.currentThread().isInterrupted() && !stop_thread) {
try {
int bytesAvailable = mmInputStream.available();
if (bytesAvailable > 0) {
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for (int i = 0; i < bytesAvailable; i++) {
byte b = packetBytes[i];
if (b == delimiter) {
byte[] encodedBytes = new byte[buffer_index];
System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
final String data = new String(encodedBytes, "US-ASCII");
plot(data);
buffer_index = 0;
} else {
readBuffer[buffer_index++] = b;
}
}
}
}
catch (IOException ex)
{
stop_thread = true;
}
}
}
}
);
runThread.start();
}
void closeBluetooth() throws IOException {
stop_thread = true;
mmOutputStream.close();
mmInputStream.close();
mmSocket.close();
println("Bluetooth stopped");
}
class Permission {
PApplet parent;
boolean requestedPortraitImage = false;
Permission(PApplet pParent, String permissionName) {
parent = pParent;
parent.requestPermission("android.permission."+permissionName, "onPermissionResult", this);
}
void onPermissionResult(boolean granted) {
if (granted) println("User did grant permission.");
else println("User did NOT grant permission.");
}
}
void onPause() {
try {
closeBluetooth();
}
catch (Exception ex) {
}
super.onPause();
}
void onStop () {
try {
closeBluetooth();
}
catch (Exception ex) {
}
super.onStop();
}
void onRestart() {
try {
findBluetooth();
connect();
}
catch (IOException ex) {
println(ex);
}
}