Help Decoding a Wiper Stalk! A puzzling challenge, fallen at the first hurdle...

Hi all,

As part of my car build, one function I need is to convert the motions of my wiper stalk into CAN bus messages that I can send to another user. I have looked at the wiper stalk and it is in fact quite complex with lots of tracks internally for making and breaking connections on various relays in the original car. This is not helpful in my case as I'm looking for a simple way to understand which functions are currently being called for.

To begin then I pin tested every pin against every pin in each of the several function positions and the first table below is what I came up with. Each X means all of these pins are communicating electrically in this position, a Y or a Z is a separate communication set. As you can see, the combinations are not easy to decode in a simple way.

So after realising I can't simply feed in 5v and test where it comes out, I made a plan to make a sort of decoder. Using a Teensy 3.2. My plan is to feed 5v from the Teensy VCC pin into pin 5 of the wiper, then read the output of 7 of the other outputs, to see which are high and low. The overall combination of the 7 high or low pins should give me my answer (I hope).

So what I've done so far is simply stick a Teensy 3.2 onto a breadboard and plug it in via USB. I've connected wire 5 from the wiper to the VCC pin, then the other 7 to various digital inputs (see second table).

I've written a very simple sketch to check each pin and write to serial. I'm not getting anything written in the Serial though - do I need a resistor or something on each input to be able to generate an input?

const int InputApin = 1;
const int InputBpin = 3;
const int InputCpin = 5;
const int InputDpin = 7;
const int InputEpin = 9;
const int InputFpin = 11;
const int InputGpin = 14;

int InputA = 0;
int InputB = 0;
int InputC = 0;
int InputD = 0;
int InputE = 0;
int InputF = 0;
int InputG = 0;
String Output = "";

void setup() {
  Serial.begin(9600);
  pinMode(InputApin, INPUT_PULLDOWN);
  pinMode(InputBpin, INPUT_PULLDOWN);
  pinMode(InputCpin, INPUT_PULLDOWN);
  pinMode(InputDpin, INPUT_PULLDOWN);
  pinMode(InputEpin, INPUT_PULLDOWN);
  pinMode(InputFpin, INPUT_PULLDOWN);
  pinMode(InputGpin, INPUT_PULLDOWN);

}

void loop() {
  InputA = digitalRead(InputApin);
  InputB = digitalRead(InputBpin);
  InputC = digitalRead(InputCpin);
  InputD = digitalRead(InputDpin);
  InputE = digitalRead(InputEpin);
  InputF = digitalRead(InputFpin);
  InputG = digitalRead(InputGpin);

  ////This part will be used for testing once I can get the Serial set up and reading correctly
  //if (InputA == HIGH && InputB == HIGH && InputC == HIGH && InputD == HIGH && InputE == HIGH && InputF == HIGH && InputG == HIGH) Output = "Default";
  //else if (InputA == HIGH && InputB == HIGH && InputC == HIGH && InputD == HIGH && InputE == HIGH && InputF == HIGH && InputG == HIGH) Output = "Default";

  Serial.print(InputA);
  Serial.print(InputB);
  Serial.print(InputC);
  Serial.print(InputD);
  Serial.print(InputE);
  Serial.print(InputF);
  Serial.println(InputG);


}

What have I done wrong here...??

1 Like

Success! I re-wired it so it pin 5 is ground and the pins pull up. Here's what I now have - what would be a neat way to debounce the inputs?

//Define Input Pins
const int InputApin = 1;
const int InputBpin = 3;
const int InputCpin = 5;
const int InputDpin = 7;
const int InputEpin = 9;
const int InputFpin = 11;
const int InputGpin = 14;

//Define Input Variables
int InputA = 0;
int InputB = 0;
int InputC = 0;
int InputD = 0;
int InputE = 0;
int InputF = 0;
int InputG = 0;
int WipeMode = 0; // 0 = Error, 1 = Off, 2 = Intermediate, 3 = Low, 4 = High, 5 = Auto, 6 = Wash
int Menu = 0; //1 for press

