4WD Conversion Proposal

Midwestdrifter

Engineer In Residence
This is all new to me & trying to understand it. Does the WSS (wheel sensor) output a variable frequency proportional to wheel rpm? If that's the case, the frequency can get very small, like 1 Hz?

I believe the circuit shown is designed to operate at 1 kHz. It uses a 10 uF capacitor. At any given frequency the capacitor acts as a resistor, given by:

Xc = 1 / (2*PI*f*C)

At 1 kHz, the capacitor resistance is small: 16 ohm.

But at 1 Hz, resistance is very large: 16 kohm. Your circuit may not work. You did mention the need to "tweek" the cap. A 100 uf cap will result in 1.6 kohm resistance. That's still substantial.

As I understand it, the Schmitt trigger signal goes into the Sprinter ESP/ABS module. How do you get the front wheels and back wheels to turn at same rpm? Is the front and back wheel gearing 100% accurate?

The GM front tone wheels are 55 tooth, the sprinters are 44, so to use the GM tone wheels/sensors, I need to modify the pulse frequency by 1.25.

To do this I am looking at using an arduino to read the GM WSS output (via the schmit trigger). The trigger going high sets an interrupt. This triggers the program to record the time. Which each trigger interrupt the program evaluates how long the input pin has been high. This time is then adjusted, and the output pins are written high/low accordingly. With each cycle the program compares the high/low time for the inputs and outputs. Note that I did not author this program, a generous EE offered his time.

The sample rate will vary with program speed, but should be high enough to produce a reasonably stable output signal.

With 30" tires, the revs per mile is about 600. With 55 pules per rev, at 60MPH that is 600*55=33000 pules/min, or 550hz. At 5mph (the minimum that we need to read), its 45hz. So the range of input frequencies is ~45-750hz.

Now the output of the "black box" is where I am currently at. The arduinos digital pins can output +5V and 40mA. I need to experiment to see if the ESP/ABS module is okay with a 0 to +5V square wave. I will need a resister to simulate the WSS coil. I am not sure if I need ground isolation or not, as the ESP module will likely check for that.

If the ESP module is not okay with a 0-5V square wave, I will need to work something else out. Some Rs232 driver modules have built in caps which will allow a -5V to +5V output. Some reading indicates that the ESP module may need high voltages :idunno:.
 

calbiker

Well-known member
Do you really require the capacitor? At 45 Hz, its resistance is 3.5 kohm.

The circuit you posted is designed to maintain ac signal integrity. No changes to the waveform. You should be able to reference the signal to ground and delete the cap.

edit:

This should be a better circuit for you. Don't know if you require optoisolator U2.

 
Last edited:

Midwestdrifter

Engineer In Residence
I haven't done any analog circuit design since university. I probably don't need the capacitor. The only real concern is to make sure the circuit can handle a bit of noise at low wheel speeds, as the voltage is low. The circuit you posted above appears to have parallel caps for this purpose.
 

calbiker

Well-known member
The capacitor in the two designs have completely different purposes. The cap in the design you posted is actually a high pass filter. It will forward any noise that's on the line. If the noise is large enough, the schmitt trigger will switch states.

The other design has a second order RC low pass filter at its input. It filters any signals larger than 1 kHz. That's perfect, as you're not expecting frequencies above 550 Hz.
 

Midwestdrifter

Engineer In Residence
Great, thanks for the help. I have some parts on order, so I will add the OP-amps and caps to the list. I am going to try pushing a couple different waveforms to the ESP module, just to see what it does. If it reports wheel speeds over canbus, I will have found the winner.

After messing around with a couple different IDEs, I finally got my CAN Bridge Program to compile successfully. The memcpy function must be implemented oddly in the Arduino Due libraries...

Anyways, here is the updated code. I just switched to using the highbyte/lowbyte functions to assign the output array bytes to the correct values. Assuming there is no other mess-ups, I just need to run some data through the unit, and see what comes out of the serial console. I took an educated guess on the endianess, but I will need to confirm.

Code:
//Required Libraries

#include "Arduino.h"

//
//Set debug printing
bool SERIAL_DEBUG = true;



// CAN Layer functions
// -----------------------------------------------------------------------------------------------
#include "DueCANLayer.h"
extern byte canInit(byte cPort, long lBaudRate, int nTxMailboxes);
extern byte canTx(byte cPort, long lMsgID, bool bExtendedFormat, byte* cData, byte cDataLen);
extern byte canRx(byte cPort, long* lMsgID, bool* bExtendedFormat, byte* cData, byte* cDataLen);

