Showing posts with label button. Show all posts
Showing posts with label button. Show all posts

Monday, April 4, 2016

Swift and the BeagleBone Black

**** Update:  We have just built the first robot programmed with Swift and the SwiftyBones library.  You can read about it here:  http://myroboticadventure.blogspot.com/2016/05/the-first-robot-programed-in-swift-with.html  ****

Now that I have completed my three books on Swift (MasteringSwiftMastering Swift 2 and Protocol Oriented Programming with Swift), I told my daughters that I would take a break from writing books for the summer.  My oldest told me that she wanted to start working on robots again.  At the age of ten, She is already a second-degree black belt, and an instructor in Tae Kwon Do so when she says she wants to do do something I generally listen.  So after a little discussion about what she wanted to do, we decided that we would pull out all of the robot parts and start working with our BeagleBone Blacks again.

I had to start off by doing some reading to catch up with everything that has happened over the past year and a half that spent writing.  In my reading I happened to stumble on the iacheived.it site that showed how to install Swift on the BeagleBone Black. I also found the SwiftyGPIO package (control the BeagleBone Black GPIO with Swift) that was featured on IBM’s Swiftpackage library.  So this got me thinking about being able to program our robots with Swift, now that sounds pretty exciting doesn’t it?

Note: This post and a most of the ones that show how to use Swift with the BeagleBone Black will be crossed posted between my Robotics Blog and my Swift programming blog.

The first part of this post will walk you through setting up your BeagleBone Black.  After we get the BeagleBone Black setup we will write some code that will let us control an LED with a button.  I know controlling a led with a button isn’t that exciting but we need to start somewhere and that really is like a “Hello World” application so lets get started.

Setting up our BeagleBone Black

The following list will walk you through setting up the BeagleBone Black.  Rather than writing out detail instructions I will provide links to the pages that I followed when I set up my boards.  Since I use a Macbook Pro, the instructions are for the Mac.  Sorry, but I do not have a Windows machine to mirror the steps on however the only Mac specific areas in these steps in where we copy the image over to the SD card and the Beagle Board site has a getting started page that may help anyone that uses Windows with these steps.  If you use the Beagle Board site, once you get the image on the SD card, you can skip to step 4 below.

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 before we start to code is to get the SwiftyGPIO repository.  You can find the repository here.  Under the Sources directory you will find the file SwiftGPIO.swift file.  This is the file we will need to use with our code.

Now that our BeagleBone black setup, lets get ready to do some coding.  We will start by writing some code that will cause our LED to blink on and off.  We will then write a separate application to read the state of a button.  Finally we will combine the code to create an application that will turn the LED on and off with the button.  So lets get started.

Turning an LED On and Off

The first thing we need to do is to wire everything up.  When you do this wiring you will want to have the BeagleBone Black powered off.  The following diagram shows how we would wire a LED to our BeagleBone Black.  It is recommended that whenever we connect anything to the BeagleBone Black we should always disconnect the power.


   

We run a solder-less jumper from pin 1 of the P9 expansion header to the ground rail marked with the blue line on the breadboard and then take another solder-less jumper from pin 2 of the P9 expansion header to the power rail marked with the red line.  We will use these rails to provide power and ground for our LED and Button.

Now lets add the LED to our breadboard.  Connect the cathode end of the LED (shorter wire) to the ground rail of our breadboard and then connect the anode end of the LED (longer wire) to one of the other rows on our breadboard.

Now take a 100 OHM resistor and connect one end to the row on the breadboard that the LED is connected to and the other end of the resistor to another row on the breadboard.  Finally run a solder-less jumper from pin 12 of the P9 expansion header to the row that the 100 OHM resistor is connected too.  We are now set to power up the BeagleBone Black.

You will want to create a separate directory for each Swift project so lets begin by creating a directory named blinkyled and then change to that directory.  You will want to copy the SwiftyGPIO.swift file from the SwiftyGPIO package to this directory.

Copy the following code into a file named main.swift also in the blinkyled directory (the file needs to be named main.swift).

import Glibc

let gpios = SwiftyGPIO.getGPIOsForBoard(.BeagleBoneBlack)
var led = GPIO(name:"GPIO_60", id: 60)

led.direction = .OUT

