Skip to content
Merged
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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

### Changed

### Hardware


## [25.12.17]

### Added

### Changed
- Links card update.
- Removed indications from Control Point Characteristic to make Zwift on Android happy.
- IC4 reported HR won't override other HRM.
- Improved ERG response for homed tables.
- Slightly faster Peloton bike + homing.

### Hardware

Expand Down
7 changes: 5 additions & 2 deletions include/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const char* const DEFAULT_PASSWORD = "password";
// into actual stepper steps that move the stepper motor. It takes 2,181.76 steps to rotate the knob 1 full revolution. with hardware version 1.
// Incline_Multiplier may be able to be removed in the future by dividing ShiftSteps by ~200 to get this value but we're not quite ready
// to make that commitment yet.
#define INCLINE_MULTIPLIER 5.0f
#define INCLINE_MULTIPLIER 7.0f

// Minumum value for power correction factor user setting
#define MIN_PCF .5f
Expand Down Expand Up @@ -77,7 +77,7 @@ const char* const DEFAULT_PASSWORD = "password";
// I.E. If the difference between ERG target and Current watts were 30, and the Shift step is defined as 600 steps,
// and ERG_Sensitivity were 1.0, ERG mode would move the stepper motor 600 steps to compensate. With an ERG_Sensitivity of 2.0, the stepper
// would move 1200 steps to compensate, however ERG_Sensitivity values much different than 1.0 imply shiftStep has been improperly configured.
#define ERG_SENSITIVITY 5.0f
#define ERG_SENSITIVITY 2.0f

// Number of watts per shift expected by ERG mode for it's calculation. The user should target this number by adjusting Shift Step until WATTS_PER_SHIFT
// is obtained as closely as possible during each shift.
Expand Down Expand Up @@ -282,6 +282,9 @@ constexpr const char* ANY = "any";
// Uncomment to use the PID controller for ERG mode.
#define ERG_MODE_USE_PID

// Window where ERG mode will use PID instead of Power Table for initial changes.
#define ERG_MODE_PID_WINDOW 20

// PowerTable Version
#define TABLE_VERSION 6

