Closed BigJinge closed 5 years ago
That was the reasoning for my 1st link: it's my working minimal CPS cadence implementation I teared out of his bigger project.
cadence
[RPM] comes from outside (eBike drivetrain for me, potentiometer for you) and is converted into the 2 fields required for CPS by update_new_rpm()
and update_revs_and_time()
.
But I just realized that in the end, you won't need all these complicated calculations because you'll have real cadence impulses, not RPM (as I do) as input.
I implemented the same functionality in my code, which you can check. However, I am unable to receive RPM updates even though the BLE connection is successful. Power and heart rate data are also visible in the app. When I increase the power in the app, the cadence increases accordingly
you can check this
uint8_t FlagCSC =0b00000011;
cadence = map(analogRead(Potentio1), 0, 4095, 0, 200); // potentio data
if (cadence == 0) { cadence = 1; }
crankrev = crankrev + 1; lastcrank = lastcrank + 1024*60/cadence;
wheelrev = wheelrev + 1; lastwheel = lastwheel + 1024*60/cadence;
// Calculate Speed from cadence
// Set and Send Speed and Cadence Notify
cscmeasurement[0] = FlagCSC;
cscmeasurement[1] = wheelrev & 0xFF;
cscmeasurement[2] = (wheelrev >> 8) & 0xFF;
cscmeasurement[3] = (wheelrev >> 16) & 0xFF;
cscmeasurement[4] = (wheelrev >> 24) & 0xFF;
cscmeasurement[5] = lastwheel & 0xFF;
cscmeasurement[6] = (lastwheel >> 8) & 0xFF;
cscmeasurement[7] = crankrev & 0xFF;
cscmeasurement[8] = (crankrev >> 8) & 0xFF;
cscmeasurement[9] = lastcrank & 0xFF;
cscmeasurement[10] = (lastcrank >> 8) & 0xFF;
CadanceRPMCharacteristics.setValue(cscmeasurement, sizeof(cscmeasurement));
CadanceRPMCharacteristics.notify();
I would greatly appreciate your help with this because I have limited time and need to submit it
I think RPM in cycling normally refers to the number of rotations of the crank per minute, not the number of rotations of the wheel.
Yes, you are exactly right; it refers to cranks per minute. I replaced this in my code with dummy data from a potentiometer, as you can see in the above code. I am receiving the data on the NRF Connect app, but when I connect with Zwift, the RPM does not update there; it continuously stays at 0.
here you can check in this code, How I am doing implementaion for zwift
uint8_t FlagCSC =0b00000011;
uint16_t cadence = map(analogRead(Potentio1), 0, 4095, 0, 200); // potentio data
crankrev = crankrev + 1; lastcrank = lastcrank + 1024*60/cadence;
wheelrev = wheelrev + 1; lastwheel = lastwheel + 1024*60/cadence;
// Calculate Speed from cadence // Set and Send Speed and Cadence Notify cscmeasurement[0] = FlagCSC; cscmeasurement[1] = wheelrev & 0xFF; cscmeasurement[2] = (wheelrev >> 8) & 0xFF; cscmeasurement[3] = (wheelrev >> 16) & 0xFF; cscmeasurement[4] = (wheelrev >> 24) & 0xFF; cscmeasurement[5] = lastwheel & 0xFF; cscmeasurement[6] = (lastwheel >> 8) & 0xFF; cscmeasurement[7] = cadence & 0xFF; //update the crank with potentio cscmeasurement[8] = (cadence >> 8) & 0xFF; //update the crank with potentio cscmeasurement[9] = lastcrank & 0xFF; cscmeasurement[10] = (lastcrank >> 8) & 0xFF;
CadanceRPMCharacteristics.setValue(cscmeasurement, sizeof(cscmeasurement)); CadanceRPMCharacteristics.notify();
The numbers in your screenshot might be off: Let's assume you send 1 report/second. Then sensible values would be e.g. +1 for revs (seems like you do) and +1000 ms for timestamp.
Yes, that's correct. The incorrect values in the snapshot are a result of me only incrementing the wheel revolutions and crank revolutions previously.
My code is here and worked with Zwift, Wahoo and (excepting heart rate) RGT last I checked: https://github.com/arpruss/BLEBike
My CPS output in nRF Connect:
https://photos.app.goo.gl/CvdqiUiFf7N8q1Fb6
The value at AE
increases once/second, the one at 93
~+5/second.
Regarding power measurement, it is 100% correct. I am receiving the same values in my code as well as in Zwift. The UUID of the power measurement is 1818, but I am specifically talking about 1816, which is for cyclic speed and cadence. Zwift has three fields: power, heart rate, and cadence. I am only facing issues related to cadence values. Both power and heart rate values are fine and okay, but when it comes to cadence, the RPM (Revolutions Per Minute) is not updating as expected
cscmeasurement[0] = FlagCSC; cscmeasurement[1] = wheelrev & 0xFF; cscmeasurement[2] = (wheelrev >> 8) & 0xFF; cscmeasurement[3] = (wheelrev >> 16) & 0xFF; cscmeasurement[4] = (wheelrev >> 24) & 0xFF; cscmeasurement[5] = lastwheel & 0xFF; cscmeasurement[6] = (lastwheel >> 8) & 0xFF; cscmeasurement[7] = cadence & 0xFF; // Update the crank with potential<<<<------------------------------------- cscmeasurement[8] = (cadence >> 8) & 0xFF; // Update the crank with potential<<<<------------------------------------ cscmeasurement[9] = lastcrank & 0xFF; cscmeasurement[10] = (lastcrank >> 8) & 0xFF;
CadenceRPMCharacteristics.setValue(cscmeasurement, sizeof(cscmeasurement)); CadenceRPMCharacteristics.notify();
Cadence in CSC and CPS is similar, except the 1024/2048th of a second time unit difference. According to the 2nd link above, it's not advisable to send both.
Wait, you mean to update the RPM, I can use power measurement 0x1818, and by doing this, I can update the RPM, correct?
Yes.
Here is my power function. Can you please highlight the array location I need to adjust to update the RPM?
void Powermeasur() { dummy_data_t.Powerdata = map(analogRead(Potentio), 0, 4095, 0, 2000);
// Create a buffer to hold the formatted power data
uint8_t powerBuffer[8];
// Flags for power measurement (you can adjust this as needed)
uint16_t flags = 0x0020; // Example flags
// Pack the data into the buffer in the desired format
powerBuffer[0] = flags & 0xFF;
powerBuffer[1] = (flags >> 8) & 0xFF;
powerBuffer[2] = dummy_data_t.Powerdata & 0xFF;
powerBuffer[3] = (dummy_data_t.Powerdata >> 8) & 0xFF;
powerBuffer[4] = 0x00; // Add more data as needed
powerBuffer[5] = 0x00;
powerBuffer[6] = 0x00;
powerBuffer[7] = 0x00;
// Set the value of the BLE characteristic and notify connected devices
PowerDataCharacteristics.setValue(powerBuffer, sizeof(powerBuffer));
PowerDataCharacteristics.notify();
// Serial.print("Power measurement: "); // Serial.print(dummy_data_t.Powerdata); // Serial.println(" Watts"); }
Hi all, I just wanted to lean in on this thread. I am working as well on a Arduino project connecting my older Technogym bike to my Garmin watch.
I solved an issue I had with the code shared in this group. And I have a question as well for you!!
1) issue solved: In NRfconnect I had an "wrong value" for the CSC Feature caracteristic. I found out that the value "0b0000000000000011" didn't work (for me) and I changed it to:
BLECharacteristic CycleSpeedFeature("2A5C", BLERead, 2); // ,2 instead of ,1 byte fSpeedBuffer[2] = { 0b00000011, 0 }; // instead of 0b0000000000000011
2) question to ask So when having both services Advertised, my garmin watch can connect only to one of them. In NRfconnect both services are shown. I can only connect to the one added last. Can some one help me to solve this.
// Add the characteristics to the services. BLE.setDeviceName(BLE_DEVICE_NAME); BLE.setLocalName(BLE_LOCAL_NAME);
BLE.setAdvertisedService(CycleSpeedService); CycleSpeedService.addCharacteristic(CycleSpeedMeasurement); CycleSpeedService.addCharacteristic(CycleSpeedFeature); CycleSpeedService.addCharacteristic(CycleSpeedSensorLocation); CycleSpeedService.addCharacteristic(CycleSpeedControlPoint); BLE.addService(CycleSpeedService);
BLE.setAdvertisedService(CyclePowerService); CyclePowerService.addCharacteristic(CyclePowerFeature); CyclePowerService.addCharacteristic(CyclePowerMeasurement); CyclePowerService.addCharacteristic(CyclePowerSensorLocation); BLE.addService(CyclePowerService);
Hello, the cadence and speed sensor with ESP32 did not work for me. The XIAO nRF52840 board works fine, but with the Adafruit_nRF52_Arduino library.
https://github.com/MiloPS3/Seeed-XIAO-nRF52840-Speed-and-Cadence-Sensors-with-Garmin-FR-935.git
Hi Neil,
I'm working on taking a feed from a reed switch on an exercise bike. This will give a cadence pulse per revolution. Before I physically attach the ESP32 to the bike I sent some test data from the Arduino code via the BLE connection.
I've taken your BLE server example code (via Andreas Spiess) and have added the BLE cadence service into it.
The LightBlue BLE Explorer shows the heart and cadence services advertising fine.
However...
The cscmeasurement characteristic requires the lastcrank time (1/1024 resolution) in an uint16. I want to pass over 2048ms but it's only letting me pass 255 max which points to the BLE characteristic being a byte. I can't make the characteristic into a uint16 as the BLECharacteristic function requests a byte.
I feel I'm missing something obvious but how can I .setvalue 2048 into the byte cscMeasurementCharacteristics array?
Many thanks
Richard
`/* Multi BLE Sensor - Richard Hedderly 2019
*/
include
include
include
include
byte flags = 0b00111110; byte bpm; // Beats Per Minute byte crankrev; // Cadence RPM uint16_t lastcrank; // Last crank time byte speedkph; // Speed in KPH byte speedmph; // Speed in MPH byte heart[8] = {0b00001110, 60, 0, 0, 0 , 0, 0, 0}; byte cscmeasurement[3] = {0b00000010, 0, 0}; byte cscfeature[1] = {0b0000000000000010}; byte sensorlocation[1] = {6};
bool _BLEClientConnected = false;
// Define Heart Properties
define heartRateService BLEUUID((uint16_t)0x180D) // Define BLE Heart Rate Service
BLECharacteristic heartRateMeasurementCharacteristics(BLEUUID((uint16_t)0x2A37), BLECharacteristic::PROPERTY_NOTIFY); // Heart Rate Characteristics BLEDescriptor heartRateDescriptor(BLEUUID((uint16_t)0x2901)); //0x2901 is a custom user description
// Define Speed and Cadence Properties
define speedService BLEUUID((uint16_t)0x1816)
BLECharacteristic cscMeasurementCharacteristics(BLEUUID((uint16_t)0x2A5B), BLECharacteristic::PROPERTY_NOTIFY); //CSC Measurement Characteristic BLECharacteristic cscFeatureCharacteristics(BLEUUID((uint16_t)0x2A5C), BLECharacteristic::PROPERTY_READ); //CSC Feature Characteristic BLECharacteristic sensorLocationCharacteristics(BLEUUID((uint16_t)0x2A5D), BLECharacteristic::PROPERTY_READ); //Sensor Location Characteristic BLECharacteristic scControlPointCharacteristics(BLEUUID((uint16_t)0x2A55), BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_INDICATE); // SC Control Point Characteristic BLEDescriptor cscMeasurementDescriptor(BLEUUID((uint16_t)0x2901)); //0x2901 is a custom user description BLEDescriptor cscFeatureDescriptor(BLEUUID((uint16_t)0x2901)); //0x2901 is a custom user description BLEDescriptor sensorLocationDescriptor(BLEUUID((uint16_t)0x2901)); //0x2901 is a custom user description BLEDescriptor scControlPointDescriptor(BLEUUID((uint16_t)0x2901)); //0x2901 is a custom user description
// 0x2A5B - Notify - Client Charactaristic Config is 1 - DONE : CSC Measurement // 0x2A5C - Read - 0x200 - DONE : CSC Feature // 0x2A5D - Read - None or 0x06 when active - Done : Sensor Location - 6 is right crank // 0x2A55 - Write Indicate - Client Characteristic Config is 2 : SC Control Point
// Define Battery //Servicec UUID 0x180F - Battery Level UUID 0x2A19
// Define Firmware //UUID 0x2A26
class MyServerCallbacks : public BLEServerCallbacks { void onConnect(BLEServer* pServer) { _BLEClientConnected = true; };
};
void InitBLE() { // Create BLE Device BLEDevice::init("Multi Fitness BLE Sensor");
// Create BLE Server BLEServer *pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks());
// Create BLE Heart Configuration BLEService *pHeart = pServer->createService(heartRateService); // Create Heart Service pHeart->addCharacteristic(&heartRateMeasurementCharacteristics); heartRateDescriptor.setValue("Exercise Bike Pulse Grips Rate"); heartRateMeasurementCharacteristics.addDescriptor(&heartRateDescriptor); heartRateMeasurementCharacteristics.addDescriptor(new BLE2902());
// Create Speed and Cadence Configuration BLEService *pSpeed = pServer->createService(speedService); // Create Speed and Cadence Service pSpeed->addCharacteristic(&cscMeasurementCharacteristics); pSpeed->addCharacteristic(&cscFeatureCharacteristics); pSpeed->addCharacteristic(&sensorLocationCharacteristics); pSpeed->addCharacteristic(&scControlPointCharacteristics); cscMeasurementDescriptor.setValue("Exercise Bike CSC Measurement"); cscMeasurementCharacteristics.addDescriptor(&cscMeasurementDescriptor); cscFeatureDescriptor.setValue("Exercise Bike CSC Feature"); cscFeatureCharacteristics.addDescriptor(&cscFeatureDescriptor); sensorLocationDescriptor.setValue("Exercise Bike CSC Sensor Location"); sensorLocationCharacteristics.addDescriptor(&sensorLocationDescriptor); scControlPointDescriptor.setValue("Exercise Bike CSC SC Control Point"); scControlPointCharacteristics.addDescriptor(&scControlPointDescriptor);
// Add UUIDs for Services to BLE Service Advertising pServer->getAdvertising()->addServiceUUID(heartRateService); pServer->getAdvertising()->addServiceUUID(speedService);
// Start p Instances pSpeed->start(); pHeart->start();
// Start Advertising pServer->getAdvertising()->start(); }
void setup() { Serial.begin(115200); Serial.println("Start"); InitBLE(); bpm = 1; }
void loop() {
// _____ // HEART SECTION // ------------- // Read Heart Pins // *****
heart[1] = (byte)bpm; int energyUsed = 3000; heart[3] = energyUsed / 256; heart[2] = energyUsed - (heart[2] * 256); Serial.print("BPM = "); Serial.println(bpm);
// Send Heart Rate Notify heartRateMeasurementCharacteristics.setValue(heart, 8); heartRateMeasurementCharacteristics.notify();
bpm++;
// ___ // SPEED + CADENCE SECTION // ----------------------- // Read Cadence RPM Pins
crankrev = crankrev + 2; lastcrank = 0x7FE;
// That, lastcran in the array position 2 will only show FF or 256 even if lastcrank is set to a uint16_t
// Calculate Speed from cadence //
// Set and Send Speed and Cadence Notify cscmeasurement[1] = crankrev; cscmeasurement[2] = lastcrank; cscMeasurementCharacteristics.setValue(cscmeasurement, 3); cscMeasurementCharacteristics.notify();
cscFeatureCharacteristics.setValue(cscfeature, 1); sensorLocationCharacteristics.setValue(sensorlocation, 1);
//Serial.print("crankrev = "); //Serial.println(crankrev); //Serial.print("lastcrank = "); //Serial.println(lastcrank);
Serial.print(cscmeasurement[0]); //Should be 2 to reflect binary - Yes Serial.print(" "); Serial.print(cscmeasurement[1]); // Should increment by 2 - Yes Serial.print(" "); Serial.println(cscmeasurement[2]); Serial.println(" "); delay(2000); }
`