AzoftCase StudiesOur First Experience with Robotics: Making a Web-Controlled Robotic Arm

Our First Experience with Robotics: Making a Web-Controlled Robotic Arm

By Alexey Romanenko on September 25, 2013

As part of Programmers’ Day celebration this year, Azoft web developers decided to surprise our fellow Azoft employees with a competition. To try something new and unusual, we created an internet-controlled robotic arm.

This was our first experience with robotics and it turned out a success. The robotic arm competition was lots of fun for everyone involved, so we decided to share our experience and post this robotics tutorial to give you a fast start into building robots for your own geek parties.

What does the robotic arm do? 

Our robotic arm is controlled via a web interface: it responds to remote commands and performs simple tasks. The scheme was the following: everyone working at Azoft company received an invitation to a web page from which they could control the robot remotely, try to perform a task, and watch their efforts and results through a web camera.

The tasks could vary: from drawing lots in a lottery to grabbing objects and collecting them in a basket - it’s up to your imagination. In our case, the goal was to grab a small object, move it to a different location, and drop into a container. Whoever collects the most objects within a small time frame wins. Sounds simple, but in practice it wasn’t as easy as you may think.

Have you decided what your robot will be doing? Good, now let’s turn to the implementation.

You will need

  • microcontroller TI Stellaris Launchpad 
  • two servos (one for rotates, one for lifts) Hitec HS-322 
  • servo TowerPro SG90 for the hand

diy robotic arm 01 Our First Experience with Robotics:  Making a Web Controlled Robotic Arm

Let's get started

The process consists of several stages

  • 0. Material stage: Order servos and the microcontroller.
  • 1. Hardware / Building stage: Construct the robot.
  • 2. Software / Programming stage: Write and test the robot-side and server-side code.
  • 3. Test stage: Test before the party starts.

Stage 0. Gathering the materials

Getting hold of all the necessary parts can take quite some time, since your nearest shop might not carry all the items you need. That’s why I recommend finding and ordering all the materials on ebay.com or aliexpress.com ahead of time. 

Besides, it’s hard to find everything you need in one online store, so you’ll have to browse several websites. There are various online retailers out there, depending on where in the world you’re located. In our case, we ordered the parts on devicter.ru and hobbymarket.ru. 

Stage 1. Hardware

Once you've got all the necessary materials, you can start assembling the main device - the hand. We tried to build it from MDF at first but the prototype didn't survive the crash test. Our second try - from PVC - was a success.

diy robotic arm Our First Experience with Robotics:  Making a Web Controlled Robotic Arm

Stage 2. Software

Programming

The software part was done with Energia IDE. At first, we programmed the hand to be able to move to 5 degrees from the original location. Later, we decided that the hand should receive position values and move to that location immediately.

Testing

There are tons of instruments to track bugs and find out where and what has gone wrong. For example, you could use the log file. RubyOnRails would reflect the debugging info automatically. Besides, you could add your own comments into the log file right from the code through the Ruby logger class.

Server side

After we have tested both mechanics and electronics only one thing was left to be done - the server side. Since the stages before were pretty simple, I didn’t share any code samples (but feel free to contact me and ask for the code, if needed). But the server side was really tough, so I'll share our solution.

1. Authentication

First, a user is offered to authorize:

class HomeController < ApplicationController
  before_filter :need_auth

  def index
    define_mode
  end 
end

Then, we authorize the user: 