void setup() {
  Serial.begin(9600);
  pinMode(InputApin, INPUT_PULLUP);
  pinMode(InputBpin, INPUT_PULLUP);
  pinMode(InputCpin, INPUT_PULLUP);
  pinMode(InputDpin, INPUT_PULLUP);
  pinMode(InputEpin, INPUT_PULLUP);
  pinMode(InputFpin, INPUT_PULLUP);
  pinMode(InputGpin, INPUT_PULLUP);

}

void loop() {
  //Read Stalk Inputs
  InputB = !digitalRead(InputBpin);
  InputC = !digitalRead(InputCpin);
  InputD = !digitalRead(InputDpin);
  InputE = !digitalRead(InputEpin);
  InputF = !digitalRead(InputFpin);
  InputG = !digitalRead(InputGpin);

  //Auto Wipe Mode
  if (InputD == LOW) {
    WipeMode = 5; //Auto
    Menu = !digitalRead(InputApin); //Menu signal flips in auto mode
  }

  //Manual Mode
  else if (InputD == HIGH) {
    if (InputE == LOW && InputF == LOW && InputG == LOW) WipeMode = 1; //OFF
    else if (InputE == LOW && InputF == LOW && InputG == HIGH) WipeMode = 2; //Intermediate
    else if (InputE == LOW && InputF == HIGH && InputG == LOW) WipeMode = 3; //Low
    else if (InputE == HIGH && InputF == LOW && InputG == LOW) WipeMode = 4; //High
    else WipeMode = 0;
    if (InputC == LOW) WipeMode = 6; //Wash
    Menu = digitalRead(InputApin);
  }

  Serial.print("Input State:");
  Serial.println(WipeMode);
  Serial.print("Menu:");
  Serial.println(Menu);
}

Ok maybe I didn't need help... I got there in the end - here's what I have and it seems to work great. If anyone has any good suggestions of where to simplify the code I would be most interested :slight_smile:

#include <Bounce2.h>

//Define Input Pins
const int Wipe_InputApin = 1;
const int Wipe_InputBpin = 3;
const int Wipe_InputCpin = 5;
const int Wipe_InputDpin = 7;
const int Wipe_InputEpin = 9;
const int Wipe_InputFpin = 11;
const int Wipe_InputGpin = 14;

//Define Input Variables
int Wipe_InputA = 0;
int Wipe_InputB = 0;
int Wipe_InputC = 0;
int Wipe_InputD = 0;
int Wipe_InputE = 0;
int Wipe_InputF = 0;
int Wipe_InputG = 0;
int WipeMode = 0; // 0 = Error, 1 = Off, 2 = Intermediate, 3 = Low, 4 = High, 5 = Auto, 6 = Wash
int WipeModePrev = 0;
int Menu = 0; //1 for press

//Time Variables
unsigned long Prev_Time_Wipe = 0;
unsigned long Wipe_Interval = 500;
unsigned long currentMillis = 0;
unsigned long Time_Millis = 0;
  

