Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug/sim temp #1072

Closed
wants to merge 11 commits into from
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public void StartUpdating(TimeSpan? updateInterval = null)
{
UpdateInterval = updateInterval ?? TimeSpan.FromSeconds(1);
IsSampling = true;
_reportTimer = new Timer(ReportTimerProc, null, updateInterval!.Value, updateInterval.Value);
_reportTimer = new Timer(ReportTimerProc, null, UpdateInterval, UpdateInterval);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Meadow.Foundation.Telematics.OBD2;

public partial class Dtc
{
private string GetReadableBodyErrorCode(int code)
{
return code switch
{
// TODO
_ => $"B{code:N4}"
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Meadow.Foundation.Telematics.OBD2;

public partial class Dtc
{
private string GetReadableChassisErrorCode(int code)
{
return code switch
{
// TODO
_ => $"C{code:N4}"
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Meadow.Foundation.Telematics.OBD2;

public partial class Dtc
{
private string GetReadableNetworkErrorCode(int code)
{
return code switch
{
// TODO
_ => $"U{code:N4}"
};
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;

namespace Meadow.Foundation.Telematics.OBD2;

public partial class Dtc
{
private byte[] _data = new byte[2];

public DtcCategory Category
{
get => (DtcCategory)(_data[0] & 0xC0);
set => _data[0] = (byte)((_data[0] & 0x3f) | (byte)value);
}

public int Code
{
get
{
return ((_data[0] & 0x03) * 100)
+ (((_data[1] & 0xf0) >> 4) * 10)
+ (_data[1] & 0x0f);
}
set
{
if (value > 0x3ff) throw new ArgumentException("Max code value is 3ff");
var digit = value / 100;
_data[0] = (byte)(digit & 0x03);
value = value % 100;
digit = value / 10;
_data[1] = (byte)(digit << 4);
value = value % 10;
_data[1] |= (byte)value;
}
}

public Dtc(byte[] data)
{
if (data.Length != 2) throw new ArgumentException("DTC codes must be exactly 2 bytes");
_data = data;
}

public override string ToString()
{
return $"{Category}{Code:N4}";
}

public string ToReadableErrorCode()
{
switch (Category)
{
case DtcCategory.P:
return GetReadablePowertrainErrorCode(Code);
case DtcCategory.C:
return GetReadableChassisErrorCode(Code);
case DtcCategory.B:
return GetReadableBodyErrorCode(Code);
case DtcCategory.U:
return GetReadableNetworkErrorCode(Code);
}

throw new NotImplementedException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Meadow.Foundation.Telematics.OBD2;

public enum DtcCategory : byte
{
/// <summary>
/// Powertain
/// </summary>
P = 0b0000_0000,
/// <summary>
/// Chassis
/// </summary>
C = 0b0100_0000,
/// <summary>
/// Body
/// </summary>
B = 0b1000_0000,
/// <summary>
/// Network
/// </summary>
U = 0b1100_0000,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
using Meadow.Hardware;
using System;

namespace Meadow.Foundation.Telematics.OBD2;

public class Ecu
{
private readonly ICanBus _bus;

public PidRequestHandlerCollection PidRequestHandlers { get; } = new();

public short Address { get; }

public Ecu(ICanBus bus, short ecuAddress = 0x7e8)
{
_bus = bus;
Address = ecuAddress;
bus.FrameReceived += OnCanFrameReceived;
}

private void HandleSupportedPidsRequest(SaeStandardQueryFrame queryFrame)
{
Resolver.Log.Info($"handling {queryFrame.Pid} response frame");

switch (queryFrame.Pid)
{
case Pid.SupportedPids_01_20:
case Pid.SupportedPids_21_40:
uint supportBits = 0;

for (var i = 0; i < 32; i++)
{
if (PidRequestHandlers.GetHandler((ushort)(i + (int)queryFrame.Pid + 1)) != null)
{
supportBits |= (uint)(i << (31 - i));
}
}
Resolver.Log.Info($"support: {supportBits:x8}");
var payload = new byte[4];
payload[0] = (byte)((supportBits & 0xff000000) >> 24);
payload[1] = (byte)((supportBits & 0x00ff0000) >> 16);
payload[2] = (byte)((supportBits & 0x0000ff00) >> 8);
payload[3] = (byte)((supportBits & 0x000000ff) >> 0);

var supportedFrame = new Obd2ResponseFrame(queryFrame.Service, queryFrame.Pid, payload, Address);

Resolver.Log.Info($"Sending {queryFrame.Pid} response frame");

_bus.WriteFrame(supportedFrame);

break;
}
}

private void HandleRequestCurrentData(SaeStandardQueryFrame queryFrame)
{
switch (queryFrame.Pid)
{
case Pid.SupportedPids_01_20:
case Pid.SupportedPids_21_40:
HandleSupportedPidsRequest(queryFrame);
return;
}

var handler = PidRequestHandlers.GetHandler(queryFrame.Pid);
var pid = (ushort)queryFrame.Pid;
var service = queryFrame.Service;

if (handler != null)
{
var data = handler.Invoke(pid);
// data cannot be > 4 bytes long
if (data.Length > 4)
{
throw new Exception("Handler data exceeded 4 bytes in length");
}

var payload = new byte[8];
payload[0] = (byte)(2 + data.Length);
payload[1] = (byte)(service + 0x40);
payload[2] = (byte)pid;
data.CopyTo(payload, 3);

// create a response frame
var response = new Obd2ResponseFrame(queryFrame.Service, queryFrame.Pid, payload, Address);
_bus.WriteFrame(response);
}
}

private void OnCanFrameReceived(object sender, ICanFrame e)
{
if (e is StandardDataFrame sdf)
{
Obd2Frame? obdFrame = null;

try
{
obdFrame = Obd2Frame.FromCanFrame(sdf);
}
catch (Exception ex)
{
Resolver.Log.Warn($"Unable to convert CAN frame to OBD2: {ex.Message}");
return;
}

PidRequestHandler? handler = null;
ushort pid = 0;
Service service = 0;

if (obdFrame is SaeStandardQueryFrame sqf)
{
switch (sqf.Service)
{
case Service.Current:
HandleRequestCurrentData(sqf);
break;
case Service.FreezeFrame:
Resolver.Log.Info("Request for Freeze frame");
break;
case Service.StoredDtcs:
Resolver.Log.Info("Request for stored DTCs");
break;
case Service.PendingDtcs:
Resolver.Log.Info("Request for pending DTCs");
break;
case Service.ControlOperations:
Resolver.Log.Info("Request for control ops");
break;
case Service.PermanentDtcs:
Resolver.Log.Info("Request for permanent DTCs");
break;
case Service.TestResultsOther:
Resolver.Log.Info("Request for test results");
break;
case Service.VehicleInfo:
Resolver.Log.Info("Request for vehicle info");
break;
}

Resolver.Log.Warn($"Standard OBD2 Frame Received for Service:PID {sqf.Service}:{sqf.Pid}");
}
else if (obdFrame is VehicleSpecificQueryFrame vqf)
{
handler = PidRequestHandlers.GetHandler(vqf.Pid);
pid = vqf.Pid;
service = vqf.Service;
}
else
{
Resolver.Log.Warn($"OBD2 Frame Received for Service:PID {service}:{pid}");
}
}
}
}
Loading
Loading