I have a code that reads the power from my cycling power meter and changes the color of some LED lights accordingly. It connects to the device by matching the ServiceUUID and Characteristic UUID. The problem is that I have 4 devices in my garage that all have the same UUIDs as my target device, so it often connects to the wrong one. The code works just fine when connected to the right device. How do I get it to connect to my device with the BLE address?
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
/**
Called for each advertising BLE server.
*/
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.print("BLE Advertised Device found: ");
Serial.println(advertisedDevice.toString().c_str());
// We have found a device, let us now see if it contains the service we are looking for.
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
BLEDevice::getScan()->stop(); // stop scanning when device is found
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
This section of code is where it checks the UUIDs. I have tried pasting in the following snippet instead of that if statement, but when I run the code it goes in an endless loop and never works (I did add the DEF_BLE_ADDR with the target address at the top of my code):
They are four different power meters (three for cycling, one for running). I need it to connect to the one I use that is on my bike that I ride on the trainer. They all have different addresses, but the UUIDs are the same. When the program runs, it lists the device name and the UUID and address and seemingly connects to whichever one has the strongest signal (or whichever it finds first, not sure).
Here is what I get on the serial monitor. In this case it connected to a device called 'Tacx Flux' which is one of my power meters, but not the one I need.
#include "BLEDevice.h"
#include "Arduino.h"
#include "FastLED.h"
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#define NUM_LEDS 150
#define LED_PIN 4
#define CHIPSET WS2812B
#define COLOR_ORDER GRB
CRGB led[NUM_LEDS];
#define DEF_BLE_ADDR "eb:56:d2:fb:28:ec"
// The remote service we wish to connect to.
static BLEUUID serviceUUID("00001818-0000-1000-8000-00805f9b34fb"); //powermeter
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID("00002a63-0000-1000-8000-00805f9b34fb"); // Cycling power measurement
// 2A63: Cycling power measurement
static bool doConnect = false;
static bool connected = false;
static bool doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEAdvertisedDevice* myDevice;
bool success;
static void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
Serial.print("Power: ");
int Power = pData[3]*256 + pData[2]; //pData[2] = instantaneous power LSB, GATT_Specification_Supplement_v5.pdf 3.59.2
// pData[3] = instantaneous power MSB, GATT_Specification_Supplement_v5.pdf 3.59.2
Serial.print(Power); // prints to serial port
Serial.println(" W");
// Determine power zone and set color
int zone;
int FTP = 331; // FTP value as entered/measured in Zwift
int startLed = 1; // LED 0 is used as status light (blue = connected, red = not connected)
if (Power < 10){ // LEDs off below 10 W
zone = 0;
for (int i = startLed; i < NUM_LEDS; i++) {
led[i] = CRGB(0,0,0);
}
}
else if (Power >= 10 && Power <= FTP * 0.6) { // Zone 1 (Grey, Recovery): 10 W to 60% FTP
zone = 1;
for (int i = startLed; i < NUM_LEDS; i++) {
led[i] = CRGB(100,100,100);
}
}
else if (Power > FTP * 0.60 && Power <= FTP * 0.75){ // Zone 2 (Blue, Endurance): 60-75%
zone = 2;
for (int i = startLed; i < NUM_LEDS; i++) {
led[i] = CRGB(0,0,255);
}
}
else if (Power > FTP * 0.75 && Power <= FTP * 0.89){ // Zone 3 (Green, Tempo): 76-89%
zone = 3;
for (int i = startLed; i < NUM_LEDS; i++) {
led[i] = CRGB(0,255,0);
}
}
else if (Power > FTP * 0.89 && Power <= FTP * 1.04){ // Zone 4 (Yellow, Threshold): 90-104%
zone = 4;
for (int i = startLed; i < NUM_LEDS; i++) {
led[i] = CRGB(255,255,0);
}
}
else if (Power > FTP * 1.04 && Power <= FTP * 1.18){ // Zone 5 (Orange, VO2 Max): 105-118%
zone = 5;
for (int i = startLed; i < NUM_LEDS; i++) {
led[i] = CRGB(255,40,0);
}
}
else if (Power > FTP * 1.18){ // Zone 6 (Red, Anaerobic): above 118%
zone = 6;
for (int i = startLed; i < NUM_LEDS; i++) {
led[i] = CRGB(255,0,0);
}
}
else{ // if not in range but this basically is not possible
zone = 0;
for (int i = startLed; i < NUM_LEDS; i++) {
led[i] = CRGB(255,0,255);
}
Serial.println("Error: This zone should not be possible.");
}
Serial.print("zone: ");
Serial.println(zone);
FastLED.show();
delay(500); //Delay otherwise too many updates and color changes (drives me mad!). So update every 500 ms
}
class MyClientCallback : public BLEClientCallbacks {
void onConnect(BLEClient* pclient) {
}
void onDisconnect(BLEClient* pclient) {
connected = false;
Serial.println("onDisconnect");
}
};
// Start connection to the BLE Server
bool connectToServer() {
Serial.print("Forming a connection to ");
Serial.println(myDevice->getAddress().toString().c_str());
BLEClient* pClient = BLEDevice::createClient();
Serial.println(" - Created client");
pClient->setClientCallbacks(new MyClientCallback());
// Connect to the remove BLE Server.
pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
Serial.println(" - Connected to server");
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (pRemoteService == nullptr) {
Serial.print("Failed to find our service UUID: ");
Serial.println(serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our service");
// Obtain a reference to the characteristic in the service of the remote BLE server.
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our characteristic");
// Read the value of the characteristic.
if (pRemoteCharacteristic->canRead()) {
std::string value = pRemoteCharacteristic->readValue();
Serial.print("The characteristic value was: ");
Serial.println(value.c_str());
}
if (pRemoteCharacteristic->canNotify())
pRemoteCharacteristic->registerForNotify(notifyCallback);
connected = true;
return true;
}
/**
Scan for BLE servers and find the first one that advertises the service we are looking for.
*/
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
/**
Called for each advertising BLE server.
*/
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.print("BLE Advertised Device found: ");
Serial.println(advertisedDevice.toString().c_str());
// We have found a device, let us now see if it contains the service we are looking for.
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
BLEDevice::getScan()->stop(); // stop scanning when device is found
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
void setup() {
Serial.begin(115200);
Serial.println("Starting Arduino BLE Client application...");
BLEDevice::init("");
// Retrieve a Scanner and set the callback we want to use to be informed when we
// have detected a new device. Specify that we want active scanning and start the
// scan to run for 5 seconds.
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setInterval(1349);
pBLEScan->setWindow(449);
pBLEScan->setActiveScan(true);
pBLEScan->start(5, false); //scan for 5 seconds
// voor LEDS
FastLED.addLeds<WS2812B, LED_PIN, GRB > (led, NUM_LEDS);
//FastLED.setMaxPowerInVoltsAndMilliamps(5, 500); // maximize current
// turn all LEDS off
FastLED.clear(true); // clear all pixel data and push to strip
} // End of setup.
// This is the Arduino main loop function.
void loop() {
// If the flag "doConnect" is true then we have scanned for and found the desired
// BLE Server with which we wish to connect. Now we connect to it. Once we are
// connected we set the connected flag to be true.
if (doConnect == true) {
if (connectToServer()) {
Serial.println("We are now connected to the BLE Server.");
} else {
Serial.println("We have failed to connect to the server; there is nothing more we will do.");
led[0] = CRGB::Red; // not connected, statuslight (LED 0) red
FastLED.show();
}
doConnect = false;
}
// If we are connected to a peer BLE Server, update the characteristic each time we are reached
// with the current time since boot.
if (connected) {
led[0] = CRGB(0,0,150); //Connected, status light (LED 0) blue
FastLED.show();
} else if (doScan) {
BLEDevice::getScan()->start(0); // this is just example to start scan after disconnect, most likely there is better way to do it in arduino
}
} // End of loop
If you know the specific address of the server you want to connect with, the I think you should be using it in this section
/**
Scan for BLE servers and find the first one that advertises the service we are looking for.
*/
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks
{
/**
Called for each advertising BLE server.
*/
void onResult(BLEAdvertisedDevice advertisedDevice)
{
Serial.print("BLE Advertised Device found: ");
Serial.println(advertisedDevice.toString().c_str());
if (advertisedDevice.getAddress() == "eb:56:d2:fb:28:ec")
{
// We have found a device, let us now see if it contains the service we are looking for.
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID))
{
BLEDevice::getScan()->stop(); // stop scanning when device is found
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
} // Found our service
} //found our server address
} // onResult
}; // MyAdvertisedDeviceCallbacks
Arduino: 1.8.19 (Mac OS X), Board: "ESP32 Dev Module, Disabled, Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS), 240MHz (WiFi/BT), QIO, 80MHz, 4MB (32Mb), 921600, None"
In file included from /Users/kevinadams/Documents/Arduino/libraries/FastLED/src/FastLED.h:67:0,
from /Users/kevinadams/Documents/Arduino/Rockerplate_LED/Rockerplate_LED.ino:10:
/Users/kevinadams/Documents/Arduino/libraries/FastLED/src/fastspi.h:145:23: note: #pragma message: No hardware SPI pins defined. All SPI access will default to bitbanged output
# pragma message "No hardware SPI pins defined. All SPI access will default to bitbanged output"
^
/Users/kevinadams/Documents/Arduino/Rockerplate_LED/Rockerplate_LED.ino: In member function 'virtual void MyAdvertisedDeviceCallbacks::onResult(BLEAdvertisedDevice)':
Rockerplate_LED:181:41: error: no match for 'operator==' (operand types are 'BLEAddress' and 'const char [18]')
if (advertisedDevice.getAddress() == "eb:56:d2:fb:28:ec") {
^
In file included from /Users/kevinadams/Documents/Arduino/libraries/FastLED/src/controller.h:9:0,
from /Users/kevinadams/Documents/Arduino/libraries/FastLED/src/FastLED.h:49,
from /Users/kevinadams/Documents/Arduino/Rockerplate_LED/Rockerplate_LED.ino:10:
/Users/kevinadams/Documents/Arduino/libraries/FastLED/src/pixeltypes.h:749:44: note: candidate: bool operator==(const CRGB&, const CRGB&)
inline __attribute__((always_inline)) bool operator== (const CRGB& lhs, const CRGB& rhs)
^
/Users/kevinadams/Documents/Arduino/libraries/FastLED/src/pixeltypes.h:749:44: note: no known conversion for argument 1 from 'BLEAddress' to 'const CRGB&'
Multiple libraries were found for "BLEDevice.h"
Used: /Users/kevinadams/Documents/Arduino/libraries/ESP32_BLE_Arduino
Not used: /Users/kevinadams/Library/Arduino15/packages/esp32/hardware/esp32/1.0.6/libraries/BLE
exit status 1
no match for 'operator==' (operand types are 'BLEAddress' and 'const char [18]')
/**
Scan for BLE servers and find the first one that advertises the service we are looking for.
*/
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
/**
Called for each advertising BLE server.
*/
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.print("BLE Advertised Device found: ");
Serial.println(advertisedDevice.toString().c_str());
// if (advertisedDevice.getAddress() == "eb:56:d2:fb:28:ec")
// {
if (strcmp(advertisedDevice.getAddress().toString().c_str(), "eb:56:d2:fb:28:ec") == 0) {
// We have found a device, let us now see if it contains the service we are looking for.
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
BLEDevice::getScan()->stop(); // stop scanning when device is found
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
} // Found our server
} //if(strcmp
}// onResult
}; // MyAdvertisedDeviceCallbacks
Okay, so that does indeed compile and upload. However it still keeps rebooting when the program is run. Here is what is happening on the serial monitor:
Okay, I ran the Decoder, though I'm not exactly sure how it is supposed to work. I pasted the exception text and got a 'Decode Success' message, but not sure what to do from this point.
I don't understand, but when I paste the backtrace you posted in the Exception decoder I have installed in my IDE, I see more detail.
PC: 0x400d1e2a: CPixelLEDController(EOrder)66, 1, 4294967295u>::show(CRGB const*, int, CRGB) at C:\Users\cattledog\Documents\Arduino\libraries\FastLED/controller.h line 229
EXCVADDR: 0x00000000
Decoding stack results
0x400d1e2a: CPixelLEDController(EOrder)66, 1, 4294967295u>::show(CRGB const*, int, CRGB) at C:\Users\cattledog\Documents\Arduino\libraries\FastLED/controller.h line 229
0x400d7d40: BLERemoteService::retrieveCharacteristics() at c:\users\cattledog\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r1\xtensa-esp32-elf\include\c++\8.4.0\bits/stl_tree.h line 2320
0x400902da: _ungetc_r at /builds/idf/crosstool-NG/.build/HOST-i686-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/stdio/ungetc.c line 203
I don't really understand, but its possible that you are seeing the crash from something related to FastLED and not BLE. I do see many warnings related to FastLED when I compile.
You did say all was working correctly except for the connection, so I'm confused. Perhaps you can write a simplified sketch which just connects and prints the characteristic without the FastED display.
Without a power meter, it's really difficult for me to help debug this.
I think at this point, you are connecting with the correct address, but there may be an issue with either the characteristic or the Fast LED display, but I'm not clear how this would be related to the connection.
I believe that indicates an attempt to de-reference the NULL pointer. Could be caused by an overflow of the process's stack. Or something completely unrelated. Since ESP32 has built-in FreeRTOS, I prefer not to follow the Arduino 'loop()' paradigm for this processor but use the muti-tasking capabilities of the OS. That helps in debugging when things go wrong and it's using the processor / OS combination as intended.
Absolutely. I think it's time to start from scratch and build up the functionality piece-by-piece ... starting with the BLE stuff.
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Given that IllegalInstruction cause then I think this is particularly relevant
IllegalInstruction
This CPU exception indicates that the instruction which was executed was not a valid instruction. Most common reasons for this error include:
FreeRTOS task function has returned. In FreeRTOS, if a task function needs to terminate, it should call vTaskDelete() and delete itself, instead of returning.
Failure to read next instruction from SPI flash. This usually happens if:
Application has reconfigured the SPI flash pins as some other function (GPIO, UART, etc.). Consult the Hardware Design Guidelines and the datasheet for the chip or module for details about the SPI flash pins.
Some external device has accidentally been connected to the SPI flash pins, and has interfered with communication between ESP32 and SPI flash.
In C++ code, exiting from a non-void function without returning a value is considered to be an undefined behavior. When optimizations are enabled, the compiler will often omit the epilogue in such functions. This most often results in an IllegalInstruction exception. By default, ESP-IDF build system enables -Werror=return-type which means that missing return statements are treated as compile time errors. However if the application project disables compiler warnings, this issue might go undetected and the IllegalInstruction exception will occur at run time.
I believe that indicates an attempt to de-reference the NULL pointer.
Yes, this is true, but I would have thought that the error would have been Load/Store Prohibited and not Illegal Instruction. But I am not very experienced with ESP fatal error debugging.
LoadProhibited, StoreProhibited
These CPU exceptions happen when an application attempts to read from or write to an invalid memory location. The address which has been written/read is found in the EXCVADDR register in the register dump. If this address is zero, it usually means that the application has attempted to dereference a NULL pointer. If this address is close to zero, it usually means that the application has attempted to access a member of a structure, but the pointer to the structure is NULL. If this address is something else (garbage value, not in 0x3fxxxxxx - 0x6xxxxxxx range), it likely means that the pointer used to access the data is either not initialized or has been corrupted.
This seems to be getting quite complicated. I'm in a little over my head with these programming issues (I have a little experience with Visual Basic, but none with C++) and I might just need spend some time playing around with the code.
In the meantime, I will try using a different power meter (the bike trainer itself) for the LEDs, which seems to have a stronger signal in general and should work better. I was trying to avoid this because it can only connect to two devices and I've been connecting it to my phone and computer during workouts, so now it will be LEDs and phone or computer. I'll just keep messing around with the code when I can.
I truly appreciate all of the help you guys have provided, it is greatly appreciated. Thanks!