AVR to Arduino - VPW

Ecu Hardware Modifications
User avatar
Posts: 1607
Joined: Thu May 17, 2012 8:53 pm
Location: WA

Re: AVR to Arduino - VPW

Postby Tazzi » Sun Aug 17, 2014 12:33 am

Yeah spot on, I read that in the elm327 diagrams pdf. That 317L adjustable voltage regulator allows the change between 5v and 8v.

Posts: 11
Joined: Thu Jun 11, 2015 11:53 am

Re: AVR to Arduino - VPW

Postby tek1229 » Wed Jan 06, 2016 6:37 am

I'll have to try and dig up some files, any interest in this still?? I converted Michael's code years ago for arduino for VPW back when I had my 04 S10.. For transmitting I think I just used 1 transistor and maybe a zener diode, nothing fancy and no other 8 volt regulator.. Used it to control the electric fans I installed.. Would poll for VSS,CTS, and whether a/c was requested and then control the fans..
Let me know if there's any interest, I'll find the code, sounds like you have it figured out though..

Site Admin
User avatar
Posts: 4788
Joined: Sat Feb 28, 2009 8:34 pm

Re: AVR to Arduino - VPW

Postby antus » Wed Jan 06, 2016 8:15 am

Please do post it, i would be interested in giving it a try.
Have you read the FAQ? For lots of information and links to significant threads see here: viewtopic.php?f=7&t=1396

Posts: 11
Joined: Thu Jun 11, 2015 11:53 am

Re: AVR to Arduino - VPW

Postby tek1229 » Thu Jan 07, 2016 5:24 am

Here goes... I did a lot of searching, found some files.. Posting one of them now, can I just post a link to my drop box folder with the files in it? be a lot easier.. I did try the file below, seems to be an issue with the serial communication, might be one of my first tries, or it might be because I was using an atmega 168 and a much older arduino version? anyways, take a look..





Code: Select all
/************************************************************************* 
** Code is modified to run on an Arduino and is shamelessly copied/borrowed/ported over from...
**  AVR J1850 VPW Interface 
**  by Michael Wolf 
** 
**  Released under GNU GENERAL PUBLIC LICENSE 
** 
**  contact: webmaster@mictronics.de 
**  homepage: www.mictronics.de 
** 
**  Revision History 
** 
**  when         what  who          why 
**  31/12/04         v1.00 Michael  Initial release 
**  07/01/05     v1.01 Michael  * changed timeout in j1850_recv_msg() to 4ms 
**                                use an external timeout loop to call the function 
**                                25 times maximum to get the requirment of 100ms 
**  05/05/05     v1.03 Michael  * changed integer types 
**  08/05/05     v1.04 Michael  * changed to use Timer1 
**  11/08/05     v1.05 Michael  * changed EOD to EOF after last databyte send 
** 
**  NOTE: 
**  This file is based on code from Bruce D. Lightner. 
**  (lightner AT lightner DOT net) 
**  This code is part of his project at 
**  http://www.circuitcellar.com/avr2004/first.html 
**  The code is modified and reworked to remove all "GOTO's" and   
**  deprecated macros to be compatible with the latest version of WinAVR. 
**************************************************************************/   
#include <stdint.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <ctype.h>
#define MCU_XTAL 16000000


/*** CONFIG START ***/

#define J1850_PORT_OUT   PORTC   // J1850 output port
#define J1850_DIR_OUT    DDRC   // J1850 direction register
#define J1850_PIN_OUT      3   // J1850 output pin

#define J1850_PORT_IN      PINC   // J1850 input port
#define J1850_PULLUP_IN   PORTC   // J1850 pull-up register
#define J1850_DIR_IN       DDRC   // J1850 direction register
#define J1850_PIN_IN      0   // J1850 input pin analog input 0
#define j1850_active() J1850_PORT_OUT |= _BV(J1850_PIN_OUT)
#define j1850_passive() J1850_PORT_OUT &=~ _BV(J1850_PIN_OUT)
#define is_j1850_active() bit_is_clear(J1850_PORT_IN, J1850_PIN_IN)

/* Define Timer1 Prescaler here */
#define c_start_pulse_timer   0x01  // Timer1 runs without Prescaler, 135ns tick @ 7,3728MHz
#define c_stop_pulse_timer   0x00  // Timer1 runs no prescaler, 292ns ticks @ 16,000,000MHz


