Giter Club home page Giter Club logo

elmduino's Issues

Problem with ESP32 to connect ELM327 via Bluetooth Classic

I have discovered by Serial Bluetooth Terminal the ODBII device with MAC 23:32:17:05:88:45, located as classic bluetooth device. It's this one: http://de.nsautodiagnostic.com/elm327-auto-code-reader/45440948.html
Using the following code with ESP32 (https://joy-it.net/de/products/SBC-NodeMCU-ESP32), Arduino IDE 1.8.13, ELMDuino 2.3.0, powered by USB with Power-HUB
Code:

#include "BluetoothSerial.h"
#include "ELMduino.h"


BluetoothSerial SerialBT;
#define ELM_PORT   SerialBT
#define DEBUG_PORT Serial


ELM327 myELM327;


uint32_t rpm = 0;
String name = "OBDII";

String MACadd = "23:32:17:05:88:45";
// converted to hex
uint8_t address[6]  = {0x17, 0x20, 0x11, 0x05, 0x58, 0x2D};


void setup()
{
#if LED_BUILTIN
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
#endif

  DEBUG_PORT.begin(115200);
  SerialBT.setPin("1234");
  ELM_PORT.begin("ArduHUD", true);
  
  if (!ELM_PORT.connect(name))
  {
    DEBUG_PORT.println("Couldn't connect to OBD scanner - Phase 1");
    while(1);
  }

  if (!myELM327.begin(ELM_PORT))
  {
    Serial.println("Couldn't connect to OBD scanner - Phase 2");
    while (1);
  }

  Serial.println("Connected to ELM327");
}


void loop()
{
  float tempRPM = myELM327.rpm();

  if (myELM327.status == ELM_SUCCESS)
  {
    rpm = (uint32_t)tempRPM;
    Serial.print("RPM: "); Serial.println(rpm);
  }
  else
    printError();
}


void printError()
{
  Serial.print("Received: ");
  for (byte i = 0; i < myELM327.recBytes; i++)
    Serial.write(myELM327.payload[i]);
  Serial.println();
  
  if (myELM327.status == ELM_SUCCESS)
    Serial.println(F("\tELM_SUCCESS"));
  else if (myELM327.status == ELM_NO_RESPONSE)
    Serial.println(F("\tERROR: ELM_NO_RESPONSE"));
  else if (myELM327.status == ELM_BUFFER_OVERFLOW)
    Serial.println(F("\tERROR: ELM_BUFFER_OVERFLOW"));
  else if (myELM327.status == ELM_UNABLE_TO_CONNECT)
    Serial.println(F("\tERROR: ELM_UNABLE_TO_CONNECT"));
  else if (myELM327.status == ELM_NO_DATA)
    Serial.println(F("\tERROR: ELM_NO_DATA"));
  else if (myELM327.status == ELM_STOPPED)
    Serial.println(F("\tERROR: ELM_STOPPED"));
  else if (myELM327.status == ELM_TIMEOUT)
    Serial.println(F("\tERROR: ELM_TIMEOUT"));
  else if (myELM327.status == ELM_TIMEOUT)
    Serial.println(F("\tERROR: ELM_GENERAL_ERROR"));

  delay(100);
}

Connection by using name produces this debug output:
[I][BluetoothSerial.cpp:510] _init_bt(): device name set [I][BluetoothSerial.cpp:225] esp_spp_cb(): ESP_SPP_INIT_EVT [I][BluetoothSerial.cpp:702] connect(): master : remoteName [I][BluetoothSerial.cpp:379] esp_bt_gap_cb(): ESP_BT_GAP_DISC_STATE_CHANGED_EVT [I][BluetoothSerial.cpp:327] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT [I][BluetoothSerial.cpp:329] esp_bt_gap_cb(): Scanned device: 0c:ec:84:0a:c1:72 [I][BluetoothSerial.cpp:327] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT [I][BluetoothSerial.cpp:329] esp_bt_gap_cb(): Scanned device: 23:32:17:05:88:45 [I][BluetoothSerial.cpp:379] esp_bt_gap_cb(): ESP_BT_GAP_DISC_STATE_CHANGED_EVT [I][BluetoothSerial.cpp:379] esp_bt_gap_cb(): ESP_BT_GAP_DISC_STATE_CHANGED_EVT Couldn't connect to OBD scanner - Phase 2

Connection by using address produces this debug output:
[I][BluetoothSerial.cpp:510] _init_bt(): device name set [I][BluetoothSerial.cpp:225] esp_spp_cb(): ESP_SPP_INIT_EVT [I][BluetoothSerial.cpp:722] connect(): master : remoteAddress [I][BluetoothSerial.cpp:290] esp_spp_cb(): ESP_SPP_DISCOVERY_COMP_EVT Couldn't connect to OBD scanner - Phase 2

Connection by using name and set PIN produces this output:
[E][BluetoothSerial.cpp:784] isReady(): BT is not initialized. Call begin() first [I][BluetoothSerial.cpp:510] _init_bt(): device name set [I][BluetoothSerial.cpp:118] btSetPin(): pin set [I][BluetoothSerial.cpp:225] esp_spp_cb(): ESP_SPP_INIT_EVT [I][BluetoothSerial.cpp:702] connect(): master : remoteName [I][BluetoothSerial.cpp:379] esp_bt_gap_cb(): ESP_BT_GAP_DISC_STATE_CHANGED_EVT [I][BluetoothSerial.cpp:327] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT [I][BluetoothSerial.cpp:329] esp_bt_gap_cb(): Scanned device: 23:32:17:05:88:45 [I][BluetoothSerial.cpp:336] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT : EIR : OBDII : 32 [I][BluetoothSerial.cpp:327] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT [I][BluetoothSerial.cpp:329] esp_bt_gap_cb(): Scanned device: 23:32:17:05:88:45 [I][BluetoothSerial.cpp:336] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT : EIR : OBDII : 32 [I][BluetoothSerial.cpp:327] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT [I][BluetoothSerial.cpp:329] esp_bt_gap_cb(): Scanned device: 23:32:17:05:88:45 [I][BluetoothSerial.cpp:336] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT : EIR : OBDII : 32 [I][BluetoothSerial.cpp:327] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT [I][BluetoothSerial.cpp:329] esp_bt_gap_cb(): Scanned device: 23:32:17:05:88:45 [I][BluetoothSerial.cpp:336] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT : EIR : OBDII : 32 [I][BluetoothSerial.cpp:327] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT [I][BluetoothSerial.cpp:329] esp_bt_gap_cb(): Scanned device: 23:32:17:05:88:45 [I][BluetoothSerial.cpp:336] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT : EIR : OBDII : 32 [I][BluetoothSerial.cpp:327] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT [I][BluetoothSerial.cpp:329] esp_bt_gap_cb(): Scanned device: 23:32:17:05:88:45 [I][BluetoothSerial.cpp:336] esp_bt_gap_cb(): ESP_BT_GAP_DISC_RES_EVT : EIR : OBDII : 32 [I][BluetoothSerial.cpp:379] esp_bt_gap_cb(): ESP_BT_GAP_DISC_STATE_CHANGED_EVT [I][BluetoothSerial.cpp:379] esp_bt_gap_cb(): ESP_BT_GAP_DISC_STATE_CHANGED_EVT Couldn't connect to OBD scanner - Phase 2

Using Torque or Serial Bluetooth Terminal is working well with sending AT request and getting a propper result. Also the BT classic send and receive with Serial Bluetooth Monitor has worked fine.

Can you help me please?

>ASSERT_WARN(1 8), in lc_task.c at line 5054ASSERT_WARN(1 8), in lc_task.c at line 5054

Hi,

First of all thanks for this great library, i'm using arduino UNO+ESP32S+Bluetooth ELM327 dongle, with ESP32_test example from your library, it connects to elm327 dongle and communicating without any problem but i'm receiving this line on the terminal and didn't figure it out why it's returning this ">ASSERT_WARN(1 8), in lc_task.c at line 5054ASSERT_WARN(1 8), in lc_task.c at line 5054"

When i send 010C(speed) it returns "41 0C 00 00" without any problem. But in every 3-5 seconds it returns ">ASSERT_WARN(1 8), in lc_task.c at line 5054ASSERT_WARN(1 8), in lc_task.c at line 5054"

Why? and how i can fix it?
My console output as below;

ASSERT_WARN(1 8), in lc_task.c at line 5054010C
010C
ASSERT_WARN(1 8), in lc_task.c at line 505441 0C 00 00 


>ASSERT_WARN(1 8), in lc_task.c at line 5054ASSERT_WARN(1 8), in lc_task.c at line 5054010C
010C
41 0C 00 00 


>ASSERT_WARN(1 8), in lc_task.c at line 5054010C
010C
41 0C 00 00 


>ASSERT_WARN(1 8), in lc_task.c at line 5054010D
010D
41 0D 00 


>ASSERT_WARN(1 8), in lc_task.c at line 5054ASSERT_WARN(1 8), in lc_task.c at line 5054

Add special PID

Hi,

how i can add a new PID (221E12) i have foud who i can change the PIDs, but all PID is in 0x???? form and im stuck on this point

Make PAYLOAD_LEN redefinable.

Now PAYLOAD_LEN is fixed to 40.
Some ECU have custom pids with custom reply.
For example mine reply with 36, 44 and 126 characters on ELM side.

Small ELM327::initializeELM improvements

I try to suggest some small improvements to ELM327::initializeELM :

A previously used software may have done settings adjustments that prevent ELMduino from communicating properly with ELM327. So it is a practice for many software to send ATZ as a first command.

ELMduino initialize protocol using AT SP. This write the protocol to the ELM327 eeprom every time. Anoter practice is to use AT TP (or AT TP A), that doesn't store default, and fallback to AT SP if previous command fails (some ELM327 doesn't support AT TP).