Expand Down
16 changes: 13 additions & 3 deletions src/BLE_Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1057,15 +1057,25 @@ void SpinBLEAdvertisedDevice::set(const NimBLEAdvertisedDevice* device, int id,
if (id != BLE_HS_CONN_HANDLE_NONE) {
NimBLEClient* pClient = NimBLEDevice::getClientByPeerAddress(device->getAddress());
if (pClient) {
const char* cfgHRM = userConfig->getConnectedHeartMonitor();
const bool cfgHrmIsNone = (strcmp(cfgHRM, NONE) == 0);
const bool cfgHrmIsAny = (strcmp(cfgHRM, ANY) == 0);
const std::string addrStr = device->getAddress().toString();
const bool hrmNameMatch = (adevName == cfgHRM);
const bool hrmAddrMatch = (addrStr == cfgHRM);

// Get all services
const std::vector<NimBLERemoteService*>& services = pClient->getServices(true);
for (auto& pService : services) {
BLEUUID serviceUUID = pService->getUUID();

if (serviceUUID == HEARTSERVICE_UUID) {
this->isHRM = true;
spinBLEClient.connectedHRM = true;
SS2K_LOG(BLE_CLIENT_LOG_TAG, "Registered HRM on Connect");
if (cfgHrmIsNone || cfgHrmIsAny || hrmNameMatch || hrmAddrMatch) {
Comment thread
doudar marked this conversation as resolved.
spinBLEClient.connectedHRM = true;
SS2K_LOG(BLE_CLIENT_LOG_TAG, "Registered HRM on Connect");
} else {
SS2K_LOG(BLE_CLIENT_LOG_TAG, "Heart service on %s ignored (cfgHRM='%s')", this->uniqueName.c_str(), cfgHRM);
}
} else if (serviceUUID == CSCSERVICE_UUID) {
this->isCSC = true;
spinBLEClient.connectedCD = true;
Expand Down
45 changes: 28 additions & 17 deletions src/ERG_Mode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
#include <numeric>
#include <unordered_map>

static unsigned long ergTimer = millis();
static unsigned long ergTimer = millis() + ERG_MODE_DELAY;
static bool isDelayed = false;

void ErgMode::runERG() {
static ErgMode ergMode;
Expand All @@ -27,9 +28,15 @@ void ErgMode::runERG() {
static bool simulationRunning = false;
static int loopCounter = 0;

if ((millis() - ergTimer) > ERG_MODE_DELAY) {
if ((millis() > ergTimer)) {

if (isDelayed) {
SS2K_LOG(ERG_MODE_LOG_TAG, "ERG wait expired");
isDelayed = false;
}

// reset the timer.
ergTimer = millis();
ergTimer = millis() + ERG_MODE_DELAY;

static unsigned long int saveFlagCooldown = 0;
// save powertable if saveFlag has been set for 10 seconds using a saveFlagCooldown timer
Expand Down Expand Up @@ -85,7 +92,7 @@ void ErgMode::runERG() {
// only do this twice as often as ERG_MODE_DELAY
static float previousPower = 0;
static unsigned long int pTab4pwrTimer = millis();
int _smoothPWR = 0;
int _smoothPWR = 0;
if (millis() - pTab4pwrTimer > ERG_MODE_DELAY / 2) {
// reset the timer.
pTab4pwrTimer = millis();
Expand All @@ -102,11 +109,11 @@ void ErgMode::runERG() {
saveStateTimer = millis();
}
}
// So the user knows pTab4PWR is enabled, provide some cadence feedback even if the value returned by the table is 0.
int minimumPower = rtConfig->cad.getValue()/2; // 50% of the cadence value
_smoothPWR = _smoothPWR < minimumPower ? round((minimumPower + previousPower) / 2.0f) : _smoothPWR;
rtConfig->watts.setValue(_smoothPWR);
previousPower = (rtConfig->watts.getValue() + previousPower) / 2;
// So the user knows pTab4PWR is enabled, provide some cadence feedback even if the value returned by the table is 0.
int minimumPower = rtConfig->cad.getValue() / 2; // 50% of the cadence value
_smoothPWR = _smoothPWR < minimumPower ? round((minimumPower + previousPower) / 2.0f) : _smoothPWR;
rtConfig->watts.setValue(_smoothPWR);
previousPower = (rtConfig->watts.getValue() + previousPower) / 2;
}
}
}
Expand Down Expand Up @@ -137,7 +144,7 @@ void ErgMode::computeErg() {
#ifdef ERG_MODE_USE_POWER_TABLE
// SetPoint changed
#ifdef ERG_MODE_USE_PID
if (abs(this->setPoint - newWatts.getTarget()) > 20) {
if (abs(this->setPoint - newWatts.getTarget()) > ERG_MODE_PID_WINDOW && rtConfig->getHomed()) {
#endif
_setPointChangeState(newCadence, newWatts);
return;
Expand All @@ -153,15 +160,18 @@ void ErgMode::computeErg() {
}

void ErgMode::_setPointChangeState(int newCadence, Measurement& newWatts) {
int32_t tableResult = powerTable->lookup(newWatts.getTarget(), newCadence);
// It's better to undershoot increasing watts and overshoot decreasing watts, so lets set the lookup target to the nearest side of ERG_MODE_PID_WINDOW
int adjustedTarget = newWatts.getTarget() >= newWatts.getValue() ? newWatts.getTarget() - ERG_MODE_PID_WINDOW : newWatts.getTarget() + ERG_MODE_PID_WINDOW;

int32_t tableResult = powerTable->lookup(adjustedTarget, newCadence);

// Test current watts against the table result. If We're already lower or higher than target, flag the result as a return error.
if (tableResult != RETURN_ERROR) {
if (rtConfig->watts.getValue() > newWatts.getTarget() && tableResult > ss2k->getCurrentPosition()) {
if (rtConfig->watts.getValue() > adjustedTarget && tableResult > ss2k->getCurrentPosition()) {
SS2K_LOG(ERG_MODE_LOG_TAG, "Table Result Failed High Test: %d", tableResult);
tableResult = RETURN_ERROR;
}
if (rtConfig->watts.getValue() < newWatts.getTarget() && tableResult < ss2k->getCurrentPosition()) {
if (rtConfig->watts.getValue() < adjustedTarget && tableResult < ss2k->getCurrentPosition()) {
SS2K_LOG(ERG_MODE_LOG_TAG, "Table Result Failed Low Test: %d", tableResult);
tableResult = RETURN_ERROR;
}
Expand All @@ -174,14 +184,15 @@ void ErgMode::_setPointChangeState(int newCadence, Measurement& newWatts) {
return;
}

SS2K_LOG(ERG_MODE_LOG_TAG, "SetPoint changed:%dw PowerTable Result: %d", newWatts.getTarget(), tableResult);
SS2K_LOG(ERG_MODE_LOG_TAG, "SetPoint changed:%dw PowerTable Result: %d", adjustedTarget, tableResult);
_updateValues(newCadence, newWatts, tableResult);

if (rtConfig->getTargetIncline() != ss2k->getCurrentPosition()) { // add some time to wait while the knob moves to target position.
isDelayed = true;
int timeToAdd = abs(ss2k->getCurrentPosition() - rtConfig->getTargetIncline());
if (timeToAdd > 4000) { // 4 seconds
SS2K_LOG(ERG_MODE_LOG_TAG, "Capping ERG seek time to 5 seconds");
timeToAdd = 4000;
if (timeToAdd > 3000) { // 3 seconds
SS2K_LOG(ERG_MODE_LOG_TAG, "Capping ERG seek time to 3 seconds");
timeToAdd = 3000;
}
ergTimer += timeToAdd;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,8 +537,8 @@ void SS2K::_resistanceMove() {
int direction = (actualDelta > 0) ? 1 : -1;
if (abs(actualDelta) > 20 - userConfig->getERGSensitivity()) {
rtConfig->setTargetIncline(ss2k->getCurrentPosition() + userConfig->getShiftStep() * direction);
} else if (abs(actualDelta) > 3) {
rtConfig->setTargetIncline(ss2k->getCurrentPosition() + actualDelta * 2 + (userConfig->getERGSensitivity() * direction));
} else if (abs(actualDelta) > 1) {
rtConfig->setTargetIncline(ss2k->getCurrentPosition() + actualDelta * 3 + (userConfig->getERGSensitivity() * direction));
} else {
rtConfig->setTargetIncline(ss2k->getCurrentPosition() + actualDelta + (userConfig->getERGSensitivity() * direction));
}
Expand Down
Loading