// define error return codes
#define J1850_RETURN_CODE_UNKNOWN    0
#define J1850_RETURN_CODE_OK         1
#define J1850_RETURN_CODE_BUS_BUSY   2
#define J1850_RETURN_CODE_BUS_ERROR  3
#define J1850_RETURN_CODE_DATA_ERROR 4
#define J1850_RETURN_CODE_NO_DATA    5
#define J1850_RETURN_CODE_DATA       6
// convert microseconds to counter values
#define us2cnt(us) ((unsigned int)((unsigned long)(us) / (1000000L / (float)((unsigned long)MCU_XTAL / 1L))))
#define WAIT_100us   us2cnt(100)      // 100us, used to count 100ms
// define J1850 VPW timing requirements in accordance with SAE J1850 standard
// all pulse width times in us
// transmitting pulse width
#define TX_SHORT   us2cnt(64)      // Short pulse nominal time
#define TX_LONG      us2cnt(128)      // Long pulse nominal time
#define TX_SOF      us2cnt(200)      // Start Of Frame nominal time
#define TX_EOD      us2cnt(200)      // End Of Data nominal time
#define TX_EOF      us2cnt(280)      // End Of Frame nominal time
#define TX_BRK      us2cnt(300)      // Break nominal time
#define TX_IFS      us2cnt(300)      // Inter Frame Separation nominal time
// see SAE J1850 chapter 6.6.2.5 for preferred use of In Frame Respond/Normalization pulse
#define TX_IFR_SHORT_CRC   us2cnt(64)   // short In Frame Respond, IFR contain CRC
#define TX_IFR_LONG_NOCRC us2cnt(128)   // long In Frame Respond, IFR contain no CRC
// receiving pulse width
#define RX_SHORT_MIN   us2cnt(34)   // minimum short pulse time
#define RX_SHORT_MAX   us2cnt(96)   // maximum short pulse time
#define RX_LONG_MIN      us2cnt(96)   // minimum long pulse time
#define RX_LONG_MAX      us2cnt(163)   // maximum long pulse time
#define RX_SOF_MIN      us2cnt(163)   // minimum start of frame time
#define RX_SOF_MAX      us2cnt(239)   // maximum start of frame time
#define RX_EOD_MIN      us2cnt(163)   // minimum end of data time
#define RX_EOD_MAX      us2cnt(239)   // maximum end of data time
#define RX_EOF_MIN      us2cnt(239)   // minimum end of frame time, ends at minimum IFS
#define RX_BRK_MIN      us2cnt(239)   // minimum break time
#define RX_IFS_MIN      us2cnt(280)   // minimum inter frame separation time, ends at next SOF
// see chapter 6.6.2.5 for preferred use of In Frame Respond/Normalization pulse
#define RX_IFR_SHORT_MIN   us2cnt(34)   // minimum short in frame respond pulse time
#define RX_IFR_SHORT_MAX   us2cnt(96)   // maximum short in frame respond pulse time
#define RX_IFR_LONG_MIN      us2cnt(96)   // minimum long in frame respond pulse time
#define RX_IFR_LONG_MAX      us2cnt(163)   // maximum long in frame respond pulse time
#define BAUD_RATE    38400

// J1850 message (max 12 byte - 3 byte header - 1 CRC byte) x 2
// because of 2 ASCII chars/byte + 1 terminator
// or 10 bytes for AT command
#define SERIAL_MSG_BUF_SIZE   18
const char ident_txt[]      PROGMEM = "ELM322 v2.0\r\n";
const char bus_busy_txt[]   PROGMEM = "BUSBUSY\r\n";
const char bus_error_txt[]  PROGMEM = "BUSERROR\r\n";
const char data_error_txt[] PROGMEM = "<DATAERROR\r\n";
const char no_data_txt[]    PROGMEM = "NO DATA\r\n";
// define bit macros
#define SETBIT(x,y) (x |= (y))       // Set bit y in byte x
#define CLEARBIT(x,y) (x &= (~y)) // Clear bit y in byte x
#define CHECKBIT(x,y) (x & (y))    // Check bit y in byte x
// define parameter bit mask constants
#define ECHO         0x0001 // bit 0 : Echo on/off
#define HEADER     0x0002 // bit 1 : Headers on/off
#define LINEFEED  0x0004 // bit 2 : Linefeeds on/off
#define RESPONSE  0x0008 // bit 3 : Responses on/off
#define PACKED    0x0010 // bit 4 : use packed data
#define AUTO_RECV 0x0020 // bit 5 : auto receive on/off
#define MON_TX     0x0040 // bit 6 : monitor transmitter
#define MON_RX     0x0080 // bit 7 : monitor receiver
#define MON_OBH   0x0100 // bit 8 : monitor one byte header
#define USE_OBH   0x0200 // bit 9 : use one byte header in Tx message
// use of bit-mask for parameters init to default values
volatile uint16_t parameter_bits = ECHO|LINEFEED|RESPONSE|AUTO_RECV;
uint8_t j1850_req_header[3] = {0x68, 0x6A, 0xF1};  // default request header
uint8_t j1850_ecm_header[3] = {0x6c, 0x10, 0xf1}; // mode 22 header
uint8_t j1850_bcm_header[3] = {0x6c, 0x40, 0xf1}; // BCM header
uint8_t auto_recv_addr = 0x6B;  // physical or functional address in receive mode
uint8_t mon_receiver;  // monitor receiver only addr
uint8_t mon_transmitter;  // monitor transmitter only addr
uint8_t serial_msg_buf[SERIAL_MSG_BUF_SIZE];    // serial Rx buffer
uint8_t *serial_msg_pntr;
int16_t serial_putc(int8_t data);   // send one databyte to USART
#define DEFAULT_BAUD   ((unsigned int)((unsigned long)MCU_XTAL/((unsigned long)BAUD_RATE*16)-1))   // calculate baud rate value for UBBR
uint8_t timeout_multiplier;  // default 4ms timeout multiplier

void setup()
{
  wdt_disable();   // make sure the watchdog is not running
   UBRR0H = DEFAULT_BAUD>>8;      // set baud rate
   UBRR0L = DEFAULT_BAUD;
   UCSR0B =((1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0));   // enable Rx & Tx, enable Rx interrupt
   serial_msg_pntr = &serial_msg_buf[0];  // init serial msg pointer
   
   j1850_init();   // init J1850 bus

   ident();   // send identification to terminal

   serial_putc('>');  // send initial command prompt

   sei();   // enable global interrupts
TCCR1A=0x0000; // configure timer1
TCCR1C=0x0000; // configure timer1
}

