Asked — Edited

Windows 11 Controller Python Integration

Hello @Athena,

I am currently exploring the feasibility of using a Windows 11-compatible controller to execute a Python client script that transmits XInput data to a Python server script on the ARC Skill Control Board. My objective is to manage an H-Bridge with PWM speed control to facilitate tank track movement using the Synthiam ARC platform. Do you think this setup can be achieved?

I am employing the Anbernic Win600 controller, as depicted in the image below:

User-inserted image

At present, I am endeavoring to deploy a Python-coded server on my ARC-enabled PC to process control signals. I’ve included the Python script I’m using for this configuration below.

#!/usr/bin/env python3
# Async TCP receiver that prints controller events and maintains a latest_state snapshot.

import asyncio, json, signal, sys
from collections import defaultdict

HOST = "0.0.0.0"
PORT = 5555
PRINT_EVENTS = True

latest_state = {
    "buttons": defaultdict(lambda: False),
    "axes": defaultdict(lambda: 0.0),
    "hat": {"x": 0, "y": 0},
    "t": 0.0,
}

def apply_event(ev):
    et = ev.get("type")
    latest_state["t"] = ev.get("t", latest_state["t"])

    if et == "button":
        name = ev.get("name", f"BTN{ev.get('index', -1)}")
        pressed = bool(ev.get("pressed", False))
        latest_state["buttons"][name] = pressed
        if PRINT_EVENTS:
            print(f"[BTN] {name}: {'DOWN' if pressed else 'UP'}")

    elif et == "axis":
        name = ev.get("name", f"AXIS{ev.get('index', -1)}")
        val = float(ev.get("value", 0.0))
        latest_state["axes"][name] = val
        if PRINT_EVENTS:
            print(f"[AXIS] {name}: {val:+.3f}")

    elif et == "hat":
        v = ev.get("value", {"x": 0, "y": 0})
        latest_state["hat"] = {"x": int(v.get("x", 0)), "y": int(v.get("y", 0))}
        if PRINT_EVENTS:
            x, y = latest_state["hat"]["x"], latest_state["hat"]["y"]
            dirs = []
            if y == 1: dirs.append("Up")
            if y == -1: dirs.append("Down")
            if x == -1: dirs.append("Left")
            if x == 1: dirs.append("Right")
            print(f"[HAT] DPad: {'+'.join(dirs) if dirs else 'Neutral'}")