// Timer functions
// -----------------------------------------------------------------------------------------------
#include "TimerControl.h"
extern void TimerInit(void);
extern void TimerControl(void);
extern void TimerStart(struct Timer* pTimer, int nCount);
extern void TimerReset(struct Timer* pTimer);
extern struct Timer pTimer[];


// CAN Bus Data Mapping
// -----------------------------------------------------------------------------------------------
struct Mapping
{
  byte cReceivingPort;             // 0/1
  long lReceivingMsgID;
  long lTransmittedMsgID;
};

//recieving port and trasmitted ID function retained for future usage
struct Mapping CAN_DataMapping[] = {
  // cReceivingPort, lReceivingMsgID, lTransmittedMsgID
     0,              0x208,           0x208,
     0,              0x200,           0x200   // End of Table
};

int nMappingEntries = 0;   // Will be determined in setup()

// Internal functions
// -----------------------------------------------------------------------------------------------
void LEDControl(void);

// Module variables
// -----------------------------------------------------------------------------------------------
int TimerActivity_CAN0 = 0;
int TimerActivity_CAN1 = 0;

int LED1 = 14;
int LED2 = 15;
//Low Range Switch Pin Number (TBD)
int LRSW = 22;

int nTxMailboxes = 3; // Equal portion between transmission and reception

void setup()
{
  // Set the serial interface baud rate
  Serial.begin(115200);
 
  // Initialzie the timer control; also resets all timers
  TimerInit();

  // Determine simulation entries
  nMappingEntries = 0;
  while(1)
  {
    if(CAN_DataMapping[nMappingEntries].cReceivingPort == 0 
    || CAN_DataMapping[nMappingEntries].cReceivingPort == 1)
      nMappingEntries++;
    else
      break;
  }

  // Initialize the outputs for the LEDs
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);

  // Initialize Low Range Input pin
  pinMode(LRSW, INPUT);
    // Initialize Low Range Input pin
  
  // Initialize both CAN controllers
  if(canInit(0, CAN_BPS_500K, nTxMailboxes) == CAN_OK)
    Serial.print("CAN0: Initialized Successfully.\n\r");
  else
    Serial.print("CAN0: Initialization Failed.\n\r");
  
  if(canInit(1, CAN_BPS_500K, nTxMailboxes) == CAN_OK)
    Serial.print("CAN1: Initialized Successfully.\n\r");
  else
    Serial.print("CAN1: Initialization Failed.\n\r");

   
}// end setup