void loop()
{
      while( CHECKBIT(parameter_bits, MON_RX) ||
            CHECKBIT(parameter_bits, MON_TX) ||
            CHECKBIT(parameter_bits, MON_OBH)
          )
      {
         uint8_t j1850_msg_buf[12];  // J1850 message buffer
         uint8_t *j1850_msg_pntr = &j1850_msg_buf[0];  //  msg pointer
         int8_t recv_nbytes;  // byte counter      
     
         recv_nbytes = j1850_recv_msg(j1850_msg_buf);   // get J1850 frame
      
         if( !(recv_nbytes & 0x80) ) // proceed only with no errors
         {
            j1850_msg_pntr = &j1850_msg_buf[0];
         
            // check for respond from correct addr or monitor all mode
            if( (CHECKBIT(parameter_bits, MON_RX) && CHECKBIT(parameter_bits, MON_TX))
                ||
                  ((mon_receiver == *(j1850_msg_pntr+1)) && CHECKBIT(parameter_bits, MON_RX) )
                  ||
                  ((mon_transmitter == *(j1850_msg_pntr+2)) && CHECKBIT(parameter_bits, MON_TX) )
            ||
            ((mon_transmitter == *(j1850_msg_pntr)) && CHECKBIT(parameter_bits, MON_OBH) )
               )
            {
          // surpess CRC and header bytes output
               if( !CHECKBIT(parameter_bits, HEADER) )
               {
            if( CHECKBIT(parameter_bits, MON_OBH) ||  // check if one byte header frames are used
                CHECKBIT(parameter_bits, USE_OBH)
               )
            {
              recv_nbytes -= 2;  // discard 1st header byte and CRC
              j1850_msg_pntr += 1;  // skip header byte
            }
            else
            {
              recv_nbytes -= 4;  // discard 3 header bytes and CRC
              j1850_msg_pntr += 3;  // skip 3 header bytes
            }
               }

               if(CHECKBIT(parameter_bits, PACKED))
               { // check respond CRC
                  if( *(j1850_msg_pntr+(recv_nbytes-1)) == j1850_crc(j1850_msg_buf, recv_nbytes-1) )
                     serial_putc(recv_nbytes);  // length byte
                  else
                     serial_putc(recv_nbytes&0x80);  // length byte with error indicator set
               }
     
               // output response data
               for(;recv_nbytes > 0; recv_nbytes--)
               {
                  if(CHECKBIT(parameter_bits, PACKED))
                     serial_putc(*j1850_msg_pntr++);  // data byte
                  else
                  {
                     serial_put_byte2ascii(*j1850_msg_pntr++);
                     serial_putc(' ');
                  }
               }
              
               if(!CHECKBIT(parameter_bits, PACKED))
               {// formated output with CR and optional LF
                  serial_putc('\r');
                  if(CHECKBIT(parameter_bits, LINEFEED)) serial_putc('\n');
               }               
               
            }  // end if valid monitoring addr
         } // end if message recv
      } // end while monitoring active
   }   // endless loop

uint8_t j1850_recv_msg(uint8_t *msg_buf )
{
   uint8_t nbits;         // bit position counter within a byte
   uint8_t nbytes;      // number of received bytes
   uint8_t bit_state;// used to compare bit state, active or passive
   timer1_start();   
   while(!is_j1850_active())   // wait for response from j1850
   {
      if(TCNT1 >= WAIT_100us)   // check for 100us
      {
         timer1_stop();
                        return J1850_RETURN_CODE_NO_DATA | 0x80;   // error, no responds within 100us
                }
   }
   // wait for SOF
   timer1_start();   // restart timer1
   while(is_j1850_active())   // run as long bus is active (SOF is an active symbol)
   {
      if(TCNT1 >=  RX_SOF_MAX)
                    {
                       return J1850_RETURN_CODE_BUS_ERROR | 0x80;   // error on SOF timeout
                    }
   }
   timer1_stop();  // end of 1st active pulse
   if(TCNT1 < RX_SOF_MIN)
                    {
                      return J1850_RETURN_CODE_BUS_ERROR | 0x80;   // error, not SOF
                    }
   // SOF succesfully read
   bit_state = is_j1850_active();   // store actual bus state
   timer1_start();
   for(nbytes = 0; nbytes < 12; ++nbytes)
   {
      nbits = 8;
      do
      {
         *msg_buf <<= 1;
         while(is_j1850_active() == bit_state) // compare last with actual bus state, wait for change
         {
            if(TCNT1 >= RX_EOD_MIN   )   // check for EOD symbol
            {
               timer1_stop();
               return nbytes;   // return number of received bytes
            }
         }
         bit_state = is_j1850_active();   // store actual bus state
         uint16_t tcnt1_buf = TCNT1;
         timer1_start();
         if( tcnt1_buf < RX_SHORT_MIN) return J1850_RETURN_CODE_BUS_ERROR | 0x80;   // error, pulse was to short

         // check for short active pulse = "1" bit
         if( (tcnt1_buf < RX_SHORT_MAX) && !is_j1850_active() )
            *msg_buf |= 1;

         // check for long passive pulse = "1" bit
         if( (tcnt1_buf > RX_LONG_MIN) && (tcnt1_buf < RX_LONG_MAX) && is_j1850_active() )
            *msg_buf |= 1;

      } while(--nbits);// end 8 bit while loop
      
      ++msg_buf;   // store next byte
      
   }   // end 12 byte for loop

   // return after a maximum of 12 bytes
   timer1_stop();   
   return nbytes;
}

