nativescript-community / ble

Connect to and interact with Bluetooth LE peripherals.
https://nativescript-community.github.io/ble/
MIT License
192 stars 77 forks source link

Service Not Found Error when Writing Commands to ESP32 in NativeScript BLE App #280

Open luigi7up opened 1 day ago

luigi7up commented 1 day ago

I’m currently building a NativeScript app that uses BLE to connect to an ESP32 microcontroller. The microcontroller initializes a BLE server with a name, a service UUID, and characteristic UUIDs, as shown in the code snippet below. My NativeScript app successfully connects to the BLE service, but when I attempt to write commands to the characteristic, I consistently receive a service_not_found error.

#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <ESP32Servo.h>
#include <string>

#define LOCKER_MAIN_SERVICE_UUID "3e64e8c5-6b16-4b5d-b244-5bfa29c64aa6"
#define CHARACTERISTIC_SERVO_COMMANDS_UUID "c18c2e14-0b84-4b1e-8c9e-2195c62ee5e2"

Servo myServo;
int servoPin = 5;  // Pin for servo signal

BLECharacteristic *pCharacteristic;
bool deviceConnected = false;

class LockerServerCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) {
    deviceConnected = true;
    Serial.println("Device connected");
  }

  void onDisconnect(BLEServer* pServer) {
    deviceConnected = false;
    Serial.println("Device disconnected");
    // Restart advertising
    pServer->startAdvertising();
    Serial.println("Advertising restarted");
  }
};

class ServoCharacteristicsCallbacks : public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pCharacteristic) {
    // Use Arduino's String class to get the value
    String rxValue = pCharacteristic->getValue().c_str(); // Get the value as a String

    if (rxValue.length() > 0) {
      // Convert the received string to an integer (assuming single-digit commands)
      int command = rxValue[0] - '0';  // Convert ASCII character to integer

      Serial.print("Received: ");
      Serial.println(command);  // Print the received command

      // Move the servo based on the command
      moveServo(command);
    }
  }
  void moveServo(int command) {
    //moving the servo code...
  }
};

void setup() {
  Serial.begin(115200);

  // Attach the servo to the pin
  myServo.attach(servoPin);  
  myServo.write(90); // Center the servo
  delay(1000); // Wait for 2 seconds for the servo to stabilize

  // BLE setup
  BLEDevice::init("MyESP32Locker");
  Serial.println("Initializing MyESP32Locker");

  // Create BLE Server
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new LockerServerCallbacks());

  // Create BLE service
  BLEService *pService = pServer->createService(LOCKER_MAIN_SERVICE_UUID);

  // Create a BLE characteristic
  pCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_SERVO_COMMANDS_UUID,
                      BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE
                    );

  pCharacteristic->setCallbacks(new ServoCharacteristicsCallbacks());

  // Start the service
  pService->start();
  Serial.println("BLE service started!");

  // Start advertising
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();

  // Enable scan response
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // Functions that help with iPhone connections
  pAdvertising->setMinPreferred(0x12);

  Serial.println("Starting BLE advertising...");
  // BLEDevice::startAdvertising();

  // pAdvertising->start(); 
  pServer->startAdvertising();

  Serial.println("BLE advertising started, Waiting for a client connection to notify...");

}

void loop() {
  // Do nothing here

}```

Native script code:

```javascript

const Bluetooth = require("@nativescript-community/ble").Bluetooth;
const { fromObject } = require("@nativescript/core");
const createViewModel = require("@/view-models/loading-view-model").createViewModel;
const Permissions = require("nativescript-permissions"); // Correctly import the permissions module

// UUIDs for your ESP32 BLE service and characteristic (replace with actual ones)

const LOCKER_MAIN_SERVICE_UUID                          = "3e64e8c5-6b16-4b5d-b244-5bfa29c64aa6"; // Change this to a unique UUID
const CHARACTERISTIC_SERVO_COMMANDS_UUID   = "c18c2e14-0b84-4b1e-8c9e-2195c62ee5e2"; // Change this to a uniq
// Create and export the ViewModel
const viewModel = createViewModel();
const bluetooth = new Bluetooth();
let connectedPeripheral;

// Function to check and request Bluetooth permissions
function doRequestPermissions() {
    return Permissions.requestPermissions([
        android.Manifest.permission.BLUETOOTH,
        android.Manifest.permission.BLUETOOTH_ADMIN,
        android.Manifest.permission.BLUETOOTH_CONNECT,
        android.Manifest.permission.ACCESS_FINE_LOCATION,
        android.Manifest.permission.ACCESS_COARSE_LOCATION,
    ]);
}

async function onNavigatingTo(args) {
    const page = args.object;
    page.bindingContext = viewModel; // Set the binding context to the view model

    // TODO Check if Bluetooth on the device is enabled?!

    // Request permissions
    try {
        await doRequestPermissions();
        console.log("All permissions granted");

        // Start scanning for devices after permissions are granted
        scanAndConnect();
    } catch (error) {
        console.error("Error while requesting permissions: ", error);
    }
};