home-testing Elmduino (OBD emulator?)

Hi,
Thank you for ELMduino. I'm already using it in my car with custom PIDs (discussion already closed as testing went fine). Since I don't drive the car much lately and the condition I'm waiting for has not occurred since I did my setup I cannot really prove it works. I mean - 4 out of 5 values I'm reading are ok, changing etc. But the one I'm most interested in (DPF burn status 0/1) happens once 300-700km driven.
I did already small instructable & shared with community. The only one person testing this solution said It didn't show the status properly (DPF burn was 0, while Torque showed 1 at the same time)

My question is - is there any quick ESP32/Arduino Bluetooth emulator that we program to feed other ESP32 running Elmduino to test ? I'm specially intersted in this one custom PID

Couldn't connect to OBD scanner - Phase 2

Describe the bug
Hi, I'm trying to build a bluetooth connection between ELM327( like this
image )
and an ESP32 Wroom.
I'm using the bluetooth_serial example, and I tried everything to initialize the ELM327 object myELM327.initialize('5'), myELM327.begin(ELM_PORT), myELM327.begin(ELM_PORT, ISO_14230_FAST_INIT), myELM327.begin(ELM_PORT, ISO_14230_FAST_INIT, payloadLen)
BUT everytime, I got this on serial monitor : "Couldn't connect to OBD scanner - Phase 2"
@PowerBroker2 Can you please help, what am I doing wrong?

support on stm32

I'm on my project developing car connected internet of things using lte catm1 module. Do you have any plan to modify the code that it works on arm mbed IDE to run on stm board?? I have willing to pay you a little for this project.. thank you

ELM_NO_DATA using the Wifi sketch.

Describe the bug
Using the wifi sketch in the example, I've attempted to connect an esp8266 (which I also read about in the closed issues). The ESP connects fine and the connect to server proceeds. I'm then trying some simple as a RPM read, however this always produces an ELM_NO _DATA. I thought of doing some debugging work in with a ,net program on a laptop over the wifi direct, but I though I'd ask first to see if anybody could point out the obvious.

To Reproduce
Using a Iegeek OBD2 wifi adapter and the simple wifi sketch provided running on an esp8266.
I haven't hooked up a serial monitor laptop, but rather used a simple ws2812 led matrix which lights different leds according to the error.

Expected behavior
Getting an ELM_SUCCESS:
float tempRPM = myELM327.rpm();

if (myELM327.status == ELM_SUCCESS)
{
rpm = (uint32_t)tempRPM;
Serial.print("RPM: "); Serial.println(rpm);
}

Equipment
Iegeek wifi:
https://www.bol.com/nl/p/obd2-wifi-interface-adapter-mini-elm327/9200000055136781/?country=BE&Referrer=ADVNLGOO002060-G-88680672361-S-795237665226-9200000055136781&gclid=Cj0KCQjw0Mb3BRCaARIsAPSNGpU3xTYW7FFMST7e_kB1dhrsSr1uZfIS692eeg2aOtnFi13dVnzg0-AaAkMBEALw_wcB

Nodemcu

Code

void setup_wifi()
{
    WiFi.disconnect();
    WiFi.config(ip, gate, sub); 
    WiFi.mode(WIFI_STA);
    WiFi.begin(AP_SSID);

    while (WiFi.status() != WL_CONNECTED)
    {
        FlashYellow();
        delay(100);
        FlashRed();
        delay(400);
    }

    FlashYellow();
    delay(1000);
    wifiClient.connect(server, 35000);
    while (!wifiClient.connected())
    {
        FlashGreen();
        delay(100);
        FlashYellow();
        delay(400);
    }
    FlashGreen();
    myELM327.begin(wifiClient);
}

void setup()
{

    FastLED.addLeds<NEOPIXEL, NEOPIN>(matrixleds, NUMMATRIX);
    matrix->begin();
    matrix->setTextWrap(false);
    matrix->setBrightness(40);
    matrix->setTextColor(colors[0]);
    FlashRed();
#ifdef CURRENT
    IPAddress ip1(192, 168, 1, 1);
    pzem.setAddress(ip1);

#else

#endif

    //uartP.CmdReceivedPtr = DataReceived;
    delay(10);

    setup_wifi();
#ifdef OTA_MODE
    setup_ota();
#endif
}

int x = 0;
int pass = 0;
void printError()
{

     if (myELM327.status == ELM_NO_RESPONSE)
        FlashWhite(0);
        //Serial.println(F("\tERROR: ELM_NO_RESPONSE"));
    else if (myELM327.status == ELM_BUFFER_OVERFLOW)
        FlashWhite(1);
        //Serial.println(F("\tERROR: ELM_BUFFER_OVERFLOW"));
    else if (myELM327.status == ELM_GARBAGE)
        FlashWhite(2);
        //Serial.println(F("\tERROR: ELM_GARBAGE"));
    else if (myELM327.status == ELM_UNABLE_TO_CONNECT)
        FlashWhite(3);
        //Serial.println(F("\tERROR: ELM_UNABLE_TO_CONNECT"));
    else if (myELM327.status == ELM_NO_DATA)
        FlashWhite(4);
        //Serial.println(F("\tERROR: ELM_NO_DATA"));
    else if (myELM327.status == ELM_STOPPED)
        FlashWhite(5);
        //Serial.println(F("\tERROR: ELM_STOPPED"));
    else if (myELM327.status == ELM_TIMEOUT)
        FlashWhite(6);
        //Serial.println(F("\tERROR: ELM_TIMEOUT"));
    else if (myELM327.status == ELM_GENERAL_ERROR)
        FlashWhite(7);
        //Serial.println(F("\tERROR: ELM_GENERAL_ERROR"));
    delay(100);
}
void loop()
{
#ifdef OTA_MODE
    ArduinoOTA.handle();
#endif


    unsigned long currentMillis = millis();

    // if we lose wifi
    if (WiFi.status() != WL_CONNECTED)
    {
        setup_wifi();
    }

    // get rpm and update matrix
    if (currentMillis - previousMillisHeart > heartBeat)  //heartbeat = 1000ms
    {
        float tempRPM = myELM327.rpm();

        if (myELM327.status == ELM_SUCCESS)
        {
            rpm = (uint32_t)tempRPM;
            values[1] = (int)(rpm);
            values[2] = (int)(rpm / (float)2);
            values[3] = (int)(rpm / (float)4);
            values[4] = (int)(rpm / (float)8);
            values[5] = (int)(rpm / (float)16);
            values[6] = (int)(rpm / (float)32);
            values[7] = (int)(rpm / (float)64);
            UpdateBars();
        }
        else
        {
            printError();
        }
        previousMillisHeart = currentMillis;
    }
}

Wiring
Using wifi.

Thanks in advance.

ESP32 connects via BT, can send AT commands only, no query TIMEOUT-STOPPED error

Hi, I have this problem and I tried almost everithing,
My Esp32 connects correctly to vlink bluetooth 4 adapter and I can send at commands successfully.
Example:

atz

atzELM327 v2.2

at dp

at dp AUTO, ISO 9141-2

0105

010541 05 2E

I can send and read succesfully AT commands with this sketch:

void loop()
{
   myELM327.sendCommand(READ_VOLTAGE);



  if (myELM327.status == ELM_SUCCESS)
  
  {
      
  Serial.print("Data "); Serial.println(myELM327.payload);
        
  }
  
  else
      printError();
}

But if I want to read coolant temperature with the following command is not working:

ELM327.queryPID(01, 05);

or this

myELM327.sendCommand("0105");

or this

 void loop()
{
  
float ct = myELM327.engineCoolantTemp();


  if (myELM327.status == ELM_SUCCESS)
  {
      Serial.print("Coolant C. "); Serial.println(ct);

  }
  
  else
      printError();
}

I have always the same errors 6-7 elm stopped and timeout but connection is stable, there is any problem with esp32 240mhz?
Timing of some sort to adjust in library for query command?
Thank You for Your help.

ESP8266

I want to read PIDs over my ESP8266 WiFi Modul.
How can i implement your ELM Duino to read Pids over the wifi connection ?
When i enter 010C in my serial monitor iam get the right answer from the ELM327 so the connection should be fine ?
But now i dont no know how i can use ur ELMDuino sketch to simplify things and get the data to be print in to my serial monitor ?