void j1850_init(void)
{
   j1850_passive();   // set VPW pin in passive state
   J1850_DIR_OUT |= _BV(J1850_PIN_OUT);   // make VPW output pin an output
   J1850_PULLUP_IN |= _BV(J1850_PIN_IN);   // enable pull-up on VPW pin
   J1850_DIR_IN   &=~ _BV(J1850_PIN_IN);   // make VPW input pin an input
 
}

static void j1850_wait_idle(void)
{
   timer1_start();
   while(TCNT1 < RX_IFS_MIN)   // wait for minimum IFS symbol
   {
      if(is_j1850_active()) timer1_start();   // restart timer1 when bus not idle
   }
}
 void timer1_ctrl(uint8_t val)
{
    TCCR1B = val;
}
 void timer1_start(void)
{
    TCCR1B = c_start_pulse_timer;
    TCNT1 = 0;
}
 void timer1_stop(void)
{
    TCCR1B = c_stop_pulse_timer;
}
 void timer1_set(uint16_t val)
{
    TCNT1 = val;
}
uint8_t j1850_send_msg(uint8_t *msg_buf, int8_t nbytes)
{
   if(nbytes > 12)   return J1850_RETURN_CODE_DATA_ERROR;   // error, message to long, see SAE J1850

   j1850_wait_idle();   // wait for idle bus

   timer1_start();   
   j1850_active();   // set bus active
   
   while(TCNT1 < TX_SOF);   // transmit SOF symbol
          j1850_passive();
   uint8_t temp_byte,   // temporary byte store
               nbits;      // bit position counter within a byte   
     uint16_t delay;      // bit delay time
     do
   {
      temp_byte = *msg_buf;   // store byte temporary
      nbits = 8;
//while(TCNT1 < TX_SOF);   // transmit SOF symbol
    while (nbits--)      // send 8 bits
      {
         if(nbits & 1) // start allways with passive symbol
         {
            j1850_passive();   // set bus passive
                                 timer1_start();
            delay = (temp_byte & 0x80) ? TX_LONG : TX_SHORT;   // send correct pulse lenght
                while (TCNT1 <= delay)   // wait
            {
                                       
                 if(!J1850_PORT_IN & _BV(J1850_PIN_IN))   // check for bus error
                                         {
                  timer1_stop();
                                                 return J1850_RETURN_CODE_BUS_ERROR;   // error, bus collision!
                                   }
            }
         }
         else   // send active symbol
         {
            j1850_active();   // set bus active
            timer1_start();
            delay = (temp_byte & 0x80) ? TX_SHORT : TX_LONG;   // send correct pulse lenght
               while (TCNT1 <= delay);   // wait
         }
      temp_byte <<= 1;   // next bit
      }// end nbits while loop
      ++msg_buf;   // next byte from buffer
   } while(--nbytes);// end nbytes do loop
  j1850_passive();   // send EOF symbol
  timer1_start();
  while (TCNT1 <= TX_EOF); // wait for EOF complete
   timer1_stop();
  return J1850_RETURN_CODE_OK;   // no error
}

// calculate J1850 message CRC
uint8_t j1850_crc(uint8_t *msg_buf, int8_t nbytes)
{
   uint8_t crc_reg=0xff,poly,byte_count,bit_count;
   uint8_t *byte_point;
   uint8_t bit_point;

   for (byte_count=0, byte_point=msg_buf; byte_count<nbytes; ++byte_count, ++byte_point)
   {
      for (bit_count=0, bit_point=0x80 ; bit_count<8; ++bit_count, bit_point>>=1)
      {
         if (bit_point & *byte_point)   // case for new bit = 1
         {
            if (crc_reg & 0x80)
               poly=1;   // define the polynomial
            else
               poly=0x1c;
            crc_reg= ( (crc_reg << 1) | 1) ^ poly;
         }
         else      // case for new bit = 0
         {
            poly=0;
            if (crc_reg & 0x80)
               poly=0x1d;
            crc_reg= (crc_reg << 1) ^ poly;
         }
      }
   }
   return ~crc_reg;   // Return CRC
}
void ident(void)
{
   serial_puts_P(ident_txt);  // show code description
}



/*
**---------------------------------------------------------------------------
**
** Abstract: Convert 2 byte ASCII hex to 1 byte decimal
**
**
** Parameters: Pointer to first ASCII char
**
**
** Returns: decimal value
**
**
**---------------------------------------------------------------------------
*/
static uint8_t ascii2byte(char *val)
{
   uint8_t temp = *val;
   
   if(temp > 0x60) temp -= 39;  // convert chars a-f
   temp -= 48;  // convert chars 0-9
   temp *= 16;
   
   temp += *(val+1);
   if(*(val+1) > 0x60) temp -= 39;  // convert chars a-f
   temp -= 48;  // convert chars 0-9   

   return temp;

}



