Skip to content

Commit

Permalink
Merge pull request #540 from doudar/s3_connection_fixes
Browse files Browse the repository at this point in the history
S3 Requires rolling back to previous Arduino Core
  • Loading branch information
doudar committed Mar 23, 2024
2 parents 69d80ec + 0befa34 commit aa25525
Show file tree
Hide file tree
Showing 34 changed files with 453 additions and 174 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
blank_issues_enabled : flase
contact_links:
- name: SmartSpin2K Wiki
- name: SmartSpin2k Wiki
url: https://github.com/doudar/SmartSpin2k/wiki
about: Please check here first if you have questions.
- name: SmartSpin Discussions
Expand Down
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added pass through shifting in both ERG and SIM mode.
- Refined and added BLE custom characteristics for upcoming configuration app.
-
- Added CSC Service to BLE server.

### Changed
- Updated communications overview picture.
- Updated kit purchasing links.
Expand All @@ -25,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- BLE scans blocked during firmware upgrade.
- Increased the default incline multiplier to 5.
- Added more robust activity monitoring and reboot every 30 minutes if there is no activity.
- Updated all references of SmartSkin2K to SmartSpin2k for consistency.
- Fixed bug where BT scanner "Loading" wouldn't disappear if "NONE" and "NONE" were selected.

### Hardware
- added Yesoul S3.
Expand Down Expand Up @@ -412,7 +415,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
*1.3.21
* SS2K BLE Server now accepts more than one simultaneous connection (you can not connect SS2K to both Zwift and another app simultaneously)
* Echelon bike is now supported
* SmartSpin2K.local more accessible with different browsers (fixed certain MDNS dropouts)
* SmartSpin2k.local more accessible with different browsers (fixed certain MDNS dropouts)
* Flywheel bike support built in (still untested)
* Backend (client) completely revamped to allow more device decoders, better stability, and faster network speeds.
* Lots of FTMS server and client polishing
Expand Down
2 changes: 1 addition & 1 deletion Hardware/Common Assets/Inserts/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
These are the parts that adapt your knob to the SmartSpin2K.
These are the parts that adapt your knob to the SmartSpin2k.

You'll see two of most of these because as we make them we've designed two slightly different sizes to accommodate

