this is mainactivity.java that is in android studio.
package com.example.bleapp;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.clj.fastble.BleManager;
import com.clj.fastble.callback.BleGattCallback;
import com.clj.fastble.callback.BleNotifyCallback;
import com.clj.fastble.callback.BleScanCallback;
import com.clj.fastble.data.BleDevice;
import com.clj.fastble.exception.BleException;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int REQUEST_ENABLE_BT = 1;
private static final int REQUEST_LOCATION_PERMISSION = 2;
private TextView statusTextView;
private RecyclerView recyclerView;
private Button scanButton;
private BleDeviceAdapter adapter;
private BleDevice connectedDevice;
private BluetoothGattCharacteristic readCharacteristic;
// Replace with your ESP32 BLE characteristic UUIDs
private static final UUID SERVICE_UUID = UUID.fromString("00001800-0000-1000-8000-00805f9b34fb");
private static final UUID CHARACTERISTIC_UUID = UUID.fromString("0000fef4-0000-1000-8000-00805f9b34fb");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
statusTextView = findViewById(R.id.statusTextView);
recyclerView = findViewById(R.id.recyclerView);
scanButton = findViewById(R.id.scanButton);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new BleDeviceAdapter();
recyclerView.setAdapter(adapter);
BleManager.getInstance().init(getApplication());
scanButton.setOnClickListener(v -> {
if (checkPermissions()) {
startScan();
}
});
adapter.setOnItemClickListener(this::connectToDevice);
}
private boolean checkPermissions() {
if (!BleManager.getInstance().isBlueEnable()) {
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), REQUEST_ENABLE_BT);
return false;
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION);
return false;
}
return true;
}
private void startScan() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION);
return;
}
BleManager.getInstance().scan(new BleScanCallback() {
@Override
public void onScanStarted(boolean success) {
scanButton.setEnabled(false);
statusTextView.setText("Scanning...");
Log.d(TAG, "Scan started");
}
@Override
public void onScanning(BleDevice bleDevice) {
adapter.addDevice(bleDevice);
Log.d(TAG, "Device found: " + bleDevice.getName());
}
@Override
public void onScanFinished(List<BleDevice> scanResultList) {
scanButton.setEnabled(true);
statusTextView.setText("Scan finished.");
Log.d(TAG, "Scan finished with " + scanResultList.size() + " devices found");
}
});
}
private void connectToDevice(BleDevice device) {
BleManager.getInstance().cancelScan();
BleManager.getInstance().connect(device, new BleGattCallback() {
@Override
public void onStartConnect() {
statusTextView.setText("Connecting...");
Log.d(TAG, "Connecting to " + device.getName());
}
@Override
public void onConnectFail(BleDevice bleDevice, BleException exception) {
statusTextView.setText("Connect fail: " + exception.toString());
Log.e(TAG, "Connection failed: " + exception.getDescription());
}
@Override
public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) {
statusTextView.setText("Connected");
Log.d(TAG, "Connected to " + bleDevice.getName());
// Save connected device
connectedDevice = bleDevice;
// Discover services and characteristics
try {
gatt.discoverServices();
} catch (SecurityException e) {
Log.e(TAG, "SecurityException: " + e.getMessage());
}
}
@Override
public void onDisConnected(boolean isActiveDisConnected, BleDevice device, BluetoothGatt gatt, int status) {
statusTextView.setText("Disconnected");
connectedDevice = null;
Log.d(TAG, "Disconnected from " + device.getName());
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
BluetoothGattService service = gatt.getService(SERVICE_UUID);
if (service != null) {
readCharacteristic = service.getCharacteristic(CHARACTERISTIC_UUID);
enableNotification(readCharacteristic);
Log.d(TAG, "Service and characteristics found");
} else {
Log.e(TAG, "Service not found");
}
} else {
Log.e(TAG, "Service discovery failed with status: " + status);
}
}
});
}
private void enableNotification(BluetoothGattCharacteristic characteristic) {
BleManager.getInstance().notify(
connectedDevice,
characteristic.getService().getUuid().toString(),
characteristic.getUuid().toString(),
new BleNotifyCallback() {
@Override
public void onNotifySuccess() {
Log.d(TAG, "Notification setup successful for characteristic: " + characteristic.getUuid());
}
@Override
public void onNotifyFailure(BleException exception) {
Log.e(TAG, "Failed to set up notification for characteristic " + characteristic.getUuid() + ": " + exception.getDescription());
// Handle failure scenario, e.g., retry or display error to the user
}
@Override
public void onCharacteristicChanged(byte[] data) {
if (data != null && data.length > 0) {
String hexString = bytesToHex(data);
Log.d(TAG, "Hex data received: " + hexString);
String jsonData = hexToString(hexString);
updateLabel1(jsonData);
} else {
Log.d(TAG, "Received empty data for characteristic " + characteristic.getUuid());
}
}
});
}
private void updateLabel1(String jsonData) {
try {
// Parse JSON data
JSONObject jsonObject = new JSONObject(jsonData);
double temperature = jsonObject.getDouble("temperature");
double humidity = jsonObject.getDouble("humidity");
int no2 = jsonObject.getInt("no2");
int c2h5oh = jsonObject.getInt("c2h5oh");
int voc = jsonObject.getInt("voc");
int co = jsonObject.getInt("co");
int pm1 = jsonObject.getInt("pm1");
int pm2_5 = jsonObject.getInt("pm2_5");
int pm10 = jsonObject.getInt("pm10");
// Update UI with parsed values
runOnUiThread(() -> {
((TextView) findViewById(R.id.temp_value)).setText(String.valueOf(temperature));
((TextView) findViewById(R.id.hum_value)).setText(String.valueOf(humidity));
((TextView) findViewById(R.id.no2_value)).setText(String.valueOf(no2));
((TextView) findViewById(R.id.c2h5oh_value)).setText(String.valueOf(c2h5oh));
((TextView) findViewById(R.id.voc_value)).setText(String.valueOf(voc));
((TextView) findViewById(R.id.co_value)).setText(String.valueOf(co));
((TextView) findViewById(R.id.pm1_value)).setText(String.valueOf(pm1));
((TextView) findViewById(R.id.pm2_value)).setText(String.valueOf(pm2_5));
((TextView) findViewById(R.id.pm10_value)).setText(String.valueOf(pm10));
});
} catch (JSONException e) {
Log.e(TAG, "JSON Parsing error: " + e.getMessage());
}
}
private String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
hexString.append(String.format("%02X", b));
}
return hexString.toString();
}
private String hexToString(String hex) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hex.length(); i += 2) {
String str = hex.substring(i, i + 2);
sb.append((char) Integer.parseInt(str, 16));
}
return sb.toString();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_LOCATION_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startScan();
} else {
Toast.makeText(this, "Location permission denied", Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (connectedDevice != null) {
BleManager.getInstance().disconnect(connectedDevice);
}
BleManager.getInstance().destroy();
}
}
this my activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<Button
android:id="@+id/scanButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Scan" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="387dp"
android:layout_height="156dp" />
<TextView
android:id="@+id/statusTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Status: "
android:textColor="@android:color/holo_red_light"
android:layout_marginTop="16dp" />
<TextView
android:id="@+id/label1"
android:layout_width="381dp"
android:layout_height="81dp"
android:layout_marginTop="8dp"
android:text="Text for Label1" />
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="1"
android:layout_marginTop="8dp">
<TableRow>
<TextView android:text="temp" android:padding="4dp"/>
<TextView android:id="@+id/temp_value" android:text="000" android:padding="4dp" android:background="@android:color/black" android:textColor="@android:color/holo_green_light"/>
</TableRow>
<TableRow>
<TextView android:text="hum." android:padding="4dp"/>
<TextView android:id="@+id/hum_value" android:text="000" android:padding="4dp" android:background="@android:color/black" android:textColor="@android:color/holo_green_light"/>
</TableRow>
<TableRow>
<TextView android:text="NO2" android:padding="4dp"/>
<TextView android:id="@+id/no2_value" android:text="000" android:padding="4dp" android:background="@android:color/black" android:textColor="@android:color/holo_green_light"/>
</TableRow>
<TableRow>
<TextView android:text="C2H5OH" android:padding="4dp"/>
<TextView android:id="@+id/c2h5oh_value" android:text="000" android:padding="4dp" android:background="@android:color/black" android:textColor="@android:color/holo_green_light"/>
</TableRow>
<TableRow>
<TextView android:text="VOC" android:padding="4dp"/>
<TextView android:id="@+id/voc_value" android:text="000" android:padding="4dp" android:background="@android:color/black" android:textColor="@android:color/holo_green_light"/>
</TableRow>
<TableRow>
<TextView android:text="CO" android:padding="4dp"/>
<TextView android:id="@+id/co_value" android:text="000" android:padding="4dp" android:background="@android:color/black" android:textColor="@android:color/holo_green_light"/>
</TableRow>
<TableRow>
<TextView android:text="PM1" android:padding="4dp"/>
<TextView android:id="@+id/pm1_value" android:text="000" android:padding="4dp" android:background="@android:color/black" android:textColor="@android:color/holo_green_light"/>
</TableRow>
<TableRow>
<TextView android:text="PM2" android:padding="4dp"/>
<TextView android:id="@+id/pm2_value" android:text="000" android:padding="4dp" android:background="@android:color/black" android:textColor="@android:color/holo_green_light"/>
</TableRow>
<TableRow>
<TextView android:text="PM10" android:padding="4dp"/>
<TextView android:id="@+id/pm10_value" android:text="000" android:padding="4dp" android:background="@android:color/black" android:textColor="@android:color/holo_green_light"/>
</TableRow>
<TableRow>
<TextView android:text="TEST" android:padding="4dp"/>
<TextView android:id="@+id/test_value" android:text="000" android:padding="4dp" android:background="@android:color/black" android:textColor="@android:color/holo_green_light"/>
</TableRow>
</TableLayout>
</LinearLayout>
u can see my build.gradle
plugins {
id 'com.android.application'
}
android {
namespace 'com.example.bleapp'
compileSdk 34
defaultConfig {
applicationId "com.example.bleapp"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
customDebugType {
debuggable true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1' // Updated androidx.recyclerview dependency
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
this is link where u can find jar file for app/libs: text
i tried this code in my android studio but can’t get the hex string in my android application.the below code is uploaded into esp32s3 to advertise a static hex string.
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "esp_nimble_hci.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "sdkconfig.h"
char *TAG = "ipollusense";
uint8_t ble_addr_type;
void ble_app_advertise(void);
// Write data to ESP32 defined as server
static int device_write(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
// printf("Data from the client: %.*sn", ctxt->om->om_len, ctxt->om->om_data);
char * data = (char *)ctxt->om->om_data;
printf("%dn",strcmp(data, (char *)"LIGHT ON")==0);
if (strcmp(data, (char *)"LIGHT ON")==0)
{
printf("LIGHT ONn");
}
else if (strcmp(data, (char *)"LIGHT OFF")==0)
{
printf("LIGHT OFFn");
}
else if (strcmp(data, (char *)"FAN ON")==0)
{
printf("FAN ONn");
}
else if (strcmp(data, (char *)"FAN OFF")==0)
{
printf("FAN OFFn");
}
else{
printf("Data from the client: %.*sn", ctxt->om->om_len, ctxt->om->om_data);
}
return 0;
}
// Read data from ESP32 defined as server
static int device_read(uint16_t con_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
const char *response = "{"temperature":25.40,"humidity":39.80,"no2":17,"c2h5oh":136,"voc":10,"co":55,"pm1":49,"pm2_5":67,"pm10":94}";
os_mbuf_append(ctxt->om, response, strlen(response));
return 0;
}
// Array of pointers to other service definitions
// UUID - Universal Unique Identifier
static const struct ble_gatt_svc_def gatt_svcs[] = {
{.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(0x1800), // Define UUID for device type
.characteristics = (struct ble_gatt_chr_def[]){
{.uuid = BLE_UUID16_DECLARE(0xFEF4), // Define UUID for reading
.flags = BLE_GATT_CHR_F_READ,
.access_cb = device_read},
{.uuid = BLE_UUID16_DECLARE(0xDEAD), // Define UUID for writing
.flags = BLE_GATT_CHR_F_WRITE,
.access_cb = device_write},
{0}}},
{0}};
// BLE event handling
static int ble_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type)
{
// Advertise if connected
case BLE_GAP_EVENT_CONNECT:
ESP_LOGI("GAP", "BLE GAP EVENT CONNECT %s", event->connect.status == 0 ? "OK!" : "FAILED!");
if (event->connect.status != 0)
{
ble_app_advertise();
}
break;
// Advertise again after completion of the event
case BLE_GAP_EVENT_DISCONNECT:
ESP_LOGI("GAP", "BLE GAP EVENT DISCONNECTED");
{
ble_app_advertise();
}
break;
case BLE_GAP_EVENT_ADV_COMPLETE:
ESP_LOGI("GAP", "BLE GAP EVENT");
ble_app_advertise();
break;
default:
break;
}
return 0;
}
// Define the BLE connection
void ble_app_advertise(void)
{
// GAP - device name definition
struct ble_hs_adv_fields fields;
const char *device_name;
memset(&fields, 0, sizeof(fields));
device_name = ble_svc_gap_device_name(); // Read the BLE device name
fields.name = (uint8_t *)device_name;
fields.name_len = strlen(device_name);
fields.name_is_complete = 1;
ble_gap_adv_set_fields(&fields);
// GAP - device connectivity definition
struct ble_gap_adv_params adv_params;
memset(&adv_params, 0, sizeof(adv_params));
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; // connectable or non-connectable
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; // discoverable or non-discoverable
ble_gap_adv_start(ble_addr_type, NULL, BLE_HS_FOREVER, &adv_params, ble_gap_event, NULL);
}
// The application
void ble_app_on_sync(void)
{
ble_hs_id_infer_auto(0, &ble_addr_type); // Determines the best address type automatically
ble_app_advertise(); // Define the BLE connection
}
// The infinite task
void host_task(void *param)
{
nimble_port_run(); // This function will return only when nimble_port_stop() is executed
}
void app_main()
{
nvs_flash_init(); // 1 - Initialize NVS flash using
// esp_nimble_hci_and_controller_init(); // 2 - Initialize ESP controller
nimble_port_init(); // 3 - Initialize the host stack
ble_svc_gap_device_name_set("ipollusense"); // 4 - Initialize NimBLE configuration - server name
ble_svc_gap_init(); // 4 - Initialize NimBLE configuration - gap service
ble_svc_gatt_init(); // 4 - Initialize NimBLE configuration - gatt service
ble_gatts_count_cfg(gatt_svcs); // 4 - Initialize NimBLE configuration - config gatt services
ble_gatts_add_svcs(gatt_svcs); // 4 - Initialize NimBLE configuration - queues gatt services.
ble_hs_cfg.sync_cb = ble_app_on_sync; // 5 - Initialize application
nimble_port_freertos_init(host_task); // 6 - Run the thread
}
i want hex string which is something like this {“temperature”:25.40,”humidity”:39.80,”no2″:17,”c2h5oh”:136,”voc”:10,”co”:55,”pm1″:49,”pm2_5″:67,”pm10″:94} in label1 textview and then parse the values in their attribute_value or key_value textview inplace of 000.
Madhav Raj is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.