Read from 2 potentiometers and combine them to one output

I'm making a train simulator controller (for Train Sim World 2)
The simulator accepts input from a proprietary device called a RailDriver, thankfully someone has found a workaround, with a modified .dll and a simple arduino sketch that reads potentiometer positions and sends them over the serial port in the same way a real RailDriver does.

The real RailDriver has a single lever / potentiometer for power and dynamic brake, i.e. the top half of the pot is power, bottom half is brakes,

But a lot of european trains use seperate levers for power and dynamic brakes, and the writer of the .dll said you can split the throttle and dynamic brake pot into 2 :

"SPLITTING COMBINED THROTTLE AND BRAKE

  • The combined throttle and brake in the Raildriver control is sent over Byte 2 (Throttle). To split this you can use one pot connected to the Arduino as the Throttle to output 128 to 255. Then use another pot to go from 127 to 0 for braking. The braking pot should override the Throttle pot."*

This is where i stumble, how do i read from 2 seperate potentiometers and combine them so one pot's full range translates to the upper range, and the other pot's full range translates to the lower range of a single pot?

Then that bit about the brake pot should override the throttle pot (i know why, so moving the brake lever whilst the power lever is not at zero does not result in them fighting each other, but how would i do that?)

This is the sketch i am using

/*
Allows home made levers to be used with Train Sim World 2, using a custom pieHid64.dll
By Skaako (Michael Huggins 2021)
https://github.com/skaako/raildriver

*i'm running this with STMDuino on a blue pill*

RailDriver string output = Reverser, Power, TrainBrake, LocoBrake, BailOff, Wipers, Lights.

My European train style levers are in the order below, and connected to the pins indicated on the Blue Pill... not all my levers can be used at once*/

int Reverser = PA3;
int AFB = PA5; 
int Power = PA4; 
int DynamicBrake = PA1; // Power and Dynamic Brake are on a single pot on a real raildriver
int TrainBrake = PA0;
int LocoBrake = PA2; 
int Wipers = PA6;
int Lights = PA7;
int BailOff; // American train thing not used on euro trains, so i'll give it a fixed value to pass calibration


int val[7] = {0}; // variable to store the value read

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(115200);
  while (!Serial) { // wait for serial port to connect. Needed for native USB port only
    ; 
  }
}


void loop() {
  
  val[0] = analogRead(Reverser); // Read value of potentiometer on Reverser Switch
  val[0] = map(val[0], 0, 4095, 0, 255); // Map 12 bit STM board ADC to 8 bit outoput expected for RailDriver
 
  delay(10);
  
  val[1] = analogRead(Power); 
  val[1] = map(val[1], 0, 4095, 255, 0); //Reversed direction of lever

  delay(10);
  
  val[2] = analogRead(TrainBrake);
  val[2] = map(val[2], 0, 4095, 0, 255);
 
  delay(10);
  
  val[3] = analogRead(LocoBrake);
  val[3] = map(val[3], 0, 4095, 255, 0);

  delay(10);
  
  val[4] = (BailOff);
  val[4] = (254); // Not reading a pot for this value, give it a fixed value to send
  
  delay(10);
  
  val[5] = analogRead(Wipers);
  val[5] = map(val[5], 0, 4095, 0, 255);

  delay(10);

  val[6] = analogRead(Lights);
  val[6] = map(val[6], 0, 4095, 0, 255);

  delay(10);
  

  // Start of string output
  Serial.print("Output: "); // Write 'Output' at beginning of every line
  
  // Padding for numbers
  for(int i = 0; i < 7; i++){
    if(val[i] < 10){
      Serial.print("  ");
    } else if(val[i] < 100){
      Serial.print(" ");
    }
    Serial.print(val[i]);
    Serial.print(" ");
  }
  Serial.println(""); 
  
}

I'm not sure Arduino knows what to do with this. Did you try Arduino analogRead() the STM board output voltage first such as in the ReadAnalogVoltage example sketch?

I've never seen variables assigned to PA anything on Arduino. Why not just A3?