/*
**---------------------------------------------------------------------------
**
** Abstract: Processing of received serial string
**
** Parameters: none
**
** Returns: 0 = unknown
**          1 = OK
**          2 = bus busy
**          3 = bus error
**          4 = data error
**          5 = no data
**          6 = data ( also to surpress any other output )
**
**---------------------------------------------------------------------------
*/
int8_t serial_processing(void)
{
   char *serial_msg_pntr = strlwr((char *)serial_msg_buf);  // convert string to lower
   uint8_t serial_msg_len = strlen((char *)serial_msg_buf);  // get string length
     uint8_t *var_pntr = 0;  // point to different variables

   if( (*(serial_msg_pntr)=='a') && (*(serial_msg_pntr+1)=='t'))  // check for "at" or hex
   {  // is AT command
      // AT command found
      switch( *(serial_msg_pntr+2) )  // switch on "at" command
      {
         case 'a':  // auto receive address on
            if(*(serial_msg_pntr+3) == 'r')   SETBIT(parameter_bits, AUTO_RECV);
            if( j1850_req_header[0] & 0x04)  // check for functional or physical addr
               auto_recv_addr = j1850_req_header[2]; // use physical recv addr
            else
               auto_recv_addr = j1850_req_header[1]+1;  // use funct recv addr
            return J1850_RETURN_CODE_OK ;

     
         case 'd':  // set defaults
            parameter_bits = ECHO|LINEFEED|RESPONSE|AUTO_RECV;
            timeout_multiplier = 0x19;   // set default timeout to 4ms * 25 = 100ms
            j1850_req_header[0] = 0x68;  // Prio 3, Functional Adressing
            j1850_req_header[1] = 0x6A;  // Target legislated diagnostic
            j1850_req_header[2] = 0xF1;  // Frame source = Diagnostic Tool
            return J1850_RETURN_CODE_OK ;
      
         case 'e':  // echo on/off
            if(*(serial_msg_pntr+3) == '0')
               CLEARBIT(parameter_bits, ECHO);
            else
               SETBIT(parameter_bits, ECHO);
            return J1850_RETURN_CODE_OK ;
         
         case 'i':  // send ident string
            ident();
            return J1850_RETURN_CODE_OK ;

         case 'l': // linefeed on/off (only for data strings)
            if(*(serial_msg_pntr+3) == '0')
               CLEARBIT(parameter_bits, LINEFEED);
            else
               SETBIT(parameter_bits, LINEFEED);
            return J1850_RETURN_CODE_OK ;         

         case 'h': // show headers on/off
            if(*(serial_msg_pntr+3) == '0')
               CLEARBIT(parameter_bits, HEADER);
            else
               SETBIT(parameter_bits, HEADER);
            return J1850_RETURN_CODE_OK ;

         case 'r': // show response on/off
            if(*(serial_msg_pntr+3) == '0')
               CLEARBIT(parameter_bits, RESPONSE);
            else
               SETBIT(parameter_bits, RESPONSE);
            return J1850_RETURN_CODE_OK ;

         case 'f': // send formated
            if(*(serial_msg_pntr+3) == 'd')
               CLEARBIT(parameter_bits, PACKED);
            return J1850_RETURN_CODE_OK ;

         case 'o': // one byte header on/off
            if(*(serial_msg_pntr+3) == '0')
               CLEARBIT(parameter_bits, USE_OBH);
            else
               SETBIT(parameter_bits, USE_OBH);
            return J1850_RETURN_CODE_OK ;

         case 'p': // send packed data
            if(*(serial_msg_pntr+3) == 'd')
               SETBIT(parameter_bits, PACKED);
            return J1850_RETURN_CODE_OK ;

         case 'm':  // switch into monitoring mode
            switch(*(serial_msg_pntr+3))
            {
               case 'a':
                  SETBIT(parameter_bits, MON_RX);  // monitor all
                  SETBIT(parameter_bits, MON_TX);
                  return J1850_RETURN_CODE_DATA; // return, no following parameter
           
          case 'i':
                  CLEARBIT(parameter_bits, MON_TX);
                   CLEARBIT(parameter_bits, MON_RX);
            SETBIT(parameter_bits, MON_OBH);  // monitor one byte header
                   var_pntr = &mon_transmitter;
            break;  // get folowing parameter
           
               case 'r':  // monitor only receiver addr
                  SETBIT(parameter_bits, MON_RX);  // monitor receiver only
                  CLEARBIT(parameter_bits, MON_TX);
                  var_pntr = &mon_receiver;
                  break;  // get following parameter               

               case 't':  // monitor only transmitter addr
                  CLEARBIT(parameter_bits, MON_RX);
                  SETBIT(parameter_bits, MON_TX);  // monitor transmitter only
                  var_pntr = &mon_transmitter;
                  break;  // get following parameter
         
          default:
            return J1850_RETURN_CODE_UNKNOWN;
            }
            if(
                  isxdigit(*(serial_msg_pntr+4)) && isxdigit(*(serial_msg_pntr+5))
                  &&   ( serial_msg_len == 6)
               )  // proceed when next two chars are hex
               {
                 // make 1 byte hex from 2 chars ASCII and save
                  *var_pntr = ascii2byte(serial_msg_pntr+4);
                  return J1850_RETURN_CODE_DATA;
               }

         case 's': // commands SH,SR or ST

            if(   isxdigit(*(serial_msg_pntr+4)) && isxdigit(*(serial_msg_pntr+5)) )
            {  // proceed when next two chars are hex
               switch(*(serial_msg_pntr+3))
               {
                  case 'h':  // set header bytes
                     if(
                           isxdigit(*(serial_msg_pntr+6)) && isxdigit(*(serial_msg_pntr+7))
                           &&   isxdigit(*(serial_msg_pntr+8)) && isxdigit(*(serial_msg_pntr+9))
                           &&   ( serial_msg_len == 10)
                        )  // proceed when next four chars are hex
                        {
                          // make 3 byte hex from 6 chars ASCII and save
                           j1850_req_header[0]=ascii2byte(serial_msg_pntr+4);
                           j1850_req_header[1]=ascii2byte(serial_msg_pntr+6);
                           j1850_req_header[2]=ascii2byte(serial_msg_pntr+8);
                           if( CHECKBIT(parameter_bits, AUTO_RECV) )
                           {
                              if( j1850_req_header[0] & 0x04)  // check for functional or physical addr
                                 auto_recv_addr = j1850_req_header[2]; // use physical recv addr
                              else
                                 auto_recv_addr = j1850_req_header[1]+1;  // use funct recv addr
                           }
                           return J1850_RETURN_CODE_OK ;
                        }
                                                                 
                        break;
               
                  case 't':  // set response timeout multipler
                        var_pntr = &timeout_multiplier;
                        break;
                        
                  case 'r':  // set receive address and manual receive mode
                        var_pntr = &auto_recv_addr;
                        CLEARBIT(parameter_bits, AUTO_RECV);
                        break;
           
            default:
              return J1850_RETURN_CODE_UNKNOWN;
               } // end switch char 3
               if(serial_msg_len == 6)
               {
                  *var_pntr =ascii2byte(serial_msg_pntr+4);
                  if (timeout_multiplier < 8)  timeout_multiplier = 8;  // set multiplier for minimum of 32ms timout
                  return J1850_RETURN_CODE_OK ;
               }
            } // end if char 4 and 5 isxdigit
                                     else if (*(serial_msg_pntr+4)=='b')
                                       {
                                        j1850_req_header[0]=j1850_bcm_header[0];
               j1850_req_header[1]=j1850_bcm_header[1];
               j1850_req_header[2]=j1850_bcm_header[2];
                                        return J1850_RETURN_CODE_OK ;                               
                                       }
                                        return J1850_RETURN_CODE_UNKNOWN;

         case 'z':  // reset all and restart device
            wdt_enable(WDTO_15MS);   // enable watdog timeout 15ms
            for(;;);   // wait for watchdog reset
      
         default:  // return error, unknown command
            return J1850_RETURN_CODE_UNKNOWN;      
      }

   }
         
       
        {
         if( (serial_msg_len & 1) || (serial_msg_len > 16) )  // check for "even" message lenght
         return J1850_RETURN_CODE_UNKNOWN;                                           // and maximum of 8 data bytes

      serial_msg_len /= 2;  // use half the string lenght for byte count

      while( *serial_msg_pntr )  // check all chars are valid hex chars
      {
         if(!isxdigit(*serial_msg_pntr))
            return J1850_RETURN_CODE_UNKNOWN;
         ++serial_msg_pntr;
      }
      serial_msg_pntr = (char *)&serial_msg_buf[0];  // reset pointer
   
     uint8_t j1850_msg_buf[12];  // J1850 message to be send
      uint8_t *j1850_msg_pntr = &j1850_msg_buf[0];  //  msg pointer
      uint8_t cnt;  // byte counter
      
      // store header bytes 1, use at least one header byte
      *j1850_msg_pntr = j1850_req_header[0];
   
    // store header 2-3 when three byte header is in use
    if( !CHECKBIT(parameter_bits, USE_OBH) )
    {
      *(++j1850_msg_pntr) = j1850_req_header[1];
      *(++j1850_msg_pntr) = j1850_req_header[2];
    }
   
      // convert serial message from 2 byte ASCII to 1 byte binary and store
      for(cnt = 0; cnt < serial_msg_len; ++cnt)
      {
         *(++j1850_msg_pntr) = ascii2byte(serial_msg_pntr);
         serial_msg_pntr += 2;      
      }
      
      // generate CRC for J1850 message and store, use 1 or 3 byte header
    if(CHECKBIT(parameter_bits, USE_OBH))
      *(++j1850_msg_pntr) = j1850_crc( j1850_msg_buf,serial_msg_len+1 );  // use one header bytes
    else
      *(++j1850_msg_pntr) = j1850_crc( j1850_msg_buf,serial_msg_len+3 );  // use three header byte
     
      // send J1850 message and save return code, use 1 or 3 byte header
    uint8_t return_code;
    if(CHECKBIT(parameter_bits, USE_OBH))
      return_code = j1850_send_msg(j1850_msg_buf, serial_msg_len+2);
    else
      return_code = j1850_send_msg(j1850_msg_buf, serial_msg_len+4);
     
      // skip receive in case of transmit error or RESPONSE disabled
      if( (return_code == J1850_RETURN_CODE_OK) && CHECKBIT(parameter_bits, RESPONSE) )
      {
         uint16_t time_count = 0;      

         do
         {
            /*
               Run this loop until we received a valid response frame, or response timed out,
               or the bus was idle for 100ms or an bus error occured.
            */
         
            cnt = j1850_recv_msg(j1850_msg_buf);  // receive J1850 respond

            /*
               the j1850_recv_msg() has a timeout of 100us
               so the loop will run 100ms by default before timeout
               100ms is recommended by SAE J1850 spec
            */
            ++time_count;   
            if(time_count >= 1000)
               return J1850_RETURN_CODE_NO_DATA;
               
            /*
               Check for bus error. End the loop then.
            */
            if( cnt == (J1850_RETURN_CODE_BUS_ERROR & 0x80) )  // check if we got an error code or just number of recv bytes
            {
               if(CHECKBIT(parameter_bits, PACKED))
               {
                  serial_putc(0x80);  // lenght byte with error indicator set
                  return J1850_RETURN_CODE_DATA;  // surpress any other output
               }
               else
                  return cnt & 0x7F;  // return "receive message" error code
            }

            j1850_msg_pntr = &j1850_msg_buf[0];

         } while( auto_recv_addr != *(j1850_msg_pntr+1) );   

         // check respond CRC
         if( *(j1850_msg_pntr+(cnt-1)) != j1850_crc(j1850_msg_buf, cnt-1) )
         {
            if(CHECKBIT(parameter_bits, PACKED))
            {
               serial_putc(0x80);  // length byte with error indicator set
               return J1850_RETURN_CODE_DATA;  // surpress any other output
            }
            else
               return J1850_RETURN_CODE_DATA_ERROR;
         }

         // check for respond from correct addr in auto or man recv mode
         if( auto_recv_addr != *(j1850_msg_pntr+1) )
         {
            if(CHECKBIT(parameter_bits, PACKED))
            {
               serial_putc(0x00);  // length byte
               return J1850_RETURN_CODE_DATA;  // surpress any other output
            }
            else
               return J1850_RETURN_CODE_NO_DATA;
         }
         
         if( !CHECKBIT(parameter_bits, HEADER) )
         {
         if(CHECKBIT(parameter_bits, USE_OBH) )  // check if one byte header frames are used
         {
            cnt -= 2;  // discard 1st header byte and CRC
            j1850_msg_pntr += 1;  // skip header byte
          }
          else
          {
            cnt -= 4;  // discard 3 header bytes and CRC
            j1850_msg_pntr += 3;  // skip 3 header bytes
          }
         }

         if(CHECKBIT(parameter_bits, PACKED))
            serial_putc(cnt);  // length byte
         
         // output response data
         for(;cnt > 0; --cnt)
         {
            if(CHECKBIT(parameter_bits, PACKED))
               serial_putc(*j1850_msg_pntr++);  // length byte
            else
            {
               serial_put_byte2ascii(*j1850_msg_pntr++);
               serial_putc(' ');
            }
         }
         
         if(!CHECKBIT(parameter_bits, PACKED))
         {// formated output with CR and optional LF
            serial_putc('\r');
            if(CHECKBIT(parameter_bits, LINEFEED)) serial_putc('\n');
         }
         return J1850_RETURN_CODE_DATA;  // surpress any other output

      }  // end if J1850 OK && RESPONSE
      else  // transmit error or show RESPONSE OFF, return error code
         return return_code;
      
   } // end if !AT
   
   // we should never reach this return
   return J1850_RETURN_CODE_UNKNOWN;
} // end serial_processing


