Last active
April 10, 2024 06:54
-
-
Save lostmsu/1b0d4a33e5ca2418c2b52797eb720ec7 to your computer and use it in GitHub Desktop.
Control LifeSpan treadmill from C# (treadmill drops connection if not kept alive)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// License: MIT | |
using System; | |
using System.Collections.Concurrent; | |
using System.IO; | |
using System.Linq; | |
using System.Threading.Tasks; | |
public class LifeSpanTreadmill: IDisposable | |
{ | |
readonly Stream toDevice, fromDevice; | |
readonly byte[] responseBuffer = new byte[8]; | |
volatile bool stop; | |
readonly Task keepAliveLoop; | |
readonly ConcurrentQueue<Action> requests = new ConcurrentQueue<Action>(); | |
public LifeSpanTreadmill(Stream toDevice, Stream fromDevice) { | |
this.toDevice = toDevice ?? throw new ArgumentNullException(nameof(toDevice)); | |
this.fromDevice = fromDevice ?? throw new ArgumentNullException(nameof(fromDevice)); | |
this.keepAliveLoop = Task.Run((Action)this.KeepAliveLoop); | |
} | |
public LifeSpanTreadmill(Stream deviceStream) { | |
this.toDevice = this.fromDevice = deviceStream ?? throw new ArgumentNullException(nameof(deviceStream)); | |
this.keepAliveLoop = Task.Run((Action)this.KeepAliveLoop); | |
} | |
public void Start() => this.SendCommand(Command.Start); | |
public void Stop() => this.SendCommand(Command.Stop); | |
void KeepAliveLoop() { | |
while (!this.stop) { | |
while (this.requests.TryDequeue(out Action action)) | |
action(); | |
this.SendInitSequence(); | |
long speed = this.SendPacket((uint)Command.GetSpeed); | |
} | |
} | |
void SendInitSequence() { | |
long[] resposes = new uint[] { | |
0x20000000, | |
0xC2000000, | |
0xE9FF0000, | |
0xE400F400, | |
}.Select(this.SendPacket).ToArray(); | |
} | |
long SendCommand(Command command) => this.SendCommandAsync(command).Result; | |
Task<long> SendCommandAsync(Command command) { | |
var result = new TaskCompletionSource<long>(); | |
this.requests.Enqueue(() => { | |
try { | |
this.SendInitSequence(); | |
long response = this.SendPacket((uint)command); | |
result.TrySetResult(response); | |
} catch (Exception e) { | |
result.TrySetException(e); | |
} | |
}); | |
return result.Task; | |
} | |
long SendPacket(uint command) { | |
byte[] bytes = BitConverter.GetBytes(command); | |
if (BitConverter.IsLittleEndian) | |
Array.Reverse(bytes); | |
this.toDevice.Write(bytes, 0, 4); | |
this.toDevice.WriteByte(0); | |
this.toDevice.Flush(); | |
int read = this.fromDevice.Read(this.responseBuffer, 0, 6); | |
if (read < 6) | |
throw new NotSupportedException(); | |
long result = BitConverter.ToInt64(this.responseBuffer, 0); | |
return result; | |
} | |
static decimal ToDecimal(long response) => response & 0xFF; | |
public void Dispose() { | |
this.stop = true; | |
this.keepAliveLoop.Wait(); | |
} | |
enum Command: uint | |
{ | |
Start = 0xE1000000, | |
Stop = 0xE0000000, | |
GetSteps = 0xA1880000, | |
GetCalories = 0xA1870000, | |
GetDistance = 0xA1850000, | |
GetTime = 0xA1890000, | |
GetSpeed = 0xA1820000, | |
} | |
} |
@icnocop sadly it’s non standard. Even if it was RS-232, the pins are not where they should be. I don’t have the tools needed to decode the signal. Sticking the wrong voltage on the wrong pins could cause damage. I was lucky to not cause any harm with the probing I did do. - I’m updating my retro console to the Omni hub and giving Bluetooth a shot.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@lukescott Will a RS-232 Tester work?
https://www.amazon.com/CableMax-RS-232-link-Tester-Female/dp/B004OT995U