Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop/Master Branch Merge and BicyclePower Profile and Arduino Example #4

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ A Nordic SoftDevice powered ANT+ library for the Arduino framework, intended to
- This library assumes the presence of a combined BLE+ANT multiprotocol softdevice from Nordic Semiconductor. It has been developed and tested against the [S340 Softdevice](https://www.nordicsemi.com/Software-and-tools/Software/S340-ANT), but it should be easy to use with other similar SoftDevices, such as [S312](https://www.nordicsemi.com/Software-and-tools/Software/S312-ANT) or [S332](https://www.nordicsemi.com/Software-and-tools/Software/S332-ANT).
- This library is intended to be piggy-backed onto the Adafruit Bluefruit52 libray, to provide concurrent multiprotocol capabilities with both BLE and ANT+. As a consequence of this it assumes a FreeRTOS environment.

# Bluefruit52 adaptation
One needs the changes to Bluefruit52 library issue [#501](https://github.com/adafruit/Adafruit_nRF52_Arduino/pull/501)

# W.I.P.
- Several ANT+ profiles are incomplete, or completely missing.
- Some of the profiles support only RX mode currently, for example
99 changes: 99 additions & 0 deletions examples/PowerMeter_v1/PowerMeter_v1.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@

//#include <bluefruit52.h>
//#include <Adafruit_LittleFS.h>
//#include <InternalFileSystem.h>

#include "src/PowerMeter/PowerMeter.h"

//BicyclePower pwr(Both);
//HX711_ADC dms(A0, A1);

powermeter_config PWRconfig =
{
A0,
A1,
A7,
10775.02,
250/*,
&pwr,
&dms*/
};

PowerMeter power(&PWRconfig);

//const int HX711_dout = A0; //mcu > HX711 dout pin, must be external interrupt capable!
//const int HX711_sck = A1; //mcu > HX711 sck pin

uint16_t SetRPM = 75;
unsigned long t = 0, newmessage = 0;
//volatile boolean newDataReady;


//HeartRateMonitor hrm(TX);
//EnvironmentSensor env(RX);
//SpeedDistanceMonitor sdm(RX);

//uint16_t currPWR = 100, goalPWR = 100;
//uint16_t accumulatedPWR = 0;
//uint8_t currCAD = 0;
//uint8_t PWR_EventCounter = 0;


void setup(void)
{
// bool ret = false;
Serial.begin(115200);
//delay(5000);
while (!Serial);

power.begin();
attachInterrupt(digitalPinToInterrupt(power.Get_dms_dout_Pin()), dataReadyISR, FALLING);
//attachInterrupt(digitalPinToInterrupt(power.Get_hall_out_Pin()), hallISR, RISING);

newmessage = millis() + 1000;
Serial.println("Setup is finished!");
}

void dataReadyISR() {
power.checkLoadCell();
}

void hallISR() {
power.hallInterrupt();
}

void loop(void)
{
//const int serialPrintInterval = 0;
power.update();
if (millis() > newmessage) {

power.hallInterrupt();
newmessage += round((float)60000 / (float)SetRPM);
}
if (Serial.available()) {
uint8_t temp = Serial.parseInt();
SetRPM = temp;
Serial.printf("New RPM: %d\n", SetRPM);
switch(temp) {
case 0:
//SetRPM--;
//Serial.printf("New RPM: %d\n", SetRPM);
// Serial.printf("Samples in Use: %d\n", LoadCell.getSamplesInUse());
// Serial.printf("SPS: %.2f\n", LoadCell.getSPS());
break;
case 1:
//SetRPM++;
//Serial.printf("New RPM: %d\n", SetRPM);
break;
case 2:

break;
case 3:
break;
default:
break;
}
}

} //loop
126 changes: 126 additions & 0 deletions examples/PowerMeter_v1/src/PowerMeter/PowerMeter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#include "./PowerMeter.h"

void PrintUnhandledANTEvent(ant_evt_t *evt)
{
Serial.printf("Channel #%d for %s: event %s\n", evt->channel, ANTplus.getAntProfileByChNum(evt->channel)->getName(), AntEventTypeDecode(evt));
if (evt->event != EVENT_CHANNEL_COLLISION
&& evt->event != EVENT_RX_FAIL
&& evt->event != EVENT_CHANNEL_CLOSED
)
Serial.printf(" (%s)\n", AntEventType2LongDescription(evt));
}
void ReopenANTChannel(ant_evt_t *evt)
{
if (evt->event == EVENT_CHANNEL_CLOSED ) {
Serial.printf("Channel #%d closed for %s\n", evt->channel,ANTplus.getAntProfileByChNum(evt->channel)->getName());
Serial.printf("Reopening...");
uint32_t ret;
ret = sd_ant_channel_open(evt->channel);
if (ret == NRF_SUCCESS) Serial.println("success!");
else Serial.printf("failed with code:%#x\n", ret);
}
}

PowerMeter::PowerMeter(powermeter_config * cfg) {
config.dms_dout = cfg->dms_dout;
config.dms_sck = cfg->dms_sck;
config.hall_out = cfg->hall_out;
config.calibration = cfg->calibration;
config.profileUpdateCycle = cfg->profileUpdateCycle;
//config.p_power_profile = cfg->p_power_profile;
//config.p_loadcell = cfg->p_loadcell;
pwr = new BicyclePower(Both); //config.p_power_profile;
LoadCell = new HX711_ADC(config.dms_dout, config.dms_sck); //LoadCell = config.p_loadcell;
}

void PowerMeter::begin() {
Serial.println("Starting PowerMeter Setup...");
Serial.printf("Config: %d\t%d\t%d\t%f\n", config.dms_dout, config.dms_sck, config.hall_out, config.calibration);
Serial.println("Adding PWR profile");
pwr->setUnhandledEventListener(PrintUnhandledANTEvent);
pwr->setAllEventListener(ReopenANTChannel);
pwr->setName("PWR");
ANTplus.AddProfile(pwr);

int pin = PIN_A7;
Serial.println("Bluefruit52 BLEUART Startup");
Serial.println("---------------------------\n");
Bluefruit.autoConnLed(false);
Bluefruit.configPrphBandwidth(BANDWIDTH_LOW);

Serial.print("Starting BLE stack. Expecting 'true':");
bool ret = Bluefruit.begin(0, 0);
Serial.println(ret);
Serial.print("Starting ANT stack. Expecting 'true':");
ret = ANTplus.begin(1);
Serial.println(ret);
ANTProfile* profiles[] = {pwr}; //{&hrm, &env, &sdm};
for (auto i: profiles)
{
Serial.printf("Channel number for %s became %d\n", i->getName(), i->getChannelNumber());
}

LoadCell->begin(); //begin with specified data and sck pins
Serial.printf("Loadcell begin done.\n");
unsigned long stabilizingtime = 2000; // tare preciscion can be improved by adding a few seconds of stabilizing time
bool _tare = true; //set this to false if you don't want tare to be performed in the next step
LoadCell->start(stabilizingtime, _tare);
Serial.printf("Loadcell start done.\n");
if (LoadCell->getTareTimeoutFlag())
{
Serial.println("Timeout, check MCU>HX711 wiring and pin designations");
//while (1);
}
else {
LoadCell->setCalFactor(config.calibration); // set calibration value (float)
Serial.printf("Calibration factor %f set!\n", config.calibration);
loadcelldataReady = false;
}
nextProfileUpdate = millis(); //to start with the right interval already in first update() cycle;
Serial.printf("Startup is complete.\n");
}

void PowerMeter::checkLoadCell() //Now just set ready, later add value to weightSum and increment numberofmeasurements
{
if(LoadCell->update())
{
loadcelldataReady = true;
}
}

void PowerMeter::hallInterrupt() //Calc new cadence, new power and set other values to 0;
{
currentHallInterrupt = millis();//Update Current Hall time
lastrotationDuration = currentHallInterrupt - lastHallInterrupt; //Calculate time of last rotation
float rps = 1000.0/(float)lastrotationDuration; //Last Rotation cadence in U/s
instCAD = (uint8_t)round(60 * rps); //Calc cadence of last rotation in U/min
lastHallInterrupt = currentHallInterrupt;//Now set old Hall time to now and wait for next one
float avgTorque = (weightSum/(float)numberOfMeasurements)*1.71675; //9.81 * 0.175 which is to get N instead of kg and then multiplying it with the Crank Length
weightSum = numberOfMeasurements = 0; //Set back to 0 for next rotation
instPWR = (uint16_t)round(avgTorque * TWO_PI * rps); //calc instant pwr
accPWR += instPWR;
PWREventCount++;
}

void PowerMeter::update()
{
//TODO -> Move this to checkLoadCell...
if (loadcelldataReady) //First check if a New measurement from hx711 is available
{
float currentWeight = LoadCell->getData();
weightSum += currentWeight > 0.0f ? currentWeight : 0.0f;
numberOfMeasurements++;
loadcelldataReady = false;
//Serial.printf("LoadCell Data: %f\n", weightSum);
//weightValues.push_back((uint16_t)round(LoadCell->getData()*10.0)); //add Current weight to the end of the list
}
if (millis() > nextProfileUpdate)
{
nextProfileUpdate += config.profileUpdateCycle;
pwr->SetInstantPWR(instPWR);
pwr->SetAccumulatedPWR(accPWR);
pwr->SetPWREventCount(PWREventCount);
pwr->SetInstantCadence(instCAD);
//Serial.printf("Values for Profile:\t%d\t%d\t%d\t%d\n", instPWR, accPWR, PWREventCount, instCAD);
}
}
64 changes: 64 additions & 0 deletions examples/PowerMeter_v1/src/PowerMeter/PowerMeter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#ifndef PowerMeter_h
#define PowerMeter_h

#define USE_TINYUSB


#include "sdant.h"
#include "profiles/BicyclePower.h"
#include <bluefruit.h>
#include "stdint-gcc.h"
#include "HX711_ADC.h"

//#include "list"

typedef struct powermeter_config
{
uint8_t dms_dout;
uint8_t dms_sck;
uint8_t hall_out;
float calibration;
uint16_t profileUpdateCycle;
BicyclePower* p_power_profile;
HX711_ADC* p_loadcell;
} powermeter_config;

class PowerMeter
{
public:



PowerMeter(powermeter_config*);
void begin();
void update();
void checkLoadCell();
void hallInterrupt();

uint8_t Get_dms_dout_Pin() { return config.dms_dout; }
uint8_t Get_dms_sck_Pin() { return config.dms_sck; }
uint8_t Get_hall_out_Pin() { return config.hall_out; }

void SetAccPWR(uint16_t val) { accPWR = val; }
void SetInstPWR(uint16_t val) { instPWR = val; }
void SetInstCAD(uint8_t val) { instCAD = val; }
void SetPWREventCount(uint8_t val) { PWREventCount = val; }

private:
BicyclePower* pwr;
HX711_ADC* LoadCell;
powermeter_config config;
uint16_t accPWR, instPWR;
uint8_t instCAD, PWREventCount;

uint32_t currentHallInterrupt, lastHallInterrupt, lastrotationDuration;
uint32_t nextProfileUpdate;
//std::list<uint16_t> weightValues;
float weightSum; //all measurements together in units of 100grams
uint16_t numberOfMeasurements;
bool loadcelldataReady;

};


#endif
8 changes: 5 additions & 3 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
; https://docs.platformio.org/page/projectconf.html

[env:adafruit_feather_nrf52840_s340]
platform = nordicnrf52
;platform = nordicnrf52
platform = https://github.com/orrmany/platform-nordicnrf52.git#develop-s340
board = adafruit_feather_nrf52840_s340
framework = arduino
; platform_packages = framework-arduinoadafruitnrf52 @ https://github.com/orrmany/Adafruit_nRF52_Arduino.git#develop-ant-plus-ble
platform_packages = framework-arduinoadafruitnrf52 @ https://github.com/orrmany/Adafruit_nRF52_Arduino.git#develop-ant-plus-ble
build_flags =
;monitor_filters = log2file, default
;monitor_filters = log2file, default
lib_deps = https://github.com/orrmany/Adafruit_GPS.git#add-xxvtg-sentence
2 changes: 1 addition & 1 deletion src/ANTProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ void ANTProfile::ProcessMessage(ant_evt_t* evt)
{
DecodeMessage(evt->message.ANT_MESSAGE_aucPayload);
newRxData = true;
newMillis = millis();
newTicks = xTaskGetTickCountFromISR();
}
break;

Expand Down
2 changes: 1 addition & 1 deletion src/ANTProfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class ANTProfile
//void setCustomDataPtr(void* ptr) { m_customDataPtr = ptr;}
//void* getCustomDataPtr(void) {return m_customDataPtr;}
bool newRxData = false;
uint32_t newMillis=0;
uint32_t newTicks=0;

protected:

Expand Down
Loading