Coding an ignition switch

I am trying to make myself a truck dash to be used for American Truck Sim. I have the buttons and switches working fine, but I cannot get the ignition switch to work right. I hope this is a good place to come for help.

I am using a 3 prong ignition switch (BATT/RUN/START), I have a picture below of how I have it wired to the pro micro. The code I have sorta works but not exactly how I want it to. With the code below I can start the truck when I turn the key to the START (momentary switch) on the ignition start. Once the truck is started the key "resting" place is in the RUN position. When I when to turn the truck off I need to key OFF then back to the RUN position. I would like it to shut off the truck when I turn the key to the OFF position but I am not having any luck.

Do I need a different ignition switch? My thought is maybe having an ignition switch with ACC position could act as an OFF position for my case and it might work.

Does anyone have an idea of what to try next?

Thanks,
Dave

#include <Keypad.h>
#include <Joystick.h>

//DEFINITIONS
#define ENABLE_PULLUPS
#define NUMROTARIES 4
#define NUMBUTTONS 25
#define NUMROWS 5
#define NUMCOLS 5

//BUTTON MATRIX
//first change number of rows and columns to match your button matrix, 
//then replace all "?" with numbers (starting from 0)
byte buttons[NUMROWS][NUMCOLS] = {
  {0,1,2,3,4},
  {5,6,7,8,9},
  {10,11,12,13,14},
  {15,16,17,18,19},
  {20,21,22,23,24},
  
 
};

struct rotariesdef {
  byte pin1;
  byte pin2;
  int ccwchar;
  int cwchar;
  volatile unsigned char state;
};

//ROTARY ENCODERS
//each line controls a different rotary encoder
//the first two numbers refer to the pins the encoder is connected to 
//the second two are the buttons each click of the encoder wil press 
//do NOT exceed 31 for the final button number
rotariesdef rotaries[NUMROTARIES] {
  {0,1,22,23,0}, 
  {2,3,24,25,0}, 
  {4,5,26,27,0}, 
  {6,7,28,29,0} 


};

#define DIR_CCW 0x10
#define DIR_CW 0x20
#define R_START 0x0

#ifdef HALF_STEP
#define R_CCW_BEGIN 0x1
#define R_CW_BEGIN 0x2
#define R_START_M 0x3
#define R_CW_BEGIN_M 0x4
#define R_CCW_BEGIN_M 0x5
const unsigned char ttable[6][4] = {
  // R_START (00)
  {R_START_M,            R_CW_BEGIN,     R_CCW_BEGIN,  R_START},
  // R_CCW_BEGIN
  {R_START_M | DIR_CCW, R_START,        R_CCW_BEGIN,  R_START},
  // R_CW_BEGIN
  {R_START_M | DIR_CW,  R_CW_BEGIN,     R_START,      R_START},
  // R_START_M (11)
  {R_START_M,            R_CCW_BEGIN_M,  R_CW_BEGIN_M, R_START},
  // R_CW_BEGIN_M
  {R_START_M,            R_START_M,      R_CW_BEGIN_M, R_START | DIR_CW},
  // R_CCW_BEGIN_M
  {R_START_M,            R_CCW_BEGIN_M,  R_START_M,    R_START | DIR_CCW},
};
#else
#define R_CW_FINAL 0x1
#define R_CW_BEGIN 0x2
#define R_CW_NEXT 0x3
#define R_CCW_BEGIN 0x4
#define R_CCW_FINAL 0x5
#define R_CCW_NEXT 0x6

const unsigned char ttable[7][4] = {
  // R_START
  {R_START,    R_CW_BEGIN,  R_CCW_BEGIN, R_START},
  // R_CW_FINAL
  {R_CW_NEXT,  R_START,     R_CW_FINAL,  R_START | DIR_CW},
  // R_CW_BEGIN
  {R_CW_NEXT,  R_CW_BEGIN,  R_START,     R_START},
  // R_CW_NEXT
  {R_CW_NEXT,  R_CW_BEGIN,  R_CW_FINAL,  R_START},
  // R_CCW_BEGIN
  {R_CCW_NEXT, R_START,     R_CCW_BEGIN, R_START},
  // R_CCW_FINAL
  {R_CCW_NEXT, R_CCW_FINAL, R_START,     R_START | DIR_CCW},
  // R_CCW_NEXT
  {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
};
#endif

//BUTTON MATRIX PART 2
byte rowPins[NUMROWS] = {21,20,19,18,15}; 
byte colPins[NUMCOLS] = {14,16,10,9,8}; 

Keypad buttbx = Keypad( makeKeymap(buttons), rowPins, colPins, NUMROWS, NUMCOLS);

//JOYSTICK SETTINGS
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,
  JOYSTICK_TYPE_JOYSTICK,
  32, //number of buttons
  0, //number of hat switches
  //Set as many axis to "true" as you have potentiometers for
  false, false, false, false, false, false, false, false, false, false, false); 

