Skip to content

Commit

Permalink
Merge branch 'develop' into pr/benv12/585
Browse files Browse the repository at this point in the history
  • Loading branch information
doudar committed Nov 11, 2024
2 parents b5410d3 + 83ac674 commit 20d64c4
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 148 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/update-changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ jobs:
pull-requests: write

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.ref }}

- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.x'

Expand Down
25 changes: 24 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

### Changed
- Multiple Homing refinements.
- Working with resistance mode on QZ & Peloton

### Hardware

and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [24.11.7]

### Added

### Changed
- Homing refinements.
- Resistance shifting improvement.
- Reduced Peloton logging to 1/sec.

### Hardware

## [24.11.5]

### Added
- Knob homing if calibrate trainer is selected in an app.

### Changed
- Added backing off of the stop before we test to prevent runaway grinding during homing.
- User can abort homing by pressing shifter.

### Hardware

## [24.10.30]

Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# You can now visit us at [Facebook](https://www.facebook.com/groups/716297469953492/)
# There's now a companion App!

A brand new shiny Companion app for SmartSpin2k is availiable! [SS2kConfigApp](https://github.com/doudar/SS2kConfigApp/tree/develop) (Google Play Store Coming Soon).
A brand new shiny Companion app for SmartSpin2k is availiable! [SS2kConfigApp](https://github.com/doudar/SS2kConfigApp/tree/develop)

You can get it from the Apple App Store here:

Expand All @@ -21,8 +21,6 @@ You can get it from the Apple App Store here:

</div>

If you have Android, it will be on the Play Store soon, but in the meantime, you can side load it using the .apk (located in the .zip) in the releases section of the repository: [SS2kConfigApp](https://github.com/doudar/SS2kConfigApp/releases)

# About
SmartSpin2k is a DIY project that allows you to turn any spin bike into a smart trainer. With SmartSpin2k, you can connect your spin bike to Zwift, TrainerRoad, or other popular training apps. This allows you to control your bike's resistance automatically, track your performance, and compete with other riders online.

Expand Down
2 changes: 1 addition & 1 deletion include/BLE_Fitness_Machine_Service.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class BLE_Fitness_Machine_Service {
BLE_Fitness_Machine_Service();
void setupService(NimBLEServer *pServer, MyCallbacks *chrCallbacks);
void update();
bool spinDown();
bool spinDown(uint8_t response);

private:
BLEService *pFitnessMachineService;
Expand Down
2 changes: 1 addition & 1 deletion include/SmartSpin_parameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class RuntimeParameters {
void setShifterPosition(int sp) { shifterPosition = sp; }
int getShifterPosition() { return shifterPosition; }

void setHomed(int hmd) { homed = hmd; }
void setHomed(bool hmd) { homed = hmd; }
int getHomed() { return homed; }

void setMinStep(int ms) { minStep = ms; }
Expand Down
6 changes: 3 additions & 3 deletions include/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ const char* const DEFAULT_PASSWORD = "password";
#define RUNTIMECONFIG_JSON_SIZE 512 + DEBUG_LOG_BUFFER_SIZE

// PowerTable Version
#define TABLE_VERSION 4
#define TABLE_VERSION 5

/* Number of entries in the ERG Power Lookup Table
This is currently maintained as to keep memory usage lower and reduce the print output of the table.
Expand Down Expand Up @@ -312,8 +312,8 @@ const char* const DEFAULT_PASSWORD = "password";
#define BLE_RECONNECT_SCAN_DURATION 5

// Task Stack Sizes
#define MAIN_STACK 6000
#define BLE_CLIENT_STACK 5500
#define MAIN_STACK 6500
#define BLE_CLIENT_STACK 6000

// Uncomment to enable stack size debugging info
// #define DEBUG_STACK
Expand Down
3 changes: 1 addition & 2 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ lib_deps =
https://github.com/h2zero/NimBLE-Arduino/archive/refs/tags/1.4.0.zip
https://github.com/teemuatlut/TMCStepper/archive/refs/tags/v0.7.3.zip
https://github.com/bblanchon/ArduinoJson/archive/refs/tags/v6.20.0.zip
https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/archive/refs/tags/V1.3.0.zip
https://github.com/gin66/FastAccelStepper/archive/refs/tags/0.28.3.zip
https://github.com/gin66/FastAccelStepper/archive/refs/tags/0.31.2.zip
https://github.com/gilmaimon/ArduinoWebsockets/archive/refs/tags/0.5.3.zip

[env:release]
Expand Down
2 changes: 1 addition & 1 deletion src/BLE_Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ void bleClientTask(void *pvParameters) {
// Spin Down process for the Server. It's here because it needs to be non-blocking for the maintenance loop.
if (spinBLEServer.spinDownFlag) {
if (spinBLEServer.spinDownFlag >= 2) { // Home Both Directions
fitnessMachineService.spinDown();
ss2k->goHome(true);
} else { // Startup Homing
ss2k->goHome(false);
}
Expand Down
3 changes: 0 additions & 3 deletions src/BLE_Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,6 @@ void BLECommunications() {

if (connectedClientCount() > 0 && !ss2k->isUpdating) {
spinBLEServer.update();
// if (spinDown()) {
// Possibly do something in the future. Right now we just fake the spin down.
// }

#ifdef INTERNAL_ERG_4EXT_FTMS
uint8_t test[] = {FitnessMachineControlPointProcedure::SetIndoorBikeSimulationParameters, 0x00, 0x00, 0x00, 0x00, 0x28, 0x33};
Expand Down
26 changes: 12 additions & 14 deletions src/BLE_Fitness_Machine_Service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,32 +317,31 @@ void BLE_Fitness_Machine_Service::processFTMSWrite() {
}
}

bool BLE_Fitness_Machine_Service::spinDown() {
std::string rxValue = fitnessMachineStatusCharacteristic->getValue();
bool BLE_Fitness_Machine_Service::spinDown(uint8_t response) {
uint8_t spinStatus[2] = {FitnessMachineStatus::SpinDownStatus, response};
fitnessMachineStatusCharacteristic->setValue(spinStatus, 2);
fitnessMachineStatusCharacteristic->notify();
/*std::string rxValue = fitnessMachineStatusCharacteristic->getValue();
if (rxValue[0] != 0x14) {
return false;
return false;
}
uint8_t spinStatus[2] = {0x14, 0x01};
SS2K_LOG(FMTS_SERVER_LOG_TAG, "Spin Status: %d", rxValue[1]);
Serial.printf("Spin Status: %d", rxValue[1]);
if (rxValue[1] == 0x01) {
SS2K_LOG(FMTS_SERVER_LOG_TAG, "Spin Down Initiated");
Serial.printf("Spin Down Initiated");
vTaskDelay(1000 / portTICK_RATE_MS);
spinStatus[1] = 0x04; // send Stop Pedaling
vTaskDelay(500 / portTICK_RATE_MS);
spinStatus[1] = 0x01; // Initiated
fitnessMachineStatusCharacteristic->setValue(spinStatus, 2);
fitnessMachineStatusCharacteristic->notify();
vTaskDelay(1000 / portTICK_RATE_MS);
spinStatus[1] = 0x02; // Success
vTaskDelay(500 / portTICK_RATE_MS);
spinStatus[1] = 0x04; // send Stop Pedaling
fitnessMachineStatusCharacteristic->setValue(spinStatus, 2);
fitnessMachineStatusCharacteristic->notify();
ss2k->goHome(true);
}

if (rxValue[1] == 0x04) {
SS2K_LOG(FMTS_SERVER_LOG_TAG, "Stop Pedaling");
Serial.printf("Stop Pedaling");
vTaskDelay(1000 / portTICK_RATE_MS);
vTaskDelay(500 / portTICK_RATE_MS);
spinStatus[1] = 0x02; // Success
fitnessMachineStatusCharacteristic->setValue(spinStatus, 2);
fitnessMachineStatusCharacteristic->notify();
Expand All @@ -357,8 +356,7 @@ bool BLE_Fitness_Machine_Service::spinDown() {
fitnessMachineControlPoint->setValue(returnValue, 3);
fitnessMachineControlPoint->indicate();
fitnessMachineStatusCharacteristic->notify();
ss2k->goHome(true);
}
}*/

return true;
}
134 changes: 83 additions & 51 deletions src/ERG_Mode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ void PowerTable::runERG() {

if (ss2k->resetPowerTableFlag) {
powerTable->reset();
userConfig->setHMin(INT32_MIN);
userConfig->setHMax(INT32_MIN);
rtConfig->setHomed(false);
userConfig->saveToLittleFS();
}
loopCounter++;
}
Expand Down Expand Up @@ -847,6 +851,9 @@ bool PowerTable::_manageSaveState() {
file.read((uint8_t*)&version, sizeof(version));
int savedQuality;
file.read((uint8_t*)&savedQuality, sizeof(savedQuality));
bool savedHomed;
file.read((uint8_t*)&savedHomed, sizeof(savedHomed));

if (version != TABLE_VERSION) {
SS2K_LOG(POWERTABLE_LOG_TAG, "Expected power table version %d, found version %d", TABLE_VERSION, version);
file.close();
Expand All @@ -862,32 +869,35 @@ bool PowerTable::_manageSaveState() {
this->_save();
}

SS2K_LOG(POWERTABLE_LOG_TAG, "Loading power table version %d, Size %d", version, savedQuality);

// Initialize a counter for reliable positions
int reliablePositions = 0;

// Check if we have at least 3 reliable positions in the active table in order to determine a reliable offset to load the saved table
for (int i = 0; i < POWERTABLE_CAD_SIZE; i++) {
for (int j = 0; j < POWERTABLE_WATT_SIZE; j++) {
int16_t savedTargetPosition = INT16_MIN;
int8_t savedReadings = 0;
file.read((uint8_t*)&savedTargetPosition, sizeof(savedTargetPosition));
file.read((uint8_t*)&savedReadings, sizeof(savedReadings));
// Does the saved file have a position that the active session has also recorded?
// We start comparing at watt position 3 (j>2) because low resistance positions are notoriously unreliable.
if ((j > 2) && (this->tableRow[i].tableEntry[j].targetPosition != INT16_MIN) && (this->tableRow[i].tableEntry[j].readings > MINIMUM_RELIABLE_POSITIONS) &&
(savedReadings > 0)) {
reliablePositions++;
SS2K_LOG(POWERTABLE_LOG_TAG, "Loading power table version %d, Size %d, Homed %d", version, savedQuality, savedHomed);

// If both current and saved tables were created with homing, we can skip position reliability checks
bool canSkipReliabilityChecks = savedHomed && rtConfig->getHomed();

if (!canSkipReliabilityChecks) {
// Initialize a counter for reliable positions
int reliablePositions = 0;

// Check if we have at least 3 reliable positions in the active table in order to determine a reliable offset to load the saved table
for (int i = 0; i < POWERTABLE_CAD_SIZE; i++) {
for (int j = 0; j < POWERTABLE_WATT_SIZE; j++) {
int16_t savedTargetPosition = INT16_MIN;
int8_t savedReadings = 0;
file.read((uint8_t*)&savedTargetPosition, sizeof(savedTargetPosition));
file.read((uint8_t*)&savedReadings, sizeof(savedReadings));
// Does the saved file have a position that the active session has also recorded?
// We start comparing at watt position 3 (j>2) because low resistance positions are notoriously unreliable.
if ((j > 2) && (this->tableRow[i].tableEntry[j].targetPosition != INT16_MIN) && (this->tableRow[i].tableEntry[j].readings > MINIMUM_RELIABLE_POSITIONS) &&
(savedReadings > 0)) {
reliablePositions++;
}
}
}
}
if (reliablePositions < MINIMUM_RELIABLE_POSITIONS) { // Do we have enough active data in order to calculate a (good) offset when we load the new table?
SS2K_LOG(POWERTABLE_LOG_TAG, "Not enough matching positions to load the Power Table. %d of %d needed.", reliablePositions, MINIMUM_RELIABLE_POSITIONS);
file.close();
return false;
} else {
// continue loading
if (reliablePositions < MINIMUM_RELIABLE_POSITIONS) { // Do we have enough active data in order to calculate a (good) offset when we load the new table?
SS2K_LOG(POWERTABLE_LOG_TAG, "Not enough matching positions to load the Power Table. %d of %d needed.", reliablePositions, MINIMUM_RELIABLE_POSITIONS);
file.close();
return false;
}
}
file.close();

Expand All @@ -903,44 +913,62 @@ bool PowerTable::_manageSaveState() {
// get these reads done, so that we're in the right position to read the data from the file.
file.read((uint8_t*)&version, sizeof(version));
file.read((uint8_t*)&savedQuality, sizeof(savedQuality));
std::vector<float> offsetDifferences;

reliablePositions = 0;
// Read table entries and calculate offsets
for (int i = 0; i < POWERTABLE_CAD_SIZE; i++) {
for (int j = 0; j < POWERTABLE_WATT_SIZE; j++) {
int16_t savedTargetPosition = INT16_MIN;
int8_t savedReadings = 0;
file.read((uint8_t*)&savedTargetPosition, sizeof(savedTargetPosition));
file.read((uint8_t*)&savedReadings, sizeof(savedReadings));
if ((this->tableRow[i].tableEntry[j].targetPosition != INT16_MIN) && (savedTargetPosition != INT16_MIN) && (savedReadings > 0) &&
(this->tableRow[i].tableEntry[j].readings > MINIMUM_RELIABLE_POSITIONS)) {
int offset = this->tableRow[i].tableEntry[j].targetPosition - savedTargetPosition;
offsetDifferences.push_back(offset);
SS2K_LOG(POWERTABLE_LOG_TAG, "offset %d", offset);
reliablePositions++;
file.read((uint8_t*)&savedHomed, sizeof(savedHomed));

float averageOffset = 0;
if (!canSkipReliabilityChecks) {
std::vector<float> offsetDifferences;
int reliablePositions = 0;
// Read table entries and calculate offsets
for (int i = 0; i < POWERTABLE_CAD_SIZE; i++) {
for (int j = 0; j < POWERTABLE_WATT_SIZE; j++) {
int16_t savedTargetPosition = INT16_MIN;
int8_t savedReadings = 0;
file.read((uint8_t*)&savedTargetPosition, sizeof(savedTargetPosition));
file.read((uint8_t*)&savedReadings, sizeof(savedReadings));
if ((this->tableRow[i].tableEntry[j].targetPosition != INT16_MIN) && (savedTargetPosition != INT16_MIN) && (savedReadings > 0) &&
(this->tableRow[i].tableEntry[j].readings > MINIMUM_RELIABLE_POSITIONS)) {
int offset = this->tableRow[i].tableEntry[j].targetPosition - savedTargetPosition;
offsetDifferences.push_back(offset);
SS2K_LOG(POWERTABLE_LOG_TAG, "offset %d", offset);
reliablePositions++;
}
this->tableRow[i].tableEntry[j].targetPosition = savedTargetPosition;
this->tableRow[i].tableEntry[j].readings = savedReadings;
}
}
averageOffset = std::accumulate(offsetDifferences.begin(), offsetDifferences.end(), 0.0) / offsetDifferences.size();
} else {
// If both tables were created with homing, just load the values directly
for (int i = 0; i < POWERTABLE_CAD_SIZE; i++) {
for (int j = 0; j < POWERTABLE_WATT_SIZE; j++) {
int16_t savedTargetPosition = INT16_MIN;
int8_t savedReadings = 0;
file.read((uint8_t*)&savedTargetPosition, sizeof(savedTargetPosition));
file.read((uint8_t*)&savedReadings, sizeof(savedReadings));
this->tableRow[i].tableEntry[j].targetPosition = savedTargetPosition;
this->tableRow[i].tableEntry[j].readings = savedReadings;
}
this->tableRow[i].tableEntry[j].targetPosition = savedTargetPosition;
this->tableRow[i].tableEntry[j].readings = savedReadings;
// SS2K_LOG(POWERTABLE_LOG_TAG, "Position %d, %d, Target %d, Readings %d, loaded", i, j, this->tableRow[i].tableEntry[j].targetPosition,
// this->tableRow[i].tableEntry[j].readings);
}
SS2K_LOG(POWERTABLE_LOG_TAG, "Both tables were created with homing, loaded values directly");
}

file.close();
float averageOffset = std::accumulate(offsetDifferences.begin(), offsetDifferences.end(), 0.0) / offsetDifferences.size();

// Apply the offset to all loaded positions except for INT16_MIN values
for (int i = 0; i < POWERTABLE_CAD_SIZE; i++) {
for (int j = 0; j < POWERTABLE_WATT_SIZE; j++) {
if (this->tableRow[i].tableEntry[j].targetPosition != INT16_MIN) {
this->tableRow[i].tableEntry[j].targetPosition += averageOffset;
// Apply the offset if needed
if (!canSkipReliabilityChecks) {
for (int i = 0; i < POWERTABLE_CAD_SIZE; i++) {
for (int j = 0; j < POWERTABLE_WATT_SIZE; j++) {
if (this->tableRow[i].tableEntry[j].targetPosition != INT16_MIN) {
this->tableRow[i].tableEntry[j].targetPosition += averageOffset;
}
}
}
SS2K_LOG(POWERTABLE_LOG_TAG, "Power Table loaded with an offset of %d.", averageOffset);
}

// set the flag so it isn't loaded again this session.
this->_hasBeenLoadedThisSession = true;
SS2K_LOG(POWERTABLE_LOG_TAG, "Power Table loaded with an offset of %d.", averageOffset);
}

// Implement saving on a timer
Expand Down Expand Up @@ -970,6 +998,10 @@ bool PowerTable::_save() {
int size = getNumReadings();
file.write((uint8_t*)&size, sizeof(size));

// Write homing state
bool isHomed = rtConfig->getHomed();
file.write((uint8_t*)&isHomed, sizeof(isHomed));

// Write table entries
for (int i = 0; i < POWERTABLE_CAD_SIZE; i++) {
for (int j = 0; j < POWERTABLE_WATT_SIZE; j++) {
Expand Down
Loading

0 comments on commit 20d64c4

Please sign in to comment.