I am working with Arduino Nano BLE and my device has Microchip RN4871 BLE module. When I use the code below, arduino retrieves its attributes faster and each time it connects to my device.
#include <ArduinoBLE.h>
void setup() {
Serial.begin(9600);
while (!Serial);
// begin initialization
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
while (1);
}
Serial.println("BLE Central - Peripheral Explorer");
}
void loop() {
// start scanning for peripherals
BLE.scan();
// check if a peripheral has been discovered
BLEDevice peripheral = BLE.available();
if (peripheral) {
// discovered a peripheral, print out address, local name, and advertised service
Serial.print("Found ");
Serial.print(peripheral.address());
Serial.print(" '");
Serial.print(peripheral.localName());
Serial.print("' ");
Serial.print(peripheral.advertisedServiceUuid());
Serial.println();
if (peripheral.localName().substring(0,3) == "AP-") {
Serial.print("String Found \t \t");
// stop scanning
BLE.stopScan();
explorerPeripheral(peripheral);
}
}
}
void explorerPeripheral(BLEDevice peripheral) {
// connect to the peripheral
Serial.println("Connecting ...");
if (peripheral.connect()) {
Serial.println("Connected");
} else {
Serial.println("Failed to connect!");
return;
}
// discover peripheral attributes
Serial.println("Discovering attributes ...");
if (peripheral.discoverAttributes()) {
Serial.println("Attributes discovered");
} else {
Serial.println("Attribute discovery failed!");
peripheral.disconnect();
return;
}
// read and print device name of peripheral
Serial.println();
Serial.print("Device name: ");
Serial.println(peripheral.deviceName());
Serial.print("Appearance: 0x");
Serial.println(peripheral.appearance(), HEX);
Serial.println();
// loop the services of the peripheral and explore each
for (int i = 0; i < peripheral.serviceCount(); i++) {
BLEService service = peripheral.service(i);
exploreService(service);
}
Serial.println();
// we are done exploring, disconnect
Serial.println("Disconnecting ...");
peripheral.disconnect();
Serial.println("Disconnected");
}
void exploreService(BLEService service) {
// print the UUID of the service
Serial.print("Service ");
Serial.println(service.uuid());
// loop the characteristics of the service and explore each
for (int i = 0; i < service.characteristicCount(); i++) {
BLECharacteristic characteristic = service.characteristic(i);
exploreCharacteristic(characteristic);
}
}
void exploreCharacteristic(BLECharacteristic characteristic) {
// print the UUID and properties of the characteristic
Serial.print("\tCharacteristic ");
Serial.print(characteristic.uuid());
Serial.print(", properties 0x");
Serial.print(characteristic.properties(), HEX);
// check if the characteristic is readable
if (characteristic.canRead()) {
// read the characteristic value
characteristic.read();
if (characteristic.valueLength() > 0) {
// print out the value of the characteristic
Serial.print(", value 0x");
printData(characteristic.value(), characteristic.valueLength());
}
}
Serial.println();
// loop the descriptors of the characteristic and explore each
for (int i = 0; i < characteristic.descriptorCount(); i++) {
BLEDescriptor descriptor = characteristic.descriptor(i);
exploreDescriptor(descriptor);
}
}
void exploreDescriptor(BLEDescriptor descriptor) {
// print the UUID of the descriptor
Serial.print("\t\tDescriptor ");
Serial.print(descriptor.uuid());
// read the descriptor value
descriptor.read();
// print out the value of the descriptor
Serial.print(", value 0x");
printData(descriptor.value(), descriptor.valueLength());
Serial.println();
}
void printData(const unsigned char data[], int length) {
for (int i = 0; i < length; i++) {
unsigned char b = data[i];
if (b < 16) {
Serial.print("0");
}
Serial.print(b, HEX);
}
}
But when I try to connect to my device in same fashion to write to my microchip module, it often fails to connect or if it sends data then my devices doesn't acts on it thinking it is some junk data. Most of the time, with the code below, it fails to even scan my device.
#include <ArduinoBLE.h>
void setup() {
Serial.begin(9600);
while (!Serial);
// begin initialization
BLE.begin();
Serial.println("Trying to connect to Microchip UUID");
Serial.println();
}
void loop() {
// start scanning for peripherals
BLE.scan();
// check if a peripheral has been discovered
Serial.println("Scanning");
BLEDevice peripheral = BLE.available();
if (peripheral) {
Serial.print("Detected ");
Serial.print(peripheral.address());
Serial.print(" '");
Serial.print(peripheral.localName());
Serial.print("' ");
Serial.print(peripheral.advertisedServiceUuid());
Serial.println();
if (peripheral.localName().substring(0,3) == "AP-") {
// stop scanning
BLE.stopScan();
controlLed(peripheral);
Serial.println("RESTART");
}
}
delay(5000);
}
//----------------------------------------------------------------------------
void controlLed(BLEDevice peripheral) {
if (peripheral.connect()) {
Serial.println("Connected to receiver");
} else {
Serial.println("Failed!");
return;
}
Serial.println("Discovering devices ...");
if (peripheral.discoverAttributes()) {
Serial.println("devices discovered");
} else {
Serial.println("device discovery failed!");
peripheral.disconnect();
return;
}
BLECharacteristic ledCharacteristic = peripheral.characteristic("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
if (!ledCharacteristic) {
Serial.println("No characteristic");
peripheral.disconnect();
return;
} else if (!ledCharacteristic.canWrite()) {
Serial.println("Can't write");
peripheral.disconnect();
return;
}
Serial.print("peripheral connected? \t");
Serial.println(peripheral.connected());
if (peripheral.connected()) {
char arr[6] = "b440\r";
char arr1[6] = "b0\r";
Serial.println("transmitting data");
// Serial.println(arr);
for(int i=0; i<5; i++){
ledCharacteristic.writeValue((byte)arr[i]);
Serial.println(arr[i]);
}
delay(3000);
for(int i=0; i<5; i++){
ledCharacteristic.writeValue((byte)arr1[i]);
Serial.println(arr1[i]);
}
}
Serial.println("Peripheral disconnected");
}
Can someone tell me what am I doing wrong here? Also, I want if I want to send hex data after some ASCII chars how do I do that? So far, I have tried 0xdd but that didn't work.
I suspect this does not work. How is the library supposed to know that you intend to write another byte to the characteristic? You have to write everything in one go. The writeValue function allows you to provide a buffer and length.
This is working for me when I connect to a specific device "AP-1234". But doesn't work when I try to connect to any device that starts with "AP-".
I was hoping this would work.
But this is giving me following error and I don't understand what it means.
Arduino/libraries/ArduinoBLE/src/BLECharacteristic.h:72:7: note: candidate: int BLECharacteristic::writeValue(const void*, int, bool)
int writeValue(const void* value, int length, bool withResponse = true);
^~~~~~~~~~
Arduino/libraries/ArduinoBLE/src/BLECharacteristic.h:73:7: note: candidate: int BLECharacteristic::writeValue(const char*, bool)
int writeValue(const char* value, bool withResponse = true);
^~~~~~~~~~
Using library ArduinoBLE at version 1.2.1 in folder: Arduino/libraries/ArduinoBLE
exit status 1
call of overloaded 'writeValue(char [6], int)' is ambiguous
Strings have null terminators. Normal arrays do not have null terminators. That would not work. An array element of type int can have any value including 0. The compiler knows the size of the array and so should the programmer. No, null terminator necessary. Strings on the other hand can have different length because you could shorten an English sentence and still store it in the same String. The null terminator is used for detecting the end of the meaningful text and not the end of the allocated memory.
Do you have any information about the GATT on the device you are connected to?
I believe the issue comes from you using chars. The buffer needs to be bytes or uint8_t. It looks like there is a function for writing Strings. So, the compiler does not know which one you want. You could try to set parameter 3 to true or false. Then only the first function would fit.
Could you tell me how to send "b440\r" this string in one go over BLE? I tried with bleCharacteristic.writeValue(buffer, length)
But that is giving me error.
Arduino/libraries/ArduinoBLE/src/BLECharacteristic.h:72:7: note: candidate: int BLECharacteristic::writeValue(const void, int, bool)
int writeValue(const void* value, int length, bool withResponse = true);
^~~~~~~~~~
Arduino/libraries/ArduinoBLE/src/BLECharacteristic.h:73:7: note: candidate: int BLECharacteristic::writeValue(const char*, bool)
int writeValue(const char* value, bool withResponse = true);
^~~~~~~~~~
Using library ArduinoBLE at version 1.2.1 in folder: Arduino/libraries/ArduinoBLE
exit status 1
call of overloaded 'writeValue(char [6], int)' is ambiguous*
Also, I now know why it was failing. When I am trying to connect the device, in void controlLed function, the device never got connected.
I want to know what is going wrong in above code so that I will be able to fix this issue.
char arr[] = {'b','4','4','0','\r'};//5 chars no null
bleCharacteristic.writeValue(arr);
declare the array with a null terminator and send it with the full syntax
char arr[6] = "b440\r";//5 chars plus null
bleCharacteristic.writeValue(arr,5,false);//or true
Both options compile for me when I use the sketch you previously posted
if (peripheral.connected()) {
char arr[6] = "b440\r";//5 chars plus null
char arr[] = {'b','4','4','0','\r'};//5 chars no null
char arr1[6] = "b0\r";
//ledCharacteristic.writeValue(arr,5);
ledCharacteristic.writeValue(arr);//can use with array without null
ledCharacteristic.writeValue(arr,5,false);//or true can use with array with null
works fine. Can you explain from writeValue function in Arduino BLE where arr is buffer and 5 is length, then what is false?
Also, with this code I found out that sending data in for loop or in one go wasn't the problem, but for some reason my Arduino nano 33 BLE just was not able to connect to my device and if connected then was not able to find the characteristic it should write data at.
It is a boolean parameter asking for a response or not. I'm not totally clear, but I assume it means you can get a confirming response that the writeValue was received.
int writeValue(const uint8_t value[], int length, bool withResponse = true);
but for some reason my Arduino nano 33 BLE just was not able to connect to my device and if connected then was not able to find the characteristic it should write data at
These sound like two issues.
Do you know the service and characteristics of the Microchip RN4871 BLE module?
If so, can you write code for the Nano33 BLE which finds and connects to the Microchip RN4871 BLE module looking for the known service? The LedControl example in the library examples/Central/ is probably the model, but it probably can be simplified if you know the characteristics of the rn4871.
@cattledog Below is the service and characteristics of the Microchip RN4871 BLE module I am using. This is the one that I am interested in that I have copy-pasted.
I am writing to charaactristic "49535343-1e4d-4bd9-ba61-23c647249616" which otherwise works great when I am trying to connect to a specific device like BLE.scanForName("AP-12345678"). But when I scan for all peripherals, and find out which ones start with "AP-" and then connect to it. Then the device doesn't get connected most of the time and when it does the BLE range is quite low about 1-2 meters.
Device name: AP-xxxxxxxx␍␊
Appearance: 0x0␍␊
Service 49535343-fe7d-4ae5-8fa9-9fafd205e455␍␊
Characteristic 49535343-1e4d-4bd9-ba61-23c647249616, properties 0x3C␍␊
Descriptor 2902, value 0x0000␍␊
Descriptor 2803, value 0x0C5500B39B␍␊
Descriptor 9bb3, value 0x␍␊
Characteristic 49535343-8841-43f4-a8d4-ecbe34729bb3, properties 0xC␍␊
Descriptor 2803, value 0x1857007E3B␍␊
Descriptor 3b7e, value 0x␍␊
Characteristic 49535343-4c8a-39b3-2f49-511cff073b7e, properties 0x18␍␊
Descriptor 2902, value 0x0000␍␊
How many RN4871 BLE modules are there in your environment?
Can you please post the current version of the two sketches--the one using .scanForName() and the other with the .scan().
Is there any serial printing information you receive in the two sketches?
I have two RN modules. Also, what I noticed was the range was weaker in the outside open field than in the building which doesn't make sense to me. Since the building has too much metal stuff around, shouldn't it be that the Arduino and my device has better range in the outside clear field?!
Below is the generic code that I am using to connect multiple devices that start with "AP-"
/*
Peripheral Explorer
This code scans for BLE peripherals looking for AssetPack devices starting with "AP-" and finds it.
Once found then it connects, and discovers all the peripheral's attributes and sends commands to AP device.
This code can connect with AP from a distance of 1-2 meters.
It runs keeps looking for different APs for 30 seconds and sends commands.
Then it turns BLE off for half an hour and turns it back ON before looking for devices another 30seconds.
*/
#include <ArduinoBLE.h>
unsigned long timeNow, timeNowService;
unsigned long timeScan = 0;
void setup() {
Serial.begin(9600);
while (!Serial);
// begin initialization
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
while (1);
}
Serial.println("BLE Central - Peripheral Explorer");
}
// ---------------- void loop ---------------
void loop() {
if (millis() - timeScan <= 30000){
// start scanning for peripherals
BLE.scan();
// check if a peripheral has been discovered
BLEDevice peripheral = BLE.available();
if (peripheral) {
// discovered a peripheral, print out address, local name, and advertised service
Serial.print("Found ");
Serial.print(peripheral.address());
Serial.print(" '");
Serial.print(peripheral.localName());
Serial.print("' ");
Serial.print(peripheral.advertisedServiceUuid());
Serial.println();
if (peripheral.localName().substring(0,3) == "AP-") {
Serial.print("String Found \t \t");
// stop scanning
BLE.stopScan();
timeNow = millis();
// wait in while loop until connected or 5 seconds are up
while(!peripheral.connected() && (millis() - timeNow <= 5000 ) ) {
peripheral.connect();
}
Serial.println("Connected to receiver");
// print the RSSI
Serial.print("RSSI: ");
Serial.println(peripheral.rssi());
timeNowService = millis();
// wait in while loop until service is found or 5 seconds are up
while(!peripheral.hasService("49535343-fe7d-4ae5-8fa9-9fafd205e455") && (millis() - timeNowService <= 5000 ) ) {
peripheral.discoverAttributes();
}
Serial.println("Service");
BLEService ledservice = peripheral.service("49535343-fe7d-4ae5-8fa9-9fafd205e455");
BLECharacteristic ledCharacteristic = ledservice.characteristic(0);
Serial.print("ledCharacteristic \t");
Serial.println(ledCharacteristic.uuid());
char arr[10] = "xxx xxxx\r";
ledCharacteristic.writeValue(arr,9,false);
peripheral.disconnect();
delay(500);
}
}
}
else {
BLE.end();
Serial.println("Start delay");
Serial.println("");
Serial.println("");
delay(30000);
while(!BLE.begin());
timeScan = millis();
}
}
I thought it was wokring fine but I just noticed that it didn't. It is supposed to send data or turn on the buzzer. Since I didn't see meaningful data, I tried the buzzer thing and seems nothing is happening.
Let's focus on the case with the specific device where I believe that you can always find and connect to the device.
I think you are writing to the wrong characteristic. BLECharacteristic ledCharacteristic = peripheral.characteristic("49535343-1e4d-4bd9-ba61-23c647249616");
I think you should be using this characteristic to write to the device.
49535343-8841-43f4-a8d4-ecbe34729bb3
Detail from the manual
The Transparent UART Service is instantiated as a Primary Service. The service UUID
of the Transparent UART Service is set to
49535343-FE7D-4AE5-8FA9-9FAFD205E455. The Transparent UART Service
contains the following data characteristics:
• Transparent UART Transmit (TX) Characteristic
• Transparent UART Receive (RX) Characteristic The Transparent UART TX Characteristic is used for data transmission by the Server
or the Client. Once the Client Characteristic Configuration Descriptor (CCCD) of
Transparent UART TX Characteristic is enabled, the Server sends data to the Client
using the Notify property. The Client can also send data to the Server using the
Write/Write without response properties. The Transparent UART RX Characteristic is used for data transmission by the Client.
The Client can send data to the Server using the Write/Write without response
properties.
Table B-1 shows the UUIDs and properties of the data characteristics.
TABLE B-1: CHARACTERISTIC PROPERTIES
Characteristic Name UUID Properties
Transparent UART TX 49535343-1E4D-4BD9-BA61-23C647249616 Notify, Write, Write
without response Transparent UART RX 49535343-8841-43F4-A8D4-ECBE34729BB3 Write, Write without
response
Have you confirmed that the RN4870 unit is programmed properly to run a buzzer to do whatever other instructions you are sending to it?
Also, avoid delay() at any cost. I think mbedOS may sleep during that time. You do not know how that affects the BLE stack. After you call BLE.end(), it is probably fine to use delay while you wait/sleep.
Somehow I do not like that code. I do not have the time to analyse it thoroughly, but discoverAttributes() returns a code, just use that. I believe the library examples only call it once. I recommend you avoid hammering the BLE stack with calls. You do not know what needs to happen in the background.
The same for connect. If peripheral.connect() fails. Do not try again. Start from the beginning.
Hey @cattledog yes, I know for sure that RN4871 unit is programmed properly. Well, I am using the other characteristic as you suggested and I can get the buzzer working not the other command yet.
Also, it is RN4871 chip that has a weak signal to begin with, with the mobile App I can connect to it over 5-6 meters. But it should be same for Arduino NANO 33 BLE as well. Nano BLE can only connect at 2-3 meters.
yes, I know for sure that RN4871 unit is programmed properly.
What is the other command, and what is the expected response?
Do you have the source code for RN4871?
Also, it is RN4871 chip that has a weak signal to begin with, with the mobile App I can connect to it over 5-6 meters. But it should be same for Arduino NANO 33 BLE as well. Nano BLE can only connect at 2-3 meters.
I really can't comment on the hardware issues, but others have raised the issue of poor range, as a google search on
"poor range nano33 ble arduino" will bring up plenty of hits.