const int numReadings = 20;
 
int readings[numReadings];      // the readings from the analog input
int index = 0;              // the index of the current reading
int total = 0;                  // the running total
int currentOutputLevel = 0;

//POTENTIOMETERS PART 1
//add all the axis' which are enabled above
int zAxis_ = 0;
int RxAxis_ = 0;   

               
//POTENTIOMETERS  PART 2
//Which pins are your potentiometers connected to?
int potentiometerPin1 = 4; //Change "?" to the pin your potentiometer is connected to
int potentiometerPin2 = 6;
const bool initAutoSendState = true;


void setup() {
  Joystick.begin();
  rotary_init();
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
}

void loop() {

  CheckAllEncoders();
  CheckAllButtons();
  CheckAllPotentiometers();
 
}

//POTENTIOMETERS PART 3
//change the details to match teh details above for each potentiometer you are using
void CheckAllPotentiometers(){
                           
  //potentiometer 1
  currentOutputLevel = getAverageOutput(potentiometerPin1);
  zAxis_ = map(currentOutputLevel,0,1023,0,255);
  Joystick.setZAxis(zAxis_); 

  //potentiometer 2
  currentOutputLevel = getAverageOutput(potentiometerPin2);
  RxAxis_ = map(currentOutputLevel,0,1023,0,255);
  Joystick.setRxAxis(RxAxis_);


}

int getAverageOutput(int pinToRead){
  index = 0;
  total = 0; 
 
  while (index < numReadings){
    readings[index] = analogRead(pinToRead);
    total = total + readings[index];
    index = index + 1;
    //delay (1);
  }
  return total / numReadings;
}


void CheckAllButtons(void) {
      if (buttbx.getKeys())
    {
       for (int i=0; i<LIST_MAX; i++)   
        {
           if ( buttbx.key[i].stateChanged )   
            {
            switch (buttbx.key[i].kstate) { 
                    case PRESSED:
                    case HOLD:
                              Joystick.setButton(buttbx.key[i].kchar, 1);
                              break;
                    case RELEASED:
                    case IDLE:
                              Joystick.setButton(buttbx.key[i].kchar, 0);
                              break;
            }
           }   
         }
     }
}


void rotary_init() {
  for (int i=0;i<NUMROTARIES;i++) {
    pinMode(rotaries[i].pin1, INPUT);
    pinMode(rotaries[i].pin2, INPUT);
    #ifdef ENABLE_PULLUPS
      digitalWrite(rotaries[i].pin1, HIGH);
      digitalWrite(rotaries[i].pin2, HIGH);
    #endif
  }
}


unsigned char rotary_process(int _i) {
  //Serial.print("Processing rotary: ");
  //Serial.println(_i);
  unsigned char pinstate = (digitalRead(rotaries[_i].pin2) << 1) | digitalRead(rotaries[_i].pin1);
  rotaries[_i].state = ttable[rotaries[_i].state & 0xf][pinstate];
  return (rotaries[_i].state & 0x30);
}

void CheckAllEncoders(void) {
  Serial.println("Checking rotaries");
  for (int i=0;i<NUMROTARIES;i++) {
    unsigned char result = rotary_process(i);
    if (result == DIR_CCW) {
      Serial.print("Rotary ");
      Serial.print(i);
      Serial.println(" <<< Going CCW");
      Joystick.setButton(rotaries[i].ccwchar, 1); delay(50); Joystick.setButton(rotaries[i].ccwchar, 0);
    };
    if (result == DIR_CW) {
      Serial.print("Rotary ");
      Serial.print(i);
      Serial.println(" >>> Going CW");
      Joystick.setButton(rotaries[i].cwchar, 1); delay(50); Joystick.setButton(rotaries[i].cwchar, 0);
    };
  }
  Serial.println("Done checking");
}