`#include <ESP8266WiFi.h>

const char* ssid = "WiFi_OBDII";
const char* password = "your-password";

//IP Adress of your ELM327 Dongle
IPAddress server(192, 168, 0, 10);

boolean last_in = false;

WiFiClient client;

void setup() {
Serial.begin(115200);
delay(10);

// Connecting to ELM327 WiFi
Serial.print("Connecting to ");
Serial.println(ssid);

WiFi.mode(WIFI_STA);
WiFi.begin(ssid);
// WiFi.begin(ssid, password); //Use this line if your ELM327 has a password protected WiFi

while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}

Serial.println("");
Serial.println("Connected to Wifi");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());

if (client.connect(server, 35000)) {
Serial.println("connected");
Serial.println("<ATZ");
// Make a Request
client.println("ATZ");
}
else {
Serial.println("connection failed");
ESP.reset();
}
}

void loop() {
if (!client.connected()) {
Serial.println("CLIENT DISCONNECTED - RESET!");
client.stop();
ESP.reset();
}
// if there are incoming bytes available
// from the server, read them and print them:
if (client.available()) {
if (!last_in) {
Serial.println("");
Serial.print(">");
last_in = true;
}
char c = client.read();
/Serial.print(" 0x");
Serial.print(c, HEX);
/
Serial.print(c);
}

// as long as there are bytes in the serial queue,
// read them and send them out the socket if it's open:
while (Serial.available() > 0) {
if (last_in) {
Serial.println("");
Serial.print("<< ");
last_in = false;
}
char inChar = Serial.read();
if (client.connected()) {
client.print(inChar);
}
}
}`

Compiler warning

Describe the bug
Compiler warning when enabled.

C:\Users\Stuart\Documents\Arduino\libraries\ELMDuino\src\ELMduino.cpp: In member function 'bool ELM327::initializeELM(char)':
C:\Users\Stuart\Documents\Arduino\libraries\ELMDuino\src\ELMduino.cpp:116:5: warning: this 'if' clause does not guard... [-Wmisleading-indentation]
     if (sendCommand(command) == ELM_SUCCESS)
     ^~
C:\Users\Stuart\Documents\Arduino\libraries\ELMDuino\src\ELMduino.cpp:120:2: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'
  return connected;
  ^~~~~~

To Reproduce
Enable compiler warnings.

Expected behavior
No compiler warnings.

Code

    if (sendCommand(command) == ELM_SUCCESS)
        if (strstr(payload, "OK") != NULL)
            connected = true;

	return connected;

Troubleshooting Connection Issues

  • Try 38400 baud instead of 115200
  • If using an HC-05 for communication, ensure it's configured properly
  • Ensure the car is on and running during troubleshooting

DISP_ID is incorrect

const char * const DISP_ID = "AT HI"; // General

ELM327 datasheet page 17

I
[ Identify yourself ]
Issuing this command causes the chip to identify
itself, by printing the startup product ID string (currently
‘ELM327 v2.0’). Software can use this to determine
exactly which integrated circuit it is talking to, without
having to reset the IC.

How can I use ELMduino over Uart?

I have ELM32 but the Bluetooth module on ELM is broken.
I extracted the RX and TX line and connected them to my laptop. It's work.
I would like to know. How to connect them over arduino with your library?

ESP32 Connection

I used the following to connect to my ELM327, to this Point ist al working good but the readout is ERROR: 7 and it wont be displaying my rpm.
I also check my ELM327 with torque app and it all Works fine.

#include "BluetoothSerial.h"
#include "ELMduino.h"


#define ELM_PORT SerialBT
#define ESP_BLUETOOTH_NAME "ESP32"


BluetoothSerial SerialBT;
ELM327 myELM327;


uint32_t rpm = 0;


void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);

  Serial.begin(115200);
  ELM_PORT.begin(ESP_BLUETOOTH_NAME, true);

  Serial.println("Attempting to connect to ELM327...");

  if (!ELM_PORT.connect("Carly Adapter"))
  {
    Serial.println("Couldn't connect to OBD scanner - Phase 1");
    while (1);
  }

  if (!myELM327.begin(ELM_PORT))
  {
    Serial.println("Couldn't connect to OBD scanner - Phase 2");
    while (1);
  }

  Serial.println("Connected to ELM327");
}


void loop()
{
  float tempRPM = myELM327.rpm();

  if (myELM327.status == ELM_SUCCESS)
  {
    rpm = (uint32_t)tempRPM;
    Serial.print("RPM: "); Serial.println(rpm);
  }
  else
  {
    Serial.print(F("\tERROR: "));
    Serial.println(myELM327.status);
    delay(100);
  }
}

ERROR 7 On ESP32 Wrover Board

Arduino IDE Version: 1.8.5
ELMduino Library Version: 2.0.11 (Latest at the time of opening this issue)
ESP32 Build Tools Version: 1.0.4 (Latest at the time of opening this issue)
ESP32 Board Used: Wroom 32 ESP32 Dev Board
ELM327 Module: Generic Bluetooth ELM327

Issue description
Getting ERROR 7 on the serial console while running the ESP32_Bluetooth_Serial.io example, Used the file ESP32_Bluetooth_Serial.ino that came with the library, only changes were updating the Bluetooth name of the ELM327 module & commenting the following lines since LED_BUILTIN is not defined for this particular ESP32 board:

pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);

No issues with compilation or connection with the ELM327 module, ESP32 is connecting fine with the module quickly its a generic known working device, Torque works fine.

Tried sending AT commands using the minimal example & it worked fine, module responds back with values for AT commands.

Attaching the screenshot of the serial monitor:
aa

rpm() function

Hi, if I call the rpm() function, I should get 2 bytes as answer, but I get these 2 bytes two times, so for example: 0D8C would be the right answer, but I get 0D8C0D8C, if I give the explicit AT cmd 010C to the ELM327, get 4 bytes as an answer, as well. Do you know why is this happenning?
Thank you!
@PowerBroker2

How to send an AT command and get response via WiFi?

I have a successful connection to my OBD dongle, but could not get a response on my 'AT Z' command using a terminal program. I use ESP8266_WiFi.ino.

Connected to Wifi
IP address:
192.168.0.1
connected

Thank you for any help.

Help with Reading speed (KPH) instead of RPM

Hi,
I am a beginner here.

I am interested in using the ESP32_Bluetooth_Serial code. However I want to read speed (KPH) instead of rpm, so can you please help me change the necessary parameters to read the car speed?
(I am getting confused whether to use the term "speed" or "kph")

Esp32 connection

Hello,Firstly i would like to thank you for all the hard work you have put into making this library.unlike the previous esp32 issue #11. I havent been able to get a bluetooth connnection between the esp32 and obd2 scanner .while running the ESP32_Bluetooth_Serial.ino code provided in the example, i get the following output "Couldn't connect to OBD scanner - Phase 2".In the previous issuses you recommended that people olso try out the esp32 test.ino example.i have tested that example and realised that i get a "Connected to ELM327" output even when the is no obd2 scanner present.

I am using the LILYGO® TTGO T-Call V1.3 ESP32 Wireless Module GPRS Antenna SIM Card SIM800L Board and a standard elm32 bluetooth obd2 scanner

images (2)

Couldn't connect to OBD scanner...

hello powerbroker~

