Support for j1850 VPW/PWM?

This at least does something with the Due…

it does detect the start of frame properly, so its a start.

I dont know why it seems to stop working after its detected the SOF though.

//
// J1850 decoder, v1.5
// Mats Ekberg (c) 2013
// 
// This software decodes the binary data stream from a J1850 compliant 
// OBD2 device. Using just one digital input for the data stream and
// transmitting the decoded data on the UART.
//
// http://www.systemconnection.com/downloads/PDFs/OBD-IIJ1850Whitepaper.pdf
//
// Chip:  Arduino Mini Pro w ATmega168 at 16MHz
//
#include <DueTimer.h>

// I/O and interrupts. Pin/Int
// UNO:   2/0, 3/1. 
// Mega:  2/0, 3/1, 21/2, 20/3, 19/4, 18/5
#define J1850_PIN  15
#define J1850_INT  15

unsigned long tstamp2;
unsigned long delta2;
unsigned long longbit2;
unsigned long shortbit2;
unsigned long abyte2;


// Timing for start of frame
#define SOF_TIME      200
#define SOF_DEV        18

// Timing for end of frame
#define EOF_TIME      200
#define EOF_DEV        18

// Timing for a long bit pulse
#define LONGBIT_TIME   128
#define LONGBIT_DEV     16

// Timing for short bit pulse
#define SHORTBIT_TIME   64
#define SHORTBIT_DEV    15

// timeout after 250 microsec
#define  TMR_PRELOAD (65536 - (EOF_TIME*16))

#define TMROVF_INT_OFF   Timer1.stop(); 
#define TMROVF_INT_ON    Timer1.start(); 
#define TMROVF_INT_CLR   Timer1.setPeriod(0);


// forward decl
void j1850_interrupt(void);

volatile boolean idle = true;
// Storage, max 11 data bytes + CRC
#define BUFSIZE 13
volatile uint8_t msgbuf[BUFSIZE];
volatile uint8_t msgLen;


//
// Initialization
//
void setup(void) 
{  
  pinMode(J1850_PIN, INPUT);
  Serial.begin(115200);
  delay(2000);
  Serial.println(F("DUE_j1850decoder/v1.5"));

  TMROVF_INT_OFF;
//  TCCR1A = 0;
  Timer1.setPeriod(TMR_PRELOAD);  // preload timer 65536-16MHz/256/2Hz
//  TCCR1B = _BV(CS10);  // no prescaler, start timer 

  idle = true;
  msgLen = 0;
  attachInterrupt(J1850_INT, j1850_interrupt, CHANGE);
  interrupts();
}


//
// Background loop - print message when available
//
void loop(void)
{
  if (msgLen > 0) {
    Serial.print(F("> "));
    for (int i = 0; i < msgLen; i++) {
          if ((i == 3) || (i == msgLen -1)){
        Serial.print("   ");
      }
      if (msgbuf[i] < 16) Serial.print("0");
      Serial.print(msgbuf[i], HEX);
    }
    Serial.println();
    msgLen = 0;
  }
}

//
// Interrupt routine for changes on j1850 data pin
//

volatile unsigned long lastInt = 0;
volatile uint8_t bitCnt;
volatile long delta;
volatile unsigned long tstamp;
volatile uint8_t aByte;
volatile uint8_t buf[BUFSIZE];
volatile uint8_t bufIdx;


void j1850_interrupt(void)
{

  tstamp = micros();

  uint8_t pin = digitalRead(J1850_PIN);

  // reload the overflow timer with EOF timeout
  Timer1.setPeriod(TMR_PRELOAD);         
  delta = tstamp - lastInt;
  long longbit, shortbit;

  if (idle)
  {
    if (pin == 0) 
    {
      longbit = delta - SOF_TIME;
      if (abs(longbit) < SOF_DEV)
      {
        Serial.println("SOF");
        // found SOF, start header/data sampling
        idle = false;
        bitCnt = 0;
        bufIdx = 0;
        aByte = 0;
      }
    }
  } 
  else
  {
    shortbit = delta - SHORTBIT_TIME;
    longbit = delta - LONGBIT_TIME;
    if (abs(shortbit) < SHORTBIT_DEV) {
      // short pulse
      if (pin == 0)
        // short pulse & pulse was high => active "1"
        aByte = (aByte << 1) | 0x01;
      else
        // short pulse & pulse was low => passive "0"
        aByte = (aByte << 1) & 0xFE;
      bitCnt++;
    } 
    else if (abs(longbit) < LONGBIT_DEV) {
      // long pulse
      if (pin == 0)
        // long pulse & pulse was high => active "0"
        aByte = (aByte << 1) & 0xFE;

      else
        // long pulse & pulse was low => passive "1"
        aByte = (aByte << 1) | 0x01;
      bitCnt++;

    } 
    else {
      // unknown bit, reset
      TMROVF_INT_OFF; 
      idle = true;
      lastInt = tstamp;
      return;
    }

    if (bitCnt >= 8) {

      buf[bufIdx++] = aByte;
      bitCnt = 0;
      if (bufIdx >= sizeof(buf)) {
        // too many data bytes, error
        TMROVF_INT_OFF; 
        idle = true;
      } 
      else {
        // if all is ok, start the EOF timeout
        TMROVF_INT_CLR; 
        TMROVF_INT_ON; 
      }
//              abyte2 = aByte;
    }
  }
  lastInt = tstamp;
}

