cujomalainey/antplus-arduino

Help with NativeAnt on nRF52840

Closed this issue · 13 comments

Hi all,
following the known blog entries I was finally able to flash the arduino core on a nRF52840 MDK USB Dongle (https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/) using the S340 SoftDevice.
After setting the required Network keys and so on I am trying to let the first examples work.
Unfortunately something is not working and I am having issues debugging the system.

I tried the DeviceSearch example and substituted the "ArduinoSerialAntWithCallbacks" with "ArduinoNativeAntWithCallbacks", but I am unfortunately not able to find my HR Monitor.
Also trying the Sensor examples, the device remains not visible.
How can I ensure that the Bootloader and Softdevice are correctly working?

thank you for the great project
Silvio

Hi @silviocross

Sounds like you are using the wrong ANT Driver. The serial driver is meant for an nRF52 (running the network processor firmware) connected to an external microcontroller and the driver runs on external controller. E.g. in this below example the code would run on ESP32

+------------+                           +--------------+
|            |      TX                   |              |
|            +--------------------------->              |
|  ESP32     |      RX                   |    NRF52     |
|            <---------------------------+              |
|            |                           |              |
|            |                           |              |
+------------+                           +--------------+

What you are looking for is the native driver. It will connect directly to the soft device APIs. It is hidden behind a compiler flag so it doesn't break non nRF52 builds. You need to specify your softdevice, via -DSXXX where XXX is your softdevice. In your case it would be -DS340

If you want to test without the driver I recommend calling some of the basic functions in the softdevice API, e.g. sd_ant_enable and sd_softdevice_enable. When and how you call this depends on a number of factors such as what/if you are using a bootloader, what exact chip you have, if you intended to enable BT etc.

Let me know if you have anymore questions.

Hi @cujomalainey

thank you for the very quick reply.
I am actually tring to use the native driver (for doing so, I modified the examples from your library in oder to call the native functions, unfortunately with no luck).
I am using the -DS340 build flag already in order to properly compile and flash the board, I get no compiling error and the basic code works (Serial monitor messages) but no ANT device is being found.
How can I test if the Native Driver is being called properly, and that the driver is actually properly working?
Do you have any basic example I can use for testing the native ANT communication?

Thank you

Ah apologies, i misread your message.

My recommendation is drop down to calling the API directly. A good function to check is sd_softdevice_is_enabled. One other thing to note is if you are not using BLE, you must start the ANT API yourself with a call to sd_softdevice_enable.

The best way to test is to use a basic tx or rx example alongside a debug software such as ANTwareII and a usb stick.

perfect, I solved sending Bluefruit.begin();
I included the following check and it confirms the Softdevide is correctly enabled now:
if ((sd_softdevice_is_enabled(&sd_enabled) == NRF_SUCCESS) ) { Serial.println("NRF_SUCCESS"); if(sd_enabled == 1) Serial.println("SD_Enabled = true"); else Serial.println("SD_Enabled = false"); }

I am still not able to see proper sentor data sent to my garmin device, but finally the sensor become visible and I can connect!
thank you very much for your help

just a short update>

I am finally able to run the HR Monitor example with Native driver.
Unfortunately, the BycliclePowerSensor example does not look as good: the sensor is detected, but no data is being transmitted or understood from the ANT+ receiver.
Can anybody confirm the functionality of this example?

Silvio

Unfortunately, the BycliclePowerSensor example does not look as good: the sensor is detected, but no data is being transmitted or understood from the ANT+ receiver. Can anybody confirm the functionality of this example?

This does not surprise me as I have rarely used this profile and it was mostly externally contributed. I will try and take a look in the next 24h, otherwise it will have to wait till thursday at the earliest.

Ok sorry for the delay, my week was crazier than expected, I expect to have everything tested in the next 5h. Which exact configs were you using for bike power?

Didnt get very far, between my nrf52 acking like its borked (think i might need to update the bootloader) or platformio being tempermental with headers. I wasn't able to flash. If you can provide the exact config you are using the profile and the errors you are seeing that would be very helpful.

Please excuse my late reply, here below you can find my modified program.
I am able to compile and flash the sketch, and the device is becoming visible to my ANT+ receiver.
Unfortunately, Power and Cadence remain at 0 and do not increase as expected within the example