Hi, @davie56
Welcome to the forum.

Can you please post a copy of your circuit, a picture of a hand drawn circuit in jpg, png?
Hand drawn and photographed is perfectly acceptable.

Your diagram is covering up the pin numbers on the Micro.

Where abouts in your code do you read the ignition switch.

Can you please post a link to data/specs?

Thanks... Tom... :grinning: :+1: :coffee: :australia:

pictures and link below. Note the orientation of the pins on the back of the switch in the picture is 3-2-1 starting from the top going CW

For clarity...
Orange wire - 1 (BATT) to pin A3
brown wire - 2 (IGN) to pin 16
white/brown wire - 3 ( START) to pin 14

https://www.amazon.com/dp/B084M7WL4H?psc=1&ref=ppx_yo2ov_dt_b_product_details

I have the ignition switch as apart of the button matrix right now. I can pull it out for testing, but I know the rest of the button matrix works since the simple push buttons and toggle switches work.

It's been a minute since I started a car with a key…

In position 0, no pins are connected. Off.

In position 1, pins B and C are shorted. Run.

In position 2, pins A, B and C are shorted. Start.

Connect pin C to ground.

Pin B and A on separate inputs pulled up. Call then InA and InB.

Off: InA and InB read HIGH.
Run: InA reads HIGH, InB reads LOW.
Start: InA and InB read LOW.

HTH

a7

1 Like

Hi,
image

Does your keypad system/code recognise two pads pressed at the same time?

Tom... :grinning: :+1: :coffee: :australia:

Do you have pull up resistors on those inputs so they are in a defined state when the switch is not operating ( off ).

Your code looks pretty complex and very long and I’ve not studied it ( life’s too short ) , as suggested, it’s worth running simple code to check it ( I can’t see any pinMode in your code , I can see something about pull-ups - they might be there , but worth checking ).

Note that switch contacts bounce and that maybe an issue too

I think the code below is doing what you recommend, but it does not seem to work. Does this look right to you?

#include <Keypad.h>
#include <Joystick.h>

const int ROW_NUM    = 1;
const int COLUMN_NUM = 2; 

char keys[ROW_NUM][COLUMN_NUM] = {
  {'1', '2'},
};

byte pin_rows[ROW_NUM]    = {9}; 
byte pin_column[COLUMN_NUM] = {8, 7}; 

Keypad keypad = Keypad(makeKeymap(keys), pin_rows, pin_column, ROW_NUM, COLUMN_NUM);

const int inputPin1 = A0; 
const int inputPin2 = A1; 

Joystick_ Joystick;

void setup() {
  Serial.begin(9600);
  pinMode(inputPin1, INPUT);
  pinMode(inputPin2, INPUT);
}

void loop() {
  char key = keypad.getKey();
  
  if (key) {
    Serial.println(key);
    
    if (digitalRead(8) == LOW) {
      digitalWrite(inputPin1, LOW);
      digitalWrite(inputPin2, HIGH);
    } else if (digitalRead(7) == LOW) {
      digitalWrite(inputPin1, LOW);
      digitalWrite(inputPin2, LOW);
    } else {
      digitalWrite(inputPin1, HIGH);
      digitalWrite(inputPin2, HIGH);
    }
    
    Joystick.setXAxis(analogRead(A0));
    Joystick.setYAxis(analogRead(A1));
    Joystick.sendState();
  }
}

My original code would let me press 2 push or more buttons at a time

No. You are writing to input pins

      digitalWrite(inputPin1, LOW);

and also using them as analog inputs.

My suggestion is completely divorced from the keypad, it just a simple idea to test the switch configuration and (probably) see it can be used as two switches.

void setup() {
   pinMode(5, INPUT_PULLUP);
   pinMode(6, INPUT_PULLUP);
   pinMode(7, OUTPUT);
   pinMode(8, OUTPUT);
}

