概要
elm327の研究やってみた。
練習問題やってみた。
練習問題
c#で、ELM327のエミュレータを書け。
参考にしたページ
写真
サンプルコード
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.Resources;
namespace ExpressOBD
{
public class ELM327 {
public bool IsOpen = true;
public int BytesToRead = 2;
public string res = "";
public void Open() {
}
public void Close() {
}
public string Read() {
if (BytesToRead > 1)
{
BytesToRead = 1;
return res;
}
else
{
BytesToRead = 0;
return ">";
}
}
public void Write(string a) {
//Console.WriteLine(a);
string[] b = a.Split(new string[] {
"\r"
}, StringSplitOptions.None);
res = "";
int len = b[0].Length;
if (len < 2)
{
return;
}
if (b[0].Substring(0, 2) == "AT")
{
switch (b[0])
{
case "ATWS":
case "ATE0":
case "ATL0":
case "ATH1":
case "ATSP0":
break;
case "AT@1":
res += "OBDII to RS232 Interpreter";
break;
case "ATRV":
res += "12.8V";
break;
case "ATZ":
case "ATI":
res += "ELM327 v1.4";
break;
case "ATDP":
res += "AUTO, ISO 9141-2";
break;
default:
res += " NO COMMAND\n";
break;
}
}
else
{
switch (b[0])
{
case "01 00":
res += "41 00 FF FF FC FF";
break;
case "01 01":
res += "41 01 84 07 61 00";
break;
case "01 20":
res += "41 20 FF FF FC FF";
break;
case "01 40":
res += "41 40 FF FF FC FF";
break;
case "01 60":
res += "41 60 FF FF FC FF";
break;
case "01 80":
res += "41 80 FF FF FC FF";
break;
case "01 A0":
res += "41 A0 FF FF FC FF";
break;
case "01 C0":
res += "41 C0 FF FF FC FF";
break;
case "04 ":
res += "44";
break;
default:
res += b[0] + " NO DATA\n";
break;
}
}
BytesToRead = 2;
//Console.WriteLine(res);
return;
}
}
public partial class MainForm: Form {
private ELM327 sp;
private GroupBox gbConnection;
private Button btnConnect;
private Label descRate;
private Label descPort;
private ComboBox cbBaud;
private ComboBox cbPort;
private GroupBox gbOBDActions;
private Button btnResetErrors;
private Label descReset;
private GroupBox gbStatus;
private Label label1;
private ListView lvLog;
private ColumnHeader chLog;
private ImageList ilListviewImages;
private int numberOfPromptsSinceInit = 0;
private bool ecuIsAvailable = false;
private bool serialPortsAreAvailable = true;
private bool connectingInProgress = false;
StringBuilder serialBuffer = new StringBuilder();
private IContainer components = null;
public MainForm() {
this.components = new Container();
ListViewItem listViewItem2 = new ListViewItem("ExpressOBD Initializing..", 2);
ResXResourceSet resources = new ResXResourceSet("MainForm.resx");
this.gbConnection = new GroupBox();
this.label1 = new Label();
this.btnConnect = new Button();
this.descRate = new Label();
this.descPort = new Label();
this.cbBaud = new ComboBox();
this.cbPort = new ComboBox();
this.gbOBDActions = new GroupBox();
this.btnResetErrors = new Button();
this.descReset = new Label();
this.gbStatus = new GroupBox();
this.lvLog = new ListView();
this.chLog = ((ColumnHeader)(new ColumnHeader()));
this.ilListviewImages = new ImageList(this.components);
this.gbConnection.SuspendLayout();
this.gbOBDActions.SuspendLayout();
this.gbStatus.SuspendLayout();
this.SuspendLayout();
this.gbConnection.Controls.Add(this.label1);
this.gbConnection.Controls.Add(this.btnConnect);
this.gbConnection.Controls.Add(this.descRate);
this.gbConnection.Controls.Add(this.descPort);
this.gbConnection.Controls.Add(this.cbBaud);
this.gbConnection.Controls.Add(this.cbPort);
this.gbConnection.Font = new Font("Segoe UI Light", 15.75F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
this.gbConnection.Location = new Point(12, 12);
this.gbConnection.Name = "gbConnection";
this.gbConnection.Padding = new Padding(15);
this.gbConnection.Size = new Size(300, 241);
this.gbConnection.TabIndex = 0;
this.gbConnection.TabStop = false;
this.gbConnection.Text = "Connection";
this.label1.AutoSize = true;
this.label1.Font = new Font("Segoe UI", 9.75F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
this.label1.Location = new Point(15, 122);
this.label1.Name = "label1";
this.label1.Size = new Size(246, 51);
this.label1.TabIndex = 5;
this.label1.Text = "To identify the correct port,\r\nDisconnect device && note available ports\r\nReconne" + "ct device && select the new entry";
this.btnConnect.Enabled = true;
this.btnConnect.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
this.btnConnect.Location = new Point(18, 186);
this.btnConnect.Name = "btnConnect";
this.btnConnect.Size = new Size(264, 37);
this.btnConnect.TabIndex = 4;
this.btnConnect.Text = "Connect";
this.btnConnect.UseVisualStyleBackColor = true;
this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click);
this.descRate.AutoSize = true;
this.descRate.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
this.descRate.Location = new Point(18, 83);
this.descRate.Name = "descRate";
this.descRate.Size = new Size(80, 21);
this.descRate.TabIndex = 3;
this.descRate.Text = "Baud Rate";
this.descPort.AutoSize = true;
this.descPort.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
this.descPort.Location = new Point(18, 48);
this.descPort.Name = "descPort";
this.descPort.Size = new Size(85, 21);
this.descPort.TabIndex = 2;
this.descPort.Text = "Port Name";
this.cbBaud.DropDownStyle = ComboBoxStyle.DropDownList;
this.cbBaud.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
this.cbBaud.FormattingEnabled = true;
this.cbBaud.Items.AddRange(new object[] {
"9600",
"19200",
"38400",
"57600",
"115200"
});
this.cbBaud.Location = new Point(129, 80);
this.cbBaud.Name = "cbBaud";
this.cbBaud.Size = new Size(153, 29);
this.cbBaud.TabIndex = 1;
this.cbPort.DropDownStyle = ComboBoxStyle.DropDownList;
this.cbPort.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
this.cbPort.FormattingEnabled = true;
this.cbPort.Location = new Point(129, 45);
this.cbPort.Name = "cbPort";
this.cbPort.Size = new Size(153, 29);
this.cbPort.TabIndex = 0;
this.gbOBDActions.Anchor = ((AnchorStyles)(((AnchorStyles.Top | AnchorStyles.Bottom) | AnchorStyles.Left)));
this.gbOBDActions.Controls.Add(this.btnResetErrors);
this.gbOBDActions.Controls.Add(this.descReset);
this.gbOBDActions.Font = new Font("Segoe UI Light", 15.75F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
this.gbOBDActions.Location = new Point(12, 264);
this.gbOBDActions.Name = "gbOBDActions";
this.gbOBDActions.Padding = new Padding(15);
this.gbOBDActions.Size = new Size(300, 250);
this.gbOBDActions.TabIndex = 5;
this.gbOBDActions.TabStop = false;
this.gbOBDActions.Text = "OBD Actions";
this.btnResetErrors.Enabled = false;
this.btnResetErrors.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
this.btnResetErrors.Location = new Point(18, 195);
this.btnResetErrors.Name = "btnResetErrors";
this.btnResetErrors.Size = new Size(264, 37);
this.btnResetErrors.TabIndex = 4;
this.btnResetErrors.Text = "Reset Errors";
this.btnResetErrors.UseVisualStyleBackColor = true;
this.btnResetErrors.Click += new System.EventHandler(this.btnResetErrors_Click);
this.descReset.AutoSize = true;
this.descReset.Font = new Font("Segoe UI", 9.75F);
this.descReset.Location = new Point(18, 43);
this.descReset.Name = "descReset";
this.descReset.Size = new Size(259, 136);
this.descReset.TabIndex = 2;
this.descReset.Text = "Reset Errors with 04 Command\r\n\r\nAvailable after connecting\r\n\r\nClear MIL (\"Check E" + "ngine Light\")\r\nErase Diagnostic Trouble Codes\r\nErase Freeze Frame Data\r\nErase Ox" + "ygen Test Data, Mode 06, 07 Data";
this.gbStatus.Anchor = ((AnchorStyles)((((AnchorStyles.Top | AnchorStyles.Bottom) | AnchorStyles.Left) | AnchorStyles.Right)));
this.gbStatus.Controls.Add(this.lvLog);
this.gbStatus.Font = new Font("Segoe UI Light", 15.75F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
this.gbStatus.Location = new Point(318, 12);
this.gbStatus.Name = "gbStatus";
this.gbStatus.Padding = new Padding(15);
this.gbStatus.Size = new Size(604, 502);
this.gbStatus.TabIndex = 6;
this.gbStatus.TabStop = false;
this.gbStatus.Text = "Status";
this.lvLog.Anchor = ((AnchorStyles) ((((AnchorStyles.Top | AnchorStyles.Bottom) | AnchorStyles.Left) | AnchorStyles.Right)));
this.lvLog.Columns.AddRange(new ColumnHeader[] {
this.chLog
});
this.lvLog.Font = new Font("Segoe UI Semibold", 9.75F, FontStyle.Bold, GraphicsUnit.Point, ((byte)(0)));
this.lvLog.HeaderStyle = ColumnHeaderStyle.Nonclickable;
this.lvLog.Items.AddRange(new ListViewItem[] {
listViewItem2
});
this.lvLog.Location = new Point(18, 46);
this.lvLog.Name = "lvLog";
this.lvLog.Size = new Size(570, 438);
this.lvLog.SmallImageList = this.ilListviewImages;
this.lvLog.TabIndex = 1;
this.lvLog.UseCompatibleStateImageBehavior = false;
this.lvLog.View = View.Details;
this.chLog.Text = "Log Details";
this.chLog.Width = 570;
this.ilListviewImages.ImageStream = ((ImageListStreamer) (resources.GetObject("ilListviewImages.ImageStream")));
this.ilListviewImages.TransparentColor = Color.Transparent;
this.ilListviewImages.Images.SetKeyName(0, "accept.png");
this.ilListviewImages.Images.SetKeyName(1, "error.png");
this.ilListviewImages.Images.SetKeyName(2, "information.png");
this.ilListviewImages.Images.SetKeyName(3, "delete.png");
this.ilListviewImages.Images.SetKeyName(4, "application_xp_terminal.png");
this.AutoScaleDimensions = new SizeF(6F, 13F);
this.AutoScaleMode = AutoScaleMode.Font;
this.ClientSize = new Size(934, 526);
this.Controls.Add(this.gbStatus);
this.Controls.Add(this.gbOBDActions);
this.Controls.Add(this.gbConnection);
this.Font = new Font("Segoe UI", 8.25F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
this.Name = "MainForm";
this.StartPosition = FormStartPosition.CenterScreen;
this.Text = "ExpressOBD";
this.FormClosing += new FormClosingEventHandler(this.MainForm_FormClosing);
this.Load += new System.EventHandler(this.MainForm_Load);
this.gbConnection.ResumeLayout(false);
this.gbConnection.PerformLayout();
this.gbOBDActions.ResumeLayout(false);
this.gbOBDActions.PerformLayout();
this.gbStatus.ResumeLayout(false);
this.ResumeLayout(false);
}
private void MainForm_Load(object sender, EventArgs e) {
lvLog.Items.Clear();
cbBaud.SelectedIndex = 2;
LogCustom("ExpressOBD 1.0 [github.com/jglim]. Icons: [famfamfam.com/lab/icons/silk]", 4);
}
protected override void Dispose(bool disposing) {
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void btnConnect_Click(object sender, EventArgs e) {
connectingInProgress = true;
sp = new ELM327();
sp.Open();
numberOfPromptsSinceInit = 0;
serialBuffer.Clear();
sp.Write("ATWS\r");
tmrSerialEvent();
SetupFormForPostConnect();
}
private void SetupFormForPreConnect() {
cbBaud.Enabled = true;
cbPort.Enabled = true;
btnConnect.Enabled = serialPortsAreAvailable;
btnResetErrors.Enabled = false;
}
private void SetupFormForPostConnect() {
cbBaud.Enabled = false;
cbPort.Enabled = false;
btnConnect.Enabled = false;
btnResetErrors.Enabled = ecuIsAvailable;
}
private void tmrSerialEvent() {
while (sp.IsOpen && sp.BytesToRead > 0)
{
string inString = sp.Read();
if (inString == ">")
{
numberOfPromptsSinceInit++;
Console.WriteLine("PC: {0}, Value: {1}", numberOfPromptsSinceInit, serialBuffer.ToString().Replace("\r", ""));
if (numberOfPromptsSinceInit == 1)
{
sp.Write("ATZ\r");
tmrSerialEvent();
}
else if (numberOfPromptsSinceInit == 2)
{
if (serialBuffer.ToString().Contains("ELM327"))
{
sp.Write("ATE0\r");
}
}
else if (numberOfPromptsSinceInit == 3)
{
sp.Write("ATL0\r");
}
else if (numberOfPromptsSinceInit == 4)
{
sp.Write("ATH1\r");
}
else if (numberOfPromptsSinceInit == 5)
{
sp.Write("ATSP0\r");
}
else if (numberOfPromptsSinceInit == 6)
{
sp.Write("ATI\r");
}
else if (numberOfPromptsSinceInit == 7)
{
if (serialBuffer.ToString().Contains("ELM327"))
{
Log("Connected to " + serialBuffer.ToString());
sp.Write("AT@1\r");
}
else
{
LogError("No compatible ELM327 device available on " + cbPort.Text);
DisconnectAndTidyUp();
}
}
else if (numberOfPromptsSinceInit == 8)
{
Log("Device Description: " + serialBuffer.ToString());
sp.Write("ATRV\r");
}
else if (numberOfPromptsSinceInit == 9)
{
Log("Vehicle Voltage: " + serialBuffer.ToString());
sp.Write("01 00\r");
}
else if (numberOfPromptsSinceInit == 10)
{
if (serialBuffer.ToString().Contains("UNABLE TO CONNECT"))
{
LogError("ECU not detected! Check OBD2 connection, or if vehicle is switched ON");
DisconnectAndTidyUp();
}
else
{
Log("ECU Ready for OBD actions");
ecuIsAvailable = true;
sp.Write("ATDP\r");
}
}
else if (numberOfPromptsSinceInit == 11)
{
Log("Communicating with ECU via " + serialBuffer.ToString());
sp.Write("01 01\r");
}
else if (numberOfPromptsSinceInit == 12)
{
string milData = serialBuffer.ToString();
milData = milData.Replace("\r", " ").Trim();
string[] milDataSegments = milData.Split(' ');
if (milDataSegments.Length != 6)
{
LogWarn("MIL Data could not be parsed");
}
else
{
byte milByte = byte.Parse(milDataSegments[2], System.Globalization.NumberStyles.HexNumber);
bool milIsActive = ((milByte & 0x80) > 0);
int milCount = (milByte & 0x7F);
if (milIsActive)
{
LogWarn("MIL (Check Engine Light) is ON. Number of Errors: " + milCount.ToString());
}
else
{
LogSuccess("MIL (Check Engine Light) is OFF");
}
}
sp.Write("01 01\r");
}
else if (numberOfPromptsSinceInit > 99)
{
LogSuccess("Erase acknowledged by ECU");
LogWarn("Restart vehicle to reflect changes");
DisconnectAndTidyUp();
}
serialBuffer.Clear();
}
else
{
serialBuffer.Append(inString);
}
}
}
private void btnResetErrors_Click(object sender, EventArgs e) {
if (MessageBox.Show("This action will: \r\n\r\n\r\n" + "Reset Trouble Code Count and MIL (Check Engine Light)\r\n\r\n" +
"Erase Diagnostic Trouble Codes, Freeze Frame Data(and associated DTCs), Oxygen Test Data, Mode 06, 07 Data\r\n\r\n" +
"Mode 0A(Permanent Trouble Codes) are unaffected and can only be reset by ECU\r\n\r\n\r\n" +
"Would you like to continue?", "Confirm Reset", MessageBoxButtons.OKCancel, MessageBoxIcon.Information) == DialogResult.OK)
{
numberOfPromptsSinceInit = 99;
sp.Write("04 \r");
tmrSerialEvent();
}
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e) {
if (sp != null)
{
if (sp.IsOpen)
{
sp.Close();
}
}
}
private void DisconnectAndTidyUp() {
sp.Close();
serialBuffer.Clear();
connectingInProgress = false;
ecuIsAvailable = false;
Log("Connection closed automatically");
SetupFormForPreConnect();
}
private void Log(string Message) {
lvLog.Items.Add(new ListViewItem(Message, 2));
}
private void LogWarn(string Message) {
lvLog.Items.Add(new ListViewItem(Message, 1));
}
private void LogSuccess(string Message) {
lvLog.Items.Add(new ListViewItem(Message, 0));
}
private void LogCustom(string Message, int ImageIndex) {
lvLog.Items.Add(new ListViewItem(Message, ImageIndex));
}
private void LogError(string Message) {
lvLog.Items.Add(new ListViewItem(Message, 3));
}
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
以上。