Merge develop into master #1

Merged
SemvdH merged 40 commits from develop into master 2020-09-11 13:34:57 +00:00
6 changed files with 565 additions and 26 deletions

182
ProftaakRH/BLEHandler.cs Normal file
View File

@@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avans.TI.BLE;
using System.Threading;
using System.Security.Cryptography;
namespace Hardware
{
/// <summary>
/// <c>BLEHandler</c> class that handles connection and traffic to and from the bike
/// </summary>
class BLEHandler
{
IDataConverter dataConverter;
private BLE bleBike;
private BLE bleHeart;
public bool Running { get; set; }
/// <summary>
/// Makes a new BLEHandler object
/// </summary>
/// <param name="dataConverter">the dataconverter object</param>
public BLEHandler(IDataConverter dataConverter)
{
this.dataConverter = dataConverter;
bool running = false;
}
/// <summary>
/// Checks for available devices to connect to, and if one is found, it connects to it
/// </summary>
public void Connect()
{
BLE bleBike = new BLE();
Thread.Sleep(1000); // We need some time to list available devices
// List available devices
List<String> bleBikeList = bleBike.ListDevices();
Console.WriteLine("Devices found: ");
foreach (var name in bleBikeList)
{
Console.WriteLine(name);
if (name.Contains("Avans Bike"))
{
Console.WriteLine("connecting to {0}", name);
Connect(name);
break;
}
}
}
/// <summary>
/// Connects to the device with the given name
/// </summary>
/// <param name="deviceName">The name of the device to connect to</param>
public async void Connect(string deviceName)
{
int errorCode = 0;
bleBike = new BLE();
bleHeart = new BLE();
errorCode = errorCode = await bleBike.OpenDevice(deviceName);
if (errorCode > 0)
{
disposeBLE();
return;
}
// Set service
errorCode = await bleBike.SetService("6e40fec1-b5a3-f393-e0a9-e50e24dcca9e");
if (errorCode > 0)
{
disposeBLE();
return;
}
// Subscribe
bleBike.SubscriptionValueChanged += BleBike_SubscriptionValueChanged;
errorCode = await bleBike.SubscribeToCharacteristic("6e40fec2-b5a3-f393-e0a9-e50e24dcca9e");
if (errorCode > 0)
{
disposeBLE();
return;
}
// Heart rate
errorCode = await bleHeart.OpenDevice(deviceName);
if (errorCode > 0)
{
disposeBLE();
return;
}
errorCode = await bleHeart.SetService("HeartRate");
if (errorCode > 0)
{
disposeBLE();
return;
}
bleHeart.SubscriptionValueChanged += BleBike_SubscriptionValueChanged;
errorCode = await bleHeart.SubscribeToCharacteristic("HeartRateMeasurement");
if (errorCode > 0)
{
disposeBLE();
return;
}
Console.WriteLine("connected to BLE");
this.Running = true;
}
/// <summary>
/// Callback for when the subscription value of the ble bike has changed
/// </summary>
/// <param name="sender"> the sender object</param>
/// <param name="e">the value changed event</param>
private void BleBike_SubscriptionValueChanged(object sender, BLESubscriptionValueChangedEventArgs e)
{
if (e.ServiceName == "6e40fec2-b5a3-f393-e0a9-e50e24dcca9e")
{
byte[] payload = new byte[8];
Array.Copy(e.Data, 4, payload, 0, 8);
this.dataConverter.Bike(payload);
}
else if (e.ServiceName == "00002a37-0000-1000-8000-00805f9b34fb")
{
this.dataConverter.BPM(e.Data);
}
else
{
Console.WriteLine("received data from unknown source {0}", e.ServiceName);
}
}
/// <summary>
/// Disposes of the current BLE object, if it exists.
/// </summary>
private void disposeBLE()
{
this.bleBike?.Dispose();
this.bleHeart?.Dispose();
this.Running = false;
}
/// <summary>
/// Method <c>setResistance</c> converts the input percentage to bytes and sends it to the bike.
/// </summary>
/// <param name="percentage">The precentage of resistance to set</param>
public void setResistance(float percentage)
{
byte[] antMessage = new byte[13];
antMessage[0] = 0x4A;
antMessage[1] = 0x09;
antMessage[2] = 0x4E;
antMessage[3] = 0x05;
antMessage[4] = 0x30;
for (int i = 5; i < 11; i++)
{
antMessage[i] = 0xFF;
}
antMessage[11] = (byte)Math.Max(Math.Min(Math.Round(percentage / 0.5), 255), 0);
byte checksum = 0;
for (int i = 0; i < 12; i++)
{
checksum ^= antMessage[i];
}
antMessage[12] = checksum;//reminder that i am dumb :P
bleBike.WriteCharacteristic("6e40fec3-b5a3-f393-e0a9-e50e24dcca9e", antMessage);
}
}
}