everything works right now, just i am stuck with a single pot/lever thing for throttle and dynamic brake,

The mapping thing, if i don't have it, it'll read in what i presume is 12 bit values, but the RailDriver outputs 8 bit values, i am pretty bad at coding stuff, so i just used the arduino code and changed a few things untill it worked on the blue pill.

It seems the P prefix for the pins is needed on these board, i.e. trying 'A3' doesn't read anything, but 'PA3' does read from that pin.

Did you try?

int powerVal = 0;
int outputPower = 0;

void setup (){whatever you have)}

void loop(){
...
int powerVal = analogRead(Power);
outputPower = map(powerVal, 0, 1023, 255, 0);
...
}

If it's similar to a car they WILL fight each other, with the break "slightly-winning". You could figure out a weighted average. If you know a little algebra that shouldn't be too hard.

Or, you might want to completely ignore the throttle whenever the break is not zero (or maybe when it's less than 10, or something like that). That would be an if-condition.

First, figure-out the logic before you try to program it.

Yes, that's how mapping works. But it's just a proportion (or sometimes an offset proportion) . Algebra again. The "bit range" may be important, or not, or maybe only important in the abstract. On a regular Arduino it's pretty common to map the 10-bit (0-1023) analog input to the 8-bit (0-255) PWM output to dim an LED.

1 Like

at the moment, with the throttle and brake on one pot... there is actually a bit of a deadband set when you calibrate the thing in the simulator, so it may be throttle is the 255 to 165 portion of the pot, and brakes are the 140 to 0 part,

The bit in-between ensure the brakes and throttle dont wobble / fight each other i guess.
.

So i'd be reading the 2 separate pot's input, at 12 bits as that's what the blue pill's output (some have 16 bit ADC's),

so thats 0 to 4095 each, and i need something like ''if brake input is anything other than say... 20, ignore the throttle input'' and output the 140 to 0 '8 bit signal'

When brake input is below 20, ignore brake input and read throttle input, and output in the 165 to 255 range.

I think i've done it. :astonished: :exploding_head:

i'm amazed i managed to work this out, i've written a simple sketch that is only reading the 2 potentiometers i'm interested in, and sending the output to the serial monitor,
Should be easy to add this into the full sketch that mimics a RailDriver (famous last words)

If the brake lever is all the way forwards, i.e. off, the power lever's pot is read, and maps the output from 255 down to 165 on the single combined power and brake lever 'output'

As soon as the brake lever is moved back / on, even if the power lever was on full, the brake lever's pot is read and the output value jumps down to 160,
As the brake lever is moved more, the combined power / brake lever output goes down to zero with the levers movement.

The power lever does not become 'active' again until the brake lever is back to fully off.

//I'm using a STM32f Blue Pill board, with STMDuino, this is why the raw values from the potentiometers are in 12 bits.. 0 - 4095. 

int val[] = {0}; // Variable to store values read... set to zero at start?

void setup() {
  
  Serial.begin(115200); // initialize serial port
  while (!Serial) { // wait for port to connect 
    ; 
  }
}

void loop() {

val[0] = analogRead(PA4); // Read pot on Power lever

delay(10);

val[1] = analogRead(PA5); // Read pot on Brake lever

delay(10); 


if(val[1] > 4090) // If the brake lever is all the way off...

{val[2] = map(val[0], 0, 4095, 255, 165); // read the Power lever's pot, and map it from 12 to 8 bits, then half the range it outputs
}

else{
  (val[1] < 4080, val[2] = map(val[1], 4095, 0, 160, 0)); // If the brake lever is on, ignore the Power lever, and read the brake lever
}



Serial.print("Power: "); 
Serial.print(val[0]);  // Print the value of the Power levers potentiometer
Serial.print("  ");
Serial.print("Brake: ");
Serial.print(val[1]); // Print the value of the Brake levers potentiometer
Serial.print("  ");
Serial.print("Combined: ");
Serial.print(val[2]); // Print the 8 bit value that would be sent to the simulator, 255 - 165 = from the Power lever, 160 - 0 = from the Brake lever
Serial.println();
  
}

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