//Define Debouncers
Bounce Wipe_Debounce_A = Bounce();
Bounce Wipe_Debounce_B = Bounce();
Bounce Wipe_Debounce_C = Bounce();
Bounce Wipe_Debounce_D = Bounce();
Bounce Wipe_Debounce_E = Bounce();
Bounce Wipe_Debounce_F = Bounce();
Bounce Wipe_Debounce_G = Bounce();


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

  //Set Up Wiper Stalk Inputs
  //Setup Wipe Input A
  pinMode(Wipe_InputApin, INPUT_PULLUP);
  Wipe_Debounce_A.attach(Wipe_InputApin);
  Wipe_Debounce_A.interval(5);

  //Setup Wipe Input B
  pinMode(Wipe_InputBpin, INPUT_PULLUP);
  Wipe_Debounce_B.attach(Wipe_InputBpin);
  Wipe_Debounce_B.interval(25);

  //Setup Wipe Input C
  pinMode(Wipe_InputCpin, INPUT_PULLUP);
  Wipe_Debounce_C.attach(Wipe_InputCpin);
  Wipe_Debounce_C.interval(5);

  //Setup Wipe Input A
  pinMode(Wipe_InputDpin, INPUT_PULLUP);
  Wipe_Debounce_D.attach(Wipe_InputDpin);
  Wipe_Debounce_D.interval(25);

  //Setup Wipe Input E
  pinMode(Wipe_InputEpin, INPUT_PULLUP);
  Wipe_Debounce_E.attach(Wipe_InputEpin);
  Wipe_Debounce_E.interval(25);

  //Setup Wipe Input F
  pinMode(Wipe_InputFpin, INPUT_PULLUP);
  Wipe_Debounce_F.attach(Wipe_InputFpin);
  Wipe_Debounce_F.interval(25);

  //Setup Wipe Input G
  pinMode(Wipe_InputGpin, INPUT_PULLUP);
  Wipe_Debounce_G.attach(Wipe_InputGpin);
  Wipe_Debounce_G.interval(25);

}

void loop() {
  //Read Stalk Inputs

  //Update Bouncers
  Wipe_Debounce_A.update();
  Wipe_Debounce_B.update();
  Wipe_Debounce_C.update();
  Wipe_Debounce_D.update();
  Wipe_Debounce_E.update();
  Wipe_Debounce_F.update();
  Wipe_Debounce_G.update();

  Wipe_InputB = !Wipe_Debounce_B.read();
  Wipe_InputC = !Wipe_Debounce_C.read();
  Wipe_InputD = !Wipe_Debounce_D.read();
  Wipe_InputE = !Wipe_Debounce_E.read();
  Wipe_InputF = !Wipe_Debounce_F.read();
  Wipe_InputG = !Wipe_Debounce_G.read();


    //Auto Wipe Mode
    if (Wipe_InputD == LOW) {
      WipeMode = 5; //Auto
      Menu = !Wipe_Debounce_A.read(); //Menu signal flips in auto mode
    }

    //Manual Mode
    else if (Wipe_InputD == HIGH) {
      if (Wipe_InputE == LOW && Wipe_InputF == LOW && Wipe_InputG == LOW) WipeMode = 1; //OFF
      else if (Wipe_InputE == LOW && Wipe_InputF == LOW && Wipe_InputG == HIGH) WipeMode = 2; //Intermediate
      else if (Wipe_InputE == LOW && Wipe_InputF == HIGH && Wipe_InputG == LOW) WipeMode = 3; //Low
      else if (Wipe_InputE == HIGH && Wipe_InputF == LOW && Wipe_InputG == LOW) WipeMode = 4; //High
      else WipeMode = 0;
      if (Wipe_InputC == LOW) WipeMode = 6; //Wash
      WipeModePrev = WipeMode;
      Menu = Wipe_Debounce_A.read();
    }

  //~~~~~~~~~Diagnostics~~~~~~~~~~~
  Serial.print("Wipe Mode:");
  if (WipeMode == 0) Serial.print("Error");
  if (WipeMode == 1) Serial.print("Off");
  if (WipeMode == 2) Serial.print("Intermediate");
  if (WipeMode == 3) Serial.print("Low");
  if (WipeMode == 4) Serial.print("High");
  if (WipeMode == 5) Serial.print("Auto");
  if (WipeMode == 6) Serial.print("Wash");
  Serial.print("   Menu:");
  Serial.println(Menu);
}

I'm quit sure you can simplify that code by time by putting those pins into an array (function and all). That does away with the repetition.

That would be great, how would I go about doing this??

what would be a neat way to debounce the inputs?

Why would you want/need to debounce them?

450nick:
That would be great, how would I go about doing this??

For example