void loop()
{
  // Declarations
  byte cPort, cTxPort;
  long lMsgID;
  bool bExtendedFormat;
  byte cData[8];
  byte bData[4];
  byte cDataLen;
  int lowrange;
  unsigned short wSpeedL;
  unsigned short wSpeedR;
  unsigned short wSpeedA;
  unsigned long previousMillis = 0;
  unsigned long interval = 1000;
  //Serial printing interval
  bool SERIAL_OUTPUT= false;

 
  // Start timer for LED indicators
  TimerStart(&pTimerLEDs, TIMER_RATE_LED_BLINK);

  while(1)  // Endless loop
  {
    // Control LED status according to CAN traffic
    LEDControl();
	
	//Read low range switch and assign to lowrange varaible
	lowrange=digitalRead(LRSW);

  //Serial Print Interval
  
  if (SERIAL_DEBUG)
    {  
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis >= interval) 
      {
      // save the last time Serial Printing Occured    
      previousMillis = currentMillis;
      // Set Print to serial
      SERIAL_OUTPUT = true;
      
      }
      else (SERIAL_OUTPUT = false);
    
    }
    // Check for received CAN messages
    for(cPort = 0; cPort <= 1; cPort++)
    {
		
      if(canRx(cPort, &lMsgID, &bExtendedFormat, &cData[0], &cDataLen) == CAN_OK)
      {
        // Scan through the mapping list, If frame matches ID and lowrange is high. 
        for(int nIndex = 0; nIndex < nMappingEntries; nIndex++)
        {
          if(lMsgID == CAN_DataMapping[nIndex].lReceivingMsgID
              // Removed matching for source CAN port
              // && cPort == CAN_DataMapping[nIndex].cReceivingPort
              && lowrange == 1
			      )
          {
			//If recieved port is CAN0, transmit on CAN1, otherwise transmit on CAN0.
            cTxPort = 0;
            if(cPort == 0) cTxPort = 1;
			
			//Initialize wheel speed int to zero
			wSpeedL = 0;
			wSpeedR = 0;
			wSpeedA = 0;
			
			//copy speed data bytes to working array from CAN data array
		 	  bData[0] = cData[4];
			  bData[1] = cData[5];
			  bData[2] = cData[6];
			  bData[3] = cData[7];
			  
			//Set wSpeed to array data
			  wSpeedL = (wSpeedL  << 8) + bData[0];
			  wSpeedL = (wSpeedL  << 8) + bData[1];
			  wSpeedR = (wSpeedR  << 8) + bData[2];
			  wSpeedR = (wSpeedR  << 8) + bData[3];
			  
			  //print data for debug
        if (SERIAL_OUTPUT) 
          {
          Serial.print("wSpeedL raw =");
          Serial.println(wSpeedL);
          Serial.print("wSpeedR raw =");
          Serial.println(wSpeedR);
           }

			  //For Front wheels, frame 0x200, adjust front wheel average speed. 
			  if (lMsgID == 0x200)
				  {	
						// copy speed data to wSpeedA
						wSpeedA = (wSpeedA  << 8) + cData[2];
						wSpeedA = (wSpeedA  << 8) + cData[3];
						
						//debug printing
            if (SERIAL_OUTPUT) 
          {
						Serial.print("Front average speed CAN Raw, cData[2]..[3] =");
						Serial.print(cData[2], HEX);
						Serial.println(cData[3], HEX);
						Serial.print("wSpeedA = ");
						Serial.println(wSpeedA);
          }
						//divide average speed by 2.73 using binary scale factor,  ((1/2.73)*4096) = 1500
						wSpeedA = ((wSpeedA * 1500)>>12);

            if (SERIAL_OUTPUT) 
          {						
						Serial.print("wSpeedA Converted = ");
						Serial.println(wSpeedA);
          }//end If 

						//Copy revised speed to data array
						// memcpy ((byte*)cData[2], (byte*)(wSpeedA), sizeof(wSpeedA));
						//memcpy (cData[2], (byte*)&wSpeedA, sizeof(wSpeedA));
            cData[2]=highByte(wSpeedA);
            cData[3]=lowByte(wSpeedA);

            if (SERIAL_OUTPUT) 
            {
              Serial.print("Front average speed Converted Raw, cData[2]..[3] =");
              Serial.print(cData[2], HEX);
              Serial.println(cData[3], HEX);
            }
					} //end if
					
			  //Divide Wheel speeds by 2.73 using binary scale factor,  ((1/2.73)*4096) = 1500
			  wSpeedL = ((wSpeedL * 1500)>>12);
			  wSpeedR = ((wSpeedR * 1500)>>12);
			  
			  //print data for debug
         if (SERIAL_OUTPUT) 
          {	  
            for (int mIndex=4; mIndex <= 7; mIndex++)
            {
              Serial.println("cData [4]..[7]");
              Serial.print(mIndex);
              Serial.print("-");
              Serial.println(cData[mIndex], HEX);
            }//end for loop
          } //end If 

			  //Copy wSpeedL and wSpeedR to working array, Note offset for start byte.  Assumes both CAN frame and chip are little endian
			   //memcpy( (byte*)&bData[0], (byte*)&(wSpeedL),sizeof(wSpeedL));
			   //memcpy( (byte*)&bData[2], (byte*)&(wSpeedR),sizeof(wSpeedR));
          bData[0]=highByte(wSpeedL);
          bData[1]=lowByte(wSpeedL);
          bData[2]=highByte(wSpeedL);
          bData[3]=lowByte(wSpeedL);
        
			  
			  //Print working array contents
         if (SERIAL_OUTPUT) 
          {	  
            for (int mIndex=0; mIndex <= 3; mIndex++)
            {
              Serial.println("bData[0]..[3]");
              Serial.print(mIndex);
              Serial.print("-");
              Serial.println(bData[mIndex], HEX);
            }//end for loop
          }//end If 
			  
			  //Copy bData to data array
			  cData[4] = bData[0];
			  cData[5] = bData[1];
			  cData[6] = bData[2];
			  cData[7] = bData[3];
			  
			
              // Transmit Frame, print error message if CAN_ERROR is returned
            if(canTx(cTxPort, CAN_DataMapping[nIndex].lTransmittedMsgID, bExtendedFormat, &cData[0], cDataLen) == CAN_ERROR)
              Serial.println("Transmision Error.");
		  
            
          }
		  
        //Transmit Frames If low range is not active, or for non-matching frames
        else 
        {
          //If recieved port is CAN0, transmit on CAN1, otherwise transmit on CAN0.
          cTxPort = 0;
          if(cPort == 0) cTxPort = 1;
          
          if (canTx(cTxPort, lMsgID, bExtendedFormat, &cData[0], cDataLen) == CAN_ERROR)  Serial.println("Transmision Error.");
				}// end if else
          
        }// end for
  
      }// end if

    }// end for
    
  }// end while

}// end loop