147
ProftaakRH/BikeSimulator.cs Normal file
View File

@@ -0,0 +1,147 @@
using LibNoise.Primitive;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Hardware.Simulators
{
class BikeSimulator : IHandler
{
IDataConverter dataConverter;
private int elapsedTime = 0;
private int eventCounter = 0;
private double distanceTraveled = 0;
private int equipmentType = 25;
private double speed = 0;
private int BPM = 0;
private int cadence = 0;
private double resistance = 0;
private double power;
private double accPower;
byte[] speedArray;
byte[] powerArray;
byte[] accPowerArray;
public BikeSimulator(IDataConverter dataConverter)
{
this.dataConverter = dataConverter;
}
public void StartSimulation()
{
//Example BLE Message
//4A-09-4E-05-19-16-00-FF-28-00-00-20-F0
float x = 0.0f;
//Perlin for Random values
ImprovedPerlin improvedPerlin = new ImprovedPerlin(0,LibNoise.NoiseQuality.Best);
while (true)
{
CalculateVariables(improvedPerlin.GetValue(x)+1);
//Simulate sending data
dataConverter.Bike(GenerateBike0x19());
dataConverter.Bike(GenerateBike0x10());
dataConverter.BPM(GenerateHeart());
Thread.Sleep(1000);
x += 0.1f;
eventCounter++;
elapsedTime++;
}
}
//Generate an ANT message for page 0x19
private byte[] GenerateBike0x19()
{
byte statByte = (byte)(powerArray[1] >> 4);
byte[] bikeByte = { 0x19, Convert.ToByte(eventCounter%256), Convert.ToByte(cadence%254), accPowerArray[0], accPowerArray[1], powerArray[0], statByte, 0x20 };
return bikeByte;
}
//Generate an ANT message for page 0x10
private byte[] GenerateBike0x10()
{
byte[] bikeByte = { 0x10, Convert.ToByte(equipmentType), Convert.ToByte(elapsedTime*4%64), Convert.ToByte(distanceTraveled), speedArray[0], speedArray[1], Convert.ToByte(BPM), 0xFF };
return bikeByte;
}
//Generate an ANT message for BPM
private byte[] GenerateHeart()
{
byte[] hartByte = { 0x00, Convert.ToByte(BPM)};
return hartByte;
}
//Generate an ANT message for resistance
public byte[] GenerateResistance(float percentage)
{
byte[] antMessage = new byte[13];
antMessage[0] = 0x4A;
antMessage[1] = 0x09;
antMessage[2] = 0x4E;
antMessage[3] = 0x05;
antMessage[4] = 0x30;
for (int i = 5; i < 11; i++)
{
antMessage[i] = 0xFF;
}
antMessage[11] = (byte)Math.Max(Math.Min(Math.Round(percentage / 0.5), 255), 0);
//antMessage[11] = 50; //hardcoded for testing
byte checksum = 0;
for (int i = 0; i < 12; i++)
{
checksum ^= antMessage[i];
}
antMessage[12] = checksum;//reminder that i am dumb :P
return antMessage;
}
//Calculates the needed variables
//Input perlin value
private void CalculateVariables(float perlin)
{
this.speed = perlin * 5 / 0.01 ;
short sped = (short)speed;
speedArray = BitConverter.GetBytes(sped);
this.distanceTraveled = (distanceTraveled+(speed*0.01)) % 256;
this.BPM = (int) (perlin * 80);
this.cadence = (int)speed/6;
this.power = ((1 + resistance) * speed)/14 % 4094;
this.accPower = (this.accPower + this.power) % 65536;
// TO DO power to power LSB & MSN
powerArray = BitConverter.GetBytes((short)this.power);
accPowerArray = BitConverter.GetBytes((short)accPower);
}
//Set resistance in simulated bike
public void setResistance(byte[] bytes)
{
//TODO check if message is correct
if(bytes.Length == 13)
{
this.resistance = Convert.ToDouble(bytes[11])/2;
}
}
}
//Interface for receiving a message on the simulated bike
interface IHandler
{
void setResistance(byte[] bytes);
}
}

115
ProftaakRH/DataConverter.cs Normal file
View File

