Klipper remote pendant wii or switch pro controller works

WARNING: not YBR.

tldr: Q1 can you help me fix my service so it will start automagically and actually work. Q2, should I be threading this or somehow triggering on a request to pair from the wii rather than using a button to initiate pairing with the bluetooth device?

So with all the discussion on the pendant for the jackpot, I have managed to sort of make one for my klipper setup. Currently it is very difficult to use and clunky to get started, but from the pendant, I can run macros in klipper and potentially have it basically do anything a macro can do in klipper which is anything… but one thing at a time. I had a similar setup on my maslow with a wii remote and it worked awesome until the wii controller just died one day. Fast forward and I can pick them up off ebay for like $7 now, so lets just say there might be a few spares.

The layout:

klipper runs on raspberry pi 3B+. The pi is set up as an mcu so that exposes the GPIO on the board for input and indication output. Ive put in macro cfg files for an ssd1306 oled and for a 3d printed custom staples EZ button that starts the connect process


the wii shows up in bluetoothctl on the zero as Device 00:19:1D:CB:71:09 Nintendo RVL-CNT-01. If you try this, be sure when you type in the pair with the mac address command to press the red sync button on the remote and it won’t ask for a password, it will just work.

To start the connect process, the macro in klipper does a curl http post command to the nearby raspberry pi zero. The raspberry pi zero is running a python flask server on port 5000 that accepts the post and indicates its status with a tricolor LED.

what you are seeing is the crowsnest camera mounted over my LR4 which I filmed from another room - but before you go all fireman on my post, I’m not running the spindle from the other room, just testing colors from the couch. the LED program and wii pairing were done manually from 2 ssh terminals. one for LED and one for sending the curl command:

curl -X POST -H “Content-Type: application/json” -d ‘{“red”: 0, “green”: 1, “blue”: 0}’ http://localhost:5000/enable_pendant
and waited for the pairing that sometimes takes a few seconds. All commands are 2 button sequences to avoid accidentally triggering something stupid. A+direction, B+ direction, etc… the +/- change the battery light and serve as a distance option for movement small, med, largee, etc.)
It works by activatingthe bluetooth on a second raspberry pi (zero) because bluetooth on the main pi3 running klipper is not allowed due to interferes with the real-time process timing or something like that. So once bluetooth turns on and assuming you pressed the wii-mote buttons to turn it on, then it will pair and give a rumble to let you know it is ready and the battery light will go to #3. Pressing button sequences on the wii remote will call an http post request to klipper using the json script option then run the macro or gcode or if via json-rpc, get position or whatever info from it (lots of info in the klipper docs).

I have a couple other plans that aren’t yet working:

  1. the .91” oled via I2C connected to the rpi mcu (with klipper) is supposed to be showing real time updates of xyz positions via an update macro. the first oled was broken and I think the second one is too. (may have to go with a 2x16 character display or something similar…) This is a tiny screen that would sit on the core as an indicator for initial positioning with the pendant.

  2. when certain commands are sent from the pendant, the led will change color or pulse or blink. This works partially, but all of the commands are not yet input and colors have not yet been assigned.

  3. If I can get this connection thing ironed out, It might be worthwhile to move to a switch pro controller or an xbox wireless…

The issues: it has been challenging to get the right program structure to make this all work in a seamless simple way. On the zero, I’m using the python cwiid library and it connects to bluetooth when you instantiate the controller object, so I have that run as a subprocess from the led code that I’m trying to run as a service on the zero. If I manually start the led code, it works.


When I can send the curl command from the zero locally or from the klipper macro button and it will start the wii subprocess and pair with the remote. Whenever I try to run the same code from a service at boot, it fails.

Aug 30 23:46:34 pendant (EZled.sh)[630]: EZled.service: Failed at step EXEC spawning /home/pi/wp/EZled.sh: Exec format error
Aug 30 23:46:34 pendant systemd[1]: Started EZled.service - EZ LED daemon.
Aug 30 23:46:34 pendant systemd[1]: EZled.service: Main process exited, code=exited, status=203/EXEC
Aug 30 23:46:34 pendant systemd[1]: EZled.service: Failed with result 'exit-code'.

here are my EZled config files for the linux service. It won’t run if it isn’t root. The sh files are set with correct permissions (777) and then some so that shouldn’t be the issue.

pi@pendant$ ls EZl*

EZled.py  EZled-restart.sh  EZled.service  EZled.sh  EZled-start.sh  EZled-stop.sh

pi@pendant$ cat EZled.sh

#!/bin/bash

/usr/bin/python3 /home/pi/wp/EZled.py

pi@pendant$ cat EZled.service

[Unit] 
Description=EZ LED daemon 
After=network.target

[Service] 
Type=forking 
ExecStart=/home/pi/wp/EZled.sh 
WorkingDirectory=/home/pi/wp
StandardOutput=tty 
TTYPath=/dev/tty1
Restart=always 
user=root

