Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 20:48 25 Nov 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : gamepad-controlled PicoMite robot

Author Message
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 172
Posted: 06:34pm 05 Apr 2024
Copy link to clipboard 
Print this post

At times it is difficult to let something go ...

I wanted to remote-control my robot with a gamepad controller, but not with a wired one. I got a Sony PS4 controller but this uses Bluetooth, unfortunately. With the cable, it works - that is, it is recognized by PicoMiteUSB - but walking a robot on a leash is besides the point ...



I thought just adding vendor and device ID of my trusted wireless gamepad to the PicoMiteUSB firmware might do the trick. Well, my controller can produce at least 4 vendor/device ID combinations and almost as many data formats. In PS3 mode, it is not recognized by the firmware, probably because the connection between dongle and controller takes too long to negotiate; MMBasic "looses interest" and disconnects the controller.




Therefore, I decided to see if I can add a controller type (here, XBox/Android) to the firmware ...
 
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 172
Posted: 06:35pm 05 Apr 2024
Copy link to clipboard 
Print this post

For this, I needed to compile the firmware; how I did this is documented here:
README.md

There is probably an easier way, but these instructions worked for me.

It was fun, playing with `USBKeyboard.c`, exploring what different controllers generate in terms of data. I managed to add a controller type (`XBOX`) that the modified release candidate (5.09.00R3) can recognize and handle. It has the ID 131 (`XBOX`) and understands a data protocol that provides 9 bytes/package (the controller manual says this was an Android format, whatever that means). But it is nice and compact. Here is the code that decodes a package:

if(len == 9) {
       if(report[0]&0x10)b|=0x0400;  // Button y/triangle
       if(report[0]&0x02)b|=0x0800;  // Button b/circle
       if(report[0]&0x08)b|=0x1000;  // Button x/square
       if(report[0]&0x01)b|=0x2000;  // Button a/cross
       if(report[1]&0x08)b|=0x0002;  // Button start -> start?
       if(report[1]&0x04)b|=0x0008;  // Button home -> xbox/PS?
       if(report[1]&0x10)b|=0x0004;  // Button select -> back/share?
       if(report[0]&0x80)b|=0x0001;  // Button R/R1  
       if(report[0]&0x40)b|=0x0010;  // Button L/L1
       if(report[2] == 0x4)b|=0x20;  // Button down cursor
       if(report[2] == 0x2)b|=0x40;  // Button right cursor
       if(report[2] == 0x0)b|=0x80;  // Button up cursor
       if(report[2] == 0x6)b|=0x100; // Button left cursor
       nunstruct[n].ax=report[3];
       nunstruct[n].ay=report[4];
       nunstruct[n].Z=report[5];
       nunstruct[n].C=report[6];
       nunstruct[n].L=report[8];
       nunstruct[n].R=report[7];
   }


If you are interested, the modified `USBKeyboard.c` is here. Note that the modifications (additions) to Peter's code are marked:

// ===>>>
...
// <<<===


I am aware that the next change Peter does to the firmware will ``kill`` my modifications, but then I don't want to run the robot remote-controlled forever - at some point, I hope it becomes more autonomous.
 
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 172
Posted: 06:35pm 05 Apr 2024
Copy link to clipboard 
Print this post

A small Basic program to test gamepads (I tested it with the PS4 controller as well) you can find here: gamepad_test.bas

' Test - Gamepad
'
Option Explicit
Option Base 0
Option Default Float

Dim integer running = 1, ch
Dim string s$

' Gamepad-related variables
' Types:
'   0=not in use, 1=keyboard, 2=mouse, 128=ps4, 129=ps3, 130=SNES/Generic, 131=Xbox
Dim integer gpad.ch = 0, gpad.type = 0, gpad.changed = 0
Dim integer gpad.data(6), gpad._intDetect = 0

initGamePad 131
If gpad.ch = 0 Then
 Print "No gamepad found"
 End
EndIf

Do While running
 ' Update gamepad data and if new, print values out
 If updateGamepad() Then
    Math V_print gpad.data()
    checkGamepad
 EndIf

 ' Check for escape, which aborts program
 ch = Asc(LCase$(Inkey$))
 If ch = 27 Then running = 0
 Pause 20
Loop
End

