Thursday, May 5, 2016

The first robot programed in Swift with SwiftyBones


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

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 rightMotorPower = SBPWM(header: .P8, pin: 13)

        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.


9 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Great job Jon! Would you perhaps be interested in joining our Swift Everywhere community? https://www.linkedin.com/groups/8527482

    ReplyDelete
    Replies
    1. I did not know that group existed, sure

      Delete
  3. This comment has been removed by a blog administrator.

    ReplyDelete
  4. Excellent 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.

    Robot programming

    ReplyDelete
    Replies
    1. Thank you, thinking about using Swift for some of the industrial robots?

      Delete
  5. This comment has been removed by the author.

    ReplyDelete
  6. This comment has been removed by a blog administrator.

    ReplyDelete
  7. You know your projects stand out of the herd. There is something special about them. It seems to me all of them are really brilliant!
    robots programming

    ReplyDelete