[Install]
WantedBy=sysinit.target

I’m not ready to share the actual led code, but I have a more structural question as well:

Would it be better to run the LED flask server and the cwiid bluetooth connection as separate threads spawned from the same program? AI has been helping me see what that could potentially look like. The issue is I can’t leave the bluetooth on because without a controller, the object doesn’t work, so you have to create it when the controller is around…
The reason a thread seems better has to do with the macro from klipper. When the macro sends a curl http post command to the zero, klipper halts and waits for the bluetooth connection to finish before it will respond again and this can be minutes. If the pendant was running as a thread, a variable flag could be set and the flask server could return with a code and klipper won’t care anymore.

any thoughts or help would be appreciated. I have only recently had a few minutes in the evening to try a few things, so it moves slowly, but could be fun to get it working well.

Edited service file with suggestions from below

3 Likes

This should be #!/bin/bash. It doesn’t know to run this .sh in bash. When you run from the terminal, you are running it in a shell, which is bash.

Threading is a complex question. Ideally, yes, thing that are orthogonal should be running asynchronously. As for what to do when the BT controller isn’t there, is there a way to ask cwiid if the controller is there and just poll (in a thread) for the controller?

1 Like

+1 for the shebang. That was part of it. The other part was the restart specified in the .service file. has to be “no” or “always” or one of the allowed states, but “never” doesn’t work. the journal and status now say running, but the curl connection is rejected, so closer, but not quite there.

after some iteration, I got the code all put in one program.

The base code is a flask server starts a thread that idles (rather than a subprocess) until a pair command is given and then it instantiates the wii object and can connect with the remote and communicate with klipper. Was simpler than I anticipated… just moved the subprocess code to the thread code. This solves the klipper or post pairing delay. Still need to figure out the auto pairing.

2 Likes

Button on klipper pi works
Service on pi zero works
Remote pairs
Remote runs klipper macros
Remote can be disabled and service continues to run for the process to repeat

Now to verify and test all features…

3 Likes

LR4 runs klipper firmware

Pendant connection is raspberry pi zero bluetooth to either or both:

  • Wii remote

  • Switch Pro Controller

The raspberry pi zero runs a python script to read the connect button input, gather x,y,z position from klipper to display on the small oled screen, set the indication led, and communicate with the controller.

3 Likes

Ok. Now that is intriguing!

Whoa!

So what makes the device work with klipper? I never thought about anything because I have it on my printer so no need.

which part?

klipper is the firmware engine that moves motors, runs heaters, from reading gcode. I told klipper in the printer.cfg file to move my CNC and didn’t give it a heated bed or an extruder, so klipper thinks it is a printer and still shows some printer indications in the mainsail interface, but it can run motors for a CNC. Since the mainsail front end on the web talks to moonraker (communication interface) that then tells klipper what to do, I had the bluetooth device send commands to moonraker over the network connection. That command interface is all documented to connect and control via HTTP POST commands. I just figured out how to get the switch controller via bluetooth at the same time I’m asking moonraker for position. It took a while and isn’t perfect yet. Still some unexplained disconnections, but it works for the most part. the controller buttons are read by a python script that then interprets various button combinations that call macros in the klipper config file that I defined. They show up as buttons on the web page (see below buttons on the left) so you can test them before you have the controller run them:

wow, nice job!

I was inferring to the python script that made the joystick work. but that was alot of detail, thanks.

The raspberry pi zero that runs this script is s separate network device from the klipper system. Klipper raspberry pi bluetooth is disabled for system resource reasons. The python script reads the EZ connect button to activate a thread in an object that uses a library that sends terminal commands to bluetoothctl to connect to the controller. Then once connected, it uses a /dev/input device call to pick up events from the controller and convert them to an http post command that calls a klipper macro. The pro controller has two input interfaces. One is for the dpad and analog sticks. The other is for the buttons. Ai helped me get the script to read them both with asynchronous calls so they effectively function simultaneously so it all works. Once both zl and zr buttons are pressed at the same time, the script disconnects the bluetooth device and that powers down the controller. It then destroys the object that runs the thread and recreates it so next time the button is pressed it will reconnect.

This ai search suggestion thing is pretty cool. I had limited thread use before this and didnt know asynchronous code was a thing in python or that you could use a library that basically remote controls other programs (pexpect i believe…very interesting). I used to scour forums and posts trying to find similar problems and modify those solutions. Now I’m basically a script kiddie cutting and debugging code snippets for very specific program functions that then get spliced together. This development effort over the last month was much faster for me to get to semifunctional code to start debugging compared with the search to find applicable information. In the evening after everything slowed down, i would query one specific question and add one feature to a test script and test that. Once that worked, on to the next then put them all together. Still working on it. But the basic functionality is there. I dont like the screen font size and need to put in better led indications for visual feedback.

1 Like