First of all, I used elm327 v1.5. Initially I removed elm327 bluetooth chip and slodered rx,tx,gnd,vcc and connect to arduino uno board. I didn't wanna communicate with bluetooth so I removed the chip following this youtube channel "https://www.youtube.com/watch?v=EG94t0rqag4&t=121s"
I used software_serial_test examples to my arduino and serial monitor just keep showing that couldn't connect to OBD scanner. what'd be the problem.... sorry for my bad english :(

Actually I m a damn fucking beginner of arduino and i don' know a shit about what the c language is and so on... I just started to learn about it.. I spent about a week to two weeks for this problem...
Screenshots

KakaoTalk_20201216_013304743_LI
KakaoTalk_20201216_023758651_LI

Additional Context
PLEASE HELP ME !!!!
Troubleshooting Connection Issues

  • Try 38400 baud instead of 115200 >> I have tried all the baud.. and I already have connected to the car but it doesn't work bro..

Some basic setup questions

Hey! I really appreciate the support. I'm new to the arduino universe, but I'm a big car enthusiast and very interested in making some basic displays for a track car I'm building. I bought one of the inexpensive bluetooth ELM327 modules and desoldered the bluetooth component so I could pass the serial TX/RX data to the arduino through pins 2 and 3 like in one of the examples.

I have a (probably very basic, simple) question about the baud rate settings. How did you determine to use 115200 for both the serial and ELMserial ports? I found a few resources that said ELM327's have a baud rate of 38400, but if I used those values, it obviously didn't work. I was just curious about those settings.

The other issue I had was when I did get the serial port to work (after it printed the "connected to elm327"), I would get around 10 values printed for the RPM (all which read 0), then it would go into the timeout error and just print that endlessly.

I was wondering if you could help point me in the right direction. I don't have any experience with data communication, so it's proving to be a bit of a steep learning curve, so any advice/guidance is very much appreciated.

Hi,can you tell me BluetoothSerial.h source,thank you

Hi, Thank you for your code, but I have a problem. I look at your code in "examples/ESP32_Bluetooth_Serial/ESP32_Bluetooth_Serial.ino", it have a "BluetoothSerial.h" ,I can't find the source on the Internet. Can you tell me its link and it's library? or can you provide the code? Thank you.

Error 7 / Timeout with ESP32

Arduino IDE Version: 1.8.12
ELMduino Library Version: 2.0.12 (Latest at the time of opening this issue)
ESP32 Board Used: TTGO T4 (based on ESP32 Dev Board)
ELM327 Module: Generic Bluetooth ELM327
Car: MCC Smart build in 2000 using the ISO14230-4 KWP fast protocoll

Issue description:

I try to use your test-sketch for ESP32 . No changes. I get the following console output:

Console Output

My engine is of cause running, I testet the adapter using the "Torque" App for Android. The adapter works well, its name is "OBDII".

I believe the connection to the ECU may fail. Any idea how to figure that out?

If I use the ESP32_Bluetooth_Serial.ino-Sketch, I can see another error-message:
WhatsApp Image 2020-04-19 at 15 29 26

getResponse and calculate resonseByte A and B

Hello powerbroker2
I'm really sorry for bothering you for keep asking questions...
I do appreciate that

Actually It is really easy to use 'findResponse' to get the values
To get more values from PID I should find out some splited values like
Distance traveled since codes cleared (256*A+B)

I have already checked the close issues about the splited values you wrote... but as mentioned, I'm not a programmer

I added to this my cpp file first.

uint16_t ELM327::runtime()
{
	if (queryPID(SERVICE_01, RUN_TIME_SINCE_ENGINE_START))

		return (uint16_t)findResponse();

	return ELM_GENERAL_ERROR;
}
//then can you tell me how to use responseByte_1 and responseByte_0 using 'findResponse' to calculate the 'runtime' value?

this is my sketch below
uint16_t runtime = 0;

uint16_t tempRUN_TIME_SINCE_ENGINE_START = myELM327.runtime();

    byte B = myELM327.responseByte_0;
    byte A = myELM327.responseByte_1; // I don't have any idea how to use this...

  runtime = (uint16_t)tempRUN_TIME_SINCE_ENGINE_START;
        Serial.print("Engine Runtime : "); Serial.print(runtime); Serial.println("seconds"); // 256*A+B is the correct runtime value

I'm really sorry for my ignorance.. plz give me a little help plz... as I metioned I'm not a programmer... sorry again.

how to read OBDII voltage in sketch on Arduino IDE

I been scratch my head regarding how to put a "read voltage"(ATRV) command in sketch in order to display the result in serial monitor.

I already try ATRV command on ESP32_test.ino sample & it work, it show ">atrvatrv12.1V" on serial monitor. But when I issue "ATRV" command in sketch on ESP32_Bluetooth_Serial.ino sample, it show 0.
the command in sketch as below

Serial.print("Batt: "); 
myELM327.sendCommand("ATRV");
Serial.println(myELM327.findResponse());
Serial.println("V");

it will show below result:
Batt: 0
V
in serial monitor.

By the way, I still at beginner stage for my Arduino command. FYI, I'm using OBDLink LX as my OBDII bluetooth dongle & ESP32.

more on custom PIDs - splitting response

This is continuation of issue 4
some custom PIDs return values and suggested calculation for result is divided in two parts A and B (all examples for Torque app)
see example of calculations suggested -> here
if A256+B makes all sense as it's simply conversion of longer response to decimal (I guess)
such a calculation :
(A
33.55/255+B*0.13/255)-12
is impossible to get with current findResponse()

Would it be possible to return A and B separately so calculation could be done on them ?

currently the most complex query & response for me is
Distance Since Last DPF Regeneration Dist. DPF 223039 A*256+B 0 65535 km 7E0
myELM327.queryPID(34, 12345) //0x22 and 0x3039 => 34 and 12345

Some information about my wifi dongle problems

Hi maintainers. This is just a short information about my previous wifi obd dongle problems. Hope this help someone out, for further debugging and tests. The root cause is not ELMduino, this library work as expected.

I've submitted a pull request for the connected state problem a while ago. And what i can say, WIFI_MODE doesn't fix anything :) Sorry for the wrong input last time.

Here is my summary of what is going wrong on the side of Wifi. I've debugged days with a lot of different adapters.

ESP32 and ESP8266 both have unreliable wifi network

This is the base problem. All ESP32 and ESP8266 modules i've tested (5 different) have the same issues. Sending a raw AT Command works fine. Like for example AT @1. Receiving the answer as difficult.

  1. Most of the time, you get back what you have sent. For example AT @1 including some weird characters at the end, mostly ?.
  2. Once in a while, you get the correct answer, in the case of the example AT command, this is something like OBDII RS232 to xxx Interpreter. You can improve this behaviour when setting setNoDelay(true) on the WifiClient -> https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/client-class.html#setnodelay
  3. I wrote a testscript that just send this one command in a interval of 1 second. 76 of 100 received trash data. Only 24 received correct data.
  4. It doesnt matter how long you wait for data to be received, after the trash data is received, which curiously correctly is terminated with an >, there is no more data the comes down the line. Only after sending another AT command, you get back data.

ESP32 BluetoothSerial

Same code as above for Wifi (Except the wifi client changes with BT Serial), work without problems and 100 of 100 requests receive correct data.

Testscenario - All show similar results

  • 3 different ESP32 and 2 different ESP8266 modules
  • 7 different OBD2 Wifi dongles, from cheap to more expensive
  • I have also tested all wifi adapters with a smartphone app that can send serial messages. Here it work without any problems. So all tested wifi adapters are generally fine and work as expected.

Conclusion

Don't use OBD2 WIFI adapters on ESP32 or ESP8266. You will have troubles.

I wanted to write down my testing hell of the last days, to help others with my experience.

Have nice holidays. Cya.

Timeout Issue

Hello. I'm sorry to open another issue on this topic, I've been trying to figure this out for a few days now and can't. Probably my inexperience. Anyway my hardware is:

  • Arduino Mega
  • HC05
  • Bafx ELM327

I'm able to connect and retrieve data using AT commands, but I get timeout errors after connecting using the example code. Any ideas?

Connection problem

Hey.
I have a problem logging in ESP32 to ELM327. After uploading the code, it worked for several days, then ESP32 stopped connecting with LM. I noticed that you need to log in to another ELM237 device using the pairing code "1234" and the connection is made but only until the first restart. Then repeat the whole procedure - enter the pairing code "1234" on another device. Something is working incorrectly I have two ESP32 and ELM237 and in all cases it is so bad for this I exclude a hardware problem.
I check the operation on the unmodified test code and the adapter name is OBDII.
How it looks can be seen in the video: https://youtu.be/d-oBFSECpDg

#include "BluetoothSerial.h"
#include "ELMduino.h"


#define ELM_PORT SerialBT
#define ESP_BLUETOOTH_NAME "ESP32"


BluetoothSerial SerialBT;
ELM327 myELM327;


uint32_t rpm = 0;


void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);

  Serial.begin(115200);
  ELM_PORT.begin(ESP_BLUETOOTH_NAME, true);

  Serial.println("Attempting to connect to ELM327...");

  if (!ELM_PORT.connect("OBDII"))
  {
    Serial.println("Couldn't connect to OBD scanner - Phase 1");
    while (1);
  }

  if (!myELM327.begin(ELM_PORT))
  {
    Serial.println("Couldn't connect to OBD scanner - Phase 2");
    while (1);
  }
  
  Serial.println("Connected to ELM327");
}


void loop()
{
  float tempRPM = myELM327.rpm();

  if (myELM327.status == ELM_SUCCESS)
  {
    rpm = (uint32_t)tempRPM;
    Serial.print("RPM: "); Serial.println(rpm);
  }
  else
  {
    Serial.print(F("\tERROR: "));
    Serial.println(myELM327.status);
    delay(100);
  }
}

ERROR: ELM_NO_DATA on ESP32Dev device

Describe the bug
Hello! First of all, many thanks for your library, what an excellent work! Nevertheless, I'm having some troubles while trying to use it with a ESP32Dev device. I'm continuously being shown the following error:

Attempting to connect to ELM327...
Connected to ELM327
Payload received for rpm: NODATA
ERROR: ELM_NO_DATA
Payload received for kph: NODATA
ERROR: ELM_NO_DATA

I have tested the ESP32_test.ino example, with a successful connection to the device, as follows:

Attempting to connect to ELM327...
Connected to ELM327
Ensure your serial monitor line ending is set to 'Carriage Return'
Type and send commands/queries to your ELM327 through the serial monitor

