Having had a bit of an unproductive Easter break I think itβs about time I got back to posting the concluding details of how the transmitter for the Bluetooth RC was implemented. I built the PIC-based motor and servo controller before acquiring the BlueSMiRF module so initially I wrote the following simple Python script based on the PyGame module to control the board via the direct (wired) serial connection using a USB gamepad.
#!python.exe
import pygame
import math
import serial
import threading
MAX_SERVO_POS = 0x3f
MAX_SPEED = 0x1f
def clamp(min_val, max_val, val):
return max(min(val, max_val), min_val)
# initialise pygame module
pygame.init()
clock = pygame.time.Clock()
running = 1
# initialise serial port 8 [COM9] for direct serial connection
ser = serial.Serial(8, 19200, timeout=1)
# initialise serial port 5 [COM6] for Serial over Bluetooth
#ser = serial.Serial(5, 115200, timeout=1)
print(βusing serial port: %sβ % ser.portstr)
# reset microchip
ser.setRTS()
pygame.time.wait(500)
ser.setRTS(False)
# setup asynchronous serial port reader
class AsyncSerialPortReader(threading.Thread):
def run(self):
while running:
debug_output = ser.readline()
if debug_output:
try:
print(β>>%sβ % debug_output.decode(βutf-8β))
except:
continue
port_reader = AsyncSerialPortReader()
port_reader.start()
# initialise joystick
print(βfound %d joysticks\nβ % pygame.joystick.get_count())
joystick = pygame.joystick.Joystick(0)
print(βinitialising joystick: %s\nβ % joystick.get_name())
joystick.init()
# setup allowed events
pygame.event.set_allowed(None)
pygame.event.set_allowed(pygame.QUIT)
pygame.event.set_allowed(pygame.JOYAXISMOTION)
# enter joystick event loop
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = 0
elif event.type == pygame.JOYAXISMOTION:
if event.axis == 0:
servo_pos = math.floor(MAX_SERVO_POS * (1 + clamp(-0.7, 0.7, -event.value))/2)
print(βsetting servo position %sβ % servo_pos)
ser.write(bytes([servo_pos]))
elif event.axis == 1:
speed = math.floor(MAX_SPEED * clamp(-1, 1, event.value))
dir = 0 if speed <= 0 else 1
speed = abs(speed)
speed_dir = 1<<6 | dir<<5 | speed
print(βsetting motor speed and direction %sβ % bin(speed_dir))
ser.write(bytes([speed_dir]))
pygame.time.wait(100)
clock.tick(20)
# cleanup before exiting
port_reader.join()
ser.close()
pygame.quit()
The code basically bootstraps the PyGame library and enters into an event loop, processing joystick events on the gamepad by mapping them to control commands for the car and writing these to the serial port. The event loop is rate limited because the absence of flow-control on the serial connections means that it is quite easy to overwhelm the PIC UART code with data. The script also includes an asynchronous reader thread for printing diagnostic information outputted from the board. When the BlueSMiRF wireless serial modem was integrated into the board, it was just a matter of commenting out line 20 and using line 22 instead which establishes the serial connection over my PCβs Bluetooth adapter i.e. using the RFCOMM protocol. Note, the BlueSMiRF must first be paired to the PC (default PIN 1234) before the Serial Port Profile (SSP) COM port becomes available for use.
The final step was to somehow port this code to my Android phone, using the accelerometer as if it were a free floating joystick. I decided on the Scripting Layer for Android (or SL4A)Β platform which is a neat little collection of facades over the traditional Android Java API, with a wide variety of language bindings thus allowing one to quickly write simple applications in their favourite scripting language to harness the power of their Android device. Unfortunately the Android install packages for these apps are quite mainstream enough to be on the PlayStore so I had to manually download and install them from the website:
For more detail: SL4A Bluetooth Controller