Reputation: 23
I am trying to create an android app using Android Studio in order to send some data to an ESP32 in order to display those data on a transparent monitor.
Searching on the web I found several ways to do it but all of them are quite complicated. I was able to search for the paired devices and store the mac address of the ESP32 on my app.
Can anyone help me how to continue from now on ???
Thanks a lot !!
Upvotes: 1
Views: 17809
Reputation: 161
Bluetooth Classic
1. ESP32 implementation (Slave)
(notes: built by current 1.0.6 esp platform, older 1.0.4 - working good as master but had some issues as slave for me)
#include "Arduino.h"
#include "BluetoothSerial.h"
BluetoothSerial SerialBT;
void setup()
{
Serial.begin(9600);
SerialBT.begin("ESP32s"); //Bluetooth device name
Serial.println("The device started, now you can pair it with bluetooth!");
}
void loop()
{
if (SerialBT.available()){ Serial.write(SerialBT.read()); }
delay(20);
}
2. Android (Master)
AndroidManifest.xml
<application
...
android:name=".ApplicationEx"
</application>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/id_btn_hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Say Hello"/>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
Button btnHello;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnHello = findViewById(R.id.id_btn_hello);
btnHello.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(((ApplicationEx)getApplication()).writeBt("Hello World\n".getBytes(StandardCharsets.UTF_8))){
Toast.makeText(getApplicationContext(), "OK, sent", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(getApplicationContext(), "ESP not connected", Toast.LENGTH_SHORT).show();
}
}
});
}
}
ApplicationEx.java
import android.app.Application;
import android.os.Handler;
public class ApplicationEx extends Application{
private final int STATUS_CHECK_INTERVAL = 500;
private MyBtEngine mBtEngine;
@Override
public void onCreate() {
super.onCreate();
mBtEngine = new MyBtEngine();
handlerStatusCheck.postDelayed(new Runnable() {
@Override
public void run() {
onBtStatusCheckTimer();
handlerStatusCheck.postDelayed(this, STATUS_CHECK_INTERVAL);
}
}, STATUS_CHECK_INTERVAL);
}
private final Handler handlerStatusCheck = new Handler();
private void onBtStatusCheckTimer(){
if(mBtEngine.getState() == MyBtEngine.BT_STATE_NONE) {
mBtEngine.start();
}
}
boolean writeBt(byte [] buffer){ return mBtEngine.writeBt(buffer); }
}
MyBtEngine.java
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
public class MyBtEngine {
static final int BT_STATE_NONE = 0;
static final int BT_STATE_CONNECTING = 1;
static final int BT_STATE_CONNECTED = 2;
//uid for all 3rd party devices (not android)
private static final UUID UUID_BT_DEVICE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final String BT_DEVICE_MAC = "24:0A:C4:61:16:EE";//put your device MAC !!!!
private BtWaitConnThread mWaitConnThread = null;
private BtWorkThread mWorkThread = null;
private final BluetoothAdapter mAdapter;
private int mState;
MyBtEngine() {
mAdapter = BluetoothAdapter.getDefaultAdapter();
mState = BT_STATE_NONE;
}
synchronized void start(){
if(mAdapter == null){ return; }
BluetoothDevice device;
try{
device = mAdapter.getRemoteDevice(BT_DEVICE_MAC);
}
catch (Exception e){ return; }
if (mWaitConnThread != null) {mWaitConnThread.cancel(); mWaitConnThread = null;}
if (mWorkThread != null) { mWorkThread.cancel(); mWorkThread = null;}
mWaitConnThread = new BtWaitConnThread(device);
mWaitConnThread.start();
setState(BT_STATE_CONNECTING);
}
public synchronized void stop() {
if (mWaitConnThread != null) { mWaitConnThread.cancel(); mWaitConnThread = null; }
if (mWorkThread != null) { mWorkThread.cancel(); mWorkThread = null; }
setState(BT_STATE_NONE);
}
private synchronized void setState(int state) {
Log.e("MyBtEngine", "setState() " + mState + " -> " + state);
mState = state;
}
synchronized int getState() { return mState; }
public boolean writeBt(byte[] out) {
BtWorkThread r; // temp obj , just to keep write function not to destroyed if mWorkThread finish
synchronized (this) {
if((mWorkThread == null)||(mState != BT_STATE_CONNECTED)){
return false;
}
r = mWorkThread;
}
r.write(out);
return true;
}
private synchronized void startWorker(BluetoothSocket socket, BluetoothDevice device) {
if (mWaitConnThread != null) {mWaitConnThread.cancel(); mWaitConnThread = null;}
if (mWorkThread != null) { mWorkThread.cancel(); mWorkThread = null;}
mWorkThread = new BtWorkThread(socket);
mWorkThread.start();
setState(BT_STATE_CONNECTED);
}
private class BtWaitConnThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private BtWaitConnThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
try {
tmp = device.createRfcommSocketToServiceRecord(UUID_BT_DEVICE);
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
mAdapter.cancelDiscovery();
try {
mmSocket.connect();
} catch (IOException e) {
try {
mmSocket.close();
} catch (IOException e2) { }
setState(BT_STATE_NONE);
return;
}
synchronized (MyBtEngine.this) {
mWaitConnThread = null;//set itself to null because thread exit soon
}
startWorker(mmSocket, mmDevice);
}
private void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
private class BtWorkThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private BtWorkThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
while (true) {
try {
int charFromEsp = mmInStream.read();//TODO: add code here for handling input from ESP
} catch (IOException e) {
MyBtEngine.this.start();//restart from beginning
return; // exit worker thread
}
}
}
public boolean write(byte[] buffer) {
try {
mmOutStream.write(buffer);
} catch (IOException e) { return false; }
return true;
}
private void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
}
Upvotes: 2
Reputation: 656
1. ESP32 Implementation
Sending data is quite easy. Basically you need to make ESP32 as a Web Server either using native WebServer library or ESPAsyncWebServer library. Here is a quick example using native WebServer
library:
#include <WiFi.h>
#include <WebServer.h>
WebServer server(80);
void handleRoot() {
server.send(200, "text/plain", "Ready");
}
void handleGet() {
if (server.hasArg("data")) {
String data = server.arg("data");
Serial.println("Data: " + data);
}
server.send(200, "text/plain", "Data Received");
}
void handlePost() {
server.send(200, "text/plain", "Processing Data");
}
void handleUpload() {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
Serial.println("Receiving data:");
} else if (upload.status == UPLOAD_FILE_WRITE) {
Serial.write(upload.buf, upload.currentSize);
} else if (upload.status == UPLOAD_FILE_END) {
server.send(200, "text/plain", "Data: ");
}
}
void setup() {
Serial.begin(115200);
WiFi.softAP("ESP32");
server.on("/", handleRoot);
server.on("/get", HTTP_GET, handleGet);
server.on("/post", HTTP_POST, handlePost, handleUpload);
server.begin();
}
void loop() {
server.handleClient();
}
In order to send data to ESP32, you must connect your phone to the same network as ESP32, in this case you can just connect your Android phone to ESP32 Access Point. If you scan available WiFi network in your phone, you should see ESP32
. You can check if the Web Server is running using Web Browser. Just type http://192.168.4.1
in address bar and you should get response "ready". In order to send data via HTTP GET method, you can type http://192.168.4.1/get?data=HelloWorld
, you will see HelloWorld
text in the Serial Monitor.
2. Android Implementation
There is so many HTTP libraries for Android Studio, you may want to check Fast Android Networking. Here is a quick example about how to use it:
build.gradle
dependencies {
...
implementation 'com.amitshekhar.android:android-networking:1.0.2'
...
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/btn_get"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="GET"
android:textColor="@color/white"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/btn_post"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="POST"
android:textColor="@color/white"
tools:ignore="HardcodedText" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AndroidNetworking.initialize(getApplicationContext());
Button btnGet = findViewById(R.id.btn_get);
btnGet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/*Send data via HTTP GET*/
AndroidNetworking.get("http://192.168.4.1/get")
.addQueryParameter("data", "HelloWorld")
.build()
.getAsString(new StringRequestListener() {
@Override
public void onResponse(String response) {
Toast.makeText(getApplicationContext(), response, Toast.LENGTH_SHORT).show();
}
@Override
public void onError(ANError anError) {
Toast.makeText(getApplicationContext(), anError.getErrorBody(), Toast.LENGTH_SHORT).show();
}
});
}
});
Button btnPost = findViewById(R.id.btn_post);
btnPost.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/*Send data via HTTP POST*/
AndroidNetworking.post("http://192.168.4.1/post")
.addStringBody("This is my data")
.build()
.getAsString(new StringRequestListener() {
@Override
public void onResponse(String response) {
Toast.makeText(getApplicationContext(), response, Toast.LENGTH_SHORT).show();
}
@Override
public void onError(ANError anError) {
Toast.makeText(getApplicationContext(), anError.getErrorBody(), Toast.LENGTH_SHORT).show();
}
});
}
});
}
}
Here is how the app looks like:
Whenever you tap GET or POST button, you should see "HelloWorld" or "This is my data" in the Serial Monitor.
That's all. If you want Web Socket Server for faster communication, you can use ESPAsyncWebServer
as i mentioned above.
Upvotes: 3