mirror of
https://github.com/safijari/openpuck.git
synced 2026-11-03 22:30:22 +00:00
[GH-ISSUE #254] Pogo Pins, Charging/Pairing? #114
Labels
No labels
pull-request
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
safijari/openpuck#114
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @CalcProgrammer1 on GitHub (Aug 24, 2026).
Original GitHub issue: https://github.com/safijari/openpuck/issues/254
Has anyone investigated the pogo pin protocol used by the official puck to communicate with the controller? It seems like it would be doable to reimplement this portion of the puck functionality with some hardware reverse engineering. I briefly looked at the puck by itself and it is putting out a constant 5V between two pins but the third pin had nothing, likely only does anything when the controller is connected. I would need to come up with a probe jig to actually look at this protocol, but not sure if anyone else has started down this path yet.
@CalcProgrammer1 commented on GitHub (Aug 25, 2026):
Ordered some pogo pins, will have a go at 3D printing something to hold them and attach to the Steam Controller, then might have something I can sniff the protocol with. Would be neat to 3D print a charging/pairing dock like the 8BitDo Ultimate 2 has using OpenPuck.
@adamrpostjr commented on GitHub (Aug 27, 2026):
@CalcProgrammer1. What pogo pins did you order? Once my hardware arrives next week, I am hoping to start working on "Design the charging portion (and make it short proof)"
@CalcProgrammer1 commented on GitHub (Aug 28, 2026):
These are the ones I ordered, I 3D printed a holder for them with 2mm holes, 6.4mm thick. The holes are a bit loose but I was planning to glue them in and solder wires on the back.
SteamControllerPogoPins.zip
@adamrpostjr commented on GitHub (Aug 28, 2026):
Nice, looks good! I would open to colab on the short proof charging!
@CalcProgrammer1 commented on GitHub (Aug 28, 2026):
Yeah, I'm looking at the physical interface now. I'm currently printing an inverted design that will attach to the puck so I can put wires between the two halves. Going to use small nails to create the pads and use a soldering iron to heat set them into the print. Looking into short proof charging would be good, I have not tested if the official puck has short protection (not sure I want to test but I'm sure Valve thought of it). Not sure what the charging current is either but I can measure once I separate puck from controller. Mostly interested in what happens on that communication pin.
@CalcProgrammer1 commented on GitHub (Aug 28, 2026):
Steam Controller extension cord works, was able to pair my controller with the puck over it. Orange is 5V, gray is ground, assuming yellow is signal. When holding it to controller wires are facing the top of the controller. Might print a new controller side piece that I can glue magnets to.
Edit: Here are the 3D files for the puck connector:
SteamPuckAdapter.zip
@CalcProgrammer1 commented on GitHub (Aug 29, 2026):
Ok, I got it backwards. The pin that always has +5 on it when not connected is actually the data line. It looks like there is a periodic request/response as long as the controller is connected. The yellow pin activates 5V when the controller is connected, though I'm not sure exactly how it determines that unless the controller has to respond to the periodic ping first.
@adamrpostjr commented on GitHub (Aug 29, 2026):
Nice setup! When the controller is docked and charging what does the data pin read on the multi meter?
@CalcProgrammer1 commented on GitHub (Aug 29, 2026):
I've been trying to figure out the protocol using a capture I took on my oscilloscope. After playing around with it I want to say it's 56K baud half duplex serial, 8 bits, no parity, one stop bit. This gets rid of framing errors.
Zooming out it looks like a command/response format, not sure which side initiates. This happens periodically but I haven't looked at the exact timing. It might be easier to hook up a USB UART now to decode the signal rather than use the scope though. I might try that later.
Here is the decoded serial data for this capture:
SerialData.txt
@CalcProgrammer1 commented on GitHub (Aug 29, 2026):
Ok, made some huge progress on this! I hooked up the UART pogo pin to a CH340G USB adapter. I re-measured the bit timing with my scope as the capture I was looking at earlier didn't properly decode the timebase. I confirmed an 8.8us bit time which means the protocol runs at a standard 115200 baud rate, not 56000. It is still standard 8N1 format, which makes decoding easy.
I then spent some time going back and forth with Google's AI to try and decode the protocol. I noticed that on the scope, there were two distinct data packets (slightly different voltage levels, gap in between) indicating a half-duplex command and response architecture. Both are identical in length. The first packet is sent by the puck and the second packet is sent by the controller.
I went back and forth with the AI and eventually got a script that parses out the two packets live:
steamcontroller_decode7.py
Looking at a sample, I am getting an output as such:
The puck seems to send either
5A 00or5A 03and the controller responds with one of two different message formats. The controller's response to5A 00contains the pairing data while the response to5A 03contains data that changes, possibly sensor or battery related data.The
5A 00response is the one that we probably care about though, as it is what we would use to pair the controller.This is the AI's summary of the packet:
Valve Steam Controller Identity Response Packet (73 Bytes)
When the puck sends
5A 00, the controller responds with a fixed 73-byte packet containing its unique manufacturing descriptors and radio network configuration block.Packet Field Map
AC 00 00 00 00 00 00 00 00 00 00 00E3 85 4D 6A 2E F9 D2 68 48 00 00 00 00 00 00 00 0046 58 41 39 39 36 31 31 30 30 37 35 45FXA996110075E(Controller Hardware Serial)4D 58 41 32 41 36 30 36 30 30 34 39 31MXA2A60600491(RF Sub-Assembly Module ID)32 36 65 31 36 66 33 37 36 35 33 3526e1f7ef1955(EDIT - Firmware version/git hash)00 00 00 0094 FANotably, I can clearly see the controller's serial number. I tested with two different controllers to confirm and they each report their own serial. The field at 0x037-0x042 was the same on both controllers, so I think it represents something the controllers are paired to (since both are the same it could be my Steam Machine, but I had repaired my first controller with my OpenPuck as well as the real puck I was using for testing since then).
@Leseratte10 commented on GitHub (Aug 29, 2026):
26e1f7ef1955is not the puck's MAC address, it is the git commit hash of the controller's firmware. It means your controller is running firmware 6A4D85E3.Also, I'd be interested if there's button inputs somewhere in that struct, too? AFAIK, if you have a controller connected through the puck but not yet paired, you can use the D-Pad and A/B buttons to navigate the "Do you want to pair this controller" prompt, so Steam somehow has to receive button inputs through the wired connection.
Amazing that that's just a simple UART-like protocol, so maybe it can actually be implemented in Openpuck.
@CalcProgrammer1 commented on GitHub (Aug 29, 2026):
That probably explains the 5A 03 packets. The rate is slow, only every 80ms or so, but that's enough for 10Hz which should be fine for button navigation. The data in the 5A 03 packet does indeed change so it very likely does contain controller inputs. I will try to investigate further tomorrow, it's 3 AM and I should probably go to bed.
@CalcProgrammer1 commented on GitHub (Aug 29, 2026):
The 03 command does indeed appear to be an input request. The controller responds with:
0x00:
AC 03 00 00 00 00 00 008 bytes, padding/command value0x08: 1 byte frame counter, increments each frame
0x09: Button bitfield 0 (0b[R4][Start][R3][...][Y][X][B][A])
0x0A: Button bitfield 1 (0b[L3][Select][DPad Up][DPad Left][DPad Right][DPad Down][R1][R5])
0x0B: Button bitfield 2 (0b[R2][0][0][RTouch][L1][L5][L4][0])
0x0C: Button bitfield 3 (0b[0][0][LGrip][RGrip][L2][0][0][LTouch])
@CalcProgrammer1 commented on GitHub (Aug 30, 2026):
Messed around with this some today and got my OpenPuck periodically outputting the 00 request on pin 17, confirmed the bytes and timing on my scope, but have not figured out the physical interface needed to convert from full duplex to half duplex yet. Also still looking into protocol R/E, figured out partial on the input request but there's still al lot of bytes left to decode.
@adamrpostjr commented on GitHub (Aug 31, 2026):
Ran the CRC on the packets from your earlier capture — CRC-16/KERMIT over bytes 0–70, little-endian, checks out clean on all four including the residue-to-zero check over the full 73 bytes:
Figured it might save you regenerating vs. just replaying captures
@safijari commented on GitHub (Oct 14, 2026):
Subscribed