' ----------------------------------------------------------------------------
Sub initGamepad _type
 ' Look for gamepad `_type` and initialize it, if available
 Local integer i, j
 gpad.ch = 0
 gpad.type = 0
 Math Set 0, gpad.data()
 For i=1 To 4
   j = MM.Info(USB i)
   If j = _type Then
     Print "Gamepad type="+Str$(_type)+" found."
     gpad.ch = i
     gpad.type = _type
     Device gamepad interrupt enable i, _cb_gamepad
     Exit Sub
   EndIf
 Next
End Sub

Function updateGamepad()
 ' Keeps gamepad data up to date (call frequently)
 Static integer v(6)
 If gpad.type < 128 Then updateGamepad = 0 : Exit Sub : EndIf

 ' Read gamepad sticks etc
 gpad.data(0) = DEVICE(gamepad gpad.ch, lx)
 gpad.data(1) = DEVICE(gamepad gpad.ch, ly)
 gpad.data(2) = DEVICE(gamepad gpad.ch, rx)
 gpad.data(3) = DEVICE(gamepad gpad.ch, ry)
 gpad.data(4) = DEVICE(gamepad gpad.ch, l)
 gpad.data(5) = DEVICE(gamepad gpad.ch, r)

 ' Check if any stick value as substantially changed
 Math C_Sub v(), gpad.data(), v() : v(6) = 0
 gpad.changed = gpad._intDetect Or Abs(Math(Sum v())) > 2
 gpad._intDetect = 0
 updateGamepad = gpad.changed
 Math Scale gpad.data(), 1, v()
End Function

' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' &H0001 := R1
' &H0002 := Options/Start
' &H0004 := PS/Home
' &H0008 := Share/Select
' &H0010 := L1
' &H0020 := pad, down
' &H0040 := pad, right
' &H0080 := pad, up
' &H0100 := pad, left
' &H0200 := R2 pressed, value in `gpad.data(5)`
' &H0400 := Triangle
' &H0800 := Circle
' &H1000 := Square
' &H2000 := Cross
' &H4000 := L2 pressed, value in `gpad.data(4)`
' &H8000 := touchpad pressed

Sub checkGamepad
 Local string s$ = ""
 If gpad.data(6) And &H0400 Then s$ = s$ +"Triangle "
 If gpad.data(6) And &H1000 Then s$ = s$ +"Square "
 If gpad.data(6) And &H0800 Then s$ = s$ +"Circle "
 If gpad.data(6) And &H2000 Then s$ = s$ +"Cross "
 If gpad.data(6) And &H0010 Then s$ = s$ +"L1 "
 If gpad.data(6) And &H0001 Then s$ = s$ +"R1 "
 If gpad.data(6) And &H0002 Then s$ = s$ +"Start "
 If gpad.data(6) And &H4000 Then s$ = s$ +"L2 ("+Str$(gpad.data(4))+") "
 If gpad.data(6) And &H0200 Then s$ = s$ +"L2 ("+Str$(gpad.data(5))+") "
 If gpad.data(6) And &H0008 Then s$ = s$ +"Share "
 If gpad.data(6) And &H0004 Then s$ = s$ +"PS "
 If gpad.data(6) And &H0100 Then s$ = s$ +"< "
 If gpad.data(6) And &H0040 Then s$ = s$ +"> "
 If gpad.data(6) And &H0080 Then s$ = s$ +"^ "
 If gpad.data(6) And &H0020 Then s$ = s$ +"v "
 If gpad.data(6) And &H8000 Then s$ = s$ +"Touchpad "
 Print "&B"+Bin$(gpad.data(6), 32)""
 Print s$
End Sub

' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Sub _cb_gamepad
 ' Interrupt callback for gamepad
 If gpad.type < 128 Then Exit Sub
 gpad.data(6) = DEVICE(gamepad gpad.ch, b)
 gpad._intDetect = 1
End Sub
' ----------------------------------------------------------------------------
 
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 172
Posted: 06:37pm 05 Apr 2024
Copy link to clipboard 
Print this post

The robot is one of my long-long-term projects (one can see that from the dust on some parts ...   ).



The mechanical setup was inspired by the remote-controlled Arduino-based vorpal robot, but with one more joint per leg added.

Its 18 servo motors are controlled by a Maestro 18, which receives commands from one of two Pico (W) microcontrollers. They both run MMBasic, one -- the server -- controls the gait and generates the leg movements, the second one -- the client -- currently receives the gamepad input and talks to the server via a serial connection. Eventually, the client will control the robot autonomously.



Other features are:

- 8-channel servo-load sensing
- BNO055-based 9-DOF IMU
- two WS2812 LEDs for signalling the robot's state
- a TFT display (not mounted here)
- a buzzer
- separate logic and servo batteries with voltage monitoring
- hardware handshaking between the Picos
- a potentiometer for mode selection (not yet used)