Expand Down
2 changes: 1 addition & 1 deletion Hardware/Common Assets/Shifters/README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Fusion 360 exports and generic STEP files are both provided
- [See Shifter Assembly video linked in Wiki](https://github.com/doudar/SmartSpin2k/wiki/Building-How-To)

## Setup
- In case you need to flip the orientation of the shifter, you can easily do so in the SmartSpin2K User Interface. Navigate to [http://SmartSpin2K.local/](http://SmartSpin2K.local). Here, you can toggle the Shifter Direction button until the device shifts in the right direction and save your settings. You can verify your settings in the [Web Shifter](http://smartspin2k.local/shift.html)
- In case you need to flip the orientation of the shifter, you can easily do so in the SmartSpin2k User Interface. Navigate to [http://SmartSpin2k.local/](http://SmartSpin2k.local). Here, you can toggle the Shifter Direction button until the device shifts in the right direction and save your settings. You can verify your settings in the [Web Shifter](http://smartspin2k.local/shift.html)

## BOM
- Momentary microswitches
Expand Down
8 changes: 4 additions & 4 deletions Hardware/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SmartSpin2K Hardware
# SmartSpin2k Hardware
V3 is the brand spanking new version where we've gone to a more rigid mount and designed a completly custom PCB with SMT components which can be manufactured using automated techniques. The newest PCB sports a uart->serial driver to allow direct communication with Peloton bikes.

V2 prints easier, is smaller and uses less hardware. It has a much better gear ratio than V1 and supports a carrier board PCB onto which the devkits mount for easy wiring/soldering. A rigid mount referred to as Direct Mount is available for stable mounting.
Expand All @@ -18,7 +18,7 @@ Use with the SMT manufaactured PCB and for compatibility with Peloton bikes.

## [Common Assets](https://github.com/doudar/SmartSpin2k/tree/develop/Hardware/Common%20Assets)
### [Arm](https://github.com/doudar/SmartSpin2k/tree/develop/Hardware/Common%20Assets/Arm)
The Arm is used to connect the SmartSpin2K to the bike mount. See Readme for sizing and fitment information.
The Arm is used to connect the SmartSpin2k to the bike mount. See Readme for sizing and fitment information.

Required for V3 and V2 Direct Mount.

Expand All @@ -28,7 +28,7 @@ Attaches to the head tube of your bike and provides an attachment point for the
Required for V3 and V2 Direct Mount

### [Gears](https://github.com/doudar/SmartSpin2k/tree/develop/Hardware/Common%20Assets/Bike%20Mount)
Internal drive gears for the SmartSpin2K. 11:40t gearing.
Internal drive gears for the SmartSpin2k. 11:40t gearing.

Required for all V2 and V3 variations

Expand All @@ -39,7 +39,7 @@ Required for all V2 and V3 variations


### [KnobCup](https://github.com/doudar/SmartSpin2k/tree/develop/Hardware/Common%20Assets/KnobCups)
Knob Cup retains the insert and connects to the internal gearbox of the SmartSpin2K
Knob Cup retains the insert and connects to the internal gearbox of the SmartSpin2k

Required for all V2 and V3 variations

Expand Down
2 changes: 1 addition & 1 deletion Hardware/V2 - Through Hole/readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<img src="/Pictures/Version2.0.jpg" alt="Hardware 2.0"/>
# [Description]
This version of SmartSpin2K is designed with DIY in mind. The PCB can be hand soldered using readily available components.
This version of SmartSpin2k is designed with DIY in mind. The PCB can be hand soldered using readily available components.

The case for V2 is available in multiple versions that are suitable for various needs. The direct mount design is the currently recommended case design.

Expand Down
2 changes: 1 addition & 1 deletion data/bluetoothscanner.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
background-color: #03245c;
}
</style>
<title>SmartSpin2K Bluetooth Scanner</title>
<title>SmartSpin2k Bluetooth Scanner</title>
<meta name="viewport" content="width=device-width, initial-scale=1">

</head>
Expand Down
2 changes: 1 addition & 1 deletion data/btsimulator.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
background-color: #03245c;
}
</style>
<title>SmartSpin2K Web Server</title>
<title>SmartSpin2k Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>

Expand Down
2 changes: 1 addition & 1 deletion data/develop.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
}
</style>

<title>SmartSpin2K Web Server</title>
<title>SmartSpin2k Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<fieldset>
Expand Down
2 changes: 1 addition & 1 deletion data/hrtowatts.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
background-color: #03245c;
}
</style>
<title>SmartSpin2K Web Server</title>
<title>SmartSpin2k Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>

Expand Down
2 changes: 1 addition & 1 deletion data/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
}
</style>

<title>SmartSpin2K Web Server</title>
<title>SmartSpin2k Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<fieldset><legend><a href="http://github.com/doudar/SmartSpin2k">http://github.com/doudar/SmartSpin2k</a></legend>
Expand Down
2 changes: 1 addition & 1 deletion data/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
background-color: #03245c;
}
</style>
<title>SmartSpin2K Web Server</title>
<title>SmartSpin2k Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>

Expand Down
2 changes: 1 addition & 1 deletion data/shift.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
background-color: #03245c;
}
</style>
<title>SmartSpin2K Web Server</title>
<title>SmartSpin2k Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>

Expand Down
2 changes: 1 addition & 1 deletion data/status.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
background-color: #03245c;
}
</style>
<title>SmartSpin2K Web Server</title>
<title>SmartSpin2k Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>

Expand Down
2 changes: 1 addition & 1 deletion data/streamfit.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
<title>SmartSpin2K Web Server</title>
<title>SmartSpin2k Web Server</title>
</head>

<body>
Expand Down
35 changes: 21 additions & 14 deletions include/BLE_Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,14 @@ class MyCallbacks : public NimBLECharacteristicCallbacks {

class ss2kCustomCharacteristicCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *);
void onSubscribe(NimBLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc, uint16_t subValue);
};

