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