class UsersController < ApplicationController
  before_filter :check_request_method, :except => [ :sign_up, :logout ]

  def sign_up
    username = params[:username]
    password = params[:password]

    ldap_user = get_ldap_user(username, password)

    return do_respond(:wrong_credentials) if ldap_user.empty?

    user = User.get_by_names ldap_user[:first_name], ldap_user[:last_name]

    unless user.nil?
      authorize_user user
      return render_need_email if ldap_user[:email].nil? and user.email.nil?
      return do_respond(:choose_queue)
    end

    user = User.new
    user.email = ldap_user[:email] unless ldap_user[:email].nil?
    user.first_name = ldap_user[:first_name]
    user.last_name = ldap_user[:last_name]

    if user.save
      authorize_user user
      return render_need_email if user.email.nil?
      return do_respond :choose_queue
    else
      return do_respond :wrong_credentials
    end
  end

  def update_email
    if request.method == 'GET'
      return render_sign_up
    end

    if params[:email].empty?
      return render_need_email
    end

    email = params[:email]

    user = self.current_user

    return do_respond :wrong_credentials if user.nil?

    user.update_attribute('email', email)

    return do_respond :choose_queue
  end

  def logout
    session[:user] = nil

    respond_to do |f|
      f.html { redirect_to root_url }
    end
  end

  private
  def get_ldap_user(username, password)
     Net::LDAP.new(
        :host => 'example.com',
        :auth => { :method => :simple, :username => 'YOUR_LDAP_DOMAIN' + username, :password => password }
    )

    @ldap_user = {}

    if ldap.bind

      ldap_base = "cn=users,dc=test,dc=test,dc=com"
      ldap_filter = Net::LDAP::Filter.eq( "samaccountname", username )
      ldap.search(:base => ldap_base, :filter => ldap_filter, :return_result => true ) do |entry|
        @ldap_user[:first_name] = entry[:givenname][0] unless entry[:givenname].nil? and entry[:givenname][0].nil?
        @ldap_user[:last_name] = entry[:sn][0] unless entry[:sn].nil? and entry[:sn][0].nil?
        @ldap_user[:email] = entry[:mail][0] unless entry[:mail].nil? and entry[:mail][0].nil?
      end
    end

    @ldap_user
  end

  def check_request_method
    if request.method == 'GET'
      respond_to do |f|
        f.html { redirect_to get_out_and_come_in_correctly and return }
      end
    end
  end
end

2. Control access

This code is responsible for giving commands to the hand:

require 'fileutils'
class PlayController < ApplicationController
  #receive request from client (web-browser)
  def do_command
    # if current user can not play we send false response wich will be handled on client-side
    return false_json_response unless can_play?

    write_command(params)

    respond_to do |f|
      f.json { render :json => { :status => 'Ok' } } # it is not required to set anything in json block, you can pass just empty hash like this: {}
    end
  end

  # check if user can still play
  # return true or false (what to do in each of these cases is your own deal)
  def check_can_play
    respond_to do |f|
      f.json { render :json => can_play? }
    end
  end

  private
  # this method sends command to the hand
  def write_command(params)
    # defining which of serves should be moved
    serva_num = params[:serva_num]
    command = params[:command].rjust(3, '0')

    # hand is device actually; we are going to write into it like into file
    device = '/dev/ttyACM0'

    File.open(device, 'w') { |f| f.write(serva_num + command + "\n") }
  end
end

Next, the challenge: at a certain period of time only one user should be able to access the robot control function. The hardest part was to determine the period of time during which a next user could be granted access to the controls:

class ApplicationController < ActionController::Base
  protect_from_forgery

  def current_user
    return session[:user]
  end

  # check if current user signed in
  def signed_in?
    !self.current_user.nil?
  end

  # check if current user is admin
  def is_admin?
    signed_in? and current_user.role == User::ROLE_ADMIN
  end

  # check if it is time to play for current user
  def its_time?
    is_queued = false
    queues = UserQueue.all

    now = ((Time.now + Time.now.gmt_offset).utc.to_f * 1000).to_i

    queues.each do |queue|
      time_start = (queue.time_start.to_f * 1000).to_i
      time_end = (queue.time_end.to_f * 1000).to_i

      if !queue.user.nil? and queue.user.id == self.current_user.id and (time_start..time_end).include?(now)
        is_queued = true
        break
      end
    end

    is_queued
  end

  # check if current user still can play
  def can_play?
    return true if is_admin?
    return true if its_time?
    false
  end

  helper_method :current_user, :signed_in?, :is_admin?, :its_time?
end

Robot testing

Once the robotic arm was built and the code written, we moved on to testing. Ruby debugger allows us to stop the code execution anytime, fix the error, and then continue. From console you can check the domain, make changes and save them to a database.

diy robotic arm 04 Our First Experience with Robotics:  Making a Web Controlled Robotic Arm

Ready, Set, Go!

On September 13th (the day of our Programmers’ Day party) our robot faced the challenge of more than 40 runs. It broke down once, but was quickly fixed within a couple of minutes, so that small accident didn't spoil the fun. We wish you an easy start in your robotics adventures. Good luck and have fun!

VN:F [1.9.22_1171]
Rating: 5.0/5 (5 votes cast)
VN:F [1.9.22_1171]
Rating: +5 (from 5 votes)
Our First Experience with Robotics: Making a Web-Controlled Robotic Arm, 5.0 out of 5 based on 5 ratings

Content created by Alex Romanenko