PySticksGui
A very old and no longer updated python program that turned the analog inputs of my Taranis Q-X7 radio controller into a visible chart. A youtube video was made about it. Click to see more and to find today’s quote of the day
Quote of the day
Programmer (noun): A machine that turns coffee into code.
Available At Github
What is it?
This project’s post isn’t going to be too long. I used an open source libary by Simondlevy called PySticks to collect information and telemetry about my controller. This was from 2021 and was when I was beginning to push my self to learn and excell at computer science. This program, which at the time, seemed big and complicated is basic 78 line script written in python. This will be explained down below
Source Code
Skip this large chuck if you want it broken down:
import pygame
import keyboard
#import pystick libary
from pysticks import *
#define con
con = get_controller()
#Initiate PyGame
pygame.init()
#defining
color1 = ('blue')
color2 = ('purple')
color3 = ('red')
color4 = ('orange')
colortxt = ('gray')
text = (0, 0, 0)
#Main Loop
while True:
throttle = float("%+2.2f" % con.getThrottle())
yaw = float("%+2.2f" % con.getYaw())
pitch = float("%+2.2f" % con.getPitch())
roll = float("%+2.2f" % con.getRoll())
surface = pygame.display.set_mode((800, 600))
#Yaw
if yaw < 0:
yaw = yaw + yaw * -2
#draw the rectangles with dyamic sizes w1, h1, w2, h2
pygame.draw.rect(surface, color1, pygame.Rect(100, 225, 600 * yaw, 50))
#pygame.draw.rect(surface, colortxt, pygame.Rect(100, 225, 145, 30))
#Thorttle
#draw the rectangles with dyamic sizes w1, h1, w2, h2
pygame.draw.rect(surface, color2, pygame.Rect(100, 125, 600 * throttle, 50))
#pygame.draw.rect(surface, colortxt, pygame.Rect(100, 125, 145, 30))
#Roll
if roll < 0:
roll = roll + roll * -2
pygame.draw.rect(surface, color3, pygame.Rect(100, 325, 600 * roll, 50))
#pygame.draw.rect(surface, colortxt, pygame.Rect(100, 325, 145, 30))
#Pitch
if pitch < 0:
pitch = pitch + pitch * -2
#draw the rectangles with dyamic sizes w1, h1, w2, h2
pygame.draw.rect(surface, color4, pygame.Rect(100, 425, 600 * pitch, 50))
#pygame.draw.rect(surface, colortxt, pygame.Rect(100, 425, 145, 30))
pygame.display.flip()
if keyboard.is_pressed('ctrl+q'):
pygame.display.quit()
print('Bye :)')
break
try:
con.update()
print('Throttle: %+2.2f Roll: %+2.2f Pitch: %+2.2f Yaw: %+2.2f' %
(con.getThrottle(), con.getRoll(), con.getPitch(), con.getYaw()))
except KeyboardInterrupt:
break
Code Breakdown
There are 9 components that make this script operational
- Importing all of the required libaries.
- Define variables and instantiate objects
- Main loop:
- Get all values of controller
- Draw pygame window
- Invert axis if the output an output < 0
- Draw and scale the rectangles that show the axis’ values
- Update Display
- Quit Statement and Printing Debugging
1 - Importing required libaries
import pygame
import keyboard
#import pystick libary
from pysticks import *
As written above all this code does is import required libaries. PyGameGui to have a colorful and fancy GUI, Keyboard to wait for keyboard inputs and Pysticks to allow the use of the libary by Simondlevy.
2 - Define variables and instantiate objects
#define con
con = get_controller()
#Initiate PyGame
pygame.init()
#defining
color1 = ('blue')
color2 = ('purple')
color3 = ('red')
color4 = ('orange')
colortxt = ('gray')
All this code does is defines and instantiates variables and objects. It defines the object con, as pysticks.get_controller(), this simply starts pysticks’ listening function and will later use this to store the values in variables. Pygame.init() as it suggests starts pygame and allows the script to call it and use it to write and draw on the canvas as we will see later. The colors, color1, color2, color3, color4, and colortxt are simply just defined as standard colors as seen above.
3.1 - Get all values of controller
while True:
throttle = float("%+2.2f" % con.getThrottle())
yaw = float("%+2.2f" % con.getYaw())
pitch = float("%+2.2f" % con.getPitch())
roll = float("%+2.2f" % con.getRoll())
As the name of this sections suggests these 5 lines of code just stores the values of the controllers inputs as a variable with the name that corresponds with the axis. Each one has virtually the same code. Each one equals the Modulus plus 2.2, Modulus con.get{AXIS} (controller object).
3.2 - Draw pygame window
surface = pygame.display.set_mode((800, 600))
Simply all this line does is that is “summons” a pygame window with the dimensions of 800 and 600.
3.3 - Invert axis if the output an output < 0
#Yaw
if yaw < 0:
yaw = yaw + yaw * -2
#Roll
if roll < 0:
roll = roll + roll * -2
#Pitch
if pitch < 0:
pitch = pitch + pitch * -2
As you can see all this does is if the axis that the variable name corresponds to is equal to a negative value, then invert the number to make it a positive. For example if pitch == -1, then it actually = 1.
3.4 - Draw and scale the rectangles that show the axis’ values
#draw the rectangles with dyamic sizes w1, h1, w2, h2
pygame.draw.rect(surface, color1, pygame.Rect(100, 225, 600 * yaw, 50))
#pygame.draw.rect(surface, colortxt, pygame.Rect(100, 225, 145, 30))
#draw the rectangles with dyamic sizes w1, h1, w2, h2
pygame.draw.rect(surface, color2, pygame.Rect(100, 125, 600 * throttle, 50))
#pygame.draw.rect(surface, colortxt, pygame.Rect(100, 125, 145, 30))
#draw the rectangles with dyamic sizes w1, h1, w2, h2
pygame.draw.rect(surface, color3, pygame.Rect(100, 325, 600 * roll, 50))
#pygame.draw.rect(surface, colortxt, pygame.Rect(100, 325, 145, 30))
#draw the rectangles with dyamic sizes w1, h1, w2, h2
pygame.draw.rect(surface, color4, pygame.Rect(100, 425, 600 * pitch, 50))
#pygame.draw.rect(surface, colortxt, pygame.Rect(100, 425, 145, 30))
This block of code looks dense and scary although all it does is draw a rectangle and scales the y axis size on the pygame plane depending on the imput. The size is decided with the equation 600 * pitch. The height of the rectangles
3.5 - Update Display
#Update the Pygame display
pygame.display.flip()
The title tells you exactly what this does. Every frame after all the maths is done, pygame calls it’s flip function which updates the pygame window.
3.6 - Quit Statement and Printing Debugging
if keyboard.is_pressed('ctrl+q'):
pygame.display.quit()
print('Bye :)')
break
try:
con.update()
print('Throttle: %+2.2f Roll: %+2.2f Pitch: %+2.2f Yaw: %+2.2f' %
(con.getThrottle(), con.getRoll(), con.getPitch(), con.getYaw()))
except KeyboardInterrupt:
break
The first if statement of this segment is using the keyboard libary to detect if the keys control and Q are pressed at the same time provided the window is in focus. If this is the case the loop will stop, and the program will quit. The other part, The Try statement will try to update the controller values every frame. If this succedes if prints the values and update the variables. Otherwise it will quit or not update. Also if Ctrl+C is pressed, the program will quit.