I Created A Mod/software To Directly Control In Game Trains Using A Joystick (no Raildriver)

Discussion in 'TSW General Discussion' started by liah#5302, Apr 3, 2025.

  1. Bekns

    Bekns Active Member

    Joined:
    Jan 20, 2019
    Messages:
    204
    Likes Received:
    70
    You are correct about the about setting steps /notches, the value is now steady although the levers do jitter a little still but Im not looking at them mostly. Yeah with smoothing you will lose a little bit of accuracy as a trade off, maybe a slider that can increase / decrease sensitivity but I understand if this is not an issue for others I dont expect you to spend any time on that.

    Using the second profile did work but it comes with issues for me at least, because I have pulled the internals out and installed them in different casings if I want to invert a control I need to it in the individual profile rather than the calibration profile which is fine really but just one of those thing's you notice.

    The real issue for me is the Arduino button box, because you can only calibrate one and it saves it to both I dont think it would work for my setup. The button box has 8 toggle switches, 16 momentary switches, 2 5pos rotary switches, and 2 latching switches, one of these is an led that lights up when latched. It uses one Arduino Mega which converts to 2 devices in Windows but when calibrating in your program it saves the first calibration to both devices, I dont think I would bother setting up different profiles for this unless it was maybe in sync mode and the profile being generic for all trains.

    Again these are really only problems for me and I dont expect you to change the world for me, if no one else is having the issues I will happily work around them. :)
     
  2. Bekns

    Bekns Active Member

    Joined:
    Jan 20, 2019
    Messages:
    204
    Likes Received:
    70
    Has anyone got the _{SIDE} option to work, I have tried on 2 locos (the Arosa GE 4/4 and the Liberac Class 843) but neither seem to want to work, it appears to operate the controls in the other end still. Probably Im doing something wrong, any suggestions from people who have got it to work?

    EDIT: I have found the {SIDE:1:2} needed for the Czech locos in the documentation, I missed this on the first read through. I havent checked if it works when switching ends but I definitely couldnt get it to work for the Arosa GE 4/4 which uses the _F and _B nomenclature.
     
    Last edited: May 28, 2026
  3. Bekns

    Bekns Active Member

    Joined:
    Jan 20, 2019
    Messages:
    204
    Likes Received:
    70
    Hi Liah

    I know Im being painful for here and I apologize but could you please look at the below code, Im trying to implement code that does a stepped notch throttle lever with the last notch being held as per a previous post in this thread but Im doing something wrong as the profile will not show up. Usually when this happens I have left a comma out somewhere but I have spent some time trying to spot where I have gone wrong and I think the issue might be more than an errant comma.

    Code:
    {
        "name": "Liberec843",
        "controls": [
            {
                "name": "Throttle",
                "assignments": [
                    {
                        "conditions": [{"control": "Throttle", "operator": "lte", "value": 1.1}],
                        "type": "direct_control",
                        "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                        "input_value": {
                            "min": -3,
                            "max": 1,
                            "steps": 1                   
                        },
                        "enable_api_fallback": true
                    },
                    {
                        "conditions": [{"control": "Throttle", "operator": "gt", "value": 1.1}],
                        "type": "momentary",
                        "threshold": 1.2,
                        "action_activate": {
                            "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                            "value": 2,
                            "hold": true
                        },
                        "action_deactivate": {
                            "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                            "value": 1
                        },
                        "enable_api_fallback": true
                    }
                ]
            },
            {
                "name": "LocoBrake",
                "assignments": [
                    {
                        "type": "direct_control",
                        "controls": "EngineBrakeLeverCab_{SIDE:1:2}",
                        "input_value": {
                            "min": -2,
                            "max": 2,
                            "step": 1
                        },
                        "enable_api_fallback": true
                    }
                ]
            },
            {
                "name": "Reverser",
                "assignments": [
                    {
                        "type": "direct_control",
                        "controls": "ReverserCab_{SIDE:1:2}",
                        "input_value": {
                            "min": -1,
                            "max": 2,
                            "step": 1
                        },
                        "enable_api_fallback": true
                    }
                ]
            }
        ]
    }
    
    Any help greatly appreciated :)
     
  4. liah#5302

    liah#5302 Member

    Joined:
    Jan 11, 2025
    Messages:
    58
    Likes Received:
    26
    Glad you found the problem with the Czech loco; that one for some reason is the only one ever to not use _F and _R

    For the GE4-4 I actually used the {SIDE} for it in the example ge4_4.tswprofile
    https://github.com/LiahMartens/tsw-controller-app/blob/main/shared-profiles/ge4_4.tswprofile
    Does that one not work?
     
  5. liah#5302

    liah#5302 Member

    Joined:
    Jan 11, 2025
    Messages:
    58
    Likes Received:
    26
    No problem at all;

    I believe the problem is your conditions; the physical control values are always normalized to a [0:1] range (or [-1:0:1] when using a more complex centered set-up)

    This essentially translates to a engagement percentage; ie: if your physical lever is roughly in the middle it should report 0.5

    This means your conditions should be something more like

    Code:
              "conditions": [
                { "control": "Throttle", "operator": "lte", "value": 0.9 }
              ],
    
    and


    Code:
              "conditions": [
                { "control": "Throttle", "operator": "gt", "value": 0.9 }
              ],
    
    You can always use the "Calibration > Inspect" functionality to check the actual values being reported by each physical control as you interact with them
     
  6. Bekns

    Bekns Active Member

    Joined:
    Jan 20, 2019
    Messages:
    204
    Likes Received:
    70
    I will double check again against your example profile and test, when I did try it out it seemed it was operating the other cab controls still but I will test again :)

    Of course that makes sense, I will fix all that up but would that prevent the profile from displaying in the drop down list or is that another issue?
     
  7. liah#5302

    liah#5302 Member

    Joined:
    Jan 11, 2025
    Messages:
    58
    Likes Received:
    26
    Ha no that's because you have `"steps": 1` but the `"steps"` key is only for providing a list of steps; if you want to let it figure out the steps on it's own using the number you just provide `"step"`

    You can always check by using the `Import profile` button and import the profile from the UI (you would need to change the extension of your file to `.tswprofile`) that way you can get feedback on why it might not be loading

    [​IMG]
     
    Last edited: May 29, 2026
  8. liah#5302

    liah#5302 Member

    Joined:
    Jan 11, 2025
    Messages:
    58
    Likes Received:
    26
    FYI Currently implementing calibration by unique device ID option
     
  9. Bekns

    Bekns Active Member

    Joined:
    Jan 20, 2019
    Messages:
    204
    Likes Received:
    70
    Wow that is so cool, thanks a heap for looking into that.

    Yeah I went through line by line and managed to spot that one, i did have set steps at one point while trying things out and forgot to put it back. I didnt realize you could try to import and it would flag errors, that is really helpful. I have now managed to get the throttle on the 843 to hold when it it is at maximum so I feel I have the know how to implement custom levers for almost any situation, this is really exciting!
     
  10. NSNL

    NSNL New Member

    Joined:
    May 29, 2026
    Messages:
    3
    Likes Received:
    0
    Hi Liah,

    Can you please help me install my TSC-X on Fedora 44. I must say I am kind of a noob and also tried to use AI to work around my issues, but I am really stuck here.

    So, I have installed tswcontrollerapp, calibrated my TSC-X, applied all the settings in Steam for the controller, but there is no cab debugging happening at all. And I couldnt find a log file as well. Could you please point me towards some directions to check?

    Thanks in advance

    Kind regards
     
  11. NSNL

    NSNL New Member

    Joined:
    May 29, 2026
    Messages:
    3
    Likes Received:
    0
    So I do have Train Simulator Classic working with the Cab debugger. But TSW6 just won't work. And I don't get it why it isn't working.
     
  12. liah#5302

    liah#5302 Member

    Joined:
    Jan 11, 2025
    Messages:
    58
    Likes Received:
    26
    hm TSC and TSW use the same connection pattern; I assume you installed the TSW mod?
    If you go to the mod install location can you find a file called UE4SS.log in the ue4ss_tsw_controller_mod directory next to the TSW binary location?

    You can send me those logs + the logs from the tswcontrollerapp itself (go the the logs tab) and click "Save logs" - both logs together might give me a better idea
     
  13. liah#5302

    liah#5302 Member

    Joined:
    Jan 11, 2025
    Messages:
    58
    Likes Received:
    26
    New release is available with an added option in the calibration dialog to enable calibrating by unique device ID - you can try it out; just note that if you're calibrating devices by their unique device ID all devices which share that same Vendor and Product ID should be configured with their unique ID instead of the normal ID to make sure they don't pollute each other
     
  14. Bekns

    Bekns Active Member

    Joined:
    Jan 20, 2019
    Messages:
    204
    Likes Received:
    70
    Just installed and had a quick look, where I had 2 Arduinos connected before I now only have 1 but it appears all buttons across both are responding, is this how it is intended to work, like it merges both controllers into 1? The 2 throttle quadrants are still appearing under the previous profiles but I will be able to have a more in depth play tomorrow when I remove the existing profiles and recalibrate.

    Thanks again for taking the time and looking at this

    EDIT: It looks like it is not merging both controllers, with the new update Im only getting one controller show up instead of 2 as mentioned previously but it doesnt give me separate button inputs if the buttons are the same number. This is with both the new option ticked and unticked, the behavior is the same, I presume the controllers should still show up separately?
     
    Last edited: May 30, 2026
  15. NSNL

    NSNL New Member

    Joined:
    May 29, 2026
    Messages:
    3
    Likes Received:
    0
    Okay, so the placement of the Mod was totally wrong. I finally got it to work.

    Edited two downloaded profiles because they were missing a lot of buttons and the auto select feature. I love it. Thanks for the awesome tool.
     
    Last edited: May 30, 2026
  16. liah#5302

    liah#5302 Member

    Joined:
    Jan 11, 2025
    Messages:
    58
    Likes Received:
    26
    Looks like the underlying system SDL library is not reporting unique GUIDs for identical VIDPID devices - will have a look

    You can downgrade back down to 1.14.3 for now if you need to
     
  17. Bekns

    Bekns Active Member

    Joined:
    Jan 20, 2019
    Messages:
    204
    Likes Received:
    70
    All good, for myself using the vJoy setup it behaves as the previous version did but for others in the same boat it might cause issues.

    Also quickly, is there a way to string to conditions together as in 'A gt X and lt Y', sorry if I have missed it in the documentation somewhere.
     
  18. liah#5302

    liah#5302 Member

    Joined:
    Jan 11, 2025
    Messages:
    58
    Likes Received:
    26
    Yea you can specify multiple conditions; you can just add another one in the list. They will always behave as "and"
     
  19. liah#5302

    liah#5302 Member

    Joined:
    Jan 11, 2025
    Messages:
    58
    Likes Received:
    26
    Can you try the 1.16 version?
     
  20. Bekns

    Bekns Active Member

    Joined:
    Jan 20, 2019
    Messages:
    204
    Likes Received:
    70
    So far this one has worked a treat, I now have both Quadrants working natively and calibrated uniquely. This is a huge advancement and I can not thank you enough. Now I just need to improve my coding because it is obvious I have no idea what Im doing :D
     
  21. Bekns

    Bekns Active Member

    Joined:
    Jan 20, 2019
    Messages:
    204
    Likes Received:
    70
    Im trying to code the CD843 throttle but Im not having much luck, I can get 1 to hold its value (2) the below code but nothing I try will get 0.4 to hold its value.

    Ideally the throttle will go like this from 0 to 1
    0 - Emergency Brake (-3)
    0.2 - Pneumatic Brake (-2)
    0.4 - Electric Brake (-1) -Spring position reverting to 0.6 (0)
    0.6 - Neutral (0)
    0.8 - Positive amp hold / negative amp reduce (1)
    1 - Positive amp increase (2) -Spring position reverting to 0.8 (1)

    Code:
            
            {
                "name": "Throttle",
                "assignments": [
                    {
                        "conditions": [{"control": "Throttle", "operator": "lte", "value": 0.8}],
                        "type": "direct_control",
                        "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                        "input_value": {
                            "min": -3,
                            "max": 2,
                            "step": 1
                        },
                        "enable_api_fallback": true
                    },
                    {
                        "conditions": [{"control": "Throttle", "operator": "gt", "value": 0.95}],
                        "type": "momentary",
                        "threshold": 0.96,
                        "action_activate": {
                            "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                            "value": 2,
                            "hold": true
                        },
                        "action_deactivate": {
                            "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                            "value": 1
                        },
                        "enable_api_fallback": true
                    }
                ]
           }
    I have tried different conditions to try and get 0.4 to hold its value but it always springs back, once I was able to get it to work but it would only work in one direction, it would be really cool if you could just specify the 'Steps' with say a 'Hold' function and that step held its value until the throttle was moved again.

    Sorry to be a pain, I realize this is a really niche case throttle but I feel if I can crack this one then any throttle is possible
     
  22. liah#5302

    liah#5302 Member

    Joined:
    Jan 11, 2025
    Messages:
    58
    Likes Received:
    26
    Yea for more complex lever assignments where you want to specifically manually control each step you would probably be looking at a linear assignment type instead.

    The below worked on my CD 843
    Code:
    {
      "name": "Liberec843",
      "controls": [
        {
          "name": "Lever3",
          "assignments": [
            {
              "type": "linear",
              "thresholds": [
                {
                  "value": 0.2,
                  "action_deactivate": {
                    "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                    "value": -3
                  },
                  "action_activate": {
                    "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                    "value": -2,
                    "hold": true
                  }
                },
                {
                  "value": 0.4,
                  "action_deactivate": {
                    "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                    "value": -2,
                    "hold": true
                  },
                  "action_activate": {
                    "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                    "value": -1,
                    "hold": true
                  }
                },
                {
                  "value": 0.6,
                  "action_deactivate": {
                    "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                    "value": -1,
                    "hold": true
                  },
                  "action_activate": {
                    "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                    "value": 0
                  }
                },
                {
                  "value": 0.8,
                  "action_deactivate": {
                    "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                    "value": 0
                  },
                  "action_activate": {
                    "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                    "value": 1
                  }
                },
                {
                  "value": 0.99,
                  "action_deactivate": {
                    "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                    "value": 1
                  },
                  "action_activate": {
                    "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
                    "value": 2,
                    "hold": true
                  }
                }
              ]
            }
          ]
        }
      ]
    }
    
    ALTHOUGH here since most notches need to be held - you can probably just make this a simple hold direct control
    assignment

    Code:
    {
      "$schema": "https://tsw-controller-app.vercel.app/profile-builder/schema.json",
      "name": "Liberec843",
      "controls": [
        {
          "name": "Lever3",
          "assignments": [
            {
              "type": "direct_control",
              "hold": true,
              "controls": "ThrottleAndBrakeCab_{SIDE:1:2}",
              "input_value": {
                "min": -3,
                "max": 2,
                "steps": [-3, -2, -1, 0, 1, 2],
                "step_thresholds": [
                  { "threshold": 0 },
                  { "threshold": 0.2 },
                  { "threshold": 0.4 },
                  { "threshold": 0.6 },
                  { "threshold": 0.8 },
                  { "threshold": 1.0 }
                ]
              }
            }
          ]
        }
      ]
    }
    
    Also remember you can always refer to the profile builder
    https://tsw-controller-app.vercel.app/profile-builder

    And the profile explainer
    https://tsw-controller-app.vercel.app/docs/profile-explainer
     
  23. Bekns

    Bekns Active Member

    Joined:
    Jan 20, 2019
    Messages:
    204
    Likes Received:
    70
    Oh my, that is elegantly simple and here I was trying all sorts of complex stuff. Thank you for putting that together, I am going to have a go at applying it to the Direct Brake now :)
     
    • Like Like x 1
  24. liah#5302

    liah#5302 Member

    Joined:
    Jan 11, 2025
    Messages:
    58
    Likes Received:
    26
    Good to hear!
     
  25. Bekns

    Bekns Active Member

    Joined:
    Jan 20, 2019
    Messages:
    204
    Likes Received:
    70
    Hi Liah

    The last 2 updates seem to want me to recalibrate some of my controllers, reinstalling to 1.16.0 solves the issue but wondering if it has something to do with the same id controllers, I dont really want to recalibrate if I dont have to as its about 40 buttons / switches, I was wondering if there would be a way to manually edit the existing calibration files to (I presume) updated formats.

    Thanks
     

Share This Page