/*
**---------------------------------------------------------------------------
**
** Abstract: Send one byte via USART
**
** Parameters: data byte
**
** Returns: NULL
**
**---------------------------------------------------------------------------
*/
int16_t serial_putc(int8_t data)
{
   // wait for USART to become available
      while ( (UCSR0A & _BV(UDRE0)) != _BV(UDRE0));
      UDR0 = data;                            // send character
      return 0;
}; //end usart_putc


/*
**---------------------------------------------------------------------------
**
** Abstract: Make 2 byte ASCII from one byte binary and send to terminal
**
** Parameters: input byte
**
** Returns: none
**
**---------------------------------------------------------------------------
*/
void serial_put_byte2ascii(uint8_t val)
{
   uint8_t ascii1=val;
   serial_putc( ((ascii1>>4) < 10) ? (ascii1>>4) + 48 : (ascii1>>4) + 55 );  // upper nibble
   serial_putc( ((val&0x0f) < 10) ? (val&0x0f) + 48 : (val&0x0f) + 55 );  // lower nibble
}


/*
**---------------------------------------------------------------------------
**
** Abstract: Print string in program memory to terminal
**
** Parameters: Pointer to string
**
** Returns: none
**
**---------------------------------------------------------------------------
*/
void serial_puts_P(const char *s)
{
   while( pgm_read_byte(&*s)) serial_putc( pgm_read_byte(&*s++));// send string char by char
}