Originally, I developed MicroPython code for it but gave up at some point, because it did too much in the background (e.g., garbage collection) and that kept messing up the servo update timing as well as the communication between the two "brains" ... Newer MicroPython releases may be better with that now, but I was wondering if a solution with MMBasic running on "bare metal" on a Pico would be better. And indeed, it runs very well and reliable, the communication between the Picos is a dream.

And if the timing is too slow, I just overclock it more  

Finally, here's a video,

I'll document the hardware and the MMBasic code soon in the repository - in case someone is interested.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 9111
Posted: 06:58pm 05 Apr 2024
Copy link to clipboard 
Print this post

Thomas

Great stuff - I'll add the xbox code to the standard build if that is OK?
Note there is a bug in TinyUSB 0.16 that I found and that has now been fixed so you want to download the latest from the github.

Please could you provide details of the controller you use that works wirelessly and how to configure it.

Thanks

Peter
 
lizby
Guru

Joined: 17/05/2016
Location: United States
Posts: 3150
Posted: 08:02pm 05 Apr 2024
Copy link to clipboard 
Print this post

Terrific work. Very impressive.
PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 4226
Posted: 06:14am 06 Apr 2024
Copy link to clipboard 
Print this post

Wauw, controlling 18servos to create movements if quite impressive.

Volhout
PicomiteVGA PETSCII ROBOTS
 
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 172
Posted: 04:31pm 06 Apr 2024
Copy link to clipboard 
Print this post

  matherp said  Thomas

Great stuff - I'll add the xbox code to the standard build if that is OK?
Note there is a bug in TinyUSB 0.16 that I found and that has now been fixed so you want to download the latest from the github.

Please could you provide details of the controller you use that works wirelessly and how to configure it.

Thanks

Peter

Thank you!

Of course, you can add it, however, I am not sure how useful that is for others. I bought it 4 years ago to work with my Windows PC, which is does well. It is from a company called EasySMX (PS3 Controller, EasySMX 2,4G Wireless Gamepad, Joysticks Dual Vibration TURBO) and it can work in different modes:



Note that it is not really "XBox" - I gave it that name in the code before I figured out  what it really is. Depending on the mode, it returns different vendor/device ID combinations and data packages:

"Android", vendor ID: 0x11c0, product ID: 0x5500
"X-input", vendor ID: 0x045e, product ID: 0x028e
"D-input, emulation", vendor ID: 0x11c1, product ID: 0x9101
"PS3", vendor ID: 0x054c, product ID: 0x09cc

When connected to a Windows 11 PC, all modes are recognized.

For the PicoMiteUSB, "PS3" would have been the obvious choice, by just adding another device ID to your `is_sony_ds3()` function, but for some reason that did now work. This is the controllers default mode. When it is started, it behaves like a PS3 controller. But then changes mode, if no PlayStation responds (I guess). This is probably why the PicoMiteUSB firmware was not able to stay connected.

"D-input, emulation" works but generates very long (27 byte) data packages that contain all relevant data (some redundantly).

"Android" generates nicely compact 9-byte data packages, providing data about almost all buttons and joy sticks that your firmware supports, therefore I opted for this format.

To cut a long story short, I think it would be great to add another type of controller/gamepad, but I am not sure which format. Maybe that "Android" format, that I mislabelled in my code as XBOX. However, I don't know how common that is.

As to the wireless: I expect that Gamepads that use a special dongle (like the one here) should generally work - if their IDs/data format are known and they make it through the "negotiation phase". All newer wireless gamepads seem to rely on Bluetooth and are therefore out of the question.

I will try your newest firmware version with the TinyUSB 0.16 bug fix to see if my controller now stays connected if I add its device code.

Thanks for all your fantastic work on the PicoMite and Co!

Best
Thomas
 
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 172
Posted: 04:31pm 06 Apr 2024
Copy link to clipboard 
Print this post

  lizby said  Terrific work. Very impressive.

Thank you!
 
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 172
Posted: 04:32pm 06 Apr 2024
Copy link to clipboard 
Print this post

  Volhout said  Wauw, controlling 18servos to create movements if quite impressive.
Volhout

Thank you!
 
stanleyella

Guru

Joined: 25/06/2022
Location: United Kingdom
Posts: 2127
Posted: 08:35pm 06 Apr 2024
Copy link to clipboard 
Print this post