void loop() {
  digitalWrite(7, digitalRead(5));
  digitalWrite(8, digitalRead(6));
 }

Wire ttwo switch pins to 5 and 6 and the common one to ground. Place an LED with series limiting resistor between pin 7 and ground, and another LED with series limiting resistor between pin 8 and ground.

See and tell us what happens in three positions of the ignition thing switch.

In your matrix, the two equivalent switches that make up the ignition key thing must find themselves on the same row, or column, as they have the Batt lead in common.

The other two switch pins would be on separate columns, or rows. Where they would just appear to operate like anything at that matrix position, and yield only three of the four possible conditions that two regular pushbuttons would be capable of, onacconna that's how the ignition switch works.

FWIW y'all have missed quite the trip,\ down memory lane her under the umbrella as we recall the little dance that hot wiring a car once was…

a7

I kept the keypad library because I need to do this on the computer as I do not have LEDs to test code on a breadboard. I will need to experiment with this idea

So… channel your dessert island creativity. No LEDs?

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

  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
}

void loop() {
  Serial.print("pin 5 = "); Serial.print(digitalRead(5));
  Serial.print("      pin 6 = "); Serial.print(digitalRead(6));
  Serial.println(".");

  delay(123);  // or don't.
}

HTH

a7

I wish I could be creative with this kind of stuff, but all of my knowledge on the topic before this forum post was from a handful of YT videos I watched to get everything else working.

Anyway, the code you posted does work. When the key is OFF, both are HIGH. Key at RUN, 5 is HIGH and 6 is LOW. Key at IGN, both are LOW. But I am not sure what this proves? I already knew the switch was working, I just could not get it to work correctly.

I agree with @Delta_G, and I understand how hard it can be to take that kind of advice. Over the long run, every minute you spend now will pay off in hours and more later.

Still, maybe you could have a bit of success. Go back to the code and circuit you used that let you press multiple buttons.

I may have it backwards or mirrored, but you must have been using a diode-isolated scanned matrix.

Each switch is wired between one row and one column. The drawing below, which looks like I drew it with my finger for good reasons, shows one regular button at matrix position R1C1.

It also shows how to place the ignition switch thing onto the matrix. Since it is effectively two switches, I used R0C2 for one (A), and R1C2 for the other (B).

A, B and C according to my table above. C is the common pin.

Since the normal operation of the ignition has the ability to close both its internal switches at once, you have to have the diodes, the same as you must have with your previous success.

My diodes may be reversed, so use your best common sense.

It's nothing more than two switches with a special mechanical arrangement that makes them operate non-independently.

a7

So yes, after working on this project I have interest in learning more about coding. The main reason for even posting here is that this will end up as a Christmas present for my nephew. I did not/could not struggle with the coding portion of the project and not have enough time to complete the rest of it. Sorry if it feels like I’m just here for the answers.

Going back to the project… I understand the button matrix and how to wire it in. I understand why I would need diodes to prevent ghosting when pressing more than one button. What I don’t understand how it will work is the code for the ignition switch. Sure the little test code above shows it working, but I’m not sure that will work in game. Here is why..

If the IGN is a momentary switch, once released it will “rest” in the RUN position on the switch. So won’t the game read that as a double reading? One for the momentary and one for the rest position? I feel like my original code was close to the answer, just the order of operation was wrong. Does that make sense??

If your button scanning reports switch events like closing and opening, the ignition thing would

Report B closing, then A closing as you turned from position 0 to 1 to 2, then

A opening as you let it spring back to position 1, then

B opening as you turned it from position 1 to position 0.

If your button scanning allows determining the state of all the buttons, then in position 0 those matrix points will be open, in position 1 B will be closed and A open and in position 2 A closed and B closed.

It seems you are mostly using code meant for American Truck Sim. It may be that it has some kind of configuration setting that would allow it to interpret these events and states as a good old ignition switch.

a7

Below is my most recent code, everything works but the game does not recognize it how I want.

The more I think about it, I don't think I want the game to recognize a key in the RUN position, instead I want the game to see when the key is turned to the OFF position (not just in the OFF position).