async function scanAndConnect() {

    try {
        viewModel.isConnecting = true;
        viewModel.connectionStatus = `Scanning for Locker device with service UUID ${LOCKER_MAIN_SERVICE_UUID}`;
        console.log(`Scanning for Locker device with service UUID ${LOCKER_MAIN_SERVICE_UUID}`);
        // Scan for devices with the matching SERVICE_UUID
        await bluetooth.startScanning({
            serviceUUIDs: [LOCKER_MAIN_SERVICE_UUID],
            seconds: 20,
            onDiscovered: (peripheral) => {
                if (peripheral.name && peripheral.name.startsWith("MyESP32Locker")) {
                    console.log("Discovered Locker device");
                    viewModel.connectionStatus = "Discovered Locker. Now Connecting...";
                    console.log("Peripheral object attributes:")
                    console.dir(peripheral)
                    connectedPeripheral = peripheral;
                    bluetooth.stopScanning();
                    connectToLocker(peripheral);
                }
            },
            onError:(error)=> {
                console.error("Error during BLE scan: ", error);
                viewModel.connectionStatus = "Could not find the device. Try again.";
                viewModel.isConnecting = false;
                bluetooth.stopScanning();

            }
        });
    } catch (error) {
        console.error("Error during BLE scan: ", error);
        viewModel.connectionStatus = "Error scanning for devices.";
        viewModel.isConnecting = false;
    }
}

// Function to connect to the Locker device
async function connectToLocker(peripheral) {
    try {
        console.log("Connecting to peripheral")
        viewModel.connectionStatus = "Connecting to Locker...";

        await bluetooth.connect({
            UUID: peripheral.UUID,
            onConnected: (peripheral) => {
                console.log("Connected successfuly to "+ peripheral.name)

                console.log("Periperhal connected with name: " + peripheral.name);

                // // the peripheral object now has a list of available services:
                // peripheral.services.forEach(function(service) {
                //     console.log("service found: " + JSON.stringify(service));
                // });< 

                viewModel.connectionStatus = "Connected to " + peripheral.name;
                viewModel.isConnecting = false;
            },
            onDisconnected: () => {
                console.log("Disconnected successfuly to "+ peripheral.name)
                viewModel.connectionStatus = "Disconnected from " + peripheral.name;

            }
        });
    } catch (error) {
        console.error("Error connecting to peripheral: ", error);
        viewModel.connectionStatus = "Connection failed.";
        viewModel.isConnecting = false;
    }
}

// Functions to send commands to the ESP32
async function sendCommand(command) {
    try {
        if (connectedPeripheral) {

            console.log("Sending command:")
            console.log("connectedPeripheral.UUID "+connectedPeripheral.UUID)
            console.log("LOCKER_MAIN_SERVICE_UUID "+LOCKER_MAIN_SERVICE_UUID)
            console.log("CHARACTERISTIC_SERVO_COMMANDS_UUID "+CHARACTERISTIC_SERVO_COMMANDS_UUID)
            console.log("[command] "+command)
            console.log("[command as Uint8Array] "+new Uint8Array([command]) ) // Use Uint8Array directly

            await bluetooth.write({
                peripheralUUID: connectedPeripheral.UUID,  // Use peripheralUUID instead of UUID
                serviceUUID: LOCKER_MAIN_SERVICE_UUID,
                characteristicUUID: CHARACTERISTIC_SERVO_COMMANDS_UUID,
                value: [0]
            });
            console.log(`Command ${command} sent to Locker.`);
        } else {
            console.warn("No device connected.");
        }
    } catch (error) {
        console.error("Error sending command: ", error);
    }
}

// Export functions for button taps
exports.onNavigatingTo = onNavigatingTo;

exports.onScanAndConnectTap = ()=> scanAndConnect();
exports.onOpenButtonTap = () => sendCommand(0); // Send 0 to open
exports.onCloseButtonTap = () => sendCommand(180); // Send 180 to close

Error I receive:

Sending command:
Error sending command: [BluetoothError]:service_not_found,arguments: {"peripheralUUID":"08:B6:1F:28:62:86","serviceUUID":"180e","characteristicUUID":"c18c2e14-0b84-4b1e-8c9e-2195c62ee5e2","value":[0]}

Additional Notes:

nrfconnect

I would greatly appreciate any help or suggestions on how to resolve this issue. Thank you in advance!

farfromrefug commented 1 day ago

@luigi7up you need to discover services first. This is not done automatically (or can using a connect parameter). Without this services cant be used

luigi7up commented 1 day ago

@farfromrefug hi, thank you for your reply... I¡m sorry for not providing the whole code where you can see that I'm calling the bluetooth.startScanning({}) method

I'll update my question above

farfromrefug commented 1 day ago

@luigi7up i am.not talking about scanning. I am talking about discovering services and characteristics. It is mandatory. Look at the typings for how to do it either manually or through the connect method