// ------------------------------------------------------------------------
// LED Data Traffic
// ------------------------------------------------------------------------
// Note: CAN0 --> LED1
//       CAN1 --> LED2
//
void LEDControl(void)
{
    if(pTimerLEDs.bExpired == true)
    {
      // Restart the timer
      TimerStart(&pTimerLEDs, TIMER_RATE_LED_BLINK);

      // Check for activity on CAN0
      if(TimerActivity_CAN0 > 0)
      {
        TimerActivity_CAN0--;
        digitalWrite(LED1, HIGH);
      }// end if
      else
        digitalWrite(LED1, LOW);

      // Check for activity on CAN1
      if(TimerActivity_CAN1 > 0)
      {
        TimerActivity_CAN1--;
        digitalWrite(LED2, HIGH);
      }// end if
      else
        digitalWrite(LED2, LOW);

    }// end if

}// end LEDControl
 

Midwestdrifter

Engineer In Residence
Calbiker, any idea what OA59 diode is in that diagram? I am not seeing any parts with that number. The source document lists it as a "Germanium Detector Diode". Part 1N34A comes up from a search, bits its not common.
 

calbiker

Well-known member
The diode D1 is used as a rectifier. Say we have an ac signal that's goes from -7V to +7V. The diode blocks all the negative voltages. A germanium diode is used because it has a smaller voltage drop (0.3V) compared to a silicon diode (0.7V). We want as low as possible voltage drop across the diode. A large voltage drop will change the output square wave duty cycle (t_on/T) to something less than 50%.

D2 is a 5.1V zener diode that protects the amp from voltages greater than the supply.


There might be a better circuit for you. Do a search on zero crossing detector.



The op amp requires +/- V supplies. R2 could be changed to the dual RC filter configuration seen in the previous circuit. D1 & D2 protect the op amp to +/- 0.7V. In other words, the signal is clamped to a diode drop. That's OK, as were only interested in the 0V crossing. The output signal is a true 50% duty cycle +/- 5V square wave.

As I understand it, this signal goes into Arduino to get its frequency modified. You can use a transistor to get signal back to a 0V to 5V square wave. The output of Arduino goes into a dual supply op amp to convert to a +/- 5V square wave. We're assuming the ABS module wants to see an ac signal. 741 is a very old op amp. There's probably something better out there now. Get package with dual or quad op amps.

https://www.ebay.com/itm/30W-DC-DC-Buck-Step-Down-Converter-5V-9V-12V-15V-3A-Dual-Power-Supply-Module/322825072832?epid=21007203095&hash=item4b29dfb0c0:g:0bQAAOSwZw1bvxGF

Edit:

Instead of zero crossing you could go with schmitt trigger. This give you noise immunity.



The key is that there are dual supplies to the op amp or comparitor. If the sine wave is symmetrical then output should be 50% duty cycle.

You'll need an osclloscope to check operation. The dual voltage power supply has advantage if Sprinter module likes to see +/- 5V waveform.
 
Last edited:

Midwestdrifter

Engineer In Residence
This circuit I posted earlier is a crossing zero detector. The Op-Amp is configured for single voltage operation, and has a couple of resisters for hysteresis on the output. I could incorporate a low pass filter into the input. The LT1017 has in input limit of -0.3V to 40V, so I need to make sure the negative half of the cycle is clipped off.



For the output I guess I need to start with a 5V square wave, then look at ground isolation. If needed I will need an isolated DC-DC converter for the output stage.
 
Last edited:

calbiker