The game allows you to have 2 keybinds for Start/Stop Engine. So in my game I have button 1 as primary and a combo of buttons 1 and 2 as secondary. They both do the same thing (Start/Stop cannot be made into separate functions). But my thought is to make button 1+2 into the Start because that is for IGN. Then button 1 would be for stopping the engine. The problem with that is OFF on the ignition switch does not give me functionality in the game.

example of what the keybinding looks like....

#include <Joystick.h>

Joystick_ Joystick;

const int buttonPin1 = 5; 
const int buttonPin2 = 6; 

int buttonState1 = 0;
int buttonState2 = 0;

void setup() {
  Joystick.begin();

  pinMode(buttonPin1, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
}

void loop() {
  buttonState1 = !digitalRead(buttonPin1);
  buttonState2 = !digitalRead(buttonPin2);

  Joystick.setButton(0, buttonState1);
  Joystick.setButton(1, buttonState2);

  delay(10);
}

If the game has no way to react to a switch opening, you will have to handle that yourself.

You need to use state change detection and grab the opportunity it would afford to react to either (or both) the opening and the closing of some switch.

Wire the ignition switches separately, not on the matrix, and handle them with state change detection.

With the facts about opening and closing of the two equivalent switches, spoon feed the equivalent in button presses.

When you see the ignition switch part for "OFF" contacts open up, send a button press to the start/stop part of the game.

I've made it sound easy and it may be. I have no experience with button boxes and how games listen for whatever they produce. I do not know how start/stop,operates, I would guess it's a momentary switch that turns the engine off and on alternately with each press.

See this post and the thread it lives on for the state change detection idea:

HTH

a7

I think the example you mention in that post is what I want to do. I took the example and changed it a bit to do what I want it to do, but the game is not recognizing any change in the ignition switch. Do you see anything wrong here? I kept the original comments and added some to show what it would being doing for my specific project.

Using the Joy.cpl I can test the code with the ignition switch outside of the game. It shows the code works as I intended.
image

#include <Joystick.h>

// Define the Joystick
Joystick_ Joystick;

// Define pin numbers for buttons
const int buttonPin1 = 5; // Button 1 pin
const int buttonPin2 = 6; // Button 2 pin

// Variables to store button states
int buttonState1 = 0;
int buttonState2 = 0;

int lastButtonState1 = 0;     // previous state of button1
int lastButtonState2 = 0;     // previous state of button2

void setup() {
  // Initialize Joystick library
  Joystick.begin();

  // Set button pins as INPUT
  pinMode(buttonPin1, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
}

void loop() {
  // Read the state of the button
  buttonState1 = !digitalRead(buttonPin1);
  buttonState2 = !digitalRead(buttonPin2);


  // compare the buttonState1 to its previous state....  **THIS IS IGN**
  if (buttonState1 != lastButtonState1) {
    // the state has changed, check what it is now
    if (buttonState1 == HIGH) {
      // if the current state is HIGH then the button went from off to on: **turn the IGN on**
           Joystick.setButton(0, buttonState1);
    } 
    else {
      // if the current state is LOW then the button went from on to off: **do nothing**
      
    }
  }
  // update the last state for next time through the loop
  lastButtonState1 = buttonState1;



  // compare the buttonState2 to its previous state....  **THIS IS RUN**
  if (buttonState2 != lastButtonState2) {
    // the state has changed, check what it is now
    if (buttonState2 == HIGH) {
      // if the current state is HIGH then the button went from off to on: **do nothing**
     
    } 
    else {
      // if the current state is LOW then the button went from on to off: **if IGN is ON turn RUN off**
         Joystick.setButton(0, buttonState2);
    }
  }
  // update the last state for next time through the loop
  lastButtonState2 = buttonState2;



  // Update the Joystick buttons
//Joystick.setButton(0, buttonState1);
//Joystick.setButton(1, buttonState2);

  // Add a small delay to avoid button bouncing
  delay(10);
}


I'm in transit looking through the tiny window, but it looks like you are following the idea.

If I understand, you see the correct usable behaviour of the ignition switches in some kind of "test your button box" utility program.

But the game does not react as it should.

You coded what should work, a utility program indicates you are creating button presses on edges, or at least a button press on the opening of some switch…

There's nowhere else to look except why the game doesn't respect the faked button press.

I'll be able to look closer, but am pessimistic about it turning on your code at this moment.

a7