// Timer overlflow interrupt
// Occurs when the EOF pulse times out the timer
//
ISR(TIMER1_OVF_vect)  
{

  Timer1.setPeriod(TMR_PRELOAD);  
  TMROVF_INT_OFF; 

  // copy the data so that we can start to fill the buffer again
  // but only if the buffer has been consumed in the background
  if (bufIdx > 0 && msgLen == 0)
  {
    memcpy((void*)msgbuf, (const void*)buf, bufIdx);
    msgLen = bufIdx;
  }
  idle = true;
}

Good practice
Should you not test the incrementing of the buffer pointer “bufIdx++” will not exceed the bounds of the buffer before writing to the buffer otherwise you could start writing to random areas of memory & then all hell would break out
’’'
if (bitCnt >= 8) {

  buf[bufIdx++] = aByte;
  bitCnt = 0;
  if (bufIdx >= sizeof(buf)) {
    // too many data bytes, error
    TMROVF_INT_OFF; 
    idle = true;
  } 
  else {
    // if all is ok, start the EOF timeout
    TMROVF_INT_CLR; 
    TMROVF_INT_ON; 
  }

// abyte2 = aByte;
}

‘’’

Now that I have my setup so I can actually start working on it will see how this goes…

To tired to do anything tonight but going to try to get this going tomorrow. I now have my UTH module wired to my simulator. Just have to add power to it and test it out. Assuming this works I should be able to start testing myself shortly.

Rodney

Ahh shoulda left that out.

The abyte2, delta2, longbit2, etc

was purely just for debug purposes, I wanted to see where the sketch was going in real life, and what the Macchina was actually reading/seeing.

Ignore them…

Here is another library I found…

1 Like

Ok…now have complete success with my Uno and MCZ33990!!!

I can now send and receive messages on J1850-VPW.

I found this library, but I had to modify it and invert some things before it would work with the MCZ33990…but it works now as you can see in the pics.

Im gonna try it on the Macchina now that I know it 100% works for receiving AND sending using a “proper” J1850 transceiver chip…now to see if it works the same with the Macchina’s “DIY” transceiver setup. :grin:

1 Like

Cool, which library was that?

Since you have a working library I will wait to see what you find out with the M2.

Alright guys, here ya go.

I started with some existing J1850 libraries I found, but they were incompatible with the M2 “as-is”…so I had to modify the libraries, add and remove some existing code, recompile, test/debug, and now they work perfectly for the Macchina M2. :grin:

Attached is the the full re-worked M2-specific J1850-VPW library, along with a demo sketch.

Send/receive message support
Print received messages to serial terminal
Echo sent messages to serial terminal
Automatic CRC calculation

I also added automatic setup/config of the specific J1850 hardware on the M2, so you dont have to manually configure the various pins and hardware on the M2.

https://drive.google.com/open?id=1XVBlZSFDAqD7iAVaNcUGj-wKl8fH5DYq

Ben

4 Likes

Here’s a pic of the M2 successfully sending and receiving messages on J1850-VPW.

The response/processing time is great, considering its “slow” 10.4k J1850.

When you send a large 8-byte message onto the bus from an external node, the M2 receives the full message, prints it to the serial terminal, AND sends back out a separate 6-byte response message all within 8 milliseconds.

Im sure someone could tweak the library to use the the DueTimer, for true non-blocking background receive/transmit capability…but I was pleasantly surprised how smoothly and quickly it works “as-is”.

2 Likes

Great work! I can’t wait to see what people do with this.

This is huge! Well done! Great to hear it works even without interrupts, which means it will work even better once implemented. Again great work,@dmaxben!

Do you have a Github repo we can link to in the Macchina Showcase?

If not, we’d be happy to host on the Macchina org repo.

I cant take all the credit, it was an existing library that I just modified and reworked to be compatible with the M2.

I dont know if it would still work smoothly in a complex sketch without interrupts.

It might need an interrupt on the Rx line if you’re using it with a large sketch and doing lots of other stuff simultaneously??

The only way to find out is to try though, I dont have any complex J1850 projects so someone else would have to mess around with it.

I can grab it when I get home and put it up on my Repo. Thanks for your work Ben, I will continue developing it to blend it into the M2RET library. Now that I have a working Lab I should be able to take this and the LIN library and try integrating it with the CANBUS and SWCAN stuff and get it driven towards ELM/STN compatibility.

Again thanks. I have some other stuff that can be blended into this library to help give it full diagnostic capabilities.

So what protocols do we now have currently working on the M2? CANBUS, SWCAN, J1850VPW, LIN ?

Rodney

I think now with J-1850 VPW working, that completes full protocol support for the M2…?

CAN, SWCAN, J-1850 VPW, ISO9141, LIN all work and are fully functional now I believe.

J-1850 PWM, im not sure, but thats just old fords so who cares LOL.

I think you are pretty much right! J1850 PWM and KWP2000 still need some work. KWP2000 is similar to ISO9141 and has not been tested that I am aware of. There is, however, a start to that project here.

Ok I posted it in my Github repository.

Because J1850 actually has two protocols it is in its own directory for now.

I also plan to add PWM once it works to this.

Since this is working I will use it as the base and go from there.

Again thanks Ben for your time on this.

The github repository was already mentioned above but just in case it was missed here it is:

2 Likes

Great work everyone. We’ve added this library to the main showcase page here:

http://showcase.macchina.cc/libraries.html

1 Like

Thought this might be helpful for getting PWM working:

I haven’t had a chance to play around with it.

Has way more than we need but I will see what I can do.

Going to download and verify that all of the libraries in the spotlight work with my setup then look into this. (Works great with my STN dongle.)

Rodney