Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/eMadman/SmartSpin2k into…
Browse files Browse the repository at this point in the history
… develop
  • Loading branch information
eMadman committed Jan 23, 2023
2 parents 2f417d9 + 6d38d0f commit 1811ee8
Show file tree
Hide file tree
Showing 41 changed files with 864 additions and 515 deletions.
21 changes: 20 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,25 @@
},
"C_Cpp.errorSquiggles": "Disabled",
"cSpell.words": [
"endianness"
"Appender",
"Baranick",
"BINFILE",
"Doud",
"endianness",
"IRAM",
"LOGW",
"MDNS",
"millis",
"Peloton",
"POWERTABLE",
"PWCFILENAME",
"RUNTIMECONFIG",
"ssid",
"STEALTHCHOP",
"UPDATEURL",
"Upgrader",
"userconfig",
"VERSIONFILE",
"WEBSERVER"
]
}
26 changes: 25 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,32 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
-
### Changed
-
### Hardware
-

## [23.1.22]
### Added
- Added blocking for shifts above or below min/max setpoints.
- Added Peloton serial decoder to sensor factory.
- Added blocking for shifts above or below min/max set points.
- Added power scaler for new board.
- Added Main Index link to develop.html.
- Added feature to automatically reconnect BLE devices if both are specified.
- Added ftms passthrough. FTMS messages from the client app are now passed to a connected FTMS device.
- Added resistance capture to Echelon.
- Added Resistance capture to Flywheel.
- Added Resistance Capture to Peloton.
- Added Resistance capture to FTMS.
- Added scanning when devices are not connected.
- Added ability to set travel limits based on resistance feedback from a bike.
- Added shifting in ERG mode (changes watt target).
- Added shifting in resistance mode (changes resistance target.)

### Changed
- PowerTable values are now adjusted to 90 RPM cad on input.
Expand All @@ -22,13 +39,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Moved serial checking to own function.
- Reduced verbosity of ERG logging.
- Fixed instance of BLE PM dropdown not being saved correctly.
- Moved post connect handling to the ble communication loop. (improves startup stability)
- Fixed bug submitted by @flo100 where MIN_WATTS in ERG should have been userConfig.getMinWatts();
- FTMS resistance mode now changes the attached bike resistance with feedback. (i.e. setting resistance to 50 with a Peloton attached will set 50 on the Peloton)
- Refactored rtConfig to use more measurement class.
- Increased stepper speed when a Peloton is connected. (very light resistance)
- Updated libraries to latest

### Hardware
- Removed duplicate directory in direct mount folder.
- New case for the new PCB :)
- Revised directory structure in /hardware
- updated Bowflex C7 mount for improved usability
- updated Echelon knob insert for durability
- Peloton bike mount updated for improved usability

## [22.10.8]

Expand Down Expand Up @@ -363,7 +387,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* WiFi Fallback to AP mode is now 10 seconds.
* WiFi AP mode Fallback SSID is now device name (MDNS name), and the password is whatever you have set.
* ERG Mode slightly more aggressive.
* Stealthchop 2 now selectable in settings.
* StealthChop 2 now selectable in settings.
* Holding both shifters at boot resets the unit to defaults and erases filesystem. (firmware remains intact)
* Holding both shifters for 3 seconds after boot preforms a BLE device scan/reconnect.

Expand Down
4 changes: 2 additions & 2 deletions CustomCharacteristic.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ From BLE_common.h
|BLE_deviceName |0x07 | |Not Implemented |
|BLE_shiftStep |0x08 |int |Stepper steps per shifter button press |
|BLE_stepperPower |0x09 |int |Stepper power in ma |
|BLE_stealthchop |0x0A |bool |Stepper stealthchop on/off |
|BLE_stealthChop |0x0A |bool |Stepper stealthChop on/off |
|BLE_inclineMultiplier |0x0B |float|- multiplied by incline to get steps per % gradient|
|BLE_powerCorrectionFactor |0x0C |float|.5 - 2.0 to calibrate power output |
|BLE_simulateHr |0x0D |bool | |
|BLE_simulateWatts |0x0E |bool | |
|BLE_simulateCad |0x0F |bool | |
|BLE_ERGMode |0x10 |bool | |
|BLE_FTMSMode |0x10 |bool | |
|BLE_autoUpdate |0x11 |bool |updates on (01) or off (00) |
|BLE_ssid |0x12 | |Not Implemented |
|BLE_password |0x13 | |Not Implemented |
Expand Down
Binary file modified Hardware/Common Assets/Bike Mount/CAD/Peloton.f3d
Binary file not shown.
Binary file modified Hardware/Common Assets/Bike Mount/Peloton.stl
Binary file not shown.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ SmartSpin2k adds automatic resistance control to any spin bike! It physically co

