As far as I know, this is the first robot programming
entirely in the Swift programming language.
I programmed it as an example of what can be done using Swift with the new SwiftyBones library and the BeagleBone Black.
EDIT: See the first update to BuddyBot here
The second update to BuddyBot is here
the third update with parts list is here
EDIT: See the first update to BuddyBot here
The second update to BuddyBot is here
the third update with parts list is here
SwiftyBones is
a modular Swift library for interacting with both the digital GPIO, PWM and
Analog pins on the BeagleBone Black. This
library is written to make it easy to write Swift applications that run on the
Beaglebone Black that interacts with external
sensors, LED or any device connected to the Digital GPIO, PWM and/or Analog
pins. You can read about the
SwiftyBones library on its github page here.
Here is a picture of the robot (no comments about the messy
wiring please) J
Before I explain how everything works, below is a video of
the robot in action:
Wiring the Robot
For this project I am using the Rover 5 Tracked Chassis with the Rover 5 Motor Controller Board. Everything is
connected similar to what I did in the My first working robot, It’s alive post from 2014. For instructions on how
everything is connected please see that post.
I use a LED to indicate that everything has been initiated and
a button to start the robot. I wired them as shown in the following diagram
Setting up the
BeagleBone Black
1. Get the latest Debian 8.3 image from Beagle
Board’s site. You can find the image here.
2. We need to unzip the images. We can
do this using TheUnarchiver
for Mac.
3. Now we need to copy the image over to the SD
card. I would recommend using at least a 8 gig SD
card. Everything needed will take up 3.3 gig which will fit on a 4
gig card but you are not leaving yourself much extra space. I
use Pi Filler to copy the
image onto the SD card. Once installed, run the Pi Filler app and
follow the on screen prompts.
4. Once you have the image on the SD Card, go
ahead and plug it into your BeagleBone Black and power it up.
5. If you are using a SD card greater than 4 gig,
you will need to manually expand the file system since the image only uses 4
gig. To do this you can following these
instructions.
6. Now we are set to install Swift. The
instructions to do this are on the iachieved.it
site.
7. The last thing we
need to do is to download the SwiftyBones library. You can do that by running the following
command: wget https://github.com/hoffmanjon/SwiftyBones/archive/master.zip
Programming our
Robot:
The first thing we need to do is to create a directory
structure for our code. My directory
structure looks like this (I am naming the robot Buddy):
Buddy|
|
|--SwiftyBones|
| |-SwiftyBonesCommon.swift
| |-SwiftyBonesDigitalGPIO.swift
| |-SwiftyBonesPWM.swift
|
|--BuddyBot|
|
|-BuddyBot.swift
|
|--main.swift
|--swiftybuild.sh
I have two subdirectories below the main directory named SwiftyBones and BuddyBot. The SwiftyBones
directory contains the three SwiftyBones files that I need for my robot. The SwiftyBonesCommon.swift
file contains the common code needed for any project that uses
SwiftyBones. The SwiftyBonesDigitalGPIO.swift
file contains the code to interact with digital GPIO pins and the SwiftyBonesPWM.swift file contains the
code to interact with the PWM pins. The BuddyBot directory contains one file
which is the code for my specific robot and is the code that interacts with the
SwiftBones library.
SwiftyBones provides a script called swiftybuild.sh that can be used to easily compile applications that contains multiple files and subdirectories like this one. It basically searches the current directory and all subdirectories for any file that has the .swift extensions and then compiles them and builds the application.
Lets look at the BuddyBot.swift
code first:
#if
arch(arm) && os(Linux)
import Glibc
#else
import Darwin
#endif
public
struct BuddyBot {
private let FORWARD = DigitalGPIOValue.LOW
private let REVERSE =
DigitalGPIOValue.HIGH
private let runningLed: SBDigitalGPIO
private let startButton: SBDigitalGPIO
private let rightMotorDirection:
SBDigitalGPIO
private let leftMotorDirection:
SBDigitalGPIO
private let rightMotorPower: SBPWM
private let leftMotorPower: SBPWM
init?() {
let
runningLed = SBDigitalGPIO(id: "gpio30", direction: .OUT)
let
startButton = SBDigitalGPIO(id: "gpio60", direction: .IN)
let rightMotorDirection =
SBDigitalGPIO(id: "gpio26", direction: .OUT)
let leftMotorDirection =
SBDigitalGPIO(id: "gpio46", direction: .OUT)
let
leftMotorPower = SBPWM(header: .P8, pin: 19)
if runningLed == nil ||
startButton == nil ||
rightMotorDirection == nil
||
rightMotorPower == nil ||
leftMotorDirection == nil ||
leftMotorPower == nil {
return nil
}
self.runningLed = runningLed!
self.startButton = startButton!
self.rightMotorDirection =
rightMotorDirection!
self.rightMotorPower =
rightMotorPower!
self.leftMotorDirection =
leftMotorDirection!
self.leftMotorPower =
leftMotorPower!
enableTracks(false)
}
func goForward(speed: Int) {
enableTracks(false)
rightMotorDirection.setValue(FORWARD)
leftMotorDirection.setValue(FORWARD)
rightMotorPower.setValue(speed)
leftMotorPower.setValue(speed)
enableTracks(true)
}
func goReverse(speed: Int) {
enableTracks(false)
rightMotorDirection.setValue(REVERSE)
leftMotorDirection.setValue(REVERSE)
rightMotorPower.setValue(speed)
leftMotorPower.setValue(speed)
enableTracks(true)
}
func turnRight(speed: Int) {
enableTracks(false)
rightMotorDirection.setValue(REVERSE)
leftMotorDirection.setValue(FORWARD)
rightMotorPower.setValue(speed)
leftMotorPower.setValue(speed)
enableTracks(true)
}
func turnLeft(speed: Int) {
enableTracks(false)
rightMotorDirection.setValue(FORWARD)
leftMotorDirection.setValue(REVERSE)
rightMotorPower.setValue(speed)
leftMotorPower.setValue(speed)
enableTracks(true)
}
func allStop() {
enableTracks(false)
rightMotorDirection.setValue(FORWARD)
leftMotorDirection.setValue(FORWARD)
rightMotorPower.setValue(0)
leftMotorPower.setValue(0)
}
func enableTracks(enable: Bool) {
rightMotorPower.setEnable(enable)
leftMotorPower.setEnable(enable)
}
func ledOn(on: Bool) {
let newValue = (on) ?
DigitalGPIOValue.HIGH : DigitalGPIOValue.LOW
runningLed.setValue(newValue)
}
func getStartButton() -> Bool {
let butVal = startButton.getValue()
return (butVal ==
DigitalGPIOValue.LOW) ? false : true
}
}
We start the code off by defining a number of
constants. These are:
1. FORWARD – The motors that control the
tracks on my robot can go in the forward or reverse direction. We set the direction pin LOW
to go forward. This constant defines the forward direction as being low.
2. REVERSE – The motors that control the
tracks on my robot can go in the forward or reverse direction. We set the direction pin
HIGH to go in reverse. This constant defines the reverse direction as being
high.
3. runningLed: This constant will define the GPIO pin for
the LED .
4. startButton: This constant will define the GPIO pin for
the start button.
5. rightMotorDirection: This constant defines the GPIO pin that is connected
to direction pin on the motor controller board that is connected to the right
motor.
6. leftMotorDirection: This constant defines the GPIO pin that is
connected to direction pin on the motor controller board that is connected to
the left motor.
7. rightMotorPower: This constant defines the PWM pin that is
connected to the PWM pin on the motor controller board that controls the speed
of the right motor.
8. leftMotorPower: This constant defines the PWM pin that is
connected to the PWM pin on the motor controller board that controls the speed
of the left motor.
We have one failable initializer in our BuddyBot type. In this
initializer we initialize all of the pins and if any of them fail to properly initialize
then the initializer fail and we will return nil.
After the initializer we have several methods that we can
use with our BuddyBot type. The first two are goForward()
and goReverse(). Calling these two methods will cause the
robot to move in the forward or reverse directions. In these two functions we start off by
disabling the tracks by calling the enableTracks()
method with a value of false. I would
recommend always disabling the tracks prior to setting the direction. We then set the direction of the tracks
(FORWARD in the goForward() method and
REVERSE in the goReverse() method)
followed by setting the speed we want to go.
The speed can be set anywhere from 0 to 10000 where a value of 10000
causes the motor to go full speed and a value of 0 causes the motor to stop. Finally we enable the tracks by calling the enableTracks() method with a value of true.
The next two methods turnRight()
and turnLeft() are very
similar to the goForward() and goReverse() methods except we set one
track to move in the forward direction and one in the reverse direction causing
the robot will turn.
The allStop()
method disables the tracks, sets the direction of both tracks to forward and
sets the speed of the tracks to 0. This
stops the robot in place.
The ledON()
method will turn the LED on or off depending on the value passed in. A value of true will turn the LED on and a
value of false will turn the LED off.
Finally the getStartButton()
will return true if the button is being pressed is being pressed.
To understand more about how SwiftyBones works, you can read
about the library on its github page here.
Now lets look at the main.swift file. This is the file that is executed when our
application starts. The following is the
code from the BuddyBot main.swift file:
#if arch(arm) && os(Linux)
import Glibc
#else
import Darwin
#endif
if let buddy = BuddyBot() {
buddy.ledOn(true)
while
!buddy.getStartButton() {}
buddy.goForward(7000)
usleep(6000000)
buddy.turnLeft(3000)
usleep(1500000)
buddy.turnRight(3000)
usleep(1500000)
buddy.goReverse(5000)
usleep(3000000)
buddy.allStop()
buddy.ledOn(false)
} else {
print("Error
init")
}
In this code we start off by initializing an instance of the
BuddyBot type. If we get an instance of the BuddyBot type (the initializer did not
fail) we then turn on the LED using the ledOn()
method. We then use the goForward(), turnLeft(),
turnRight(), goReverse()
and allStop() methods from
the BuddyBot type to make the robot move.
We use the usleep()
method to wait a certain amount of time between each step.
We use the swiftybuild.sh script to compile the
application. This script will search the
currently directory and all subdirectories for files with the .swift
extensions. It will then compile all of
those files and build the application.
That is how the first robot programming using the Swift
language was written and built. To learn
more about SwiftyBones go to its github page here.
This comment has been removed by the author.
ReplyDeleteGreat job Jon! Would you perhaps be interested in joining our Swift Everywhere community? https://www.linkedin.com/groups/8527482
ReplyDeleteI did not know that group existed, sure
DeleteThis comment has been removed by a blog administrator.
ReplyDeleteExcellent blog!! Having a great stuff and i like the way you have presented about robot programming using the Swift language is amazing. Thank you for sharing with us.
ReplyDeleteRobot programming
Thank you, thinking about using Swift for some of the industrial robots?
DeleteThis comment has been removed by the author.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteYou know your projects stand out of the herd. There is something special about them. It seems to me all of them are really brilliant!
ReplyDeleterobots programming