diff --git a/ProftaakRH/BLEHandler.cs b/ProftaakRH/BLEHandler.cs
new file mode 100644
index 0000000..da2d90a
--- /dev/null
+++ b/ProftaakRH/BLEHandler.cs
@@ -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
+{
+ ///
+ /// BLEHandler class that handles connection and traffic to and from the bike
+ ///
+ class BLEHandler
+ {
+ IDataConverter dataConverter;
+ private BLE bleBike;
+ private BLE bleHeart;
+ public bool Running { get; set; }
+
+ ///
+ /// Makes a new BLEHandler object
+ ///
+ /// the dataconverter object
+ public BLEHandler(IDataConverter dataConverter)
+ {
+ this.dataConverter = dataConverter;
+ bool running = false;
+ }
+
+ ///
+ /// Checks for available devices to connect to, and if one is found, it connects to it
+ ///
+ public void Connect()
+ {
+ BLE bleBike = new BLE();
+ Thread.Sleep(1000); // We need some time to list available devices
+
+ // List available devices
+ List 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;
+
+ }
+ }
+ }
+
+ ///
+ /// Connects to the device with the given name
+ ///
+ /// The name of the device to connect to
+ 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;
+
+ }
+
+ ///
+ /// Callback for when the subscription value of the ble bike has changed
+ ///
+ /// the sender object
+ /// the value changed event
+ 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);
+ }
+
+ }
+
+ ///
+ /// Disposes of the current BLE object, if it exists.
+ ///
+ private void disposeBLE()
+ {
+ this.bleBike?.Dispose();
+ this.bleHeart?.Dispose();
+ this.Running = false;
+ }
+
+ ///
+ /// Method setResistance converts the input percentage to bytes and sends it to the bike.
+ ///
+ /// The precentage of resistance to set
+ 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);
+ }
+ }
+}
diff --git a/ProftaakRH/BikeSimulator.cs b/ProftaakRH/BikeSimulator.cs
new file mode 100644
index 0000000..5664f9e
--- /dev/null
+++ b/ProftaakRH/BikeSimulator.cs
@@ -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);
+
+ }
+}
diff --git a/ProftaakRH/DataConverter.cs b/ProftaakRH/DataConverter.cs
new file mode 100644
index 0000000..7c93ff2
--- /dev/null
+++ b/ProftaakRH/DataConverter.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Hardware
+{
+ ///
+ /// DataConverter class that handles all conversion of received data from the BLE bike.
+ ///
+ class DataConverter : IDataConverter
+ {
+ ///
+ /// Receives, parses and displays any incoming data from the bike.
+ ///
+ /// the array of bytes that was received
+ 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();
+ }
+
+ ///
+ /// Gets and prints the BPM from the message received from the bike.
+ ///
+ /// The array with bytes that was received
+ 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();
+ }
+ }
+
+ ///
+ /// Dataconverter interface for handling data received from the bike
+ ///
+ interface IDataConverter
+ {
+ void BPM(byte[] bytes);
+ void Bike(byte[] bytes);
+ }
+}
diff --git a/ProftaakRH/FietsDemo.cs b/ProftaakRH/FietsDemo.cs
index 4edbdc1..120292e 100644
--- a/ProftaakRH/FietsDemo.cs
+++ b/ProftaakRH/FietsDemo.cs
@@ -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);
+
+
+
}
}
}
\ No newline at end of file
diff --git a/ProftaakRH/Main.cs b/ProftaakRH/Main.cs
new file mode 100644
index 0000000..0938089
--- /dev/null
+++ b/ProftaakRH/Main.cs
@@ -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
+ }
+ }
+ }
+ }
+}
diff --git a/ProftaakRH/ProftaakRH.csproj b/ProftaakRH/ProftaakRH.csproj
index ca49088..8f98a2c 100644
--- a/ProftaakRH/ProftaakRH.csproj
+++ b/ProftaakRH/ProftaakRH.csproj
@@ -4,6 +4,10 @@
Exe
netcoreapp3.1
+
+
+
+
.\dll\BLELibrary.dll