Page 7 of 8

Re: AVR to Arduino - VPW

Posted: Sun Aug 17, 2014 12:33 am
by Tazzi
Yeah spot on, I read that in the elm327 diagrams pdf. That 317L adjustable voltage regulator allows the change between 5v and 8v.

Re: AVR to Arduino - VPW

Posted: Wed Jan 06, 2016 6:37 am
by tek1229
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..

Re: AVR to Arduino - VPW

Posted: Wed Jan 06, 2016 8:15 am
by antus
Please do post it, i would be interested in giving it a try.

Re: AVR to Arduino - VPW

Posted: Thu Jan 07, 2016 5:24 am
by tek1229
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
}

Re: AVR to Arduino - VPW

Posted: Thu Jan 07, 2016 10:40 am
by tek1229
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..

Re: AVR to Arduino - VPW

Posted: Tue Feb 16, 2016 12:05 am
by HOYS
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.

Re: AVR to Arduino - VPW

Posted: Tue Jul 11, 2017 4:34 am
by redheadedrod
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...

Re: AVR to Arduino - VPW

Posted: Tue Jul 11, 2017 9:38 am
by antus
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.

Re: AVR to Arduino - VPW

Posted: Sat Aug 05, 2017 11:48 am
by shorts
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

Re: AVR to Arduino - VPW

Posted: Sat Aug 05, 2017 9:04 pm
by antus
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.