I am trying to get a list of nearby BLE devices along with their UUIDs and characteristics but I could not find any sample codes that do just that. So I turned to ChatGPT and it came back with this:
#include "Particle.h"
SYSTEM_MODE(AUTOMATIC);
void setup() {
Serial.begin(9600); // Initialize serial communication for debugging
BLE.on(); // Turn on BLE
BLE.setScanTimeout(5000); // Set scan timeout to 5 seconds
Log.info("Starting BLE scan...");
scanForDevices(); // Scan for BLE devices
}
void loop() {
// Do nothing in the loop, just setup and scan
}
// Function to scan for nearby BLE devices
void scanForDevices() {
BleScanResult scanResults[20]; // Array to hold scan results
int foundDevices = BLE.scan(scanResults, 20); // Scan for up to 20 devices
if (foundDevices > 0) {
Log.info("Found %d devices", foundDevices);
for (int i = 0; i < foundDevices; i++) {
BleAddress address = scanResults[i].address();
Log.info("Device %d: %s", i + 1, address.toString().c_str());
// Try connecting to the device
BlePeerDevice peerDevice = BLE.connect(scanResults[i].address());
if (peerDevice.connected()) {
Log.info("Connected to device %d", i + 1);
// Discover all services and characteristics
discoverServicesAndCharacteristics(peerDevice);
// Disconnect after discovering the services and characteristics
peerDevice.disconnect();
Log.info("Disconnected from device %d", i + 1);
} else {
Log.error("Failed to connect to device %d", i + 1);
}
}
} else {
Log.error("No BLE devices found");
}
}
// Function to discover services and characteristics of a connected BLE device
void discoverServicesAndCharacteristics(BlePeerDevice& peerDevice) {
Vector<BleService> services;
peerDevice.discoverAllServicesAndCharacteristics(services);
// Loop through all discovered services
for (BleService& service : services) {
BleUuid serviceUUID = service.UUID();
Log.info("Service UUID: %s", serviceUUID.toString().c_str());
// Loop through all characteristics in this service
Vector<BleCharacteristic> characteristics = service.characteristics();
for (BleCharacteristic& characteristic : characteristics) {
BleUuid characteristicUUID = characteristic.UUID();
Log.info(" Characteristic UUID: %s", characteristicUUID.toString().c_str());
}
}
}
Unfortunately, when I try to compile it it says:
obdii_ble_002.ino:52:16: 'class particle::BlePeerDevice' has no member named 'discoverAllServicesAndCharacteristics'; did you mean 'discoverAllCharacteristics'?
and
obdii_ble_002.ino:60:61: 'class particle::BleService' has no member named 'characteristics'
Changing "discoverAllServicesAndCharacteristics" to "discoverAllCharacteristics" did not solve the problem.
Any ideas of what I am doing wrong, or perhaps some pointers to a sample code that does what I am trying to do?
I checked that documentation but could not find an example of what I need to do. Based on your suggestion to use scanWithFilter, I tried this code but it is not finding the device. I am not sure if I am using the right values for deviceName and serviceUUID. I am using what appeared to be the most likely values that were returned using a BLE sniffer app on my phone. I was hoping that the sample code in the original post would show me the right values as they are seen by the Photon.
#include "Particle.h"
SYSTEM_MODE(AUTOMATIC);
void scan() {
BleScanFilter filter;
filter.deviceName("00002A00-0000-1000-8000-00805F9B34FB").minRssi(-50).serviceUUID(0x1801);
Vector<BleScanResult> scanResults = BLE.scanWithFilter(filter);
if (scanResults.size()) {
Log.info("%d devices found", scanResults.size());
Serial.println(scanResults.size() + " devices found");
for (int ii = 0; ii < scanResults.size(); ii++) {
Log.info("MAC: %s | RSSI: %dBm", scanResults[ii].address().toString().c_str(), scanResults[ii].rssi());
Serial.println("MAC: " + scanResults[ii].address().toString());
Serial.println("RSSI: " + scanResults[ii].rssi());
String name = scanResults[ii].advertisingData().deviceName();
if (name.length() > 0) {
Log.info("Advertising name: %s", name.c_str());
Serial.println("Advertising name: " + name);
}
}
}
else {
Serial.println("Device not found");
}
}
// setup() runs once, when the device is first turned on
void setup() {
Serial.begin(9600);
delay(1000);
}
void loop() {
scan();
delay(3000);
}
That helped a lot, thanks! There was some progress and the Photon is now detecting devices. Turns out I had to enter "OBDII" as device name and get rid of the other filters.
Now that the ELM327 device has been detected, how do I go about sending and receiving serial data to it?
Once again, thanks. I have expanded on the snippets you provided to get to this:
#include "Particle.h"
SYSTEM_MODE(AUTOMATIC);
SerialLogHandler logHandler(LOG_LEVEL_INFO);
// UUIDs for the ELM327 characteristics (example UUIDs, replace with actual)
const BleUuid txUuid("0000fff1-0000-1000-8000-00805f9b34fb"); // UUID for TX characteristic (ELM327 to Photon)
const BleUuid rxUuid("00002a00-0000-1000-8000-00805f9b34fb"); // UUID for RX characteristic (Photon to ELM327)
BleCharacteristic peerTxCharacteristic;
BleCharacteristic peerRxCharacteristic;
BlePeerDevice peripheral;
// Function to scan and connect to ELM327
void scanAndConnect() {
BleScanFilter filter;
filter.deviceName("OBDII");
Vector<BleScanResult> scanResults = BLE.scanWithFilter(filter);
if (scanResults.size()) {
Log.info("%d devices found", scanResults.size());
for (int ii = 0; ii < scanResults.size(); ii++) {
Log.info("MAC: %s | RSSI: %dBm", scanResults[ii].address().toString().c_str(), scanResults[ii].rssi());
String name = scanResults[ii].advertisingData().deviceName();
if (name.length() > 0) {
Log.info("Advertising name: %s", name.c_str());
}
peripheral = BLE.connect(scanResults[ii].address());
if (peripheral.connected()) {
Log.info("Peripheral connected");
// Get the TX and RX characteristics
peripheral.getCharacteristicByUUID(peerTxCharacteristic, rxUuid);
peripheral.getCharacteristicByUUID(peerRxCharacteristic, txUuid);
if (peerTxCharacteristic.valid() && peerRxCharacteristic.valid()) {
Log.info("TX and RX characteristics found");
// Set callback for receiving data
peerRxCharacteristic.onDataReceived(onDataReceived);
// Send an initial command to the ELM327
sendCommand("ATI\r"); // Example command to get ELM327 version
} else {
Log.info("Failed to find TX/RX characteristics");
}
break;
} else {
Log.info("Failed to connect to peripheral");
}
}
} else {
Log.info("Device not found");
}
}
// Function to handle incoming data from ELM327
void onDataReceived(const uint8_t* data, size_t len, const BlePeerDevice& peer, void* context) {
String receivedData = String((const char*)data, len);
Log.info("Data received: %s", receivedData.c_str());
// You can process the received data here
}
// Function to send a command to the ELM327
void sendCommand(const char* command) {
if (peripheral.connected() && peerTxCharacteristic.valid()) {
peerTxCharacteristic.setValue((const uint8_t*)command, strlen(command));
Log.info("Command sent: %s", command);
} else {
Log.info("Not connected or TX characteristic invalid");
}
}
void setup() {
Serial.begin(9600);
delay(1000);
BLE.on(); // Enable BLE
}
void loop() {
if (!peripheral.connected()) {
scanAndConnect();
}
// Example of sending a command periodically
if (peripheral.connected()) {
sendCommand("010C\r"); // Request RPM data (example OBD-II PID command)
delay(5000); // Delay to allow response
}
delay(1000);
}
It works until the part where its says "Peripheral connected" but it gets stuck with the UUIDs. I am attempting to get the UUIDs using a BLE scanner as shown in the picture but I am not sure if I am getting the right ones.
The original code was pretty close. Here's a modified version that compiles and runs and discovers and prints all of the characteristics like the original code was supposed to.
#include "Particle.h"
SYSTEM_MODE(AUTOMATIC);
SYSTEM_THREAD(ENABLED);
SerialLogHandler logHandler(LOG_LEVEL_TRACE);
void scanForDevices();
void discoverServicesAndCharacteristics(BlePeerDevice &peerDevice);
const std::chrono::milliseconds scanInterval = 30s;
unsigned long scanLastMillis = 0;
void setup()
{
BLE.on(); // Turn on BLE
BLE.setScanTimeout(5000); // Set scan timeout to 5 seconds
}
void loop()
{
if (millis() - scanLastMillis >= scanInterval.count()) {
scanLastMillis = millis();
Log.info("Starting BLE scan...");
scanForDevices(); // Scan for BLE devices
}
}
// Function to scan for nearby BLE devices
void scanForDevices()
{
BleScanResult scanResults[20]; // Array to hold scan results
int foundDevices = BLE.scan(scanResults, 20); // Scan for up to 20 devices
if (foundDevices > 0)
{
Log.info("Found %d devices", foundDevices);
for (int i = 0; i < foundDevices; i++)
{
BleAddress address = scanResults[i].address();
Log.info("Device %d: %s", i + 1, address.toString().c_str());
// Try connecting to the device
BlePeerDevice peerDevice = BLE.connect(scanResults[i].address());
if (peerDevice.connected())
{
Log.info("Connected to device %d", i + 1);
// Discover all services and characteristics
discoverServicesAndCharacteristics(peerDevice);
// Disconnect after discovering the services and characteristics
peerDevice.disconnect();
Log.info("Disconnected from device %d", i + 1);
}
else
{
Log.error("Failed to connect to device %d", i + 1);
}
}
}
else
{
Log.error("No BLE devices found");
}
}
// Function to discover services and characteristics of a connected BLE device
void discoverServicesAndCharacteristics(BlePeerDevice &peerDevice)
{
// Loop through all discovered services
for (BleService &service : peerDevice.services())
{
BleUuid serviceUUID = service.UUID();
Log.info("Service UUID: %s", serviceUUID.toString().c_str());
Vector<BleCharacteristic> characteristics = peerDevice.discoverAllCharacteristics();
// Loop through all characteristics in this service
for (BleCharacteristic &characteristic : characteristics)
{
BleUuid characteristicUUID = characteristic.UUID();
Log.info(" Characteristic UUID: %s", characteristicUUID.toString().c_str());
}
}
}
Being unable to tell which is which, I tried a number of combinations using a 0x???? format as opposed to the previous long string. There was some progress because now instead of getting stuck at "Not connected or TX characteristic invalid", I am getting to the part where it says "TX and RX characteristics found" and it appears as though AT commands are being sent. However, no response is coming. No matter what combination I use, the result is the same. Based on the results that were returned, is there a way to determine which one is the txUuid and which is the rxUuid. I feel I am getting so close and yet the destination is ever so elusive!