@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Hardware
{
/// <summary>
/// DataConverter class that handles all conversion of received data from the BLE bike.
/// </summary>
class DataConverter : IDataConverter
{
/// <summary>
/// Receives, parses and displays any incoming data from the bike.
/// </summary>
/// <param name="bytes">the array of bytes that was received</param>
public void Bike(byte[] bytes)
{
if (bytes == null)
{
Console.WriteLine("HEY, didn't get bytes!\n-Bike DataConverter");
}
else
if (bytes.Length == 8)
{
switch (bytes[0])
{
case 0x10:
if (bytes[1] != 25)
{
Console.WriteLine("WTF this isn't a bike");
}
Console.WriteLine($"Time since start is: {bytes[2] / 4}s (Rollover every 4s)");
Console.WriteLine($"Distance Traveled is : {bytes[3]}m (Rollover every 256m)");
int input = bytes[4] | (bytes[5] << 8);
Console.WriteLine($"Speed is : {input * 0.01}m/s (Range 65.534m/4)");
if (bytes[6] != 0xFF)
{
Console.WriteLine("Heart rate byte: {0}", Convert.ToString(bytes[6],2));
}
break;
case 0x19:
Console.WriteLine($"Event count: {bytes[1]} (Rollover 256)");
if (bytes[2] != 0xFF)
{
Console.WriteLine($"Instantaneous cadence: {bytes[2]} RPM (Range 0-254)");
}
int accumPower = bytes[3] | (bytes[4] << 8);
Console.WriteLine($"Accumulated power: {accumPower} watt (Rollover 65536)");
int instantPower = (bytes[5]) | (bytes[6] & 0b00001111)<<8;
if (instantPower != 0xFFF)
Console.WriteLine($"Instant power: {instantPower} watt (Range 0-4094)");
int trainerStatus = bytes[6] & 0b11110000; // bit 4-7
int flags = bytes[7] >> 4;
int FEState = bytes[7] & 0b00001111;
break;
default:
Console.WriteLine("HEY, I never heard of data page {0}\n-DataConverter", bytes[0]);
break;
}
}
else
{
Console.WriteLine("HEY, I didn't get 8 bytes!\n-DataConverter");
}
Console.WriteLine();
}
/// <summary>
/// Gets and prints the BPM from the message received from the bike.
/// </summary>
/// <param name="bytes">The array with bytes that was received</param>
public void BPM(byte[] bytes)
{
if (bytes == null)
{
Console.WriteLine("HEY, didn't get bytes!\n-BPM DataConverter");
return;
}
if (bytes[0] != 0)
{
Console.WriteLine("HOLY SHIT i got flags!!! {0} now i can't do anything\n-BPM DataConverter", bytes[0]);
}
else if (bytes.Length != 2)
{
Console.WriteLine("bytes length is: {0}", bytes.Length);
}
else
{
Console.WriteLine("BPM: {0}", bytes[1]);
}
Console.WriteLine();
}
}
/// <summary>
/// Dataconverter interface for handling data received from the bike
/// </summary>
interface IDataConverter
{
void BPM(byte[] bytes);
void Bike(byte[] bytes);
}
}

View File