/*
**---------------------------------------------------------------------------
**
** Abstract: USART Receive Interrupt
**
** Parameters: none
**
** Returns: none
**
**---------------------------------------------------------------------------
*/
SIGNAL(SIG_USART_RECV)
{
   uint8_t *hlp_pntr = &serial_msg_buf[sizeof(serial_msg_buf)-1];  // get end of serial buffer

   // check for buffer end, prevent buffer overflow
   if ( serial_msg_pntr > hlp_pntr )
   {
      serial_msg_pntr = &serial_msg_buf[sizeof(serial_msg_buf)-1];
   }
   
   uint8_t in_char = UDR0;  // get received char

  // end monitor modes on any received char
   if( CHECKBIT(parameter_bits,MON_RX) ||
      CHECKBIT(parameter_bits,MON_TX) ||
      CHECKBIT(parameter_bits,MON_OBH)
    )
   {
      CLEARBIT(parameter_bits,MON_RX);
      CLEARBIT(parameter_bits,MON_TX);
    CLEARBIT(parameter_bits,MON_OBH);
      print_prompt();  // command prompt to terminal
   }
   else  // no active monitor mode
   {
      if( CHECKBIT(parameter_bits,ECHO) )  // return char when echo is on
         serial_putc(in_char);      

      // check for terminating char
      if(in_char == 0x0D)
      {
      //if(CHECKBIT(parameter_bits, LINEFEED)) serial_putc('\n');   
         *(serial_msg_pntr) = 0x00;   // terminate received message
         switch ( serial_processing() )  // process serial message
         {
            case J1850_RETURN_CODE_OK:  // success
               serial_puts_P(PSTR("\n\rOK"));
               print_prompt();  // command prompt to terminal
               break;
            case J1850_RETURN_CODE_BUS_BUSY:  // bus was busy
               serial_puts_P(bus_busy_txt);
               print_prompt();  // command prompt to terminal
               break;
            case J1850_RETURN_CODE_BUS_ERROR:  // bus error detected
               serial_puts_P(bus_error_txt);
               print_prompt();  // command prompt to terminal
               break;
            case J1850_RETURN_CODE_DATA_ERROR:  // data error detected
               serial_puts_P(data_error_txt);
             print_prompt();  // command prompt to terminal                  
               break;
            case J1850_RETURN_CODE_NO_DATA:  // no data response (response timeout)
               serial_puts_P(no_data_txt);
             print_prompt();  // command prompt to terminal   
               break;
            case J1850_RETURN_CODE_DATA:     // data response
          print_prompt();  // command prompt to terminal
               break;         
            default: // unknown error
               serial_puts_P(PSTR("\n\r?"));
             print_prompt();  // command prompt to terminal   
         }
         serial_msg_pntr = &serial_msg_buf[0];  // start new message
      }
   
      // received char was no termination
      if(isalnum((int16_t)in_char))
      {  // check for valid alphanumeric char and save in buffer
         *serial_msg_pntr = in_char;
         ++serial_msg_pntr;   
      }
  }
};// end of UART receive interrupt


