Created
January 25, 2014 17:33
-
-
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
! 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