5
\$\begingroup\$

This python code is for a homemade remote-control (RC) plane radio using the Raspberry Pi and a USB joystick. It sends values over serial to DSM2/X module for RC planes and other RC vehicles. This Python code is modified from codeforge's video "CustomRC with raspberry and joystick usb" with many improvements by me. Feedback on the Python code would be great as I rushed the modifications that add expo, channel reversing and channel offset.

########################################################################################################################
# This short code take input from a usb joystick ( I use microsoft sidewinder) and send it through serial
# to a DSM2 module to control all kind of helicopter, multicopter or spektrum compatible devices. This code
# must be run in raspbian with raspberry pi (I use a raspberry pi 2 B). You need to modified rPi speed as following:
# 1 - remove all "ttyAMA0" from /boot/cmdline.txt
# 2 - add the following lines to the end of /boot/config.txt:
# dtparam=i2c_arm=on
# dtparam=i2c1=on
# init_uart_clock=3255000
# init_uart_baud=115200
# dtparam=uart0_clkrate=3000000
#
# 3 - reboot raspberry pi
# 4 - install python modules pygame and pyserial
# 5 - power on the spektrum receiver or spektrum device in BIND mode (led flashing)
# 6 - run this code with root privileges: sudo python joystick.py
# 7 - wait the binding complete and enjoy :)
#
# PLEASE CONNECT DSM2 MODULE VCC (red or power) TO 3.3v AND NOT 5V OR YOU WILL BURN IT!
# THEN CONNECT NEGATIVE TO NEGATIVE AND SIGNAL PIN OF DSM2 MODULE TO TX PIN ON RASPBERRY PI
# NO NEED TO CONNECT RX PIN SO YOU NEED TO CONNECT JUCT 3 PIN OR CABLES
#
# For any info contact me at [email protected]
#
#
# Copyright (C) <2016> <Guido Berbacchi>
# Modified <10/11/16> <Ethan Johnston>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
########################################################################################################################
import pygame
from time import sleep
import serial
ser = serial.Serial('/dev/ttyAMA0', 115200)
def sendDSM2(): # function to send data to dsm2/x module
 ser.write(DSM2_Header)
 ser.write(DSM2_Channel)
 sleep(0.014)
def arduino_map(x, in_min, in_max, out_min, out_max): # map function as arduino map()
 return (x - in_min) * (out_max - out_min) // (in_max - in_min) + out_min 
 
def reverse_val(valinput, in_minval, in_maxval):
 return (in_maxval - valinput) + in_minval
def apply_expo(axis_val, factor):
 factor = reverse_val(factor, 0, 100)
 factor = factor / 100
 return (1 - factor) * axis_val * axis_val * axis_val + factor * axis_val
# Global Variable and static
DSM2_Header = bytearray(b'\x98\x01') # header to bind
DSM2_Channel = bytearray(b'\x18\x00\x05\xFF\x09\xFF\x0D\xFF\x10\xAA\x14\xAA')
DSM2_CHANNELS = 6
EPA = 300 #300
EPAGAS = 180 #180
fineBinding = 30
i = 0
val = 512
BExit = 0
#reverse channels
ch0rev = False #throttle
ch1rev = True #roll
ch2rev = False #pitch
ch3rev = True #yaw
ch4rev = False #gear
ch5rev = False #aux
#epo for channels
ch0expo = 0 #throttle
ch1expo = 20 #roll
ch2expo = 20 #pitch
ch3expo = 20 #yaw
#Trims
ch0offset = 0 #throttle
ch1offset = 0 #roll
ch2offset = 0 #pitch
ch3offset = 0 #yaw
ch4offset = 0 #gear
ch5offset = 0 #aux
print ("Binding")
while fineBinding >= 0:
 
 sendDSM2()
 #print "binding"
 sleep(0.1)
 fineBinding = fineBinding - 1
