Here we are going to make a 6 DOF or degrees of freedom arm robot. We need to understand first on how degrees of freedom is calculated.
Six degrees of freedom (6DoF) is also referred as the movement of a rigid body in three-dimensional space. It can commonly be attributed to yaw as the normal axis, pitch as transverse axis, and roll as the longitudinal axis.
Schematic Diagram

Code:
Arduino
First you need to Setup the Name, Pin, and the Baudrate of your Bluetooth Module
#include <SoftwareSerial.h> SoftwareSerial hc06(2,3); //Rx, Tx void setup() { // put your setup code here, to run once: Serial.begin(9600); hc06.begin(9600); Serial.println("Enter AT commands:"); } void loop() { // put your main code here, to run repeatedly: if (hc06.available()){ Serial.write(hc06.read()); } //Write from Serial Monitor to HC06 if (Serial.available()){ hc06.write(Serial.read()); } //need to set up to more information check this link https://www.aranacorp.com/en/arduino-and-bluetooth-module-hc-06/ //AT COMMAND //AT+NAMEROBOTARMBLE = to set your bluetooth name to ROBOTARMBLE //AT+PIN0000 = to set your pin to 0000 //AT+BAUD8 = baud to 115200 = to set the baudrate of your bluetooth to 115200
}Time to upload your code to your Arduino Uno.
#include <Servo.h>
#include <SoftwareSerial.h> SoftwareSerial hc06(2,3); //Rx, Tx Servo myservo;
Servo myservo1;
Servo myservo2;
Servo myservo3;
Servo myservo4; String cmd="";
String serId = "";
String serVal = ""; int serval0 = 90;
int serval1 = 90;
int serval2 = 90;
int serval3 = 90;
int serval4 = 40; void setup() { Serial.begin(115200); hc06.begin(115200); myservo.attach(9); myservo1.attach(5); myservo2.attach(6); myservo3.attach(10); myservo4.attach(11); myservo.write(90); myservo1.write(90); myservo2.write(90); myservo3.write(90); myservo4.write(40);
} void loop() { while(hc06.available()>0){ cmd += (char)hc06.read(); cmd.trim(); } char *serIdChar = strtok(&cmd[0], ","); char *serValChar = strtok(NULL, ","); serId = serIdChar; serVal = serValChar; if(cmd!= ""){ Serial.println(cmd); Serial.println(serId); Serial.println(serVal); if(serId == "0"){ if(serval0 > serVal.toInt()){ for(int iter = serval0; iter > serVal.toInt()+1; iter--){ myservo.write(iter); Serial.println(iter); delay(10); } }else{ for(int iter = serval0; iter < serVal.toInt(); iter++){ myservo.write(iter); Serial.println(iter); delay(10); } } serval0 = serVal.toInt(); Serial.println(serval0); }else if(serId == "1"){ if(serval1 > serVal.toInt()){ for(int iter = serval1; iter > serVal.toInt()+1; iter--){ myservo1.write(iter); Serial.println(iter); delay(10); } }else{ for(int iter = serval1; iter < serVal.toInt(); iter++){ myservo1.write(iter); Serial.println(iter); delay(10); } } serval1 = serVal.toInt(); Serial.println(serval1); }else if(serId == "2"){ if(serval2 > serVal.toInt()){ for(int iter = serval2; iter > serVal.toInt()+1; iter--){ myservo2.write(iter); Serial.println(iter); delay(10); } }else{ for(int iter = serval2; iter < serVal.toInt(); iter++){ myservo2.write(iter); Serial.println(iter); delay(10); } } serval2 = serVal.toInt(); Serial.println(serval2); }else if(serId == "3"){ myservo3.write(serVal.toInt()); }else if(serId == "4"){ int serArm = map(serVal.toInt(),0,90,40,90); myservo4.write(serArm); } cmd=""; serId=""; serVal=""; } }Android Studio
we do the app using Android Studio. first you need to create a Basic Activity in Android Studio then add the following code.
Layout: activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <android.support.design.widget.AppBarLayout android:layout_height="wrap_content" android:layout_width="match_parent" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay"/> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_main"/> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" android:src="@drawable/ic_bluetooth_black_24dp" app:backgroundTint="@color/colorBlack"/> </android.support.design.widget.CoordinatorLayout>Layout: content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:showIn="@layout/activity_main" tools:context=".MainActivity" android:background="@drawable/bg"> <!--<com.ramotion.fluidslider.FluidSlider--> <!--android:id="@+id/fluidSlider"--> <!--android:layout_width="match_parent"--> <!--android:layout_height="wrap_content"--> <!--android:layout_marginEnd="16dp"--> <!--android:layout_marginStart="16dp"--> <!--android:elevation="2dp"--> <!--app:layout_constraintBottom_toBottomOf="parent"--> <!--app:layout_constraintEnd_toEndOf="parent"--> <!--app:layout_constraintStart_toStartOf="parent"--> <!--app:layout_constraintTop_toTopOf="parent"--> <!--app:size="small"--> <!--app:duration="@android:integer/config_mediumAnimTime"--> <!--tools:targetApi="lollipop" />--> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <SeekBar android:layout_width="match_parent" android:layout_height="wrap_content" android:progressDrawable="@drawable/custom_seekbar_progress" android:thumb="@drawable/custom_seekbar_thumb" android:max="90" android:progress="0" android:padding="20dp" android:id="@+id/seekBararm"/> <SeekBar android:layout_width="match_parent" android:layout_height="wrap_content" android:progressDrawable="@drawable/custom_seekbar_progress" android:thumb="@drawable/custom_seekbar_thumb" android:max="180" android:progress="90" android:padding="20dp" android:id="@+id/seekBarancle"/> <SeekBar android:layout_width="match_parent" android:layout_height="wrap_content" android:progressDrawable="@drawable/custom_seekbar_progress" android:thumb="@drawable/custom_seekbar_thumb" android:max="180" android:progress="90" android:padding="20dp" android:id="@+id/seekBarelbow"/> <SeekBar android:layout_width="match_parent" android:layout_height="wrap_content" android:progressDrawable="@drawable/custom_seekbar_progress" android:thumb="@drawable/custom_seekbar_thumb" android:max="180" android:progress="90" android:padding="20dp" android:id="@+id/seekBarafterBase"/> <SeekBar android:layout_width="match_parent" android:layout_height="wrap_content" android:progressDrawable="@drawable/custom_seekbar_progress" android:thumb="@drawable/custom_seekbar_thumb" android:max="180" android:progress="90" android:padding="20dp" android:id="@+id/seekBarbase"/> </LinearLayout> </android.support.constraint.ConstraintLayout>create a custom_seekbar_progress.xml to your drawable
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background" android:gravity="center_vertical|fill_horizontal"> <shape android:shape="rectangle" android:tint="#000000"> <corners android:radius="8dp"/> <size android:height="30dp" /> <solid android:color="#000000" /> </shape> </item> <item android:id="@android:id/progress" android:gravity="center_vertical|fill_horizontal"> <scale android:scaleWidth="100%"> <selector> <item android:state_enabled="false" android:drawable="@android:color/transparent" /> <item> <shape android:shape="rectangle" android:tint="#000000"> <corners android:radius="8dp"/> <size android:height="30dp" /> <solid android:color="#000000" /> </shape> </item> </selector> </scale> </item>
</layer-list>also create custom_seekbar_thumb.xml to your drawable
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" android:thickness="4dp" android:useLevel="false" android:tint="#7ddcff"> <solid android:color="#7ddcff" /> <size android:width="32dp" android:height="32dp" />
</shape>also create ic_bluetooth_black_24dp.xml to your drawable
<vector android:height="24dp" android:tint="#ffffff" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> <path android:fillColor="#FF000000" android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
</vector>
and last create ic_bluetooth_connected_black_24dp.xml to your drawable
<vector android:height="24dp" android:tint="#0F03FF" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> <path android:fillColor="#FF000000" android:pathData="M7,12l-2,-2 -2,2 2,2 2,-2zM17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88zM19,10l-2,2 2,2 2,-2 -2,-2z"/>
</vector>
Add this code to values > colors.xml
<color name="colorBlack">#000000</color>java: MainActivity.java
package com.project.robotarm; import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.SeekBar;
import android.widget.Toast; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID; public class MainActivity extends AppCompatActivity { private SeekBar seekBarBase; private SeekBar seekBar1; private SeekBar seekBar2; private SeekBar seekBar3; private SeekBar seekBar4; private String dataToSend; private String TAG = "DEBUG"; private BluetoothAdapter myBluetooth = null; private static String address = "98:D3:31:F6:1B:DB"; //address of your bluetooth module. to check the address of your bluetooth module first pair it with your android phone then check the address of the bluetooth module private ProgressDialog progress; BluetoothSocket btSocket = null; private boolean isBtConnected = false; private boolean isConnected = false; static final UUID myUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); private OutputStream outStream = null; private InputStream inStream = null; Handler handler = new Handler(); byte delimiter = 10; boolean stopWorker = false; int readBufferPosition = 0; byte[] readBuffer = new byte[1024]; FloatingActionButton fab; @Override protected void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState ); setContentView( R.layout.activity_main ); Toolbar toolbar = findViewById( R.id.toolbar ); setSupportActionBar( toolbar ); seekBarBase = findViewById( R.id.seekBarbase ); seekBar1 = findViewById( R.id.seekBarafterBase ); seekBar2 = findViewById( R.id.seekBarelbow ); seekBar3 = findViewById( R.id.seekBarancle ); seekBar4 = findViewById( R.id.seekBararm ); fab = findViewById( R.id.fab ); fab.setOnClickListener( new View.OnClickListener() { @Override public void onClick( View view ) { myBluetooth = BluetoothAdapter.getDefaultAdapter(); if(myBluetooth == null) { //Show a mensag. that thedevice has no bluetooth adapter Toast.makeText(getApplicationContext(), "Bluetooth Device Not Available", Toast.LENGTH_LONG).show(); //finish apk finish(); }else { if (myBluetooth.isEnabled()) { Log.d( TAG, "Bluetooth is enable" ); if(isConnected){ Disconnect(); }else{ ConnectBT connectBT = new ConnectBT(); connectBT.execute( ); } } else { //Ask to the user turn the bluetooth on Intent turnBTon = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(turnBTon,1); } } } } ); seekBar4.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() { int progress= 0; @Override public void onProgressChanged( SeekBar seekBar, int i, boolean b ) { progress=i; } @Override public void onStartTrackingTouch( SeekBar seekBar ) { } @Override public void onStopTrackingTouch( SeekBar seekBar ) { dataToSend = "4,"+Integer.toString( progress ); writeData(dataToSend); } } ); seekBar3.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() { int progress = 0; @Override public void onProgressChanged( SeekBar seekBar, int i, boolean b ) { progress=i; } @Override public void onStartTrackingTouch( SeekBar seekBar ) { } @Override public void onStopTrackingTouch( SeekBar seekBar ) { dataToSend = "3,"+Integer.toString( progress ); writeData(dataToSend); } } ); seekBar2.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() { int progress = 0; @Override public void onProgressChanged( SeekBar seekBar, int i, boolean b ) { progress = i; } @Override public void onStartTrackingTouch( SeekBar seekBar ) { } @Override public void onStopTrackingTouch( SeekBar seekBar ) { dataToSend = "2,"+Integer.toString( progress ); writeData(dataToSend); } } ); seekBar1.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() { int progress = 0; @Override public void onProgressChanged( SeekBar seekBar, int i, boolean b ) { progress = i; } @Override public void onStartTrackingTouch( SeekBar seekBar ) { } @Override public void onStopTrackingTouch( SeekBar seekBar ) { dataToSend = "1,"+Integer.toString( progress ); writeData(dataToSend); } } ); seekBarBase.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() { int progress = 0; @Override public void onProgressChanged( SeekBar seekBar, int i, boolean b ) { progress = i; msg( "Changing seekbar's progress" ); } @Override public void onStartTrackingTouch( SeekBar seekBar ) { msg( "Started tracking seekbar" ); } @Override public void onStopTrackingTouch( SeekBar seekBar ) { dataToSend = "0,"+Integer.toString( progress ); writeData(dataToSend); msg( "Stopped tracking seekbar" ); } } ); } private void writeData(String data) { try { outStream = btSocket.getOutputStream(); } catch (IOException e) { Log.d(TAG, "Bug BEFORE Sending stuff", e); } String message = data; byte[] msgBuffer = message.getBytes(); try { outStream.write(msgBuffer); } catch (IOException e) { Log.d(TAG, "Bug while sending stuff", e); } } private void Disconnect() { if (btSocket!=null) //If the btSocket is busy { try { msg( "Bluetooth Disconnect" ); btSocket.close(); //close connection btSocket = null; fab.setImageResource( R.drawable.ic_bluetooth_black_24dp ); isConnected = false; } catch (IOException e) { msg("Error");} }
// finish(); //return to the first layout } private void msg(String s) { Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show(); } @Override public boolean onCreateOptionsMenu( Menu menu ) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate( R.menu.menu_main, menu ); return true; } @Override public boolean onOptionsItemSelected( MenuItem item ) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if( id == R.id.action_settings ) { return true; } return super.onOptionsItemSelected( item ); } private class ConnectBT extends AsyncTask<Void, Void, Void> // UI thread { private boolean ConnectSuccess = true; //if it's here, it's almost connected @Override protected void onPreExecute() { progress = ProgressDialog.show(MainActivity.this, "Connecting...", "Please wait!!!"); //show a progress dialog } @Override protected Void doInBackground(Void... devices) //while the progress dialog is shown, the connection is done in background { try { if (btSocket == null ||!isBtConnected) { myBluetooth = BluetoothAdapter.getDefaultAdapter();//get the mobile bluetooth device BluetoothDevice device = myBluetooth.getRemoteDevice(address);//connects to the device's address and checks if it's available btSocket = device.createInsecureRfcommSocketToServiceRecord(myUUID);//create a RFCOMM (SPP) connection BluetoothAdapter.getDefaultAdapter().cancelDiscovery(); btSocket.connect();//start connection } } catch ( IOException e) { ConnectSuccess = false;//if the try failed, you can check the exception here } return null; } @Override protected void onPostExecute(Void result) //after the doInBackground, it checks if everything went fine { super.onPostExecute(result); if (!ConnectSuccess) { msg("Connection Failed. Check the address of your BLE if it is"+address+", if not the same contact the developer. nOr Is it a SPP Bluetooth? Try again. ");
// finish(); } else { msg("Connected."); isBtConnected = true; beginListenForData(); fab.setImageResource( R.drawable.ic_bluetooth_connected_black_24dp ); isConnected = true; } progress.dismiss(); } } public void beginListenForData() { try { inStream = btSocket.getInputStream(); } catch (IOException e) { } Thread workerThread = new Thread(new Runnable() { public void run() { while(!Thread.currentThread().isInterrupted() &&!stopWorker) { try { int bytesAvailable = inStream.available(); if(bytesAvailable > 0) { byte[] packetBytes = new byte[bytesAvailable]; inStream.read(packetBytes); for(int i=0;i<bytesAvailable;i++) { byte b = packetBytes[i]; char ch = (char)b; Log.i("DEBUG", Character.toString( ch )); } } } catch (IOException ex) { stopWorker = true; Log.d( "DEBUG", "WORKERS: Stop WORKERS TRUE" ); } } } }); workerThread.start(); } }
Manifests: AndroidManifest.xml
Add this 2 permission to your Manifest:
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>Frequently Asked Questions
[faq] [faq_q]What does 6DOF mean for a robot arm?[/faq_q] [faq_a]6 degrees of freedom: six independent axes of motion. A 6DOF arm can position its end effector at any X, Y, Z point AND orient it at any angle, which matches the freedom of a human arm.[/faq_a][faq_q]What servos work for a 6DOF arm?[/faq_q] [faq_a]MG996R or DS3218 servos for the heavier base, shoulder, and elbow joints. SG90 micro servos for the wrist and gripper. Total cost typically $30-60 for the servo set.[/faq_a][faq_q]Do I need inverse kinematics for a 6DOF arm?[/faq_q] [faq_a]Not strictly. You can move each joint manually with a joystick or sliders. For point-to-point movement (like pick up object at X,Y,Z), inverse kinematics calculates the angles for you.[/faq_a][faq_q]What is the payload capacity of a 6DOF Arduino arm?[/faq_q] [faq_a]With MG996R servos, expect 100-200 g at full extension. Larger servos (DS3218 with 20+ kg/cm torque) handle 300-500 g. The arm geometry matters as much as servo torque.[/faq_a][faq_q]Can I 3D print the arm parts?[/faq_q] [faq_a]Yes, that is the typical approach. PLA works for hobby/light loads, PETG or ABS for arms that flex more. STL files are widely shared on Thingiverse for arms in this size class.[/faq_a][/faq]Frequently Asked Questions
What does this 6DOF Robot Arm tutorial cover?
Here we are going to make a 6 DOF or degrees of freedom arm robot.
What Python or library version is required for the 6DOF Robot Arm script?
Python 3.9+ on a Raspberry Pi (default on Bookworm) or MicroPython 1.20+ on Pico/ESP32. Install dependencies via pip3 install, or via mip on MicroPython.
How do I debug the 6DOF Robot Arm script when nothing prints?
Add print() at every branch. On MicroPython, watch the REPL via Thonny's serial. On a Pi, run the script with `python3 -u script.py` to disable output buffering.