AT Z
ELM327 v2.1
AT E0
OK
AT S0
OK
AT SP0
OK
010C
SEARCHING...
410C0B94

I'm wondering if you could give me a clue about what's going on. Thanks in advance!

To Reproduce
Upload to a ESP32 device the code below.
Connect to ELM327 device

Expected behavior
Show values to .kph(), .rpm() and standard PIDs

Equipment
Microcontroller: ESP32-Bit (programmable as ESP32 Dev Module), like this one: https://www.amazon.es/ESP32-Bit-Desarrollo-Bluetooth-Ethernet-ESP32-T/dp/B07GD4SGYM
OBD II Controller: OBD II Mini ELM327, like this one: https://www.amazon.es/Interfaz-Bluetooth-herramienta-an%C3%A1lisis-diagn%C3%B3stico/dp/B00WU97RH8
Car Model: Peugeot 206

Code

// modified test + my custom PIDs
// based purely on library

#include "BluetoothSerial.h"
#include "ELMduino.h"


#define ELM_PORT SerialBT
#define ESP_BLUETOOTH_NAME "ESP32"


BluetoothSerial SerialBT;
ELM327 myELM327;


void setup()
{
  //pinMode(LED_BUILTIN, OUTPUT);
  //digitalWrite(LED_BUILTIN, HIGH);

  Serial.begin(115200);
  //ELM_PORT.setPin("1234");
  ELM_PORT.begin(ESP_BLUETOOTH_NAME, true);

  Serial.println("Attempting to connect to ELM327...");

  if (!ELM_PORT.connect("OBDII"))
  //if (!ELM_PORT.connect("00:1d:a5:68:98:8d"))
  {
    Serial.println("Couldn't connect to OBD scanner - Phase 1");
    while (1);
  }

  if (!myELM327.begin(ELM_PORT))
  {
    Serial.println("Couldn't connect to OBD scanner - Phase 2");
    while (1);
  }
  
  Serial.println("Connected to ELM327");
  
/*  for (int a = 0; a < 10; ++a)
  {
    digitalWrite(LED_BUILTIN,HIGH); delay(100);
    digitalWrite(LED_BUILTIN,LOW); delay(100); 
  }
*/
}


void loop()
{
  int32_t rpm = -1;                     // Directo
  int32_t engineLoad = -1;              // A/2.55
  int32_t engineCoolant = -1;           // A - 40
  int32_t shortTermFuelTrimBank1 = -1;  // A/1.28 - 100
  int32_t longTermFuelTrimBank1 = -1;   // A/1.28 - 100
  int32_t intakeManifoldPressure = -1;  // A
  int32_t kph = -1;                     // Directo
  int32_t timingAdvance = -1;           // A/2 - 64
  int32_t intakeAir = -1;               // A - 40
  int32_t throttlePosition = -1;        // A/2.55
  

  /////////////////////////////////////////////////////// RPM
  float tempRPM = myELM327.rpm();
  
  Serial.print("Payload received for rpm: ");
  for (byte i = 0; i < myELM327.PAYLOAD_LEN; i++)
    Serial.write(myELM327.payload[i]);
  Serial.println();
  
  if (myELM327.status == ELM_SUCCESS)
  {
    rpm = (int32_t)tempRPM;
    Serial.print("RPM: "); Serial.println(rpm);
  }
  else
      printError();

  /////////////////////////////////////////////////////// Kph
  float kphTemp = myELM327.kph();
  
  Serial.print("Payload received for kph: ");
  for (byte i = 0; i < myELM327.PAYLOAD_LEN; i++)
    Serial.write(myELM327.payload[i]);
  Serial.println();
  
  if (myELM327.status == ELM_SUCCESS)
  {
    kph = (int32_t)kphTemp;
    Serial.print("KPH: "); Serial.println(kph);
  }
  else
      printError();

  /////////////////////////////////////////////////////// Engine Load
  if (myELM327.queryPID(SERVICE_01, ENGINE_LOAD))
  {
    int32_t engineLoadTemp = myELM327.findResponse();

    Serial.print("Payload received for Engine Load: ");
    for (byte i = 0; i < myELM327.PAYLOAD_LEN; i++)
      Serial.write(myELM327.payload[i]);
    Serial.println();
    
    if (myELM327.status == ELM_SUCCESS)
    {
      engineLoad = engineLoadTemp/2.55;
      Serial.print("EngineLoad: "); Serial.println(engineLoad);
    }
    else
      printError();
  }


  /////////////////////////////////////////////////////// engineCoolant = -1;       // A - 40
  if (myELM327.queryPID(SERVICE_01, ENGINE_COOLANT_TEMP))
  {
    int32_t engineCoolantTemp = myELM327.findResponse();

    Serial.print("Payload received for Engine Coolant: ");
    for (byte i = 0; i < myELM327.PAYLOAD_LEN; i++)
      Serial.write(myELM327.payload[i]);
    Serial.println();
    
    if (myELM327.status == ELM_SUCCESS)
    {
      engineCoolant = engineCoolantTemp - 40;
      Serial.print("EngineCoolant: "); Serial.println(engineCoolant);
    }
    else
      printError();
  }


  /////////////////////////////////////////////////////// shortTermFuelTrimBank1 = -1;  // A/1.28 - 100
  if (myELM327.queryPID(SERVICE_01, SHORT_TERM_FUEL_TRIM_BANK_1))
  {
    int32_t shortTermFuelTrimBank1Temp = myELM327.findResponse();

    Serial.print("Payload received for Short Term Fuel Trim Bank 1: ");
    for (byte i = 0; i < myELM327.PAYLOAD_LEN; i++)
      Serial.write(myELM327.payload[i]);
    Serial.println();
    
    if (myELM327.status == ELM_SUCCESS)
    {
      shortTermFuelTrimBank1 = shortTermFuelTrimBank1Temp/1.28 - 100;
      Serial.print("Short Term Fuel Trim Bank1: "); Serial.println(shortTermFuelTrimBank1);
    }
    else
      printError();
  }

  /////////////////////////////////////////////////////// longTermFuelTrimBank1 = -1;   // A/1.28 - 100
  if (myELM327.queryPID(SERVICE_01, LONG_TERM_FUEL_TRIM_BANK_1))
  {
    int32_t longTermFuelTrimBank1Temp = myELM327.findResponse();

    Serial.print("Payload received for Long Term Fuel Trim Bank 1: ");
    for (byte i = 0; i < myELM327.PAYLOAD_LEN; i++)
      Serial.write(myELM327.payload[i]);
    Serial.println();
    
    if (myELM327.status == ELM_SUCCESS)
    {
      longTermFuelTrimBank1 = longTermFuelTrimBank1Temp/1.28 - 100;
      Serial.print("Long Term Fuel Trim Bank1: "); Serial.println(longTermFuelTrimBank1);
    }
    else
      printError();
  }

  /////////////////////////////////////////////////////// intakeManifoldPressure = -1;  // A
  if (myELM327.queryPID(SERVICE_01, INTAKE_MANIFOLD_ABS_PRESSURE))
  {
    int32_t intakeManifoldPressureTemp = myELM327.findResponse();

    Serial.print("Payload received for Intake Manifold Absolute Pressure: ");
    for (byte i = 0; i < myELM327.PAYLOAD_LEN; i++)
      Serial.write(myELM327.payload[i]);
    Serial.println();
    
    if (myELM327.status == ELM_SUCCESS)
    {
      intakeManifoldPressure = intakeManifoldPressureTemp;
      Serial.print("Intake Manifold Absolute Pressure: "); Serial.println(intakeManifoldPressure);
    }
    else
      printError();
  }


  /////////////////////////////////////////////////////// timingAdvance = -1;           // A/2 - 64
  if (myELM327.queryPID(SERVICE_01, TIMING_ADVANCE))
  {
    int32_t timingAdvanceTemp = myELM327.findResponse();

    Serial.print("Payload received for Timing Advance: ");
    for (byte i = 0; i < myELM327.PAYLOAD_LEN; i++)
      Serial.write(myELM327.payload[i]);
    Serial.println();
    
    if (myELM327.status == ELM_SUCCESS)
    {
      timingAdvance = timingAdvanceTemp;
      Serial.print("Timing Advance: "); Serial.println(timingAdvance);
    }
    else
      printError();
  }

  /////////////////////////////////////////////////////// intakeAir = -1;               // A - 40
  if (myELM327.queryPID(SERVICE_01, INTAKE_AIR_TEMP))
  {
    int32_t intakeAirTemp = myELM327.findResponse();

    Serial.print("Payload received for Intake Air Temp: ");
    for (byte i = 0; i < myELM327.PAYLOAD_LEN; i++)
      Serial.write(myELM327.payload[i]);
    Serial.println();
    
    if (myELM327.status == ELM_SUCCESS)
    {
      intakeAir = intakeAirTemp - 40;
      Serial.print("Intake Air Temp: "); Serial.println(intakeAir);
    }
    else
      printError();
  }

  /////////////////////////////////////////////////////// throttlePosition = -1;        // A/2.55
  if (myELM327.queryPID(SERVICE_01, THROTTLE_POSITION))
  {
    int32_t throttlePositionTemp = myELM327.findResponse();

    Serial.print("Payload received for Throttle Position: ");
    for (byte i = 0; i < myELM327.PAYLOAD_LEN; i++)
      Serial.write(myELM327.payload[i]);
    Serial.println();
    
    if (myELM327.status == ELM_SUCCESS)
    {
      throttlePosition = throttlePositionTemp/2.55;
      Serial.print("Throttle Position: "); Serial.println(throttlePosition);
    }
    else
      printError();
  }



/*  for (int a = 0; a < 5; ++a)
  {
    digitalWrite(LED_BUILTIN,HIGH);
    delay(200);
    digitalWrite(LED_BUILTIN,LOW);
    delay(200); 
  }
  */
}