async def handle_client(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
    peer = writer.get_extra_info("peername")
    print(f"[server] Client connected: {peer}")
    try:
        while True:
            line = await reader.readline()
            if not line:
                break
            try:
                ev = json.loads(line.decode("utf-8"))
            except json.JSONDecodeError:
                continue
            apply_event(ev)
    except asyncio.CancelledError:
        pass
    finally:
        print(f"[server] Client disconnected: {peer}")
        try:
            writer.close()
            await writer.wait_closed()
        except Exception:
            pass

async def status_printer():
    while True:
        await asyncio.sleep(0.5)
        lx = latest_state["axes"]["LX"]; ly = latest_state["axes"]["LY"]
        rx = latest_state["axes"]["RX"]; ry = latest_state["axes"]["RY"]
        lt = latest_state["axes"]["LT"]; rt = latest_state["axes"]["RT"]
        a  = latest_state["buttons"]["A"]; b = latest_state["buttons"]["B"]
        d  = latest_state["hat"]
        print(f"[state] LX:{lx:+.2f} LY:{ly:+.2f} RX:{rx:+.2f} RY:{ry:+.2f} LT:{lt:.2f} RT:{rt:.2f} A:{int(a)} B:{int(b)} D:{d}")

async def main():
    server = await asyncio.start_server(handle_client, HOST, PORT)
    print(f"[server] Listening on {HOST}:{PORT}")
    sp = asyncio.create_task(status_printer())

    # Graceful shutdown for Windows
    stop = asyncio.Future()
    def _sig():
        if not stop.done():
            stop.set_result(True)
    loop = asyncio.get_running_loop()
    try:
        loop.add_signal_handler(signal.SIGINT, _sig)   # may raise on Windows for some signals
        loop.add_signal_handler(signal.SIGTERM, _sig)
    except (NotImplementedError, AttributeError):
        # Fallback: rely on KeyboardInterrupt
        pass

    async with server:
        await asyncio.gather(server.serve_forever(), stop)

    sp.cancel()

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        sys.exit(0)

I am eager to understand how to integrate this Python script to efficiently utilize the XInput data along with the ARC platform's capabilities for controlling tank tracks via the H-Bridge with PWM speed control. The H-Bridge interfaces with an Arduino Nano running the EZB firmware. Any insights or guidance on effectively achieving this setup would be greatly appreciated. Additionally, I aim to program other buttons for various ARC skills or script triggers.


Related Hardware Arduino Genuino Uno
Related Control Dual HBridge w/PWM

ARC Pro

Upgrade to ARC Pro

Become a Synthiam ARC Pro subscriber to unleash the power of easy and powerful robot programming

#9  

DJ,  I did not know about USB Gate but after looking at it this could be of value thanks for pointing this out.

PRO
Canada
#10  

That's great to hear, smiller29!

USB Gate can indeed be a valuable tool in your setup. In case you're not familiar with it, here's a quick overview of why it might be useful for you:

  • Remote USB Sharing: USB Gate lets you share USB devices over the network. This means if your handheld device is connected to the robot or another system, you can access that USB device remotely.
  • Enhanced Flexibility: By using USB Gate, you can avoid the need for multiple physical controllers or expensive dedicated hardware. It fits right into a system where you're already handling multiple interfaces like the ones you described.
  • Smoother Integration: It can simplify the integration of devices that aren't inherently designed for distributed or remote setups. This is especially important when your setup involves multiple systems like a Jetson for AI and ARC for motor control.

If you decide to implement USB Gate, make sure that the software configuration on both the sending and receiving ends is solid. This way, the USB joystick (or any other device) operates reliably across your systems.

If you have any questions about integrating USB Gate with ARC or how to map the controller data further to your robot’s controls, let me know-I’m here to help!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.
#11  

@Athena If I made my Python server script get started by the Script Manager skill.  Can I add code to my server script to use the CommandControl functions for the H-Bridge with PWM skill for my interface without using globals?

PRO
Canada
#12  

Yes, you can absolutely do that!

If your Python server script is started via the Script Manager skill in ARC, it’s running within ARC’s integrated environment. This means you have access to ARC’s scripting functions-such as ControlCommand()-so you can send commands directly to the Dual HBridge w/PWM skill without having to use global variables to pass the data.

Here’s what you need to keep in mind:

  1. Access to ARC Functions:
    When your Python script is launched by the Script Manager, it can call functions like ControlCommand() just like any other ARC script (JavaScript or Python). This allows you to send a command directly to any skill that supports messaging.

  2. Using ControlCommand():
    Instead of setting a global variable and then having another script poll that variable, you can invoke a command on the H-Bridge skill directly. For example, if the H-Bridge skill has a command to set the speed or change direction, you could call:

    ControlCommand("Dual HBridge w/PWM", "SetSpeed", 255)
    

    This line sends the "SetSpeed" command with a parameter (255 in this case) to the Dual HBridge w/PWM skill. (Make sure you use the exact Skill title as it appears in your ARC project.)

  3. No Need for Globals:
    By using this method, you bypass the need to store and poll global variables. Instead, you’re directly instructing the H-Bridge skill to perform an action. This can simplify your code and make the communication between your Python server and the H-Bridge skill more immediate and reliable.

  4. Skill Commands and Documentation:
    Be sure to review the documentation for the Dual HBridge w/PWM skill (and any other skills you plan to control) to know which commands it supports and what parameters are expected. You can find more information on robot skills messaging here: Robot Skills Messaging (ControlCommand).

  5. Example Scenario:
    Suppose you’re processing XInput data from your controller and you determine that the left track should run at a certain PWM value while the right track runs at another. You could map these values into commands like:

    # Example: Setting left and right speeds without using globals
    leftSpeed = 200  # example value
    rightSpeed = 200  # example value
    
    ControlCommand("Dual HBridge w/PWM", "SetLeftSpeed", leftSpeed)
    ControlCommand("Dual HBridge w/PWM", "SetRightSpeed", rightSpeed)
    

    (Note: The actual command names like "SetLeftSpeed" and "SetRightSpeed" will depend on how the Dual HBridge w/PWM skill is scripted. You might need to adjust them based on the skill’s documentation.)

  6. Testing and Iteration:
    As with any integration, test your commands to ensure that the H-Bridge skill is receiving and executing them as expected. If you run into issues, double-check the skill’s command names and parameter requirements, and consider adding logging or print statements to verify the data flow.

Do you have a specific command or parameter setup in mind for your H-Bridge? Let me know if you’d like to see a more detailed code example or if you need help mapping the controller data to particular commands in ARC!


I am a customer support GPT-powered AI and still learning. If you have doubts about my response, please use the support documentation to verify my answer.