impressive! I never finished mine, it's not easy

 
javavi

Senior Member

Joined: 01/10/2023
Location: Ukraine
Posts: 213
Posted: 10:47pm 06 Apr 2024
Copy link to clipboard 
Print this post

How can I make the Xbox360 gamepad work too?
 
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 172
Posted: 04:07pm 07 Apr 2024
Copy link to clipboard 
Print this post

  matherp said  Note there is a bug in TinyUSB 0.16 that I found and that has now been fixed so you want to download the latest from the github.


Hi Peter,

I pulled all changes in the PicoMite and TinyUSB repositories this morning. No problems building and my code with the gamepad changes still worked.

I also added the PS3 vendor/product IDs my gamepad generates in PS3 mode, however, it's still not recognized. But as I said above, I think this is due to the weirdness of my gamepad trying to negotiate the best mode of operation.

Best
Thomas
Edited 2024-04-08 02:07 by karlelch
 
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 172
Posted: 08:45pm 09 Apr 2024
Copy link to clipboard 
Print this post

  javavi said  How can I make the Xbox360 gamepad work too?

Hi Javavi,

as detailed above, I don't have an Xbox 360 gamepad. What you would need to do is to find out the vendor/product IDs and the format of the data the gamepad it supplies. I can give you here only pointers how I did it for my strange gamepad.

1) I wrote a short Python script for the PC that looks up all connected USB devices, waits for you to plug a new device in (i.e. the gamepad), then checks which device is new, and prints vendor/product ID and such.
import hid

def print_dev(d):
   print(f"vendorID={hex(d['vendor_id'])}, productID={hex(d['product_id'])} ", end="")
   print(f"manufacturer=`{d['manufacturer_string']}`, product=`{d['product_string']}`")

# Get a list of all connected HID devices
devices = hid.enumerate()
print("Device entries found:")
for i, dev in enumerate(devices):
   print(f"{i:3}: ", end="")
   print_dev(dev)

# Wait for the user to plug in the new device
input("Connect new device and press Enter")
print("New device(s):")

# Get list again and find differences
devices_new = hid.enumerate()
n_new = 0

# Iterate over the devices and retrieve the vendor and product IDs
for dev_new in devices_new:
   found = False
   for dev in devices:
       if found := dev_new["path"] == dev["path"]:
           break
       
   if not found:
       print(f"{n_new:3}: ", end="")
       print_dev(dev_new)
       n_new += 1

if n_new == 0:
   print("No new devices found")


2) My modified `USBKeyboard.c` for the PicoMiteUSB firmware contains a section, where new gamepad IDs can be added for testing:
// ===>>>
#define TE_TEST 0
#if(TE_TEST)
// Add your IDs here for testing
#define ANDROID 131  
#define TE_V_ID 0x11c0
#define TE_P_ID 0x5500
#else
// EasySMX Wireless, u, Android mode (u)
#define ANDROID 131  
#define TE_V_ID 0x11c0
#define TE_P_ID 0x5500
#endif

If `TE_TEST` is set to 1, then the code uses the IDs given in the first branch of the `#if()` directive and prints out the data from the gamepad as soon as it is connected to the Pico. (Of course, this requires re-building the firmware). The result in Tera Term looks something like this:
9 0_0 1_0 2_F 3_80 4_80 5_80 6_80 7_0 8_0
9 0_0 1_0 2_F 3_80 4_80 5_80 6_80 7_0 8_0
9 0_0 1_0 2_F 3_80 4_80 5_80 6_80 7_0 8_0
9 0_0 1_0 2_F 3_80 4_80 5_80 6_80 7_0 8_0
9 0_0 1_0 2_F 3_80 4_80 5_80 6_80 7_0 8_0

With the first number being the number of data bytes to follow (here 9), and the following `x_y` pairs the byte index (`x`) and the value (`y`). When you then move the joystick of your gamepad or press a key, you should see how the values are changing. This way you find out, which control is reflected in which data byte and how the message is to be decoded.

Note that the Pico is only printing the data, it does not populate the MMBasic-specific gamepad variables in this test mode.

3) Based on that, one can then add decoding code to `USBKeyboard.c`.

Of course, this cannot be a general solution - everyone adding their favorite gamepad. But maybe having one commonly used class of gamepads (e.g. XBox) in addition to PS3/PS4 would be useful for many.

Best
Thomas
 
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 172
Posted: 08:28am 11 Apr 2024
Copy link to clipboard 
Print this post

Burned out servo   ... surgery required ...

 
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024