/*
**---------------------------------------------------------------------------
**
** Abstract: Print command prompt to terminal
**
**
** Parameters: none
**
**
** Returns: none
**
**
**---------------------------------------------------------------------------
*/
void print_prompt(void)
{
   serial_puts_P(PSTR("\r\n>"));   // send new command prompt
}
Last edited by tek1229 on Sat Jan 16, 2016 12:14 pm, edited 1 time in total.

Posts: 11
Joined: Thu Jun 11, 2015 11:53 am

Re: AVR to Arduino - VPW

Postby tek1229 » Thu Jan 07, 2016 10:40 am

By the way, I found the problem for the serial receive issue.. the issue is because I originally did this on an Atmega168 and I'm checking it with an Atmega328.. The interupt handler is different.. Needs to be changed to (USART_RX_vect) from (SIG_USART_RECV) .. Once you do that it'll compile and load ok.. Haven't checked out the electric end of it, but I remember I hacked it.. Think I used a resistor divider for incoming and a transistor fed 12v for outgoing.. May have used a zener diode to limit voltage, or simply a resistor ladder as well, don't remember.. I'm looking for any research I can find but that was probably 5 computers and 2 houses ago.. lol.. The timing was fun to figure out, For the version I used he said I needed a crystal running at 7.3728MHz for it to work.. The makefile had a define of MCU_XTAL = 7372800.. Since I wasn't going to burn any fuses to make it run on an arduino I needed to come up with something.. What I did this I simply put #define MCU_XTAL 16000000 on the top of the arduino program so the timing calculations would work out.. Seems to work fine for what I needed..

I haven't found my actual work files but I did find a backup, have a few more revisions where I had it stand alone request data like vss and cts and a/c request.. Had another revision where I used the arduino serial library and converted all of michael's serial code to make it simpler for me to understand, looking for that one still..

Posts: 7
Joined: Sat Dec 01, 2012 6:55 pm

Re: AVR to Arduino - VPW

Postby HOYS » Tue Feb 16, 2016 12:05 am

Interested if you could post a schematic of how you connected everything in your arduino implementation. Trying to get this to work right now myself.

Posts: 1
Joined: Tue Apr 18, 2017 1:46 am

Re: AVR to Arduino - VPW

Postby redheadedrod » Tue Jul 11, 2017 4:34 am

So what is the current status of this project? I am in process of researching j1850VPW protocol to add the code to a pre-release arduino Due based interface. Since this is my first Arduino Project I have lots of questions...

Site Admin
User avatar
Posts: 4788
Joined: Sat Feb 28, 2009 8:34 pm

Re: AVR to Arduino - VPW

Postby antus » Tue Jul 11, 2017 9:38 am

If you've got a logic analyser that is supported by sigrok (or willing to get one, they can be very low cost) you'll probably find this protocol decoder I wrote very helpful in your development viewtopic.php?f=3&t=4761

Also check out the allpro adaptor from http://www.obddiag.net/allpro.html as its open source hardware and software. Its cost is low and its a quite powerful platform.
Have you read the FAQ? For lots of information and links to significant threads see here: viewtopic.php?f=7&t=1396

Posts: 1
Joined: Sat Aug 05, 2017 11:03 am

Re: AVR to Arduino - VPW

Postby shorts » Sat Aug 05, 2017 11:48 am

Folks..

New to the forum and I have pretty much confused myself already. Project is an LS1 transplant into a WB van. So far motor is almost rebuilt and trans is done (4l60). I am also grafting a VY dash / instrument cluster / AC / wiring loom into the car. Ideally I want to run antitheft etc and basically i want it to act totally as a VY.

Also I was planning on an odb2 driven additional display (tft soft dash) and recently saw obd2 arduino shields available. I will chase up the details later but i believe it acts like an ELM 327. As I want to be able to eventually edit dash, bcm and pcm data is this a good interface to start with?

Shield details to follow

Regards,
Craig

Site Admin
User avatar
Posts: 4788
Joined: Sat Feb 28, 2009 8:34 pm

Re: AVR to Arduino - VPW

Postby antus » Sat Aug 05, 2017 9:04 pm

Sure thas fine. You need VPW protocol, just 1x speed for logging. Any off the shelf elm will be enough but the ascii protocol it talks isnt elegant (wastes 2 or 2.5x the serial bandwidth on the pc side) depending if you put spaces between the ascii hex representation. If you want to go further a binary protocil is better.
Have you read the FAQ? For lots of information and links to significant threads see here: viewtopic.php?f=7&t=1396

PreviousNext

Return to Hardware Modification and Development

Who is online

Users browsing this forum: No registered users and 1 guest