void printError()
{
  if (myELM327.status == ELM_SUCCESS)
    Serial.println(F("\tELM_SUCCESS"));
  else if (myELM327.status == ELM_NO_RESPONSE)
    Serial.println(F("\tERROR: ELM_NO_RESPONSE"));
  else if (myELM327.status == ELM_BUFFER_OVERFLOW)
    Serial.println(F("\tERROR: ELM_BUFFER_OVERFLOW"));
  else if (myELM327.status == ELM_GARBAGE)
    Serial.println(F("\tERROR: ELM_GARBAGE"));
  else if (myELM327.status == ELM_UNABLE_TO_CONNECT)
    Serial.println(F("\tERROR: ELM_UNABLE_TO_CONNECT"));
  else if (myELM327.status == ELM_NO_DATA)
    Serial.println(F("\tERROR: ELM_NO_DATA"));
  else if (myELM327.status == ELM_STOPPED)
    Serial.println(F("\tERROR: ELM_STOPPED"));
  else if (myELM327.status == ELM_TIMEOUT)
    Serial.println(F("\tERROR: ELM_TIMEOUT"));
  else if (myELM327.status == ELM_GENERAL_ERROR)
    Serial.println(F("\tERROR: ELM_GENERAL_ERROR"));

  delay(500);
}

Wiring
N/A

Screenshots
N/A

Additional Context
N/A

Troubleshooting Connection Issues

  • Try 38400 baud instead of 115200 ---> Tried with no success.
  • If using an HC-05 for communication, ensure it's configured properly ---> N/A
  • Ensure the car is on and running during troubleshooting ---> Car running
    -Also tested with BT mac address, and providing PIN code, with no success.

Possible integer overflow?

Hi!

I am currently having rare weird issues in my program. For me, some numbers in other variables in a struct changes with no obvious reasons. I am reading kph and store that return value into a struct property.

As i am no c++ expert, i want to kindly ask the question, if this can overflow in some cases.

It is fact that kph sometimes gives extraordinary high numbers when something weird is happening on the OBD device.

So, lets face the code: - I use this function

int32_t ELM327::kph()

I noticed that it casts to int32_t. This function internally calls findResponse which returns a uint64_t.

uint64_t ELM327::findResponse()

So it ends up in a uint64_t to int32_t cast - Is it technically possible that this does overflow and effect other memory in code?

printerror interval & ELM NO DATA error

hi mr.powerbroker2

First of all while I'm coding my project, suddenly the printerror interval increased compared to your example code "softwareserial_test"
what would be the problem....possibly, Is it the reason that void_loop processing was increased?

second of all this below is my sketch code,, For the last time ELM327 successfully received the data from the car obd2, meanwhile suddenly when I connect to obd2 port, ELM NO DATA error occurs... I don;t even know what the f is happening. obd2 vcc normally works, and it doesn't seem to be the obd2 cable problem. Can u give me a advice plz??
thanks ><

and asking for another little help.... I want my green and red LED to indicate current status e.g. ELM327 CONNECTED, VALUES ARE RECEIVING, PRINTERROR.... It's a little bit hard for me.. actually i'm not a programmer.. plz understand

#include <SoftwareSerial.h>
#include "ELMduino.h"

SoftwareSerial mySerial(2, 3); // RX, TX
#define ELM_PORT mySerial
#define BUZZER 10

ELM327 myELM327;

int green = 8;
int red = 9;

uint32_t rpm = 0;
uint32_t kph = 0;
uint32_t engineload = 0;
uint32_t enginetemp = 0;
uint32_t throttleposition = 0;
uint32_t intakeairtemp = 0;
int32_t fueltype = 0;

const int beepFrequencyS1 = 1500; //1000Hz, 연결성공시 부저 주파수1
const int beepFrequencyS2 = 2500; //1000Hz, 연결성공시 부저 주파수2
const int beepFrequencyF = 750; //500Hz, 연결실패시 부저 주파수
const int beepDurationS = 250; //1.0sec ,성공시 지속시간, (31~65535 Hz)
const int beepDurationF = 250; //0.25sec ,실패시 지속시간,

unsigned long pre_time = 0;
unsigned long cur_time = 0;
int ledState = LOW;

void setup()
{
pinMode(green, OUTPUT);
pinMode(red, OUTPUT);
pre_time = millis();
#if LED_BUILTIN
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
#endif

Serial.begin(38400);
ELM_PORT.begin(38400);

Serial.println("Attempting to connect to ELM327...");

if (!myELM327.begin(ELM_PORT))
{
    Serial.println("Couldn't connect to OBD scanner");
    do {
        digitalWrite(red, HIGH);
        digitalWrite(green, LOW);
        tone(BUZZER, beepFrequencyF, beepDurationF);
        delay(850); //ELM327 연결 실패시 0.85초 간격으로 Fail 부저 울림
    } while (1);
}

Serial.println("Connected to ELM327");
digitalWrite(green, HIGH);
digitalWrite(red, LOW);
tone(BUZZER, beepFrequencyS1, beepDurationS);
delay(600);
tone(BUZZER, beepFrequencyS1, beepDurationS);
delay(600);
tone(BUZZER, beepFrequencyS1, beepDurationS);
delay(600);
tone(BUZZER, beepFrequencyS2, beepDurationS);
delay(600);
noTone(10);

//ELM327 연결 성공시 Success 부저 4회 울림, led green ON

}

void loop()
{
cur_time = millis();
float tempRPM = myELM327.rpm();
int32_t tempVEHICLE_SPEED = myELM327.kph();
float tempENGINE_LOAD = myELM327.engineload();
float tempENGINE_COOLANT_TEMP = myELM327.enginetemp();
float tempTHROTTLE_POSITION = myELM327.throttleposition();
float tempINTAKE_AIR_TEMP = myELM327.intakeairtemp();
float tempFUEL_TYPE = myELM327.fueltype();

digitalWrite(red, LOW);

if (cur_time = pre_time >= 500)
{
    pre_time = cur_time;
    if (ledState == LOW) {
        ledState = HIGH;
    }
    else {
        ledState = LOW;
    }
    digitalWrite(green, ledState);
    
}
if (myELM327.status == ELM_SUCCESS)
{
    rpm = (uint32_t)tempRPM;
    Serial.print("RPM : "); Serial.println(rpm);

    kph = (int32_t)tempVEHICLE_SPEED;
    Serial.print(kph); Serial.println("km/h");

    engineload = (uint32_t)tempENGINE_LOAD;
    Serial.print("EngineLoad : "); Serial.print(engineload); Serial.println("%");

    enginetemp = (uint32_t)tempENGINE_COOLANT_TEMP;
    Serial.print("EngineCoolantTemp : "); Serial.print(enginetemp); Serial.println("˚C");

    throttleposition = (uint32_t)tempTHROTTLE_POSITION;
    Serial.print("ThrottlePosition : "); Serial.print(throttleposition); Serial.println("%");

    intakeairtemp = (uint32_t)tempINTAKE_AIR_TEMP;
    Serial.print("IntakeAirTemp : "); Serial.print(intakeairtemp); Serial.println("˚C");

    fueltype = (int32_t)tempFUEL_TYPE;
    Serial.print("FuelType : "); Serial.print(fueltype);

    if (fueltype == 0)
        Serial.println("Not Available");
    else if (fueltype == 1)
        Serial.println("Gasoline");
    else if (fueltype == 2)
        Serial.println("Methanol");
    else if (fueltype == 3)
        Serial.println("Ethanol");
    else if (fueltype == 4)
        Serial.println("Disel");
    else if (fueltype == 5)
        Serial.println("LPG");
    else if (fueltype == 6)
        Serial.println("CNG");
    else if (fueltype == 7)
        Serial.println("Propane");

        tone(BUZZER, beepFrequencyS2, beepDurationS);


    delay(500);

}
else
printError();

}

