MeArm 0.4 with bluetooth control

In the previous post I discussed the MeArm 0.4 and how it works and some modifications.  One of the things I really wanted to be able to do with it was control it remotely.  My intention is to mount the arm onto my bluetooth rover chassis and then control the arm and the robot using an android application on my phone.  If you didn’t see the previous post it is here:

MeArm!

To that end I purchased a cheap (but functional) serial to Bluetooth radio module from Hobby Components.  It cost £6.49 which I think is a reasonable price.  They are available from most good vendors and if you are really stuck on ebay…

HC-05 Master Slave Bluetooth Module

I had already designed a port on the PCB to accept the module so this was always my intention…

Next I looked on Google Play for suitable Bluetooth robot control applications.  There are several to choose from, it appears everyone wants to be able to do this!

Arduino Bluetooth Joystick by Felipe Porge – This is great app and it is so simple to use.  I used this app originally for controlling the Bluetooth rover.  My only reason for not using it this time is I need more buttons!

Arduino Total Control by Juan Luis Gonzales Bello – This is a great app and a very polished offering based upon the demonstrations provided.  I found it very hard to get to grips with.  It wasn’t clear how to connect the buttons to the control aspects and where the user actually obtained the images uploading as buttons for controlling the device.  There are examples provided however and the documentation needs improving. The facility to make an android phone talk and bleep is awesome as is the accelerometer servo control.

Remote XY by Shemanuev Evgeny – This is very similar to Arduino Total Control and is a good application.  It is much easier to use in my opinion.  All that is needed is to go to the associated website and drag and drop some control items to make up a graphical user interface.  The control code is then generated for use.

I decided after trying several of the apps to use Remote XY.  I was torn between using Arduino Total Control or Remote XY but remoteXY won because I found it easier to use.  I will certainly be keeping up with Arduino Total Control because I think it is awesome and once the documentation (and my programming skills) improve I will be using it.

All I did was download the app from Google play and install it on my mobile phone which is a Galaxy Note Edge (*very cool handset*)

I then went to the remote XY website and built a graphical user interface for the MeArm 0.4 combined with the rover chassis

http://remotexy.com/

I needed controls for the following things:

  • A joystick control to ensure the servo motors controlling the forward back left right motion of the robot.
  • A pair of buttons to control the waist movement of the meArm.
  • A pair of buttons to control the shoulder movement of the meArm.
  • A pair of buttons to control the elbow movement of the meArm.
  • A pair of buttons to control the gripper opening and closing.  
  • A stop button to stop all movement
  • A start button to restart all movement

Here is what I implemented in five minutes:

I then saved my design and clicked on the ‘get source code’ button.  The website then provided a window asking me to select which type of serial control I intended to use; I chose Arduino Serial as I am using the arduino mega and I have already laid out the control PCB to use the Serial 2 port.

I then downloaded the project and copied it with the remote XY.h and remote XY.cpp files to my arduino sketchbook folder.

I then loaded it up in the arduino ide.  It is here that I had to start splicing my original rover control code and the code that I had written for the meArm.  It didn’t take very long.  Basically what was needed was to call the movement functions whenever a button pressed action is detected via Bluetooth. Here is the code:

MeArm Bluetooth Arduino Code

/* Alex's MeArm code Serial Bluetooth Control Code
12/04/2015 (c)

This code controls a meArm 0.4 via the
an Android phone and
using the remoteXY app

Enjoy!

Base servo connected to pin 9
shoulder servo connected to pin 8
Wrist servo connected to pin 7
Gripper servo connected to pin 6

Rover Servo Left connected to pin 5
Rover Servo Right Connected to pin 3

*/

#include "remotexy.h" // Include the header file for remote XY
#include <Servo.h> // Include the servo control library
#include <avr/wdt.h> // Include the watchdog library

Servo left, right, waist, shoulder, elbow, gripper ; // create servo objects to control servos

int waistPos = 74; // variable to store the base servo position
// Set to 10 to prevent movement at start

int shoulderPos = 64; // variable to store the shoulder servo position
// Set to 50 to prevent movement at start

int elbowPos = 98; // variable to store the elbow servo position
// Set to 70 to prevent movement at start

int gripperPos = 0; // variable to store the gripper servo position
// Set to 0 to prevent movement at start

int leftPos = 89; // variable to store the left servo position
// set to 89 to prevent movement at start

int rightPos = 89; // variable to store the right servo position
// set to 89 to prevent movement at start

void setup()
{
Serial.begin(9600);

RemoteXY_Init (); // Initialise the remote XY code - set screen and buttons etc

left.attach(5); // attaches the left drive servo on pin 5 to the servo object
right.attach(3); // attaches the right drive servo on pin 4 to the servo object
waist.attach(9); // attaches the waist servo on pin 9 to the servo object
shoulder.attach(8); // attaches the uppoer shoulder servo on pin 9 to the servo object
elbow.attach(7); // attaches the lower shoulder servo on pin 7 to the servo object
gripper.attach(6); // attaches the gripper servo on pin 6 to the servo object

left.write(leftPos); // Halt left servo
right.write(rightPos); // Halt right servo
waist.write(waistPos); // set intial waist position
shoulder.write(shoulderPos); // set initial shoulder position
elbow.write(elbowPos); // Set inital elbow position
gripper.write(gripperPos); // Ensure gripper is open!
}