class ss2kCustomCharacteristic {
public:
//Used internally for notify and onWrite Callback.
// Used internally for notify and onWrite Callback.
static void process(std::string rxValue);
//Custom Characteristic value that needs to be notified
// Custom Characteristic value that needs to be notified
static void notify(char _item);
// Notify any changed value in userConfig
static void parseNemit();
Expand All @@ -73,6 +74,7 @@ class SpinBLEServer {
bool Heartrate : 1;
bool CyclingPowerMeasurement : 1;
bool IndoorBikeData : 1;
bool CyclingSpeedCadence : 1;
} clientSubscribed;
NimBLEServer *pServer = nullptr;
void setClientSubscribed(NimBLEUUID pUUID, bool subscribe);
Expand All @@ -87,8 +89,10 @@ void startBLEServer();
bool spinDown();
void logCharacteristic(char *buffer, const size_t bufferCapacity, const byte *data, const size_t dataLength, const NimBLEUUID serviceUUID, const NimBLEUUID charUUID,
const char *format, ...);
void updateWheelAndCrankRev();
void updateIndoorBikeDataChar();
void updateCyclingPowerMeasurementChar();
void updateCyclingSpeedCadenceChar();
void calculateInstPwrFromHR();
void updateHeartRateMeasurementChar();
int connectedClientCount();
Expand Down Expand Up @@ -158,18 +162,21 @@ class SpinBLEClient {
private:
public: // Not all of these need to be public. This should be cleaned up
// later.
boolean connectedPM = false;
boolean connectedHRM = false;
boolean connectedCD = false;
boolean connectedCT = false;
boolean connectedRemote = false;
boolean doScan = false;
bool dontBlockScan = true;
int intentionalDisconnect = 0;
int noReadingIn = 0;
int cscCumulativeCrankRev = 0;
int cscLastCrankEvtTime = 0;
int reconnectTries = MAX_RECONNECT_TRIES;
boolean connectedPM = false;
boolean connectedHRM = false;
boolean connectedCD = false;
boolean connectedCT = false;
boolean connectedSpeed = false;
boolean connectedRemote = false;
boolean doScan = false;
bool dontBlockScan = true;
int intentionalDisconnect = 0;
int noReadingIn = 0;
long int cscCumulativeCrankRev = 0;
double cscLastCrankEvtTime = 0.0;
long int cscCumulativeWheelRev = 0;
double cscLastWheelEvtTime = 0.0;
int reconnectTries = MAX_RECONNECT_TRIES;

BLERemoteCharacteristic *pRemoteCharacteristic = nullptr;

Expand Down
139 changes: 139 additions & 0 deletions include/BLE_Definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,29 @@

#pragma once

struct FitnessMachineIndoorBikeDataFlags {
enum Types : uint16_t {
MoreDataBit = 1U << 0,
AverageSpeedPresent = 1U << 1,
InstantaneousCadencePresent = 1U << 2,
AverageCadencePresent = 1U << 3,
TotalDistancePresent = 1U << 4,
ResistanceLevelPresent = 1U << 5,
InstantaneousPowerPresent = 1U << 6,
AveragePowerPresent = 1U << 7,
ExpendedEnergyPresent = 1U << 8,
HeartRatePresent = 1U << 9,
MetabolicEquivalentPresent = 1U << 10,
ElapsedTimePresent = 1U << 11,
RemainingTimePresent = 1U << 12

};
};

inline FitnessMachineIndoorBikeDataFlags::Types operator|(FitnessMachineIndoorBikeDataFlags::Types a, FitnessMachineIndoorBikeDataFlags::Types b) {
return static_cast<FitnessMachineIndoorBikeDataFlags::Types>(static_cast<int>(a) | static_cast<int>(b));
}

// https://www.bluetooth.com/specifications/specs/fitness-machine-service-1-0/
// Table 4.13: Training Status Field Definition
struct FitnessMachineTrainingStatus {
Expand Down Expand Up @@ -166,4 +189,120 @@ struct FitnessMachineFeature {
struct FtmsStatus {
uint8_t data[8];
int length;
};

class CyclingPowerMeasurement {
public:
// Flags definition as per specification
struct Flags {
uint16_t pedalPowerBalancePresent : 1;
uint16_t pedalPowerBalanceReference : 1;
uint16_t accumulatedTorquePresent : 1;
uint16_t accumulatedTorqueSource : 1;
uint16_t wheelRevolutionDataPresent : 1;
uint16_t crankRevolutionDataPresent : 1;
uint16_t extremeForceMagnitudesPresent : 1;
uint16_t extremeTorqueMagnitudesPresent : 1;
uint16_t extremeAnglesPresent : 1;
uint16_t topDeadSpotAnglePresent : 1;
uint16_t bottomDeadSpotAnglePresent : 1;
uint16_t accumulatedEnergyPresent : 1;
uint16_t offsetCompensationIndicator : 1;
uint16_t reserved : 3;
} flags;

// Assuming these are the possible data fields based on flags
int16_t instantaneousPower; // Mandatory
// Other fields as optional, based on the flags
uint8_t pedalPowerBalance; // Example optional field
uint16_t accumulatedTorque;
uint32_t cumulativeWheelRevolutions;
uint16_t lastWheelEventTime;
uint16_t cumulativeCrankRevolutions;
uint16_t lastCrankEventTime;

std::vector<uint8_t> toByteArray() {
std::vector<uint8_t> data;
// Add flags to data vector
data.push_back(static_cast<uint8_t>(*(reinterpret_cast<uint16_t*>(&flags)) & 0xFF));
data.push_back(static_cast<uint8_t>((*(reinterpret_cast<uint16_t*>(&flags)) >> 8) & 0xFF));

// Add Instantaneous Power
data.push_back(static_cast<uint8_t>(instantaneousPower & 0xFF));
data.push_back(static_cast<uint8_t>((instantaneousPower >> 8) & 0xFF));

// Conditional fields based on flags
if (flags.crankRevolutionDataPresent) {
// Add crank revolution data if present
data.push_back(static_cast<uint8_t>(cumulativeCrankRevolutions & 0xFF));
data.push_back(static_cast<uint8_t>((cumulativeCrankRevolutions >> 8) & 0xFF));

data.push_back(static_cast<uint8_t>(lastCrankEventTime & 0xFF));
data.push_back(static_cast<uint8_t>((lastCrankEventTime >> 8) & 0xFF));
}
// Conditional fields based on flags
if (flags.wheelRevolutionDataPresent) {
// Add wheel revolution data if present
data.push_back(static_cast<uint8_t>(cumulativeWheelRevolutions & 0xFF));
data.push_back(static_cast<uint8_t>((cumulativeWheelRevolutions >> 8) & 0xFF));
data.push_back(static_cast<uint8_t>((cumulativeWheelRevolutions >> 16) & 0xFF));
data.push_back(static_cast<uint8_t>((cumulativeWheelRevolutions >> 24) & 0xFF));

data.push_back(static_cast<uint8_t>(lastWheelEventTime & 0xFF));
data.push_back(static_cast<uint8_t>((lastWheelEventTime >> 8) & 0xFF));
}

return data;
}
};

class CscMeasurement {
public:
// Flags definition as per specification
struct Flags {
uint8_t wheelRevolutionDataPresent : 1;
uint8_t crankRevolutionDataPresent : 1;
uint8_t reserved : 6;
} flags;

// Data fields
uint32_t cumulativeWheelRevolutions;
uint16_t lastWheelEventTime; // Resolution of 1/1024 seconds
uint16_t cumulativeCrankRevolutions;
uint16_t lastCrankEventTime; // Resolution of 1/1024 seconds

CscMeasurement() : cumulativeWheelRevolutions(0), lastWheelEventTime(0), cumulativeCrankRevolutions(0), lastCrankEventTime(0) {
// Clear all flags initially
*(reinterpret_cast<uint8_t*>(&flags)) = 0;
}

std::vector<uint8_t> toByteArray() {
std::vector<uint8_t> data;

// Add flags to data vector
data.push_back(*(reinterpret_cast<uint8_t*>(&flags)));

// Conditional fields based on flags
if (flags.wheelRevolutionDataPresent) {
// Add wheel revolution data if present
for (int i = 0; i < 4; ++i) {
data.push_back((cumulativeWheelRevolutions >> (i * 8)) & 0xFF);
}
for (int i = 0; i < 2; ++i) {
data.push_back((lastWheelEventTime >> (i * 8)) & 0xFF);
}
}

if (flags.crankRevolutionDataPresent) {
// Add crank revolution data if present
for (int i = 0; i < 2; ++i) {
data.push_back((cumulativeCrankRevolutions >> (i * 8)) & 0xFF);
}
for (int i = 0; i < 2; ++i) {
data.push_back((lastCrankEventTime >> (i * 8)) & 0xFF);
}
}

return data;
}
};

0 comments on commit aa25525

Please sign in to comment.