One of the main primary design goals of SS2k is to keep the parts count low making it easy for makers to build for themselves.

### Pre-Built Kits!
If you'd like to get involved in SS2K but don't want to build your own, a limited number of kits are available on sale by our community members. [CLICK HERE](https://github.com/doudar/SmartSpin2k/wiki/Prebuilt-Kits) for more information.

### Documentation
Expand Down
43 changes: 23 additions & 20 deletions data/btsimulator.html
Original file line number Diff line number Diff line change
Expand Up @@ -238,29 +238,32 @@ <h2>ERG Target Watts</h2>
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var obj = JSON.parse(this.responseText);
document.getElementById("wattsValue").innerHTML = obj.simulatedWatts + " Watts";
document.getElementById("wattsSlider").value = obj.simulatedWatts;
document.getElementById("wattsOutput").checked = obj.simulateWatts;
document.getElementById("wattsInputContainer").hidden = !obj.simulateWatts;

document.getElementById("hrValue").innerHTML = obj.simulatedHr + " BPM";
document.getElementById("hrSlider").value = obj.simulatedHr;
document.getElementById("hrOutput").checked = obj.simulateHr;
document.getElementById("hrSlider").hidden = !obj.simulateHr;
document.getElementById("hrValue").hidden = !obj.simulateHr;

document.getElementById("cadValue").innerHTML = obj.simulatedCad + " RPM";
document.getElementById("cadSlider").value = obj.simulatedCad;
document.getElementById("cadOutput").checked = obj.simulateCad;
document.getElementById("cadInputContainer").hidden = !obj.simulateCad;

document.getElementById("enableErgCheckbox").checked = obj.ERGMode;
document.getElementById("wattsValue").innerHTML = obj.watts + " Watts";
document.getElementById("wattsSlider").value = obj.watts;
document.getElementById("wattsOutput").checked = obj.simWatts;
document.getElementById("wattsInputContainer").hidden = !obj.simWatts;

document.getElementById("hrValue").innerHTML = obj.hr + " BPM";
document.getElementById("hrSlider").value = obj.hr;
document.getElementById("hrOutput").checked = obj.simHr;
document.getElementById("hrSlider").hidden = !obj.simHr;
document.getElementById("hrValue").hidden = !obj.simHr;

document.getElementById("cadValue").innerHTML = obj.cad + " RPM";
document.getElementById("cadSlider").value = obj.cad;
document.getElementById("cadOutput").checked = obj.simCad;
document.getElementById("cadInputContainer").hidden = !obj.simCad;
var ergMode = false;
if (obj.FTMSMode == "0x05"){
ergMode = true;
}
document.getElementById("enableErgCheckbox").checked = ergMode;

document.getElementById("targetWattsValue").innerHTML = obj.targetWatts + " Watts";
document.getElementById("targetWattsSlider").value = obj.targetWatts == null ? 0 : obj.targetWatts;
document.getElementById("targetWattsOutput").checked = obj.simulateTargetWatts;
document.getElementById("targetWattsSlider").hidden = !obj.simulateTargetWatts;
document.getElementById("targetWattsValue").hidden = !obj.simulateTargetWatts;
document.getElementById("targetWattsOutput").checked = obj.simTargetWatts;
document.getElementById("targetWattsSlider").hidden = !obj.simTargetWatts;
document.getElementById("targetWattsValue").hidden = !obj.simTargetWatts;

setTimeout(function () {
const watermark = document.getElementById("loadingWatermark");
Expand Down
6 changes: 3 additions & 3 deletions data/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,14 @@ <h2>
</tr>
<tr>
<td>
<p class="tooltip">Stepper Stealthchop
<p class="tooltip">Stepper StealthChop
<span class="tooltiptext">
Make stepper silent at expense of torque
</span>
</p>
</td>
<td>
<label class="switch"><input type="checkbox" name="stealthchop" id="stealthchop"><span
<label class="switch"><input type="checkbox" name="stealthChop" id="stealthChop"><span
class="slider"></span></label>
</td>
</tr>
Expand Down Expand Up @@ -319,7 +319,7 @@ <h2>
document.getElementById("stepperPower").value = obj.stepperPower;
document.getElementById("minWatts").value = obj.minWatts;
document.getElementById("maxWatts").value = obj.maxWatts;
document.getElementById("stealthchop").checked = obj.stealthchop;
document.getElementById("stealthChop").checked = obj.stealthChop;
document.getElementById("autoUpdate").checked = obj.autoUpdate;
document.getElementById("stepperDir").checked = obj.stepperDir;
document.getElementById("shifterDir").checked = obj.shifterDir;
Expand Down
6 changes: 3 additions & 3 deletions data/status.html
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,10 @@ <h2>
var obj = JSON.parse(this.responseText);
document.getElementById("currentIncline").value = obj.currentIncline;
document.getElementById("targetIncline").value = obj.targetIncline;
document.getElementById("simulatedHr").value = obj.simulatedHr;
document.getElementById("simulatedWatts").value = obj.simulatedWatts;
document.getElementById("simulatedHr").value = obj.hr;
document.getElementById("simulatedWatts").value = obj.watts;
document.getElementById("targetWatts").value = obj.targetWatts;
document.getElementById("simulatedCad").value = obj.simulatedCad;
document.getElementById("simulatedCad").value = obj.cad;
}
}
};
Expand Down
13 changes: 8 additions & 5 deletions include/BLE_Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,21 @@
#define BLE_deviceName 0x07
#define BLE_shiftStep 0x08
#define BLE_stepperPower 0x09
#define BLE_stealthchop 0x0A
#define BLE_stealthChop 0x0A
#define BLE_inclineMultiplier 0x0B
#define BLE_powerCorrectionFactor 0x0C
#define BLE_simulateHr 0x0D
#define BLE_simulateWatts 0x0E
#define BLE_simulateCad 0x0F
#define BLE_ERGMode 0x10
#define BLE_FTMSMode 0x10
#define BLE_autoUpdate 0x11
#define BLE_ssid 0x12
#define BLE_password 0x13
#define BLE_foundDevices 0x14
#define BLE_connectedPowerMeter 0x15
#define BLE_connectedHeartMonitor 0x16
#define BLE_shifterPosition 0x17
#define BLE_saveToLittleFS 0x18
#define BLE_saveToLittleFS 0x18
#define BLE_targetPosition 0x19
#define BLE_externalControl 0x1A
#define BLE_syncMode 0x1B
Expand Down Expand Up @@ -158,6 +158,7 @@ class SpinBLEAdvertisedDevice {
bool userSelectedCSC = false;
bool userSelectedCT = false;
bool doConnect = false;
bool postConnected = false;

void set(BLEAdvertisedDevice *device, int id = BLE_HS_CONN_HANDLE_NONE, BLEUUID inserviceUUID = (uint16_t)0x0000, BLEUUID incharUUID = (uint16_t)0x0000) {
advertisedDevice = device;
Expand All @@ -179,8 +180,9 @@ class SpinBLEAdvertisedDevice {
userSelectedCSC = false; // Cycling Speed/Cadence
userSelectedCT = false; // Controllable Trainer
doConnect = false; // Initiate connection flag
postConnected = false; // Has Cost Connect Been Run?
if (dataBufferQueue != nullptr) {
Serial.println("Resetting queue");
//Serial.println("Resetting queue");
xQueueReset(dataBufferQueue);
}
}
Expand Down Expand Up @@ -220,7 +222,8 @@ class SpinBLEClient {
// Reset devices in myBLEDevices[]. Bool All (true) or only connected ones
// (false)
void resetDevices();
void postConnect(NimBLEClient *pClient);
void postConnect();
void FTMSControlPointWrite(const uint8_t *pData, int length);
};
class MyAdvertisedDeviceCallback : public NimBLEAdvertisedDeviceCallbacks {
public:
Expand Down
16 changes: 9 additions & 7 deletions include/ERG_Mode.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,29 +75,31 @@ class PowerTable {
class ErgMode {
public:
ErgMode(PowerTable* powerTable) { this->powerTable = powerTable; }
void computErg();
void computeErg();
void computeResistance();
void _writeLogHeader();
void _writeLog(int cycles, float currentIncline, float newIncline, int currentSetPoint, int newSetPoint, int currentWatts, int newWatts, int currentCadence, int newCadence);
void _writeLog(float currentIncline, float newIncline, int currentSetPoint, int newSetPoint, int currentWatts, int newWatts, int currentCadence, int newCadence);

private:
bool engineStopped = false;
bool initialized = false;
int setPoint = 0;
int cycle = 0;
int offsetMultiplier = 0;
Measurement watts = Measurement(0);
int resistance = 0;
int cadence = 0;

Measurement watts;
PowerTable* powerTable;

// check if user is spinning, reset incline if user stops spinning
bool _userIsSpinning(int cadence, float incline);

// calculate incline if setpoint (from Zwift) changes
void _setPointChangeState(int newSetPoint, int newCadence, Measurement& newWatts, float currentIncline);
void _setPointChangeState(int newCadence, Measurement& newWatts);

// calculate incline if setpoint is unchanged
void _inSetpointState(int newSetPoint, int newCadence, Measurement& newWatts, float currentIncline);
void _inSetpointState(int newCadence, Measurement& newWatts);

// update localvalues + incline, creates a log
void _updateValues(int newSetPoint, int newCadence, Measurement& newWatts, float currentIncline, float newIncline);
void _updateValues(int newCadence, Measurement& newWatts, float newIncline);
};
22 changes: 13 additions & 9 deletions include/Main.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class SS2K {
bool stepperIsRunning;
bool externalControl;
bool syncMode;
int txCheck;

bool IRAM_ATTR deBounce();
static void IRAM_ATTR moveStepper(void* pvParameters);
Expand All @@ -47,11 +48,14 @@ class SS2K {
void restartWifi();
void setupTMCStepperDriver();
void updateStepperPower();
void updateStealthchop();
void updateStealthChop();
void checkDriverTemperature();
void motorStop(bool releaseTension = false);
void checkSerial();
void checkBLEReconnect();
void FTMSModeShiftModifier();
static void rxSerial(void);
void txSerial();
void pelotonConnected();

SS2K() {
targetPosition = 0;
Expand All @@ -65,12 +69,13 @@ class SS2K {
shiftersHoldForScan = SHIFTERS_HOLD_FOR_SCAN;
scanDelayTime = 10000;
scanDelayStart = 0;
txCheck = TX_CHECK_INTERVAL;
}
};

class AuxSerialBuffer {
public:
uint8_t data[20];
uint8_t data[AUX_BUF_SIZE];
size_t len;

AuxSerialBuffer() {
Expand All @@ -81,7 +86,7 @@ class AuxSerialBuffer {
}
};

// Users Physical Working Capacity Calculation Parameters (heartrate to Power
// Users Physical Working Capacity Calculation Parameters (heart rate to Power
// calculation)
extern physicalWorkingCapacity userPWC;
extern SS2K ss2k;
Expand All @@ -90,8 +95,7 @@ extern SS2K ss2k;
extern userParameters userConfig;
extern RuntimeParameters rtConfig;

//Peloton Specific Parameters
#define PELOTON_RQ_SIZE 4
const uint8_t peloton_rq_watts[]{0xF5, 0x44, 0x39, 0xF6};
const uint8_t peloton_rq_cad[]{0xF5, 0x41, 0x36, 0xF6};
const uint8_t peloton_rq_res[]{0xF5, 0x4A, 0x3F, 0xF6};
// Peloton Specific Parameters
// const uint8_t peloton_rq_watts[]{0xF5, 0x44, 0x39, 0xF6};
// const uint8_t peloton_rq_cad[]{0xF5, 0x41, 0x36, 0xF6};
// const uint8_t peloton_rq_res[]{0xF5, 0x49, 0x3F, 0xF6};
Loading

0 comments on commit 1811ee8

Please sign in to comment.