void loop()
{
RemoteXY_Handler (); //read user data from mobile phone

if (RemoteXY.roverForwards == 1) // move rover forwards button pressed
{
moveForward();
}

if (RemoteXY.roverBackwards == 1) // move rover backwards button pressed
{
moveBackward();
}

if (RemoteXY.roverLeft == 1) // move rover left button pressed
{
leftTurn();
}

if (RemoteXY.roverRight == 1) // move rover right button pressed
{
rightTurn();
}

if (RemoteXY.roverStop == 1) // stop rover button pressed
{
roverStop();
}

if (RemoteXY.waistAntiClockwise == 1) // move meArm waist antiClockwise button pressed
{
moveWaistAntiClockwise();
}

if (RemoteXY.waistClockwise == 1) // move meArm waist clockwise button pressed
{
moveWaistClockwise();
}

if (RemoteXY.shoulderExtend == 1) // extend meArm shoulder button pressed
{
shoulderExtend();
}

if (RemoteXY.shoulderRetract == 1) // retract meArm shoulder button pressed
{
shoulderRetract();
}

if (RemoteXY.elbowUp == 1) // extend meArm elbow button pressed
{
elbowUp();
}

if (RemoteXY.elbowDown == 1) // retract meArm elbow button pressed
{
elbowDown();
}

if (RemoteXY.gripperOpen == 1) // open meArm gripper button pressed
{
openGripper();
}

if (RemoteXY.gripperClose == 1) // close meArm gripper button pressed
{
closeGripper();
}

if (RemoteXY.stopAllMovement == 1) // stop all movement button pressed
{
stopMoving();
}

if (RemoteXY.startAllMovement == 1) // start all movement button pressed
{
startMoving();
}

if (RemoteXY.resetArduino == 1) // reset arduino button pressed
{
resetArduino();
}
}

void moveForward() // move the rover forwards
{
left.attach(5); // attaches the left drive servo on pin 5 to the servo object
right.attach(3); // attaches the right drive servo on pin 4 to the servo object

//set left servo going forward
leftPos = 179;
left.write(leftPos);

//set Right servo going forward
rightPos = 0;
right.write(rightPos);

Serial2.flush();
Serial.flush();

delay(15);
}

void moveBackward() // move the rover backwards
{
left.attach(5); // attaches the left drive servo on pin 5 to the servo object
right.attach(3); // attaches the right drive servo on pin 4 to the servo object

//set left servo going back
leftPos = 0;
left.write(leftPos);

//set light servo going back
rightPos = 179;
right.write(rightPos);

Serial2.flush();
Serial.flush();

delay(15);
}

void leftTurn() // turn rover left
{
left.attach(5); // attaches the left drive servo on pin 5 to the servo object
right.attach(3); // attaches the right drive servo on pin 4 to the servo object

//set left servo going left
leftPos = 0;
left.write(leftPos);

//set Right servo going left
rightPos = 0;
right.write(rightPos);

Serial2.flush();
Serial.flush();

delay(15);
}

void rightTurn() //turn rover right
{
left.attach(5); // attaches the left drive servo on pin 5 to the servo object
right.attach(3); // attaches the right drive servo on pin 4 to the servo object

//set left servo going right
leftPos = 179;
left.write(leftPos);

//set Right servo going right
rightPos = 179;
right.write(rightPos);

Serial2.flush();
Serial.flush();

delay(15);
}

void roverStop() // halt the rover
{
Serial.print("Robot Halted");
Serial.println();

//Halt left servo
leftPos = 89;
left.write(leftPos);

//Halt right servo
rightPos = 89;
right.write(rightPos);

left.detach();
right.detach();

Serial2.flush();
Serial.flush();

delay(15);
}

void moveWaistAntiClockwise() // move waist anti clockwise
{

Serial.print("Moving waist Anti clockwise ");
Serial.println();
Serial.print("Waist Position Value: ");
Serial.print(waistPos);
Serial.println();

if (waistPos >= 0 && waistPos != 179)
{
waistPos++;
waist.write(waistPos);
delay(15);
}

if (waistPos == 179)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial2.flush();
Serial.flush();

delay(15);
}

void moveWaistClockwise() // move waist clockwise
{

Serial.print("Moving base clockwise ");
Serial.println();
Serial.print("waistPos Value: ");
Serial.print(waistPos);
Serial.println();

if (waistPos <= 179 && waistPos != 0)
{
waistPos--;
waist.write(waistPos);
delay(15);
}

if (waistPos == 0)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial2.flush();
Serial.flush();

delay(15);
}

void shoulderExtend() //extend shoulder section
{

Serial.print("Extending from shoulder");
Serial.println();

Serial.print("shoulderPos Value: ");
Serial.print(shoulderPos);
Serial.println();

if (shoulderPos >= 32 && shoulderPos != 179)
{
shoulderPos++;
shoulder.write(shoulderPos);
delay(15);
}

if (shoulderPos == 179)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial2.flush();
Serial.flush();

delay(15);
}

void shoulderRetract() // retract shoulder section
{

Serial.print("Retracting from shoulder");
Serial.println();

Serial.print("shoulderPos Value: ");
Serial.print(shoulderPos);
Serial.println();

if (shoulderPos <= 179 && shoulderPos != 32)
{
shoulderPos--;
shoulder.write(shoulderPos);
delay(15);
}

if (shoulderPos == 32)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial2.flush();
Serial.flush();

delay(15);
}