Well-known member
The issue with this circuit is C1. It injects an ac waveform on a dc bias (from voltage divider R2 & R3). It also acts as a high-pass filter. It will attenuate lower frequencies. At 45 Hz, its resistance is 3.5 kohm. That may be ok since in-series with C1 is a 33 kohm resistor (3.5k << 33k). I believe the ac signal will get attenuated at the injection point.

V = Vac * (R2//R3) / ((R1 + XC1 + (R2//R3))
V = Vac * 5k / 41k = Vac * 0.12

If I got it right, assume the 45 Hz signal is 3V, then at the op amp it is 3V * 0.12 = 0.36V

The hystereses needs to be a lot smaller than 0.36V.

You'll need an oscilloscope to verify operation.
 

Midwestdrifter

Engineer In Residence
Yeah, my though is to drop the C1, and just use a low pass filter. I doubt there will be much low frequency noise on the circuit anyways.
 

Midwestdrifter

Engineer In Residence
Okay, I ordered some parts for 2 versions of the input circuit. As well as an Uno, power supply, and a few power mosfets. I will try a simple square wave output. I also have a half baked idea about two optocouplers in parallel to drive the output stage.

I have a basic Oscope. Its made from a kit I soldered together for fun a few years back. Works fine on voltages up to 60V. I do need to make a case for it...
 

Midwestdrifter

Engineer In Residence
I spotted a NP242D Tcase from a 99 durango in a local parts recycler. Only asking 110$! Sent them a message. The local pick-n-pull is only 3 miles from my house. So back willing, I will have a look there next weekend for a trans core. I still need to figure out front/rear output shaft connections. Dodge uses a internal slip yoke which seals the Tcase housing. So I need to find a good shaft to use for the custom unit, or convert it somehow. The front output is standard 32 spline NP/NV, so I can likely find a yoke with common U joint fitting.

Looks like plenty of parts for jeeps with the 32 spline output.
https://www.ebay.com/itm/JEEP-1310-...167297&hash=item2a88fe7b22:g:RvwAAOSwRbtaC1um
 

Midwestdrifter

Engineer In Residence
I received the Uno and Due today. Uploaded the sketches, and can confirm they compile and run. Good serial output. I should get the input circuit parts for the FWB on Thursday, and I can rig up a test. The serial console was showing a non-zero value for the input RPM on both channels. It may be a coding artifact, or just noise, as I don't have pull down resisters on the input pins.

The Due will be a bit easier. When I have a chance, I will splice a set of wires into the CANbus, and connect the due to see what kind of data I get from the serial console. 50/50 if I got the everything correct with the byte to short int conversion.

I need to rig up a power supply for the Due. The specs on this version indicate a wide range power supply from 8-30V. I don't want to fry it with voltage transients, so a basic filter circuit for AC ripple, and a zener to suppress spikes will likely be required. I am sure there are some good examples floating around the web. I could just use a 5V USB power adapter as well.
 

AdrianD

Member
I spotted a NP242D Tcase from a 99 durango in a local parts recycler. Only asking 110$! Sent them a message. The local pick-n-pull is only 3 miles from my house. So back willing, I will have a look there next weekend for a trans core. I still need to figure out front/rear output shaft connections. Dodge uses a internal slip yoke which seals the Tcase housing. So I need to find a good shaft to use for the custom unit, or convert it somehow. The front output is standard 32 spline NP/NV, so I can likely find a yoke with common U joint fitting.

Looks like plenty of parts for jeeps with the 32 spline output.
https://www.ebay.com/itm/JEEP-1310-...167297&hash=item2a88fe7b22:g:RvwAAOSwRbtaC1um
The NP242 out of the Dodge should have the 1310 front yoke. And all Jeep applications with the 242 should have the slip yoke. Not 100% sure if it's the same spline count on the Dodge.
 

mountainhick

Active member
The NP242 out of the Dodge should have the 1310 front yoke. And all Jeep applications with the 242 should have the slip yoke. Not 100% sure if it's the same spline count on the Dodge.

242D from dodge durango:

input from tranny 23 teeth:





front output yoke:




rear output slip yoke, driveshaft:





rear output yoke 32 teeth

 
Last edited:

Midwestdrifter

Engineer In Residence
Thanks for posting that up, that's about what I expected. The output adapter stub shaft and housing will obviously need change from the grand Cherokee to the wrangler version.
 

Top Bottom