while(true){
      print(“Changing”)
     led.value = (led.value == 0) ? 1 : 0
     usleep(150000)

In this file we start off by importing the GLibc module.  In the next line we retrieve the list of GPIOs available for the BeagleBone Black.  Next we get a reference to GPIO_60 (pin 12 of the P9 expansion header).  You can see the GPIO ports listed here.

The next line configures the port direction for the GPIO port.  We can use GPIODirection.IN or GPIODirection.OUT here.  Now we create a while loop.  Within the while loop the first line prints a message to the console letting us know that we are changing the LED.  The next line checks the value of the LED and changes it causing the LED to blink.  A value of 1 turns the LED on and a value of 0 turns it off.  We then use the usleep function to pause before we loop back.

To compile this application we use the following command:

swiftc –o blinkyled SwiftyGPIO.swift main.swift 

This command uses the swift compiler to compile SwiftyGPIO.swift and main.swift and writes the output to the file named blinkyled.  We are now able to run our application.  If you attempt to run this without super user privileges the LED will not blink.  To access the GPIO ports you will need to run the application with sudo like this:

sudo ./blinkled 

If everything is connected correctly, the LED should blink on and off pretty quickly.  Now lets look at how we would check the state of a button.

Reading the state of a button

Now that we have the LED working lets look at how we would read the state of a button.  To begin with lets connect a button to our Beaglebone Black as shown in the following diagram.  Keep in mind that whenever we connect anything to the BeagleBone Black we should always disconnect the power.
   


In this diagram we add the push button to the breadboard.  You will want the button to straddle the middle section as show in the previous image.  Using a solder-less jumper, connect the power rail of your breadboard to one end of the button.  Next connect the same end of the button to the ground rail of your breadboard using the 10K pulldown resistor.  Finally connect the other end of the button to pin 23 of the P9 expansion header.  

Now lets power up the BeagleBone Black and write our code to read the state of the button.  Create a directory named button and copy the SwiftyGPIO.swift file to this directory.  Next create a file named main.swift in the button directory and add the following code to it.

import Glibc

let gpios = SwiftyGPIO.getGPIOsForBoard(.BeagleBoneBlack)
var button = GPIO(name: "GPIO_49",id: 49)
button.direction = .IN
while(true){
      if button.value == 1 {
           print("Pressed")
      }
      usleep(10000)
}

In this file we start off by importing the GLibc module.  In the next line we retrieve the list of GPIOs available for the BeagleBone Black.  We then get a reference to GPIO_49 (pin 23 of the P9 expansion header).  

The next line configures the port direction for the GPIO port.  We can use GPIODirection.IN or GPIODirection.OUT here.  Notice in the LED example we used GPIODirection.OUT however in this example we used GPIODirection.IN.  Next we create a while loop.  Within the while loop we check the state of the port and if it is high (value of 1) we print the message “Pressed” to the console letting us know the button is pressed.  We then use the usleep function to pause before we loop back.

To compile this application we use the following command:

swiftc –o button SwiftyGPIO.swift main.swift 

This command uses the swift compiler to compile SwiftyGPIO.swift and main.swift and writes the output to the file named button.  We are now able to run our application.  The following command will run our application.

sudo ./button 

If everything is connected correctly, when you press the button you should get a message printed to the console.  Now lets put our LED and Button examples together to turn the LED on whenever the button is pressed.

Putting it together


The following diagram shows how we would wire the LED and Button to our BeagleBone Black (notice no changes from the previous two diagrams just combined them).


The following code will go in our main.swift file:

import Glibc

let gpios = SwiftyGPIO.getGPIOsForBoard(.BeagleBoneBlack)
var button = GPIO(name: "GPIO_49",id: 49)
var led = GPIO(name:"GPIO_60", id: 60)

button.direction = .IN
led.direction = .OUT

while(true){
      if button.value == 1 {
            print("Pressed")
           led.value = 1
      } else {
           led.value = 0
      }
      usleep(10*1000)

When you compile this, don’t forget to include the SwiftyGPIO.swift file.  When you run the application, the LED should turn on when you press the button and turn off when you release it.  The following image shows how my wiring looks in real life



Notice the sporty new BB8 case I made for my BeagleBone Black, pretty cool huh?  Not to mention that BB8 and BBB kind of go together.  Just printed it on my new 3D printer.  I will be talking about the printer in a post very soon and will include links to some of the stuff I have printed and designed including the BB8 case.  I am thinking about making a R2D2 case for my other BeagleBone Black.

If you are new to Swift, I will be discussing some of the basics while I am showing how to use Swift with the BeagleBone black however my assumption is you will have at least a basic understanding of Swift.  I will also be writing more blog posts that are specific to Swift on Linux on my Swift programming blog however if you are really interested in the language I would recommend my Mastering Swift 2 and Protocol Oriented Programming with Swift books.  Please keep in mind that those books talk about using Swift on the Mac however most of the language concepts themselves are the same whether you are using Swift on a Mac or with Linux.

Now that my books are done, you should start seeing a lot more posts on both my robotic and swift blogs.  I do have a question for anyone that might be able to answer: Does anyone know when/if we will see an update for the BeagleBone Black?  I am not talking about the X15 that looks like it is going to be pretty expensive.  I am looking more for an update to the BeagleBone black that will have roughly the same price point.




Thursday, July 3, 2014

Prototyping an autonomous robot with Javascript

You may be asking yourself; why would I choose Javascript for the first prototype of our autonomous robot?  The answer is really simple, I wanted to see if I could write it in Javascript and I also wondered how effective Javascript would be at controlling an autonomous robot.  First, lets see a video of our prototype in action:





If you are not familiar with using Javascript/Bonescript with the BeagleBone Black, you will probably want to take a look at my previous post Using Javascript with Bonescript to program the BeagleBone Black before reading this post.  The My firstworking robot, it’s alive post details how we built the robot and the LV-MaxSonar-EZ2 Range Finder post shows how we connected the LV-MaxSonar-EZ2 Range Finder to our robot.

In the My first working robot, it’s alive – Part 2 post, I wrote a python module that defined the basic movements of robot like changing speeds, changing direction, going forward and stop.  In this post, the first thing we will do is to write a similar Javascript module that we can load with Node.js.  Here is the Javascript code for this module:

var b = require('bonescript')

var PIN_SPEED_RIGHT = "P8_13";
var PIN_SPEED_LEFT = "P8_19";
var PIN_DIR_LEFT = "P8_14";
var PIN_DIR_RIGHT = "P8_16";
var MAX_SPEED=1;
var MIN_SPEED=.25;
var CHANGE_RATE=.05;
var STOP_SPEED=0;
var FORWARD_DIR=1;
var REVERSE_DIR=0;

var current_speed_right = STOP_SPEED;
var current_speed_left = STOP_SPEED;
var current_dir_right = FORWARD_DIR;
var current_dir_left = FORWARD_DIR;

//initiate rover
function initRover() {
       b.pinMode(PIN_DIR_LEFT,b.OUTPUT);
       b.pinMode(PIN_DIR_RIGHT, b.OUTPUT);
      
}
exports.initRover = initRover;


//Utility rover to check if the speed is within range
function checkSpeed(speed) {
       if (speed < MIN_SPEED && speed != STOP_SPEED)
              speed = MIN_SPEED;
      
       if (speed > MAX_SPEED)
              speed = MAX_SPEED;
      
       return speed;
}

//Utility sleep
function sleep(milliseconds) {
       var currentTime = new Date().getTime();

  while (currentTime + milliseconds >= new Date().getTime()) {
  }
}
exports.sleep = sleep;

//Set the speed of the tracks
function setRightSpeed(speed) {
       var newSpeed = checkSpeed(speed);
       b.analogWrite(PIN_SPEED_RIGHT, newSpeed);
       current_speed_right = newSpeed;
}
exports.setRightSpeed = setRightSpeed;

function setLeftSpeed(speed) {
       var newSpeed = checkSpeed(speed);
       b.analogWrite(PIN_SPEED_LEFT, newSpeed);
       current_speed_left = newSpeed;
}
exports.setLeftSpeed = setLeftSpeed;

function setSpeed(speed) {
       setLeftSpeed(speed);
       setRightSpeed(speed);
}
exports.setSpeed = setSpeed;

//Increase speed
function increaseRightSpeed() {
       setRightSpeed(current_speed_right + CHANGE_RATE);
}
exports.increaseRightSpeed = increaseRightSpeed;

function increaseLeftSpeed() {
       setLeftSpeed(current_speed_left + CHANGE_RATE);
}
exports.increaseLeftSpeed = increaseLeftSpeed;

function increaseSpeed() {
      
       increaseLeftSpeed();
       increaseRightSpeed();
}
exports.increaseSpeed = increaseSpeed;

//Decrease Speed
function decreaseRightSpeed() {
       setRightSpeed(current_speed_right - CHANGE_RATE);
}
exports.decreaseRightSpeed = decreaseRightSpeed;

function decreaseLeftSpeed() {
       setLeftSpeed(current_speed_left - CHANGE_RATE);
}
exports.decreaseLeftSpeed = decreaseLeftSpeed;

function decreaseSpeed() {
       decreaseLeftSpeed();
       decreaseRightSpeed();
}
exports.decreaseSpeed = decreaseSpeed;

//set direction forward
function forwardRightDirection() {
       if (current_dir_right == REVERSE_DIR)
              allStop();
       b.digitalWrite(PIN_DIR_RIGHT, b.HIGH);
       current_dir_right = FORWARD_DIR;
}
exports.forwardRightDirection = forwardRightDirection;

function forwardLeftDirection() {
       if (current_dir_left == REVERSE_DIR)
              allStop();
       b.digitalWrite(PIN_DIR_LEFT, b.HIGH);
       current_dir_left = FORWARD_DIR;
}
exports.forwardLeftDirection = forwardLeftDirection;

function forwardDirection() {
       forwardLeftDirection();
       forwardRightDirection();
}
exports.forwardDirection = forwardDirection;

//set direction reverse
function reverseRightDirection() {
       if (current_dir_right == FORWARD_DIR)
              allStop();
       b.digitalWrite(PIN_DIR_RIGHT, b.LOW);
       current_dir_right = REVERSE_DIR;
}
exports.reverseRightDirection = reverseRightDirection;

function reverseLeftDirection() {
       if (current_dir_left == FORWARD_DIR)
              allStop();
       b.digitalWrite(PIN_DIR_LEFT, b.LOW);
       current_dir_left = REVERSE_DIR;
}
exports.reverseLeftDirection = reverseLeftDirection;

function reverseDirection() {
       reverseLeftDirection();
       reverseRightDirection();
}
exports.reverseDirection = reverseDirection;

//Stop rover
function stopLeft() {
       setLeftSpeed(STOP_SPEED);
}
exports.stopLeft = stopLeft;

function stopRight() {
       setRightSpeed(STOP_SPEED);
}
exports.stopRight = stopRight;

function allStop() {
       stopLeft();
       stopRight();
}
exports.allStop = allStop;

//Full speed
function fullSpeedLeft() {
       setLeftSpeed(MAX_SPEED);
}
exports.fullSpeedLeft = fullSpeedLeft;

function fullSpeedRight() {
       setRightSpeed(MAX_SPEED);
}
exports.fullSpeedRight = fullSpeedRight;

function fullSpeed() {
       fullSpeedLeft();
       fullSpeedRight();
}
exports.fullSpeed = fullSpeed;

//spin robot
function spinRoverLeft(speed) {
       allStop();
  forwardDirection();
  forwardRightDirection();
  reverseLeftDirection();
  setRightSpeed(speed);
  setLeftSpeed(speed);
}
exports.spinRoverLeft = spinRoverLeft;

function spinRoverRight(speed) {
       allStop();
  forwardDirection();
  forwardLeftDirection();
  reverseRightDirection();
  setLeftSpeed(speed);
  setRightSpeed(speed);
}
exports.spinRoverRight = spinRoverRight;

This module exposes several functions, these are:

stop_rover():  Stops the rover
check_speed(speed):  Verifies that the speed is within the acceptable ranges.  This function returns the speed that was passed in if it was within the acceptable range otherwise it returns the MAX_SPEED or MIN_SPEED depending on if the speed that was passed in was too high or too low.
sleep():  Pauses the execution of the script for a specified amount of time.

set_right_speed():  Sets the speed of the right track.
set_left_speed():  Sets the speed of the left track.
set_speed():  Sets the speed of both tracks.

increase_right_speed():  Increases the speed of the right track.
increase_left_speed():  Increases the speed of the left track.
increase_speed():  increases the speed of both tracks.

decrease_right_speed():  Decreases the speed of the right track.
decrease_left_speed():  Decrease the speed of the left track.
decrease_speed():  Decrease the speed of both tracks.

forward_right_dir():  Sets the direction of the right track to forward.
forward_left_dir():  Sets the direction of the left track to forward.
forward_dir():  Sets the direction of both tracks to forward.

reverse_right_dir():  Sets the direction of the right track to reverse.
reverse_left_dir():  Sets the direction of the left track to reverse.
reverse_dir():  Sets the direction of both tracks to reverse.

stop_left():  Stops the left track.
stop_right():  Stops the right track.
all_stop():  Stops both tracks.

full_speed_left():  Sets the left track to full speed.
full_speed_right():  Sets the right track to full speed.
all_full_speed():  Sets both tracks to full speed.

spin_right(speed):  Spins the robot in the right direction at the speed passed in.
spin_left(speed):  Spins the robot in the left direction at the speed passed in.

Now lets take a look at the code that will control our robot.  This has very basic and simple logic for our first prototype.  The robot moves forward until it is within 18 inches of an object.  Once it is within 18 inches of an object it continuous to turn right until it has over 18 inches of clearance.    Here is the code:

var b = require('bonescript');
var rover = require("./rover.js")

var ledPin = "P8_8";
var buttonPin = "P8_11";
var sensorPin = "P9_40";

var roverStateEnum = {
       INIT : "init",
       COMPLETE_STOP : "complete stop",
       STOPPED : "stopped",
       FORWARD : "moving forward",
       REVERSE : "moving reverse",
       SPIN_RIGHT : "spinning right",
       SPIN_LEFT : "spinning left"
};

var current_speed = 0;
var min_speed = .5;
var moving = roverStateEnum.COMPLETE_STOP;
var check_interval = 500;
var interval=0;
var detect_length = 18;

b.pinMode(ledPin, b.OUTPUT);
b.pinMode(buttonPin, b.INPUT);
b.attachInterrupt(buttonPin, true, b.FALLING, buttonChange);
b.digitalWrite(ledPin, b.HIGH);

function buttonChange(button) {
       console.log("Button Pressed");
       if (moving == roverStateEnum.COMPLETE_STOP || moving == roverStateEnum.STOPPED) {
              console.log("Forward");
              rover.initRover();
              rover.forwardDirection();
              current_speed = min_speed;
              rover.setSpeed(current_speed);
              if (interval == 0) {
                      console.log("setting interval");
                      setInterval(read,check_interval);
                     interval = 1;
              }
             
              moving = roverStateEnum.FORWARD;
              b.digitalWrite(ledPin, b.HIGH);
       } else {
              console.log("Stop");
              rover.allStop();
              moving = roverStateEnum.COMPLETE_STOP;
              b.digitalWrite(ledPin, b.LOW);
       }
      
}

function read() {
       b.analogRead(sensorPin,sensorStatus);
}

function sensorStatus(v) {
       var distanceInches;
       var analogVoltage = v.value*1.8;
       distanceInches = analogVoltage/0.002148;
       console.log("Object at " + parseFloat(distanceInches).toFixed(2) + " inches away");
      
       if (distanceInches < detect_length && moving != roverStateEnum.COMPLETE_STOP) {
              console.log("Stopping and spinning");
              rover.allStop();
              rover.spinRoverRight(min_speed);
              moving = roverStateEnum.SPIN_RIGHT;
              rover.sleep(500);
              rover.allStop();
              rover.sleep(500);
              moving = roverStateEnum.STOPPED;
       } else if (moving == roverStateEnum.STOPPED) {
              console.log("Going Forward");
              rover.forwardDirection();
              rover.setSpeed(current_speed);
              moving = roverStateEnum.FORWARD;
       }
}


We start off by importing our rover module and also the Bonescript module.  We then define the pins used for an LED, a button and the MaxSonar sensor.  We use the LED to show when the rover is running and the button is used to start and stop the rover.  I wired the LED and button exactly as I did in the Using Javascript with Bonescript to program the BeagleBone Black (http://myroboticadventure.blogspot.com/2014/06/using-javascript-with-bonescript-to.html) post.

We then define an enum that we will use to define what type of movement the robot is currently doing.   Next we define several variables, these are:
current_speed:  The current speed of the robot.
min_speed:  The minimum speed we want the robot to go
moving:  The current moving state of the robot
check_interval:  How often we want to check the MaxSonar sensor.  This is in milliseconds.
interval:  If 0, the robot is not checking the MaxSonar for distance, if 1 it is already checking.
detect_length:  Is the length, in inches, that will force the robot to turn.

We set the mode of the LED pin to OUTPUT which means we will be writing to the pin and we set the mode of the button pin to INPUT which means we will be reading from the pin.  We use the attachInterrupt function to call the buttonChange function every time the button is pushed.  Finally we turn on the LED, by calling the digitalWrite function, to let us know that the robot is ready to go.

The buttonChange function is called whenever the button is pressed.  If the current moving state of the robot is roverStateEnum.COMPLETE_STOP or roverStateEnum.STOPPED then we start the rover moving forward and also use the setInterval() function to begin checking the MaxSonar every half second.  If the current state is anything other than roverStateEnum.COMPLETE_STOP or roverStateEnum.STOPPED then we stop the rover.

The read function is called every half second to check the MaxSonar sensor for the current distance.  When the distance comes back from the MaxSonar sensor, it calls the sensorStatus() function.  In the sensorStatus() function, if there is an object closer than the distance defined by the detect_length variable (18 inches) and the current state of the robot is not roverStateEnum.COMPLETE_STOP, then we stop the robot and spin to the right for half a second.

This example is just the start of our autonomous robot and we currently have four more MaxSonar sensors on order so our robot can look at the world around it and decide where and how to turn.  Not sure if I want to use Javascript or Python to develop this in but will have to make the decision soon.