void elbowUp() // move elbow up
{

Serial.print("Extending elbow ");
Serial.println();

Serial.print("elbowPos Value: ");
Serial.print(elbowPos);
Serial.println();

if (elbowPos >= 45 && elbowPos != 147)
{
elbowPos++;
elbow.write(elbowPos);
delay(15);
}

if (elbowPos == 147)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial2.flush();
Serial.flush();

delay(15);
}

void elbowDown() // move elbow down
{

Serial.print("Retracting elbow");
Serial.println();

Serial.print("elbowPos Value: ");
Serial.print(elbowPos);
Serial.println();

if (elbowPos <= 147 && elbowPos != 45)
{
elbowPos--;
elbow.write(elbowPos);
delay(15);
}

if (elbowPos == 45)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial2.flush();
Serial.flush();

delay(15);
}

void closeGripper() // close gripper
{

Serial.print("Closing Gripper Jaws ");
Serial.println();

Serial.print("gripperPos Value: ");
Serial.print(gripperPos);
Serial.println();

if (gripperPos >= 0 && gripperPos != 40)
{
gripperPos++;
gripper.write(gripperPos);
delay(15);
}

if (gripperPos == 40)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial2.flush();
Serial.flush();

delay(15);
}

void openGripper() // open gripper
{

Serial.print("Opening Gripper Jaws ");
Serial.println();

Serial.print("gripperPos Value: ");
Serial.print(gripperPos);
Serial.print(gripperPos);
Serial.println();

if (gripperPos <= 40 && gripperPos != 0)
{
gripperPos--;
gripper.write(gripperPos);
delay(15);
}

if (gripperPos == 0)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial.flush();

}

void startMoving() // start robot moving again
{

Serial.print("Start All Movement ");
Serial.println();

left.attach(5);
right.attach(3);
waist.attach(9);
shoulder.attach(8);
elbow.attach(7);
gripper.attach(6);

Serial2.flush();
Serial.flush();

delay(15);

delay(15);

}


void stopMoving() // stop robot moving completely
{

Serial.print("Stop All Movement ");
Serial.println();

left.detach();
right.detach();
waist.detach();
shoulder.detach();
elbow.detach();
gripper.detach();

Serial2.flush();
Serial.flush();

delay(15);

}