`/***********************************

  • AntPlus Bicycle Power Sensor example
  • Creates a Bike Power Sensor, in
  • Crank Torque mode and transmits
  • openly with mocked data
  • Example built for external radio
  • Author Curtis Malainey
    ************************************/
    #include <Arduino.h>
    #include "ANT.h"
    #include "ANTPLUS.h"
    #include "bluefruit.h"

#define BAUD_RATE 115200
#define TOTAL_CHANNELS 1
#define ENCRYPTED_CHANNELS 0
#define CHANNEL_0 0

int power = 0;

const uint8_t NETWORK_KEY[] = {..KEY..}; // get this from thisisant.com

ArduinoNativeAntWithCallbacks ant;
AntPlusRouter router;
ProfileBicyclePowerSensor bikePower(123, 0, ANTPLUS_BICYCLEPOWER_FLAGS_SENSORTYPE_TORQUECRANK);

void batteryStatusDataPageHandler(BatteryStatusMsg& msg, uintptr_t data);
void manufacturerIDDataPageHandler(ManufacturersInformationMsg& msg, uintptr_t data);
void productIDDataPageHandler(ProductInformationMsg& msg, uintptr_t data);
void powerOnlyDataPageHandler(BicyclePowerStandardPowerOnlyMsg& msg, uintptr_t data);
void crankTorqueDataPageHandler(BicyclePowerStandardCrankTorqueMsg& msg, uintptr_t data);
void wheelTorqueDataPageHandler(BicyclePowerStandardWheelTorqueMsg& msg, uintptr_t data);
void pedalSmoothnessDataPageHandler(BicyclePowerTorqueEffectivenessAndPedalSmoothnessMsg& msg, uintptr_t data);

void setup() {

  AssignChannel ac;
  ResetSystem rs;
  SetNetworkKey snk;
  ChannelId ci;
  ChannelPeriod cp;
  ChannelRfFrequency crf;
  OpenChannel oc;

  Bluefruit.begin();
  // ant.send(rs);
  // Delay after resetting the radio to give the user time to connect on serial
  delay(10000);
  ant.begin(TOTAL_CHANNELS, ENCRYPTED_CHANNELS);

  router.setDriver(&ant); // never touch ant again
  router.setAntPlusNetworkKey(NETWORK_KEY);
  router.setProfile(CHANNEL_0, &bikePower);
  // Delay after initial setup to wait for user to connect on serial

  Serial.begin(BAUD_RATE);
  Serial.println("Running");
  /// bikePower.createBatteryStatusMsg(batteryStatusDataPageHandler);
  bikePower.createManufacturersInformationMsg(manufacturerIDDataPageHandler);
  bikePower.createProductInformationMsg(productIDDataPageHandler);
  bikePower.createBicyclePowerStandardPowerOnlyMsg(powerOnlyDataPageHandler);
  bikePower.createBicyclePowerStandardCrankTorqueMsg(crankTorqueDataPageHandler);
  // bikePower.createBicyclePowerTorqueEffectivenessAndPedalSmoothnessMsg(pedalSmoothnessDataPageHandler);
  Serial.println("===========================");
  bikePower.begin();

   uint8_t sd_enabled;
  if ((sd_softdevice_is_enabled(&sd_enabled) == NRF_SUCCESS) ) {
    Serial.println("NRF_SUCCESS");
    if(sd_enabled == 1)
      Serial.println("SD_Enabled = true");
      else
      Serial.println("SD_Enabled = false");
    }

}

void loop() {
// call this frequently
router.loop();
bikePower.createBicyclePowerStandardPowerOnlyMsg(powerOnlyDataPageHandler);
}

void printDpMsg(int dp, const char* s) {
Serial.print("Sending DataPage: ");
Serial.print(dp);
Serial.print(" - ");
Serial.println(s);
}

/* Optional */
// void batteryStatusDataPageHandler(BatteryStatusMsg& msg, uintptr_t data) {
// printDpMsg(82, "Battery Status");
// }

void manufacturerIDDataPageHandler(ManufacturersInformationMsg& msg, uintptr_t data) {
printDpMsg(50, "Manufacturers Information");
msg.setHWRevision(1);
msg.setManufacturerId(456);
msg.setModelNumber(789);
}

void productIDDataPageHandler(ProductInformationMsg& msg, uintptr_t data) {
printDpMsg(51, "Product Information");
msg.setSWRevisionSupplemental(12);
msg.setSWRevisionMain(34);
msg.setSerialNumber(0xDEADBEEF);
}

void powerOnlyDataPageHandler(BicyclePowerStandardPowerOnlyMsg& msg, uintptr_t data) {
static uint8_t eventCount = 0;

  printDpMsg(10, "Power Only");
  msg.setUpdateEventCount(eventCount++);
  msg.setAccumulatedPower(eventCount * 3);
  msg.setInstantaneousPower(eventCount * 2);
  msg.setPedalPower(eventCount);
  msg.setPedalDifferentiation(eventCount/3);
  Serial.println(eventCount);

}

void crankTorqueDataPageHandler(BicyclePowerStandardCrankTorqueMsg& msg, uintptr_t data) {
static uint8_t ticks = 0;

  printDpMsg(18, "Crank Torque");
  msg.setCrankTicks(ticks++);
  msg.setInstantaneousCadence(ticks / 2);
  msg.setCrankPeriod(ticks * 2);
  msg.setAccumulatedTorque(ticks * 3);

}

/* Optional */
// void pedalSmoothnessDataPageHandler(BicyclePowerTorqueEffectivenessAndPedalSmoothnessMsg& msg, uintptr_t data) {
// printDpMsg(19, "Torque Effectiveness and Pedal Smoothness");
// }`

void loop() {
// call this frequently
router.loop();
bikePower.createBicyclePowerStandardPowerOnlyMsg(powerOnlyDataPageHandler);
}

You only need to set the handler once, ideally before the profile starts

Ok if the issue is isolated to the to this message then I can debug with my serial setup or a unit test. I will test it out and see if I can reproduce the issue. Are you at the 1.2.0 release?

Ah I think I know the issues here. Was able to reproduce the errors with my serial setup. So I think there are three problems.

  1. In the datapages I only restrict the bits, I do not restrict ranges. So once the counter hits > 100 you start to see the parser errors because you are only supposed to have a power range 0x00-0x64,0x7F (field is 7bits wide) so 0x65-0x7E is invalid which was being transmitted. Adding % 101 to setPedalPower fixes the issue. I'll push a commit for this.
  2. I think you might be getting confused in the simulant display about where the values are, are you looking in "Calculated Data"? If so that is not where the values are, scroll down to "Data Pages" and expand the "Standard - Power Only" tab to see the changing values.
  3. Also I fail to set the instantaneous cadence in that message, so it will use its default which is defined as the invalid value. I will also add this to my commit.

Let me know if that fixes your issues.

Please see 92326a1

I am going to assume this is fixed, please let me know if you have any other questions.