//Define Input Pins
const int InputApin = 1;
const int InputBpin = 3;
const int InputCpin = 5;
const int InputDpin = 7;
const int InputEpin = 9;
const int InputFpin = 11;
const int InputGpin = 14;

becomes

//Define Input Pins
const int InputPin[7] = {1, 3, 5, 7, 9, 11, 14};

And

  pinMode(InputApin, INPUT_PULLUP);
  pinMode(InputBpin, INPUT_PULLUP);
  pinMode(InputCpin, INPUT_PULLUP);
  pinMode(InputDpin, INPUT_PULLUP);
  pinMode(InputEpin, INPUT_PULLUP);
  pinMode(InputFpin, INPUT_PULLUP);
  pinMode(InputGpin, INPUT_PULLUP);

becomes

  for (byte i=0; i <7; i++) {
    pinMode(InputPin[i], INPUT_PULLUP);
  }

Awesome that is a neat concept I need to use more often...! Thank you. I want to debounce just to make sure I don't get any jittering around. It seems nice and stable with this setup.

You can use arrays a heck of a lot in your code, it's full of repetition right now. You can even do stuff like changing this

if (WipeMode == 0) Serial.print("Error");
  if (WipeMode == 1) Serial.print("Off");
  if (WipeMode == 2) Serial.print("Intermediate");
  if (WipeMode == 3) Serial.print("Low");
  if (WipeMode == 4) Serial.print("High");
  if (WipeMode == 5) Serial.print("Auto");
  if (WipeMode == 6) Serial.print("Wash");

into

const char wipeModeDesc[7][13] = {
  "Error", "Off", "Intermediate",
  "Low", "High", "Auto", "Wash" };
...
...
...
  Serial.print(wipeModeDesc[WipeMode]);

Maybe I'm missing something subtle, but I don't see any point debouncing these buttons, it's just making the code complex for zero benefit, and you did ask for suggestions to simplify it. Debouncing is needed only for keypads, menu mode selection buttons etc., where bouncing causes unwanted actions like entering a digit twice or skipping an option in a menu. All your buttons have a single unambiguous meanings. None of them are click-on-click-off or double-tap for an alternative action to single-tap, or anything like that.

Here's another tip;

else if (Wipe_InputD == HIGH) {

There is no need for the extra if checking for HIGH here. You already checked for LOW in the preceding if. When the sketch gets to the above line, the answer must be HIGH, so just put

else {

I would also question if "Wash" should be one of the modes. Every car I ever had, it was a separate button or you pulled the stalk toward you to wash, and this worked in any of the modes like intermittent or high. If you make it a separate mode, then when in that mode, your code could loose track of which mode it was also in.

My present car automatically wipes when you press wash, even if the wipers were off. Not sure if this happens because you press wash or because the wash liquid triggers the rain sensor. Will have to test that next time I run out of wash liquid!

Thanks Paul, some good suggestions in there... I put the debounce in as the menu button is a momentary switch and I don't want to accidentally click through more than one page on my code. Then for the other inputs, there is a gap between positions so when moving between positions it actually briefly runs passed "Off" which I want to avoid just for completeness so figured the debounce code should prevent any hiccups between switch positions. I might try it without the debounce just to see if it's ok.

Good point about the if else part - I should have noticed that one!

For the wash mode, this will also be linked to low speed. For the transition between modes the wiper will keep moving when the signal is removed until park position is found so the new signal should be taken up before this happens and even if it did find park, wash would kick it back into life. Normally this is all controlled with relays and a park switch but in my case I'm going to convert these signals to CAN and send them to a solid state power distribution module which will control a more modern motor using CAN or LIN signal hence why I need to convert this old style switch into a simple wipe mode message. Next up is the CAN bus part.. Should be fun :slight_smile:

Did you try to find the wiring diagram for the car?

I'm designing and building a completely new wiring diagram for the rebuilt car. I have the wiring diagram from the car that the wiper stalk specifically came from but the way it works is not how I'm intending to run the new system. I need to use this stalk though as it works with the steering column I want to use.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.