@@ -55,57 +55,104 @@ namespace FietsDemo
private static void BleBike_SubscriptionValueChanged(object sender, BLESubscriptionValueChangedEventArgs e)
{
Console.WriteLine("Received from {0}: {1}, {2}", e.ServiceName,
BitConverter.ToString(e.Data).Replace("-", " "),
Encoding.UTF8.GetString(e.Data));
//Console.WriteLine("Received from {0}: {1}, {2}", e.ServiceName,
// BitConverter.ToString(e.Data).Replace("-", " "),
// Encoding.UTF8.GetString(e.Data));
string[] bytes = BitConverter.ToString(e.Data).Split('-');
string[] ANT = new string[5];
if (e.ServiceName == "6e40fec2-b5a3-f393-e0a9-e50e24dcca9e")
{
Console.WriteLine("SYNC : " + bytes[0]);
ANT[0] = bytes[0];
Console.WriteLine("LENGTH : " + bytes[1]);
//Console.WriteLine("SYNC : " + bytes[0]);
//ANT[0] = bytes[0];
//Console.WriteLine("LENGTH : " + bytes[1]);
int length = Convert.ToInt32(bytes[1], 16);
ANT[1] = length.ToString();
Console.WriteLine("MSG ID : " + bytes[2]);
ANT[2] = bytes[2];
string msg = string.Empty;
for (int i = 3; i < 3 + length; i++)
{
msg += bytes[i];
}
ANT[3] = msg;
//ANT[1] = length.ToString();
//Console.WriteLine("MSG ID : " + bytes[2]);
//ANT[2] = bytes[2];
//string msg = string.Empty;
//for (int i = 3; i < 3 + length; i++)
//{
// msg += bytes[i];
//}
//ANT[3] = msg;
byte[] message = new byte[length];
byte[] message = new byte[length - 1];
Array.Copy(e.Data, 3, message, 0, length);
Array.Copy(e.Data, 4, message, 0, length - 1);
DoCrazyShitWithMsg(message);
Console.WriteLine("MSG : " + msg);
string checksum = bytes[3 + length];
ANT[4] = checksum;
Console.WriteLine("CHECKSUM : " + checksum);
//Console.WriteLine("MSG : " + msg);
//string checksum = bytes[3 + length];
//ANT[4] = checksum;
//Console.WriteLine("CHECKSUM : " + checksum);
//byte calcChecksum = 0;
//for (int i = 0; i < e.Data.Length - 1; i++)
//{
// calcChecksum ^= e.Data[i];
//}
//Console.WriteLine("Calculated checksum : " + Convert.ToString(calcChecksum,16).ToUpper());
Console.WriteLine(BitConverter.ToString(e.Data));
} else
//Console.WriteLine(BitConverter.ToString(e.Data));
}
else
{
Console.WriteLine("BPM: " + Convert.ToInt32(bytes[1], 16));
//Console.WriteLine("BPM: " + Convert.ToInt32(bytes[1], 16));
}
Console.WriteLine();
}
private static void DoCrazyShitWithMsg(byte[] bytes)
{
Console.WriteLine("doing crazy shit with {0}", bytes);
String[] hexvalues = BitConverter.ToString(bytes).Split('-');
for (int i = 0; i < hexvalues.Length; i++)
{
Console.WriteLine("Byte {0}: {1}" , i, hexvalues[i]);
Console.WriteLine("Byte {0}: {1}", i, hexvalues[i]);
}
switch (bytes[0])
{
case 0x10:
Console.WriteLine();
Console.WriteLine("Data Page Number\t\t: " + hexvalues[0]);
Console.WriteLine("Equipment Type Bit Field\t: " + hexvalues[1]);
Console.WriteLine("Elapsed Time\t\t\t: " + hexvalues[2]);
Console.WriteLine("Distance Traveled\t\t: " + hexvalues[3]);
Console.WriteLine("Speed LSB\t\t\t: " + hexvalues[4]);
Console.WriteLine("Speed LSB\t\t\t: " + hexvalues[5]);
Console.WriteLine("Heart Rate\t\t\t: " + hexvalues[6]);
Console.WriteLine("Capabilities FE State Bit Field\t: " + Convert.ToString(bytes[7], 2));
break;
case 0x19:
Console.WriteLine();
Console.WriteLine("Data Page Number\t\t: " + hexvalues[0]);
Console.WriteLine("Update Event Count\t\t: " + hexvalues[1]);
Console.WriteLine("Instantaneous Cadence\t\t: " + hexvalues[2]);
Console.WriteLine("Accumulated Power LSB\t\t: " + hexvalues[3]);
Console.WriteLine("Accumulated Power MSB\t\t: " + hexvalues[4]);
Console.WriteLine("Instant Power LSB\t\t: " + hexvalues[5]);
Console.WriteLine("Instant Power MSN and status\t: " + Convert.ToString(bytes[6], 2));
Console.WriteLine("Flags and FE state BitField\t: " + Convert.ToString(bytes[7], 2));
break;
default:
Console.WriteLine("data page number not recognized");
break;
}
//Console.WriteLine("Speed might be {0} m/s", bytes[3] * 0.093294);
}
}
}

44
ProftaakRH/Main.cs Normal file
View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Text;
using Hardware;
using Hardware.Simulators;
namespace ProftaakRH
{
class Program
{
static void Main(string[] agrs)
{
IDataConverter dataConverter = new DataConverter();
BLEHandler bLEHandler = new BLEHandler(dataConverter);
//BikeSimulator bikeSimulator = new BikeSimulator(dataConverter);
//bikeSimulator.setResistance(bikeSimulator.GenerateResistance(1f));
//bikeSimulator.StartSimulation();
bool running = true;
while (running)
{
string input = Console.ReadLine();
input.ToLower();
input.Trim();
if(input == "quit")
{
running = false;
break;
}
try
{
int resistance = Int32.Parse(input);
bLEHandler.setResistance(resistance);
}
catch
{
//do nothing
}
}
}
}
}

View File

@@ -4,6 +4,10 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LibNoise" Version="0.2.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="BLELibrary">
<HintPath>.\dll\BLELibrary.dll</HintPath>