Skip to content

Instantly share code, notes, and snippets.

@kmorel
Created January 25, 2014 17:33
Show Gist options
  • Save kmorel/8620145 to your computer and use it in GitHub Desktop.
Save kmorel/8620145 to your computer and use it in GitHub Desktop.
A techBASIC program for controlling an RC car via a RedBearLabs BLE shield on an Arduino running firmata and a jumpered motor shield.
! This app uses the accelerometer to control a car hacked to use the
! RedBear BLE Shield and an Arduino.
redBearUUID$ = "713D0000-503E-4C75-BA94-3148F18D941E"
txUUID$ = "713D0003-503E-4C75-BA94-3148F18D941E"
! The reason I am putting a button over the drag area is because I want to fill
! it with a gradient representing the speed and direction, and the button is the
! only GUI element I know of that can draw a gradient. Unfortunately, to get
! the drag events, I have to disable the button, which messes up the colors.
DIM dragArea AS Button
DIM status AS Label
DIM updateGradient AS INTEGER
DIM speed AS SINGLE
DIM turn AS SINGLE
DIM lastGradientUpdate AS DOUBLE
lastGradientUpdate = 0
DIM BLEShield AS BLEPeripheral
DIM txCharacteristic AS BLECharacteristic
DIM lastTime AS DOUBLE
haveConnection = 0
oldPout = 0
oldSpeedOut = 0
oldTurnOut = 0
! Set up motor controls
forwardMask = $04
backwardMask = $00
leftMask = $00
rightMask = $10
brakeMask = $40
! Draw the GUI and start looking for the RedBear BLE Shield
setUp
! Called when a peripheral is found. If it is a RedBear BLE shield, we
! initiate a connection to it and stop scanning for peripherals.
!
! Parameters:
! time - The time when the peripheral was discovered.
! peripheral - The peripheral that was discovered.
! services - List of services offered by the device.
! advertisements - Advertisements (information provided by the
! device without the need to read a service/characteristic)
! rssi - Received Signal Strength Indicator
SUB BLEDiscoveredPeripheral( _
time AS DOUBLE, _
peripheral AS BLEPeripheral, _
services() AS STRING, _
advertisements(,) AS STRING, _
rssi)
BLE.connect(peripheral)
BLE.stopScan
BLEShield = peripheral
haveConnection = 1
END SUB
! Called to report information about the connection status of the
! peripheral or to report that services have been discovered.
!
! Parameters:
! time - The time when the information was received.
! peripheral - The peripheral.
! kind - The kind of call. One of
! 1 - Connection completed
! 2 - Connection failed
! 3 - Connection lost
! 4 - Services discovered
! message - For errors, a human-readable error message.
! err - If there was an error, the Apple error number. If there
! was no error, this value is 0.
SUB BLEPeripheralInfo( _
time AS DOUBLE, _
peripheral AS BLEPeripheral, _
kind AS INTEGER, _
message AS STRING, _
err AS LONG)
DIM uuid(1) AS STRING
IF kind = 1 THEN
! The connection was established. Discover the service.
uuid(1) = redBearUUID$
peripheral.discoverServices(uuid)
status.setBackgroundColor(1, 0.6, 0): ! Connection made: Status Yellow.
ELSE IF kind = 2 OR kind = 3 THEN
! Lost the connection--Change the status and begin looking again.
status.setBackgroundColor(1, 0, 0): ! Connection lost: Status Red.
haveConnection = 0
BLE.connect(peripheral)
ELSE IF kind = 4 THEN
! Once the RedBear service is found, start discovery on the characteristics.
DIM availableServices(1) AS BLEService
availableServices = peripheral.services
FOR a = 1 TO UBOUND(availableServices, 1)
IF availableServices(a).UUID = redBearUUID$ THEN
uuid(1) = txUUID$
peripheral.discoverCharacteristics(uuid, availableServices(a))
END IF
NEXT
END IF
END SUB
! Called to report information about a characteristic or included
! services for a service. If it is one we are interested in, start
! handling it.
!
! Parameters:
! time - The time when the information was received.
! peripheral - The peripheral.
! service - The service whose characteristic or included
! service was found.
! kind - The kind of call. One of
! 1 - Characteristics found
! 2 - Included services found
! message - For errors, a human-readable error message.
! err - If there was an error, the Apple error number. If there
! was no error, this value is 0.
SUB BLEServiceInfo( _
time AS DOUBLE, _
peripheral AS BLEPeripheral, _
service AS BLEService, _
kind AS INTEGER, _
message AS STRING, _
err AS LONG)
IF kind = 1 THEN
! Get the characteristics.
DIM characteristics(1) AS BLECharacteristic
characteristics = service.characteristics
FOR i = 1 TO UBOUND(characteristics, 1)
IF characteristics(i).uuid = txUUID$ THEN
! Remember the transmit service.
txCharacteristic = characteristics(i)
! Connection complete: Status Green.
status.setBackgroundColor(0, 1, 0)
haveConnection = 1
! Set the mode for the pins used.
inputMode% = $00
outputMode% = $01
pwmMode% = $03
DIM pinModes(2 TO 6) AS INTEGER
pinModes = [
outputMode%, ! Pin 2: Drive direction
pwmMode%, ! Pin 3: Drive speed
outputMode%, ! Pin 4: Turn direction
pwmMode%, ! Pin 5: Turn intensity
outputMode% ! Pin 6: Brake
]
DIM value(0) AS INTEGER
FOR pin = LBOUND(pinModes, 1) TO UBOUND(pinModes, 1)
value = [ $F4, pin, pinModes(pin) ]
BLEShield.writeCharacteristic(txCharacteristic, value, 0)
NEXT
END IF
NEXT
END IF
END SUB
! Set up the GUI and start searching for the RedBear BLE Shield
SUB setUp
graphics.setToolsHidden(1)
system.showGraphics
system.setAllowedOrientations($0001)
dragSize = graphics.width
dragArea = graphics.newButton(0,graphics.height-dragSize,dragSize,dragSize)
! Mark button as disabled so we can do our own event handling.
dragArea.setEnabled(0)
DIM title AS Label
title = graphics.newLabel(0,0, graphics.width-20)
title.setText("Version 1")
status = graphics.newLabel(graphics.width-20,0,20,20)
status.setBackgroundColor(1, 0, 0)
engageBrake
! Find the BLE Shield.
BLE.startBLE
DIM uuid(1) AS STRING
uuid(1) = redBearUUID$
BLE.startScan(uuid)
END SUB
SUB touchesBegan(e AS EVENT)
recordDrag(e)
END SUB
SUB touchesMoved(e AS EVENT)
recordDrag(e)
END SUB
SUB touchesCanceled(e AS EVENT)
engageBrake
END SUB
SUB touchesEnded(e AS EVENT)
engageBrake
END SUB
SUB recordDrag(e AS EVENT)
IF eventInside(e,dragArea) THEN
where = e.where
location = eventLocation(e, dragArea)
! Make the multiplier a bit more than 2 because dragging all the way to
! the edge of the widget for maximum speed is difficult.
turn = 2.5*(location(1) - 0.5)
speed = 2.5*(0.5 - location(2))
updateGradient = 1
IF lastGradientUpdate + 0.2 < e.when THEN
drawSpeed
lastGradientUpdate = e.when
END IF
sendMotorCommands(0)
e.setConsumed(1)
END IF
END SUB
FUNCTION eventInside( _
e AS EVENT, _
widget AS CONTROL )
where = e.where
x = where(1,1)
y = where(1,2)
IF x >= widget.x _
AND y >= widget.y _
AND x <= widget.x + widget.width _
AND y <= widget.y + widget.height _
THEN
eventInside = 1
ELSE
eventInside = 0
END IF
END FUNCTION
FUNCTION eventLocation( _
e AS EVENT, _
widget AS CONTROL ) (0)
where = e.where
x = where(1,1)
y = where(1,2)
eventLocation = [ _
(x - widget.x)/widget.width, _
(y - widget.y)/widget.height ]
END FUNCTION
SUB engageBrake
updateGradient = 0
speed = 0
turn = 0
dragArea.setBackgroundColor(0.5,0.5,0.5,3)
dragArea.setGradient(0,0,0,-1,3)
sendMotorCommands(1)
END SUB
SUB drawSpeed
IF updateGradient THEN
updateGradient = 0
mag = sqr(speed*speed+turn*turn)
IF mag > 1 THEN mag = 1
dragArea.setBackgroundColor(1,1-mag,1-mag)
dragArea.setGradientColor(1,1,1)
dragArea.setGradient(0.5*turn+0.5, 0.5-0.5*speed, 0.5, 0.5, 3)
!dragArea.setEnabled(1)
!dragArea.setEnabled(0)
END IF
END SUB
! Sends updated moter commands to the car. If brake is true, state is changed
! to stop running and turn on brake.
SUB sendMotorCommands(brake AS INTEGER)
IF haveConnection THEN
DIM pout AS INTEGER, speedOut AS INTEGER, turnOut AS INTEGER
IF brake THEN
pout = brakeMask
speedOut = 0
turnOut = 0
speed = 0
turn = 0
ELSE
pout = 0
IF speed < 0 THEN
pout = pout BITOR backwardMask
speedOut = -255*speed
ELSE
pout = pout BITOR forwardMask
speedOut = 255*speed
END IF
IF speedOut > 255 THEN speedOut = 255
IF turn < 0 THEN
pout = pout BITOR leftMask
turnOut = -255*turn
ELSE
pout = pout BITOR rightMask
turnOut = 255*turn
END IF
IF turnOut > 255 THEN turnOut = 255
END IF
! Send new instructions as necessary.
IF oldPout <> pout OR brake THEN
oldPout = pout
DIM value(3) AS INTEGER
value = [ $90, (pout BITAND $7F), (pout >> 7) ]
BLEShield.writeCharacteristic(txCharacteristic, value, 0)
END IF
IF oldSpeedOut <> speedOut OR brake THEN
oldSpeedOut = speedOut
DIM value(3) AS INTEGER
value = [ $E3, (speedOut BITAND $7F), (speedOut >> 7) ]
BLEShield.writeCharacteristic(txCharacteristic, value, 0)
END IF
IF oldTurnOut <> turnOut OR brake THEN
oldTurnOut = turnOut
DIM value(3) AS INTEGER
value = [ $E5, (turnOut BITAND $7F), (turnOut >> 7) ]
BLEShield.writeCharacteristic(txCharacteristic, value, 0)
END IF
END IF
END SUB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment