Skip to content

Instantly share code, notes, and snippets.

@lostmsu
Last active April 10, 2024 06:54
Show Gist options
  • Save lostmsu/1b0d4a33e5ca2418c2b52797eb720ec7 to your computer and use it in GitHub Desktop.
Save lostmsu/1b0d4a33e5ca2418c2b52797eb720ec7 to your computer and use it in GitHub Desktop.
Control LifeSpan treadmill from C# (treadmill drops connection if not kept alive)
// 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
Copy link

icnocop commented Apr 3, 2022

@lukescott
Copy link

@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