void resetArduino() // reset the arduino remotely
{
wdt_enable(WDTO_15MS);
while (1)
{

}  

Here is the remotexy.cpp:

#include "remotexy.h" 
#include <Arduino.h>

#define REMOTEXY_CONF_LENGTH 271
#define REMOTEXY_BUFFER_LENGTH 17
#define REMOTEXY_VALUES_LENGTH 16
#define REMOTEXY_OUTPUT_INDEX 16
#define REMOTEXY_TIMOUT 300
#define REMOTEXY_TIMOUT_DISCONNECTED 2000

typedef struct {
unsigned int count;
unsigned int index;
unsigned char command;
unsigned char crc;
unsigned long timeout;
} RemoteXY_State_TypeDef;

RemoteXY_State_TypeDef RemoteXY_State;
RemoteXY_TypeDef RemoteXY;
unsigned char RemoteXY_buffer[REMOTEXY_BUFFER_LENGTH];

unsigned char RemoteXY_conf[REMOTEXY_CONF_LENGTH] =
{ 3,5,1,0,14,25,12,12,6,70
,119,100,0,1,0,14,51,12,12,6
,66,107,119,100,0,1,0,0,38,12
,12,6,76,70,84,0,1,0,28,38
,12,12,6,82,72,84,0,1,0,5
,9,12,12,1,87,45,0,1,0,20
,9,12,12,1,87,43,0,1,0,41
,25,12,12,4,83,45,0,1,0,41
,10,12,12,4,83,43,0,1,0,62
,10,12,12,5,69,43,0,1,0,62
,25,12,12,5,69,45,0,1,0,82
,10,12,12,2,71,43,0,1,0,82
,25,12,12,2,71,45,0,1,0,48
,48,12,12,1,88,0,1,0,66,48
,12,12,4,88,0,1,0,14,38,12
,12,1,88,0,1,0,83,48,12,12
,0,88,0,129,0,12,3,13,5,9
,87,97,105,115,116,0,129,0,35,3
,22,5,9,83,104,111,117,108,100,101
,114,0,129,0,60,3,15,5,9,69
,108,98,111,119,0,129,0,79,3,18
,5,9,71,114,105,112,112,101,114,0
,129,0,2,29,11,4,9,82,111,118
,101,114,0,129,0,48,40,13,6,9
,83,116,111,112,0,129,0,65,40,14
,6,9,83,116,97,114,116,0,129,0
,82,40,16,6,9,82,101,115,101,116
,0 };


void RemoteXY_SerialInit (void) {
REMOTEXY_SERIAL.begin(9600);
}
#define RemoteXY_SerialAvailable() REMOTEXY_SERIAL.available()
#define RemoteXY_SerialWrite(b) REMOTEXY_SERIAL.write (b)
#define RemoteXY_SerialRead() REMOTEXY_SERIAL.read()

void RemoteXY_Init (void) {
unsigned char i;
unsigned char* p = (unsigned char*)&RemoteXY;
for (i=0;i<REMOTEXY_VALUES_LENGTH;i++) *p++=0;
RemoteXY_State.index = 0;
RemoteXY_State.crc = 0;
RemoteXY_SerialInit ();
RemoteXY.connect_flag=0;
}

void RemoteXY_WriteByte (unsigned char c, unsigned char * crc) {
RemoteXY_SerialWrite (c);
*crc-=c;
}

void RemoteXY_Send (unsigned char *p, unsigned int len) {
unsigned char c;
unsigned char crc = 0;
unsigned int i = len+4;

RemoteXY_WriteByte (i & 0xff, &crc);
RemoteXY_WriteByte ((i & 0xff00)>>8, &crc);
RemoteXY_WriteByte (RemoteXY_State.command, &crc);
while (len--) {
RemoteXY_WriteByte (*(p++), &crc);
}
RemoteXY_SerialWrite (crc);
}

void RemoteXY_Receive (unsigned char *p, unsigned int len) {
unsigned char *pi = RemoteXY_buffer;
while (len--) {
*p++=*pi++;
}
}

void RemoteXY_Handler (void) {
unsigned char c;
unsigned long tim;
while (RemoteXY_SerialAvailable() > 0) {
c = RemoteXY_SerialRead ();
RemoteXY_State.crc+=c;
switch (RemoteXY_State.index) {
case 0:
RemoteXY_State.count=c;
break;
case 1:
RemoteXY_State.count+=c<<8;
break;
case 2:
RemoteXY_State.command=c;
break;
default:
if (RemoteXY_State.index-3<REMOTEXY_BUFFER_LENGTH)
RemoteXY_buffer[RemoteXY_State.index-3]=c;
}
RemoteXY_State.index++;
RemoteXY_State.timeout=millis();
if ((RemoteXY_State.index == RemoteXY_State.count) && (RemoteXY_State.count >= 4)) {
if (RemoteXY_State.crc == 0) {
switch (RemoteXY_State.command) {
case 0x00:
RemoteXY_Send (RemoteXY_conf, REMOTEXY_CONF_LENGTH);
RemoteXY.connect_flag=1;
break;
case 0x40:
RemoteXY_Send ((unsigned char*)&RemoteXY, REMOTEXY_VALUES_LENGTH);
break;
case 0x80:
RemoteXY_Receive ((unsigned char*)&RemoteXY, RemoteXY_State.index-4);
RemoteXY_Send (0, 0);
break;
case 0xC0:
RemoteXY_Send ((unsigned char*)&RemoteXY+REMOTEXY_OUTPUT_INDEX,
REMOTEXY_VALUES_LENGTH-REMOTEXY_OUTPUT_INDEX);
break;
}
}
RemoteXY_State.index = 0;
RemoteXY_State.crc = 0;
}
}
if (RemoteXY_State.index>0) {
if (millis()-RemoteXY_State.timeout>REMOTEXY_TIMOUT) {
RemoteXY_State.index = 0;
RemoteXY_State.crc = 0;
}
}
if (millis()-RemoteXY_State.timeout>REMOTEXY_TIMOUT_DISCONNECTED) {
RemoteXY.connect_flag=0;
}

}

Here is the remotexy.h:

#ifndef _REMOTEXY_H_ 
#define _REMOTEXY_H_


/* enter your serial port */
#define REMOTEXY_SERIAL Serial2


/* this structure defines all the variables of your control interface */
typedef struct {

/* input variable */
unsigned char roverForwards; /* =1 if button pressed, else =0 */
unsigned char roverBackwards; /* =1 if button pressed, else =0 */
unsigned char roverLeft; /* =1 if button pressed, else =0 */
unsigned char roverRight; /* =1 if button pressed, else =0 */
unsigned char waistAntiClockwise; /* =1 if button pressed, else =0 */
unsigned char waistClockwise; /* =1 if button pressed, else =0 */
unsigned char shoulderRetract; /* =1 if button pressed, else =0 */
unsigned char shoulderExtend; /* =1 if button pressed, else =0 */
unsigned char elbowUp; /* =1 if button pressed, else =0 */
unsigned char elbowDown; /* =1 if button pressed, else =0 */
unsigned char gripperOpen; /* =1 if button pressed, else =0 */
unsigned char gripperClose; /* =1 if button pressed, else =0 */
unsigned char stopAllMovement; /* =1 if button pressed, else =0 */
unsigned char startAllMovement; /* =1 if button pressed, else =0 */
unsigned char roverStop; /* =1 if button pressed, else =0 */
unsigned char resetArduino; /* =1 if button pressed, else =0 */

/* other variable */
unsigned char connect_flag; /* =1 if wire connected, else =0 */

} RemoteXY_TypeDef;

/* this variable must be used for data transfer */
extern RemoteXY_TypeDef RemoteXY;


void RemoteXY_Init (void);
void RemoteXY_Handler (void);

#endif //_REMOTEXY_H_

The code itself is pretty self-explanatory.  I have added as many comments as I felt necessary. Essentially the remoteXY.cpp and remoteXY.h files tell the application on the mobile phone what buttons are present and where to put them with the required size, text and colour.  The main loop within the arduino code then listens for button press actions on the phone when the remoteXY application is running on the mobile phone handset and connection via the Bluetooth serial communications.  If a button press is detected and the arduino processes the action and the robot responds accordingly.

It is important to ensure that the associated arduino library for use with the remoteXY application has been downloaded and correctly installed in the arduino libraries folder.

remoteXY Arduino Library

I then uploaded the code to my arduino mega, connected a stack of batteries to the rover and powered up the robot – it helpfully starts in a sensible state, no movement with the arm suitably positioned.  I then loaded up the remoteXY app on my android mobile phone and connected the Bluetooth communications to the HC-05 module and SUCCESS!  The buttons control the rover and meArm perfectly – I am very happy with the results.  Here is a video of the robot in action:

Well….thats all for now, I”m off to play with my robot.  Take care people – Langster!

MeArm!

It was my birthday last week and my better half bought me a MeArm 0.4 – she knows me well!  A meArm is a small robot arm made from laser cut acrylic and servo motors.  The MeArm can be controlled with any micro-controller capable of driving servo motors (PWM drive pins).  I’m using an arduino but that’s only for quickness.  

Mearm Pocket Sized Robot Arm

There have been several design iterations of this mini robotic arm and the design is open source and available on Thingiverse.  It is basically some laser cut acrylic sheet and four 9G servo motors which are mechanically linked to provide the required movements.  The design itself is not basic…actually I think the thought that has gone into this device (toy) is awesome!

Thingiverse page for MeArm

Putting the arm together has been well documented by the designers in their own humorous style.  I managed to break two of the parts whilst assembling and fiddling with the device but that’s because I’m a clumsy oaf what over tightens things.  It is not difficult to obtain replacement parts if you have access to a laser cutter and some 3 mm acrylic sheet.

I really like this kit!  It was great fun to put together, worked out of the box and is very simple in concept.  I particularly like the fact that other people are sharing their efforts in using it as a learning tool.  It would not be difficult to scale this arm up in size to make a substantial industrial robot arm!

The only areas I have had trouble with is in setting the servo motors working correctly.  I over tightened the screws which connect the linkages to the servo motors which meant that when I powered them up nothing happened at first…just a lot of clicking. I then found that loosening the screws allowed the servos to move correctly. Loosening things was a lot of work and I was lazy and didn’t take the arm apart completely and managed to snap parts.  Nothing that can’t be replaced though, so I’m putting it down to experience and moving on.

The tutorial recommends using 4 potentiometers fed to the analogue inputs of an arduino and using the PWM outputs to drive the servo motors.  There is nothing wrong with this approach and has been shown to work very well.  I didn’t have four potentiometers to hand whilst testing the unit so I tried using serial commands and the keyboard which did work but lacked control because there is no feedback mechanism provided.  I could have written better code…But I was in a rush to play with my toy!

To improve matters and because I want to use this robot with the Bluetooth rover I’m going to design an arduino shield.

I need to be able to control:

  • 6x servo motors – four for the MeArm and two for the rover wheels.
  • 1x Bluetooth module – I’m hoping to be able to control the arm and the rover using my android mobile phone.
  • 2x HC-SR04 Ultrasonic modules.
  • 4x 10k potentiometers – in case I cannot get the bluetooth control working. 
  • 2x LEDS – just for fun.
  • 1x Thermistor – because measuring ambient temperature is useful.

That’s a lot of connections and whilst it is possible to achieve this using a standard arduino Uno it makes the PCB layout very cramped.  Therefore I’m going to use an arduino mega 2560 – If I have any further flashes of inspiration at least I’ll be able to add more functionality as there will be lots of left over digital and analogue I/O pins.

Here is the schematic diagram on two sheets:

Interconnection Section
Micro-controller Section

Here is the PCB layout – its mostly single sided with a ground plane to reduce EMI.  I’ve used through hole components to make it easy for people to realise their own shield:

Top Layer of PCB

Combined Layers with Dimensions

Eagle files for meArm 0.4 Shield

Here is the bill of materials for this project with parts and prices – I get most of my components from Farnell but just about anywhere selling electronic components should be able to supply with the items required apart from the PCB itself.

Part Quantity Device Description Supplier Unit Cost Part No.
(£)
JP1 1 36 Way pin Header Header 3 Farnell 1.41 1822166
R1 1 RESISTOR 1/4 W Resistor Farnell 0.12 2329474
R2 1 THERMISTOR THERMISTOR Farnell 0.37 1187031
RV1 1 POT Potentiometer Farnell 0.44 2469524
RV2 1 POT Potentiometer Farnell 0.44 2469524
RV3 1 POT Potentiometer Farnell 0.44 2469524
RV4 1 POT Potentiometer Farnell 0.44 2469524
S1 1 Tactile Switch Momentary Switch Farnell 0.09 1555982
U2 1 Arduino Mega 2560 Arduino Mega 2560 Farnell 28.76 2212779
PCB 1 Printed circuit board Printed circuit board Elecrow 16.67 N/A
MeArm 0.4 1 MeArm 0.4 MeArm 0.4 Kit Phenoptix   30 N/A
Total £79.18

  • The Arduino Mega 2560 does not have to be sourced from Farnell 
  • It is possible to laser cut your own parts for the MeArm and source your own servo motors which could reduce costs considerably – however I like to support people making educational kits and toys so I bought mine from Phenoptix….well the better half did…As I already have a MeArm 0.4 kit I’m not counting this as a true cost – Happy Birthday me!
  • In my case I’m going to etch my own PCB which will have a material cost of £5.00 – (I totally plucked this figure out of thin air!)  If people show an interest in the board I may get them made and make them available for a reasonable cost.
  • A Revision 3 Arduino Mega Clone (Which should work perfectly well) is available from Hobby Components for £12.49

So the total cost is now £21.24 – much more respectable!

There are other costs to be accounted for:

  • The cost of the servos from the original Bluetooth rover and a Bluetooth module 
  • Two HC-SR04 ultrasonics modules
  • The cost of the 3d printed wheels and ‘tyres’ 
  • The cost of the acrylic sheet and laser cutter time for constructing the rover.

It all adds up but enough with the costs – It’s a learning exercise and education and experience cannot have a value applied to them…particularly when it so much fun!

Here is the PCB populated and ready to go:

The populated MeArm Controller – The six headers are for servo motors and the sockets are for the bluetooth module and Ultrasonic sensors

Here is the code I’m using to test the arm – I’ve included two versions….one for use with the potentiometers controlling the arm and the other for control with the serial commands via the keyboard.

Here is the version using the potentiometers:

/* Controlling the MeArm 0.4 - Alexander Lang
04-04-2014

Credits to the original code provided by:
Michal Rinott <http://people.interaction-ivrea.it/m.rinott>
and - Ben Gray - Phenoptix

This code allows the user to control
the meArm 0.4 using 4x potentiometers
and an Arduino Mega R3 2560
with the potentiometers connected to
digital pins 6, 7, 8, 9
The position of the servo is
printed to the serial port

The maximum travel of the servos
are limited to prevent damage to the meArm
You may wish to change these values to suit your
own arm - it might be different

Enjoy!
*/

#include <Servo.h>

Servo base, arm, wrist, gripper; // create servo objects to control the different parts of the MeArm

int basePot = A0; // analog pin used to connect the potentiometer controlling the base
int armPot = A1; // analog pin used to connect the potentiometer controlling the arm
int wristPot = A2; // analog pin used to connect the potentiometer controlling the wrist
int gripperPot = A3; // analog pin used to connect the potentiometer controlling the gripper

int basePos; // variable to read the value from the analog pin
int armPos; // variable to read the value from the analog pin
int wristPos; // variable to read the value from the analog pin
int gripperPos; // variable to read the value from the analog pin

void setup()
{
Serial.begin(9600); // Start the serial communications for debugging

base.attach(9); // attaches the servo on pin 6 to the servo object
arm.attach(8); // attaches the servo on pin 7 to the servo object
wrist.attach(7); // attaches the servo on pin 8 to the servo object
gripper.attach(6); // attaches the servo on pin 9 to the servo object

Serial.print("MeArm Control Code!");
Serial.println();
}

void loop()
{
controlBase();
controlArm();
controlWrist();
controlGripper();
}

void controlBase()
{
basePos = analogRead(basePot); // reads the value of the potentiometer (value between 0 and 1023)
basePos = map(basePos, 0, 1023, 0, 179); // scale it to use it with the base servo (value between 0 and 179)
base.write(basePos); // sets the servo position according to the scaled value
delay(15); // delay to allow servo to reach the position
Serial.print("Base Position: "); // print the current position of the base servo in degrees
Serial.print(basePos);
Serial.println();
//delay(100); // delay to allow user to read serial messages
}

void controlArm()
{
armPos = analogRead(armPot); // reads the value of the potentiometer (value between 0 and 1023)
armPos = map(armPos, 0, 1023, 32, 180); // scale it to use it with the arm servo (value between 32 and 179)
arm.write(armPos); // Sets the servo position according to the scaled value
delay(15); // delay to allow for the servo to reach the position
Serial.print("Arm Position: "); // print the current position of the servo in degrees
Serial.print(armPos);
Serial.println();
//delay(100); // delay to allow user to read serial messages
}

void controlWrist()
{
wristPos = analogRead(wristPot); // reads the value of the potentiometer (value between 0 and 1023)
wristPos = map(wristPos, 0, 1023, 45, 147); // scale it to use it with the wrist servo (value between 45 and 147)
wrist.write(wristPos); // sets the servo position according to the scaled value
delay(15); // delay to allow for the servo to reach the position
Serial.print("Wrist Position: "); // print the current position of the servo in degrees
Serial.print(wristPos);
Serial.println();
//delay(100); // waits for the servo to get there
}

void controlGripper()
{
gripperPos = analogRead(gripperPot); // reads the value of the potentiometer (value between 0 and 1023)
gripperPos = map(gripperPos, 0, 1023, 0, 30); // scale it to use it with the servo (value between 0 and 180)
gripper.write(gripperPos); // sets the servo position according to the scaled value
delay(15); // delay to allow for the servo to reach position
Serial.print("Gripper Position: "); // print the current position of the servo in degrees
Serial.print(gripperPos);
Serial.println();
//delay(100); // delay to allow user to read serial messages
}

Here is the serial control version – slightly different

/* Alex's Mearm code Serial Control Code
04/04/2015 (c)

This code controls a meArm 0.4 via a
serial termiminal.

Enjoy!

Base servo connected to pin 9
Arm servo connected to pin 8
wrist servo connected to pin 7
Gripper servo connected to pin 7

The maximum travel of the servos
are limited to prevent damage to the meArm
You may wish to change these values to suit your
own arm - it might be different

Control via keyboard over serial terminal

1 = base left
2 = base right

3 = arm up
4 = arm down

5 = wrist up
6 = wrist down

7 = gripper open
8 = gripper close

9 = stop all movement

M = restart movement

A = reset arduino

*/

#include <Servo.h>
#include <avr/wdt.h>

Servo base, arm, wrist, gripper ; // create servo objects to control servos

int basePos = 90; // variable to store the base servo position
// Set to 90 to prevent movement at start

int armPos = 90; // variable to store the arm servo position
// Set to 90 to prevent movement at start

int wristPos = 90; // variable to store the wristy servo position
// Set to 90 to prevent movement at start

int gripperPos = 0; // variable to store the gripper servo position
// Set to 15 to prevent movement at start

char var=0; // variable to store keyboard or controller input

void setup()
{
Serial.begin(9600); // Standard Serial Port for debugging

Serial2.begin(9600); // Bluetooth Serial Port for remote control

base.attach(9); // attaches the base servo on pin 6 to the servo object
arm.attach(8); // attaches the arm servo on pin 7 to the servo object
wrist.attach(7); // attaches the wrist servo on pin 8 to the servo object
gripper.attach(6); // attaches the gripper servo on pin 9 to the servo object

gripper.write(gripperPos); // Ensure gripper is open!

Serial.print("MeArm Serial Control");
Serial.println();

}

void loop()
{

Serial.flush();

var = Serial.read();

switch (var)
{
case '1':
moveBaseLeft();
break;

case '2':
moveBaseRight();
break;

case '3':
moveArmForwards();
break;

case '4':
moveArmBackwards();
break;

case '5':
moveWristUp();
break;

case '6':
moveWristDown();
break;

case '8':
openGripper();
break;

case '7':
closeGripper();
break;

case '9':
stopMoving();
break;

case 'M':
startMoving();
break;

case 'm':
startMoving();
break;

case 'A':
software_Reboot();
break;

case 'a':
software_Reboot();
break;

}
}

void moveBaseLeft()
{

Serial.print("Moving base left ");
Serial.println();
Serial.print("BasePos Value: ");
Serial.print(basePos);
Serial.println();

if (basePos >= 0 && basePos != 179)
{
basePos++;
base.write(basePos);
delay(15);
}

if (basePos == 179)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial.flush();

}

void moveBaseRight()
{

Serial.print("Moving base right ");
Serial.println();
Serial.print("BasePos Value: ");
Serial.print(basePos);
Serial.println();

if (basePos <= 179 && basePos != 0)
{
basePos--;
base.write(basePos);
delay(15);
}

if (basePos == 0)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial.flush();

}

void moveArmForwards()
{

Serial.print("Moving Arm Forwards ");
Serial.println();

Serial.print("armPos Value: ");
Serial.print(armPos);
Serial.println();

if (armPos >=32 && armPos != 179)
{
armPos++;
arm.write(armPos);
delay(15);
}

if (armPos == 179)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial.flush();

}

void moveArmBackwards()
{

Serial.print("Moving Arm backwards ");
Serial.println();

Serial.print("armPos Value: ");
Serial.print(armPos);
Serial.println();

if (armPos <= 179 && armPos != 32)
{
armPos--;
arm.write(armPos);
delay(15);
}

if (armPos == 32)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial.flush();

}

void moveWristUp()
{

Serial.print("Moving Wrist Up ");
Serial.println();

Serial.print("wristPos Value: ");
Serial.print(wristPos);
Serial.println();

if (wristPos >= 45 && wristPos != 147)
{
wristPos++;
wrist.write(wristPos);
delay(15);
}

if (wristPos == 147)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial.flush();

}

void moveWristDown()
{

Serial.print("Moving Wrist Down ");
Serial.println();

Serial.print("wristPos Value: ");
Serial.print(wristPos);
Serial.println();

if (wristPos <= 147 && wristPos != 45)
{
wristPos--;
wrist.write(wristPos);
delay(15);
}

if (wristPos == 45)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial.flush();

}

void closeGripper()
{

Serial.print("Closing Gripper Jaws ");
Serial.println();

Serial.print("gripperPos Value: ");
Serial.print(gripperPos);
Serial.println();

if (gripperPos >= 0 && gripperPos != 40)
{
gripperPos++;
gripper.write(gripperPos);
delay(15);
}

if (gripperPos == 40)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial.flush();

}

void openGripper()
{

Serial.print("Opening Gripper Jaws ");
Serial.println();

Serial.print("gripperPos Value: ");
Serial.print(gripperPos);
Serial.println();

if (gripperPos <= 40 && gripperPos != 0)
{
gripperPos--;
gripper.write(gripperPos);
delay(15);
}

if (gripperPos == 0)
{
Serial.print("Reached end of travel....stopping");
Serial.println();
}

Serial.flush();

}

void startMoving()
{

Serial.print("Start All Movement ");
Serial.println();

base.attach(9);
arm.attach(8);
wrist.attach(7);
gripper.attach(6);

Serial.flush();

delay(15);

}


void stopMoving()
{

Serial.print("Stop All Movement ");
Serial.println();

base.detach();
arm.detach();
wrist.detach();
gripper.detach();

Serial.flush();

delay(15);

}

void software_Reboot()
{
wdt_enable(WDTO_15MS);
while(1)
{
}
}

Here is a video showing the arm working – please excuse the sound effects!

The more eagle eyed among you will notice I have replaced the base section of the meArm with a 3d printed base section.  I was trawling thingiverse (link below) and noticed that many people had kindly been posting upgrades for the meArm.  The base section as provided works well enough but can cause the robot to wobble as the base section doesn’t have a very good bearing mechanism.  To solve that a clever bloke named James White created a base section which is free to download and use.

Thrust bearing base for MeArm V0.4

In order to use it properly you will need to acquire a thrust bearing and two washers from a well known vendor.  I used ebay:

AXK Needle Roller Cage Thrust Bearing &Two AS Washers

All that you need then is access to a 3d printer and some PLA material.  I went to the Hackspace in Manchester (HacMan).  Once the 3d printing was complete all I did was reconstruct the base section. My final plan was to have the meArm sit on the bluetooth rover and be remote controlled by my android mobile phone.  That is a work in progress but I have got it mostly working *really big grin*

That’s all for now, take care – Langster

Arduino Temperature logger thing

At work we had need for a multiple temperature logging device. Using one of my cheap Chinese Arduino Uno clones, some DS18B20 one wire Waterproof Temperature Sensors Temperature Transducers (like these ones), a cheap Ethernet / SD card shield (like this), and a DS1307 RTC (Real Time clock) (link). The DS1307 RTC is connected using […]

IR LED Glasses

A friend at the hackspace helps run a community centre and from time to time they run electronics workshops.  One of the proposed workshops focused around preventing a person’s face being visible to monitoring video cameras which appear to have become incredibly prevalent in the UK.

One such method of obscuring ones face is to swamp the video camera CCA (charge-coupled Array) with infra-red light.  This can be achieved with some bright infra-red LEDS which appear clear to the human eye.  What is needed is a simple electronic circuit which flashes some IR (infra-red) LEDS very quickly and can be easily mounted on a hat or a pair of glasses.  This was directly inspired by an article on Hackaday:

anti-paparazzi-sunglasses

The circuit will have to be battery powered, easily constructed, simple and cheap!  With those design constraints I decided to use one of the most popular integrated circuits in the world….the 555 timer!

555 timer IC wikipedia entry

555 Datasheet

We are going to use the 555 timer IC to output a constant square wave at high frequency.  This really means we are going to turn an electronic signal on and off very quickly…we are then going to use this constant on off signal to control a transistor which will turn the LEDS on and off very quickly.  We are doing this for a good reason – it means we can run the LEDS at full power and save battery life.  It makes it more efficient.

The circuit operation for the 555 circuit constantly outputting a square wave is known as “Astable” mode.  There are lots of calculator programs and circuits available for creating 555 circuits because it is so popular.

555 Astable Calculator

If you click the link it provides a webpage based calculator which if we type in a few component values it will tell us what frequency the square wave output will be.  I often use circuit simulators and calculators when designing circuits as it makes things much easier.  I also don’t see why I should always start from scratch when designing simple circuits – why reinvent the wheel?  As long as the circuit theory is understood there are no issues.

Here is the circuit we are going to use:

The circuit works as follows:

The 555 timer IC is configured in astable mode to constantly output a sqaure wave signal at pin three. This sqaure wave is used to drive the gate of an N-type field effect transistor which in turn pulses the infra-red LEDS on and off.  The frequency of the square wave output from the 555 timer is preset by the 10k resistor, 680k resistor and 4.7pF capacitor.  The 10nF capacitor on pin 5 (control voltage pin) is to stabilise the 555 timer operation.  The circuit can be powered from any voltage between 4.5V and 16V.

Here is a short video showing the circuit being simulated:

Constructing the circuit on stripboard or veroboard is probably the quickest method to prototype the circuit but I like designing printed circuit boards so here is my PCB layout:

IR Glasses PCB Top Layer

  

IR Glasses PCB Bottom Layer

Here is the parts list with the corresponding Farnell ordering codes and costs:

The farnell parts cost in total is £1.53 for a single unit.  Most of the parts are bought in lots of 50 pieces so the actual costs will be higher.

If we were going to make lots of this circuit I would get professional printed circuit boards manufactured.  I tend to use Elecrow Bazaar as they give great service for the price.

Elecrow Bazaar

As the PCB is less than 5 cm x 5 cm we can get ten printed circuit boards made for $9.90 or £6.58 so a single PCB will be £0.66 – that’s really quite cheap!  I haven’t included shipping in that cost but it’s around another £10 so that brings the cost of the PCB up to £1.66

All that’s needed is to register with Elecrow and then upload the Gerber files for the design and pay for the service and ten to fifteen days later some professionally made printed circuit boards arrive in the post.  Be aware that in order to do this I would have to share my original design files and you would then need to export what is known as the gerber files from Cadsoft Eagle – a topic for another post.

Just for fun here is the PCB rendered in 3D so we can see how the circuit would look if it was completed.

3D render plan View
3D render ISO view
3D render bottom side of PCB

Just for those who are keeping a tally I reckon the final cost of getting this circuit operating with a pair of glasses is:

Components – £1.53
PCB – £1.66
9V battery – £1.74
LED Glasses – £1.63
Cable ties – £0.90
Wire – £0.50

Total – £7.96 per kit

I did some development work on this circuit using a breadboard.  Here are some pictures showing the circuit in operation.  The operation of the bright IR leds does obscure black and white cameras. Modern colour cameras unfortunately have a very good infra-red filter which means this method of obfuscation won’t work particularly well.

Here are some photos showing the circuit in operation…

In Colour!
In Black and White!
At an Angle

Well…that’s about it.  If you want to make IR LEDS flash quickly without using a microcontroller then this is the circuit for you.  The costs for making this into a pair of anti camera glasses is £8.00 which I think it pretty reasonable.  That’s all for now – take care!

Weather Station – Stevenson Screen

A Stevenson screen is an outdoor enclosure to shield meteorological instruments against precipitation and direct heat radiation (sometimes called a radiation screen) from outside sources, while still allowing air to circulate freely around them. The World Meteorological Organization (WMO) agreed standard for the height of the screen is between 1.25 m (4ft 1 in) and […]