print ("Binding end")
sleep(1)
# change header to send input
DSM2_Header[0] = '\x18';
# pygame initialization to read joystick input
pygame.init()
clock = pygame.time.Clock()
pygame.joystick.init()
joystick = pygame.joystick.Joystick(0)
joystick.init()
while BExit == 0:
 pygame.event.get() 
 roll = joystick.get_axis(0) 
 pitch = joystick.get_axis(1)
 yaw = joystick.get_axis(2)
 gas = joystick.get_axis(3)
 
 
 if ch0rev == True:
 gas = reverse_val(gas, -1, 1)
 
 if ch1rev == True:
 roll = reverse_val(roll, -1, 1)
 
 if ch2rev == True:
 pitch = reverse_val(pitch, -1, 1)
 
 if ch3rev == True:
 yaw = reverse_val(yaw, -1, 1)
 
 roll = apply_expo(roll, ch1expo)
 pitch = apply_expo(pitch, ch2expo)
 yaw = apply_expo(yaw, ch3expo)
 gas = apply_expo(gas, ch0expo)
 
 hat = joystick.get_hat(0)
 BLeft = joystick.get_button( 2 )
 BRight = joystick.get_button( 3 )
 
 if BLeft == 1:
 ch3offset -= 4
 print("Yaw Trim: {}".format(ch3offset))
 
 if BRight == 1:
 ch3offset += 4
 print("Yaw Trim: {}".format(ch3offset))
 
 if hat[1] == 1:
 ch2offset += 4
 print("pitch Trim: {}".format(ch2offset))
 if hat[1] == -1:
 ch2offset -= 4
 print("pitch Trim: {}".format(ch2offset))
 
 if hat[0] == 1:
 ch1offset += 4
 print("roll Trim: {}".format(ch1offset))
 
 if hat[0] == -1:
 ch1offset -= 4
 print("roll Trim: {}".format(ch1offset))
 
 BExit = 0
 
 while (i < DSM2_CHANNELS):
 if i == 0: #throttle
 val = int(arduino_map(gas, -1.0, 1.0, 1023, 0+EPAGAS)) #723 - 300
 val = val + ch0offset
 #print ("gas",val)
 elif i == 1: #roll
 val = int(arduino_map(roll, -1.0, 1.0, 1023-EPA, 0+EPA)) #723 - 300
 val = val + ch1offset
 #print ("roll",val) 
 elif i == 2: #pitch
 val = int(arduino_map(pitch, -1.0, 1.0, 1023-EPA, 0+EPA)) #723 - 300
 val = val + ch2offset
 #print ("pitch",val)
 elif i == 3: # yaw
 val = int(arduino_map(yaw, -1.0, 1.0, 1023-EPA, 0+EPA)) #723 - 300
 val = val + ch3offset
 elif i == 4:
 val = 512
 val = val + ch4offset
 elif i == 5:
 val = 512
 val = val + ch5offset
 else: #the remaining channels set to half
 val = 512
 if val > 1023:
 val = 1023
 if val < 0:
 val = 0
 (high, low) = (val // 0x100, val % 0x100)
 DSM2_Channel[i*2] = (i<<2) | high
 DSM2_Channel[i*2+1] = low
 i+=1
 sendDSM2()
 clock.tick(135)
 i=0
pygame.quit ()
toolic
14.6k5 gold badges29 silver badges204 bronze badges
asked Nov 11, 2016 at 13:11
\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

Indentation

The code uses inconsistent indentation, and single-space indents make the code very hard to understand. The black program can be used to automatically indent the code with a more reasonable 4-space indent per level.

black also removes unnecessary semicolons like:

DSM2_Header[0] = '\x18';

and removes unnecessary parentheses like:

while (i < DSM2_CHANNELS):

Simpler

Lines like this:

if ch0rev == True:

can be simplified as:

if ch0rev:

There is no need to explicitly compare against True.

In the apply_expo function, these 2 lines:

factor = reverse_val(factor, 0, 100)
factor = factor / 100

can be combined into 1 line:

factor = (reverse_val(factor, 0, 100))/100

This line:

return (1 - factor) * axis_val * axis_val * axis_val + factor * axis_val

can be simplified as:

return axis_val * ((1 - factor) * (axis_val**2) + factor)

The following:

val = val + ch0offset

can be simplified using the special assignment operator:

val += ch0offset

Lines like this:

print("Yaw Trim: {}".format(ch3offset))

can be simplified using an f-string:

print(f"Yaw Trim: {ch3offset}")

Portability

I realize this question was posted many years ago when Python version 2.x was prevalent, but now that it is deprecated, consider porting to 3.x. Then you will be able to easily use features like f-strings.

Efficiency

These separate if statements:

if hat[1] == 1:
if hat[1] == -1:

should be combined into a single if/elif statement:

if hat[1] == 1:
elif hat[1] == -1:

The checks are mutually exclusive. This makes the code more efficient since you don't have to perform the 2nd check if the first is true, etc. Also, this more clearly shows the intent of the code.

The same is true for hat[0].

Comments

Delete all commented-out code to reduce clutter, for example:

#print "binding"
#print ("gas",val)
answered Jul 22 at 17:16
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.