void printError()
{
digitalWrite(green, LOW);
Serial.print("Received: ");
for (byte i = 0; i < myELM327.recBytes; i++)
Serial.write(myELM327.payload[i]);
Serial.println();

if (myELM327.status == ELM_SUCCESS)
    Serial.println(F("\tELM_SUCCESS"));
else if (myELM327.status == ELM_NO_RESPONSE)
    Serial.println(F("\tERROR: ELM_NO_RESPONSE"));
else if (myELM327.status == ELM_BUFFER_OVERFLOW)
    Serial.println(F("\tERROR: ELM_BUFFER_OVERFLOW"));
else if (myELM327.status == ELM_UNABLE_TO_CONNECT)
    Serial.println(F("\tERROR: ELM_UNABLE_TO_CONNECT"));
else if (myELM327.status == ELM_NO_DATA)
    Serial.println(F("\tERROR: ELM_NO_DATA"));
else if (myELM327.status == ELM_STOPPED)
    Serial.println(F("\tERROR: ELM_STOPPED"));
else if (myELM327.status == ELM_TIMEOUT)
    Serial.println(F("\tERROR: ELM_TIMEOUT"));
else if (myELM327.status == ELM_TIMEOUT)
    Serial.println(F("\tERROR: ELM_GENERAL_ERROR"));
    
    tone(BUZZER, beepFrequencyF, beepDurationF);

delay(10);

}

ELM32_test.ino ESP32 SerialBT.write("a") returns 0 bytes written

Describe the bug
ELM32_test.ino vs Android apps (sketch sends nothing to bluetooth peer)

To Reproduce
Using a generic esp32 dev board.
This works in Android:

Screenshot_2020-07-03-01-20-01

Screenshot_2020-07-03-02-24-21

image

image

As you see, sending atrv works in Android (BMW 2004 330ci). I was hoping for the same behavior, send atz, atrv etc get a reply in the serial monitor.

Make Lib use a WiFi Telnet

Hi,

it is me again... I finally got one thing working: Use a ESP32 to connect it to a WiFi OBD adapter:

#include <WiFi.h>

#include <HTTPClient.h>

#include <elapsedMillis.h>

elapsedMillis printTime = 1000;

//#define ELM_PORT client
#define DEBUG_PORT Serial

WiFiClient telnet;
const int telnetPort = 35000;



void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  
  WiFi.begin("WiFi_OBDII", NULL);
  while (WiFi.status() != WL_CONNECTED) {
     delay(500);
     Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
    
  
  if (!telnet.connect("192.168.0.10", telnetPort)) {
     Serial.println("connection failed");
     return;
  } else {
    telnet.print("AT Z");
    telnet.print('\r');
  }  
}

void loop() {
  // put your main code here, to run repeatedly:
  //EMPFANGEN
  while (telnet.available() > 0) {
     char c = telnet.read();

     if(c == '>') {
        Serial.println();        
     } else {
      Serial.write(c);
     }
  }
  //SENDEN
  if (printTime > 1000) {
  if (telnet.connect("192.168.0.10", telnetPort)) {
    telnet.print("AT RV");
    telnet.print('\r'); 
    printTime = 0;
  } else {
    Serial.println("connection failed");
  }}  
}

[This is my fast and dirty 'does it work' code]

Basically those adapters provide an open WiFi network ("WiFi_OBDII") with a telnet server at port 35000.

This telnet server passes through the serial interface. If you send a AT Z, you will get your response.

May it be possible to use this connection for your (by the way great) library?

Best regards

Pascal

ELM327 with USB cable

Hi!

I would guess that your library works using bluetooth commands, isn't it?

Have you ever saw this EML327 adapter?
image
I would be curios to know if it the same command you send via BT works the same via Serial, do you have any idea?

hi mr. powerbroker2 . question for the coding..

hello mr.

nowadays I'm really struggling to make a code
my goal is to get some PID responses to collect the information.
Actually I'm not a programmer, so it's damn fuckin super hard for me to coding..

first of all I added some PIDs in .cpp file like this
float ELM327::engineload()
{
if( status == ELM_SUCCESS)
return findResponse();

         return ELM_GENERAL_ERROR;

}

I also add float engineload(); on my .h file

then this is my sketch
#include <SoftwareSerial.h>
#include "ELMduino.h"

SoftwareSerial mySerial(2, 3); // RX, TX
#define ELM_PORT mySerial

ELM327 myELM327;

uint32_t rpm = 0;
uint32_t kph = 0;
uint32_t engineload= 0;

void setup()
{
#if LED_BUILTIN
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
#endif

Serial.begin(38400);
ELM_PORT.begin(38400);

Serial.println("Attempting to connect to ELM327...");

if (!myELM327.begin(ELM_PORT))
{
Serial.println("Couldn't connect to OBD scanner");
while (1);
}

Serial.println("Connected to ELM327");
}

void loop()
{
float tempRPM = myELM327.rpm();
int32_t tempVEHICLE_SPEED = myELM327.kph();
float tempENGINELOAD = myELM327.engineload();

//////////////////rpm///////////////////
if (myELM327.status == ELM_SUCCESS)
{
rpm = (uint32_t)tempRPM;
Serial.print("RPM: "); Serial.println(rpm);
}
///////////////////////////////////////////
///////////////////////kph///////////////////
if (myELM327.status == ELM_SUCCESS)
{
kph = (int32_t)tempVEHICLE_SPEED;
Serial.print(kph); Serial.println("km/h");
}

///////////////////////////////////////////////
///////////////////engineload/////////////////
if (myELM327.status == ELM_SUCCESS)
{
engineload = (uint32_t)tempENGINELOAD;
Serial.print("EngineLoad: "); Serial.println(engineload);
}
////////////////////////////////////////////
else
printError();

}

void printError()
{
Serial.print("Received: ");
for (byte i = 0; i < myELM327.recBytes; i++)
Serial.write(myELM327.payload[i]);
Serial.println();

if (myELM327.status == ELM_SUCCESS)
Serial.println(F("\tELM_SUCCESS"));
else if (myELM327.status == ELM_NO_RESPONSE)
Serial.println(F("\tERROR: ELM_NO_RESPONSE"));
else if (myELM327.status == ELM_BUFFER_OVERFLOW)
Serial.println(F("\tERROR: ELM_BUFFER_OVERFLOW"));
else if (myELM327.status == ELM_UNABLE_TO_CONNECT)
Serial.println(F("\tERROR: ELM_UNABLE_TO_CONNECT"));
else if (myELM327.status == ELM_NO_DATA)
Serial.println(F("\tERROR: ELM_NO_DATA"));
else if (myELM327.status == ELM_STOPPED)
Serial.println(F("\tERROR: ELM_STOPPED"));
else if (myELM327.status == ELM_TIMEOUT)
Serial.println(F("\tERROR: ELM_TIMEOUT"));
else if (myELM327.status == ELM_TIMEOUT)
Serial.println(F("\tERROR: ELM_GENERAL_ERROR"));

delay(100);
}

Acutually It's too hard to understand when to use "float" "uint32_t" "int8_t" like this kind of things. Absolutely I know the concept of these things since I ve been studying.. however, when I get in to the sketch or cpp I BECOME A FUCKING IDIOT.. plz help me EXPERT powerbroker2...
I will expect your generous reply thank you~

ESP32 crashes during startup

Hi there, thanks for your work on this library. Unfortunately in my environment / setup, the ESP32 crashes during startup, I get the following backtrace from the serial debug monitor:

Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.
Core 1 register dump:
PC      : 0x400014fd  PS      : 0x00060330  A0      : 0x800e6ed0  A1      : 0x3ffc83c0  
A2      : 0x00000030  A3      : 0x0000002c  A4      : 0x000000ff  A5      : 0x0000ff00  
A6      : 0x00ff0000  A7      : 0xff000000  A8      : 0x00000000  A9      : 0x3ffc8690  
A10     : 0x00000003  A11     : 0x00060323  A12     : 0x00060320  A13     : 0x3ffbc480  
A14     : 0x00000000  A15     : 0x3ffc68e0  SAR     : 0x0000000a  EXCCAUSE: 0x0000001c  
EXCVADDR: 0x00000030  LBEG    : 0x400014fd  LEND    : 0x4000150d  LCOUNT  : 0xffffffff  

Backtrace: 0x400014fd:0x3ffc83c0 0x400e6ecd:0x3ffc83d0 0x400e5019:0x3ffc86e0 0x400d2462:0x3ffc87a0 0x400d24ee:0x3ffc87d0 0x400d1865:0x3ffc87f0 0x400d2f3f:0x3ffc8830 0x4008e0bd:0x3ffc8850

Rebooting...
ets Jun  8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:8896
load:0x40080400,len:5816
entry 0x400806ac

Pasting this into the Exception Decoder I get this:

PC: 0x400014fd
EXCVADDR: 0x00000030

Decoding stack results
0x400e6ecd: _svfprintf_r at ../../../.././newlib/libc/stdio/vfprintf.c line 1529
0x400e5019: sprintf at ../../../.././newlib/libc/stdio/sprintf.c line 644
0x400d2462: ELM327::initializeELM(char) at /Users/patrickfelstead/Documents/Arduino/libraries/ELMDuino/src/ELMduino.cpp line 101
0x400d24ee: ELM327::begin(Stream&, unsigned short, char) at /Users/patrickfelstead/Documents/Arduino/libraries/ELMDuino/src/ELMduino.cpp line 39
0x400d1865: setup() at /Users/patrickfelstead/Documents/OBD_bluetooth/OBD_bluetooth.ino line 26
0x400d2f3f: loopTask(void*) at /Users/patrickfelstead/Library/Arduino15/packages/esp32/hardware/esp32/1.0.4/cores/esp32/main.cpp line 14
0x4008e0bd: vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c line 143

Easier to view with color coding, see screen grab:

Screen Shot 2020-05-22 at 12 11 14 pm

As you can see it is crashing due to sprintf function in ELM327::initializeELM(char protocol)

I think it's because you specified a string with %s for the auto search protocol character. So I made this change and bingo - no more crashing!!

Was:

const char * const TRY_PROT_H_AUTO_SEARCH     = "AT TP A%s"; // OBD

changed to

const char * const TRY_PROT_H_AUTO_SEARCH     = "AT TP %c"; // OBD

Now I'm at the point where I see Connected to ELM327 which is great! But it's not reading RPM. I haven't had the time to debug further, but at least I'm communicating with my ELM clone dongle, so I can try some basic commands like reading voltage with AT RV etc. I believe I can use your ESP32_test.ino sketch to do that.

Received:

Hi. i am using elm327 wifi and esp32

My code:

#include <WiFi.h>
#include "ELMduino.h"


const char* ssid = "WiFi-OBDII";
const char* password = "your-password";


//IP Adress of your ELM327 Dongle
IPAddress server(192, 168, 0, 10);
WiFiClient client;
ELM327 myELM327;


uint32_t rpm = 0;


void setup()
{
  Serial.begin(115200);

  // Connecting to ELM327 WiFi
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid);
  // WiFi.begin(ssid, password); //Use this line if your ELM327 has a password protected WiFi

  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("Connected to Wifi");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  if (client.connect(server, 35000))
    Serial.println("connected");
  else
  {
    Serial.println("connection failed");
    while(1);
  }

  myELM327.begin(client,5);
}


void loop()
{
  float tempRPM = myELM327.rpm();

  if (myELM327.status == ELM_SUCCESS)
  {
    rpm = (uint32_t)tempRPM;
    Serial.print("RPM: "); Serial.println(rpm);
  }
  else{
    printError();
  }
}


void printError()
{
  Serial.print("Received: ");
  for (byte i = 0; i < myELM327.recBytes; i++)
    Serial.write(myELM327.payload[i]);
  Serial.println();
  
  if (myELM327.status == ELM_SUCCESS)
    Serial.println(F("\tELM_SUCCESS"));
  else if (myELM327.status == ELM_NO_RESPONSE)
    Serial.println(F("\tERROR: ELM_NO_RESPONSE"));
  else if (myELM327.status == ELM_BUFFER_OVERFLOW)
    Serial.println(F("\tERROR: ELM_BUFFER_OVERFLOW"));
  else if (myELM327.status == ELM_UNABLE_TO_CONNECT)
    Serial.println(F("\tERROR: ELM_UNABLE_TO_CONNECT"));
  else if (myELM327.status == ELM_NO_DATA)
    Serial.println(F("\tERROR: ELM_NO_DATA"));
  else if (myELM327.status == ELM_STOPPED)
    Serial.println(F("\tERROR: ELM_STOPPED"));
  else if (myELM327.status == ELM_TIMEOUT)
    Serial.println(F("\tERROR: ELM_TIMEOUT"));
  else if (myELM327.status == ELM_TIMEOUT)
    Serial.println(F("\tERROR: ELM_GENERAL_ERROR"));

  delay(100);
}

My Output:

Received:
Received:
Received:
Received:
Received:

I guess it doesn't meet the following condition
if` (myELM327.status == ELM_SUCCESS){}

*How can I list if using custom pid? I think the "at" commands are not valid in the wireless network on elm327.Is there an android app where I can list pids?
*Most importantly, could this problem be caused by custom pid?

can it run on ESP32?

Hi,
was really happy to find this one, intending to do a "My own CO2 footprint" project.
Could you please help me in providing an Arduino example running on an ESP32 - which has Bluetooth build in. When I compile the sample sketch, it simply gives "serial3 unknown" error. Sorry for being only a hobby programmer....

Response byte ordering.

Have been using the latest version 2.4.1 which now has the monitorStatus method.

Reading the https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_01_PID_01

This refers to byte 1, bit 7 being the MIL status bit.

A request for this PID returns 4 bytes of data (Big-endian).

When using Bit-Encoded-Notation, quantities like C4 means bit 4 from data byte C. Each bit is numerated from 0 to 7, so 7 is the most significant bit and 0 is the least significant bit (See below).

The first byte(A) contains two pieces of information. Bit A7 (MSB of byte A, the first byte) indicates whether or not the MIL (check engine light) is illuminated.

I tried to read this out of response_byte0, but the data didn't look right. Worked out that its actually in response_byte3 as this is the 4th byte from the right in binary terms.

My question is.

  1. Is Wikipedia wrong labeling this as byte A and referring to it as the first byte and its should be should it be byte D last byte?
  2. Should the response bytes be stored in the same order??
  3. Are both correct but it just needs to be made clear that response bytes in the library are reversed and in little endian?

Supported PIDs decoder

In Issue #33 somebody was wanting to use a PID which happened to be unsupported by their vehicle. To get a list of PIDs supported by your vehicle, run this code. I have this function in the main loop().

/*
-----------------
  Detect if user has pressed 'd' key on serial monitor keyboard, if yes
  End the bluetooth connection and halt the processor
  If 'P' is pressed, outputs the PIDs that the vehicle supports.
-----------------
*/
void quit_prog_if_keypressed() {
  if (Serial.available()) {
    char c = Serial.read();
    if (c == 'd') {
      Serial.println("Disconnecting bluetooth connection");
      SerialBT.disconnect();
      //Serial.println("Closing bluetooth connection");
      //SerialBT.end();
      Serial.println("Halting program ... ");

      #ifdef lcd_20x4_line_char
      lcd.clear();
      lcd.setCursor(0,0);
      //         01234567890123456789
      lcd.print("Disconnect Bluetooth");
      lcd.setCursor(0,1);
      //         01234567890123456789
      lcd.print("Halting program ...");
      #endif

      while(1);
    }
    else if (c == 'P') {
      char cmd[100] = {0};
      // Print supported PIDS
      Serial.println("\n\nPrint supported PIDs *** Turn engine ON ***");
      Serial.println("Turning ECU filter off ATCRA");
      boost.send_command("ATCRA");
      Serial.println("Turning headers ON ATH1");
      boost.send_command("ATH1");
      Serial.println("Turning line feeds ON ATL1");
      boost.send_command("ATL1");
      Serial.println("Turning spaces ON ATS1\n");
      boost.send_command("ATS1");
      
      Serial.println("=================================");
      for (int i=0;i<6;i++) {
        uint8_t pid = i * 32;
        Serial.printf("Supported PIDs: 01%02X\n", pid);
        sprintf(cmd, "01%02X", pid);
        boost.send_command(cmd);
        Serial.printf("%s", boost.OBD_response);
        delay(100);
      }
      Serial.println("=================================\n");
      Serial.println("Disconnecting bluetooth connection");
      SerialBT.disconnect();
      Serial.println("Halting program ... ");
      while(1);
    }
  }
}

Note that the command "ATCRA" turns off the filter which prevents responses from the second ECU (with response address 7E9) in my car. If this is left on, you get duplicated responses to some PIDs (as your code deals with).

FYI to apply the filter to prevent responses from the 7E9 ECU, issue this command during initialize:
send_command("ATCRA7E8"); // Set CAN hardware filter to receive from address 7E8 only

Sample output from a 2019 BMW is:

Supported PIDs: 0100
7E8 06 41 00 BF BF A8 93 
7E9 06 41 00 98 18 80 01 

Supported PIDs: 0120
7E8 06 41 20 A0 07 B1 19 
7E9 06 41 20 80 00 00 01 

Supported PIDs: 0140
7E8 06 41 40 FE D0 85 11 
7E9 06 41 40 C0 00 00 00 

Supported PIDs: 0160
7E8 06 41 60 64 00 00 01 

Supported PIDs: 0180
7E8 06 41 80 00 04 00 0D 

Supported PIDs: 01A0
7E8 06 41 A0 04 00 00 00 

And then paste the above output into this google spreadsheet I put together. It's not that pretty in terms of user input....but anyway, put it in cell J16 (the black cells). Then edit cell B15 to point to the group of 32 PIDs you're interested in. Or alternatively, just paste a single line response into Cell J16.

https://docs.google.com/spreadsheets/d/1R2C8_kY6fuFpd4JJyIrWOXqAQGgxm2MHYHXRt3qiKCc/edit?usp=sharing

Where column E says "Yes" it means that PID is supported by the vehicle.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.