Saturday, 20 June 2015

Putting things together

After parking the Robot Arm on top of a bookcase for several months, I've decided to see if I can make something a bit more complex (and useable).

In addition to the Arduino, I also have a Raspberry Pi and the Windows 7 Laptop I use to create this blog so I thought it might be a good idea to put all these together. Now, I don't really need to use the Raspberry Pi for this particular project, but it's small, hardly draws any power and there's an increasing amount of community support for robotics.

So, Step 1, connect the Pi to the PC

Note I've installed Raspbian onto my particular Pi. To begin with, I want to connect via VNC. There are plenty of instructions on the web for setting up a VNC server. Instructions like these should be sufficient

I installed UltraVNC Viewer on the laptop which is a useful free viewer with very unobtrusive advertising.

PuTTY is also a useful tool to have installed as you can use this to connect to the Raspberry Pi from the command line. Especially handy to check what went wrong with configuring VNC.

It's important you don't use the default login credentials of User: pi, Password: raspberry, so change it using the command passwd from the command line

Step 2, Setup Wifi for the Pi

I didn't want to have to connect my Raspberry Pi to the PC everytime I use it, so I bought a cheap and low power USB Wifi dongle.
There's a lot of information about how to setup Wifi on the web. This is what I did.

Edit the /etc/network/interfaces file (I use nano for this)
auto lo
iface lo inet loopback

# Ethernet
iface eth0 inet dhcp

allow-hotplug wlan0
auto wlan0
iface wlan0 inet manual
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf

iface default inet dhcp


Create a new /etc/wpa_supplicant/wpa_supplicant.conf file
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
ssid=”YourWifi”
scan_ssid=1
proto=WPA
kep_mgmt=WPA-PSK
pairwise=TKIP
group=TKIP
pas=”password”
}

Restart the Pi to setup the connection.

Obviously setup the connection information as per your own circumstances. One thing I did do was setup a static IP address for the Pi on my router. This did save quite a bit of pain getting DHCP working.

Step 3, Connect the Pi to the Arduino
The Arduino client software is installed using sudo apt-get install arduino

The default setting wasn't compatible with my Arduino board. When I tried to upload a sketch to the board I got this wonderfully informative message:

error: avrdude stk500_getsync() not in sync resp=0x00

The fix was to set the Board from the Tools -> Board menu to Arduino Diecimila or Duemilanove w/ ATmega168 on Serial Port /dev/ttyUSB0

I then uploaded the Blink sketch to the Arduino to prove it worked.

Finally, I connected using a powered USB hub to confirm that worked as well. The images below show both the direct Raspberry Pi to Arduino and USB hub connection.

The latter will be useful in future as this will help minimise power consumption issues.



Thursday, 14 May 2015

Fixing an old Multimeter


I have an old multimeter, a Metrix MX 001B. I believe this was made sometime in the 1970s. by a French company, Metrix, now Chauvin Arnoux Metrix. This had been gathering dust in the garage for several years until I started working on my Robot Arm Controller project.



The sensor wasn't working on one of the channels so I wanted to use the multimeter to test the circuit. Needless to say, when I dusted it off and tried to use it, nothing happened.

First, it needed two 2x LR1 Size-N batteries (duh!).

Second and more serious, the connection to the negative battery terminal had snapped off. Fortunately, it was relatively simple to solder a new connection onto the board.

And here it is working.


Not perhaps the most exciting video in the world!
(Uploaded to Blogger instead of YouTube so this isn't high definition. You have to kind of imagine seeing the needle move.)

Update 4th Jan 2016
In response to Halil Karakose's comment, I thought I'd include a view of the inside. This is all wonderfully simple and at a human scale compared to some modern circuits!


Monday, 30 March 2015

Keep on Running

Smart watches may be flavour of the month but I've been using one, in the form of the Garmin Forerunner 305, for over 7 years now.

The software that came with the watch allows you to track your runs and build up a history of activity. This can be very addictive (but also a great motivator!).
I soon discovered the excellent SportTracks application from Zone Five Software where you can play around with even more statistics and also get nice view of your runs on a map. This is one of the few windows applications where it is actually worth paying for the full features.

Unfortunately though, about 18 months ago, a combination of SportTracks and Windows updates managed to frazzle the driver to such an extent I was unable to import any data from the watch. Many hours were spent trying to reinstall and update the driver to no avail and I was seriously considering whether I would end up manually inputting data to the app (or at least the summary).

It then occurred to me that I could perhaps use my Linux laptop to obtain the data.

A quick Google showed that yes, this indeed had been done and the steps I followed are listed below:
Details were originally taken from braiden.org, This is now unavailable but the sources are listed on Gitbub. https://github.com/cstrelioff/garmin-dev

1. Install Garmin Forerunner tools
 sudo apt-get install garmin-forerunner-tools

2. Install Java 1.6 VM. I used OpenJDK.
 sudo apt-get install opendk-6-jre

3. Create a folder for your scripts and uploaded files, I used Garmin

4. Download the files from the Github location

5. Change access permissions to allow us to use scripts in this folder
 chmod u+x gmn2tcx and saxon-xslt

6. Upload the data from the watch
 garmin_save_runs

This will save the raw .gmn files to dated folders under the Garmin Root. Take a look at one of them and you'll see this is really just an XML format and the script is simply going to run a transformation into the .tcx format that's used by SportTracks.

7. Run the transform
./gmn2tcx 2015/02/20150227T125809.gmn > 20150227.tcx

Linux note #1: Prefix the script name with the ./ otherwise the script won't get found as an executable
Linux note #2: The Pipe command > redirects the output to the file called 20150227.tcx

I get output looking something like this:

jonathan@easynote1:~/Garmin$ garmin_save_runs
Extracting data from Garmin Forerunner305 Software Version 2.80.
Files will be saved in '/home/jonathan/Garmin'
Wrote:   /home/jonathan/Garmin/2015/02/20150227T125809.gmn
Wrote:   /home/jonathan/Garmin/2015/03/20150303T064648.gmn
Wrote:   /home/jonathan/Garmin/2015/03/20150312T130929.gmn

jonathan@easynote1:~/Garmin$ ./gmn2tcx 2015/02/20150227T125809.gmn > 20150227.tcx
WARN: "xmllint" not found. Will not validate XML schema.
jonathan@easynote1:~/Garmin$ 

The xmllint warning can be ignored, I'm not worried so much about validation.

8. Finally, upload the files in SportTracks.
I set up Samba on my Window and Linux laptops to be able to transfer the data. This is unfortunately a bit flaky so I mainly transfer using the USB drive. If I get round to resolving the Samba problems it'll certainly appear in a future blog!

Monday, 2 February 2015

Laser Alarm

As a bit of fun I decided to make a trip alarm to catch Santa on Christmas Eve. As I also have a Laser level surely a Laser trip-wire is the ideal Santa catcher!

I based this on the Alarm System Example from the Franzis Tutorial Kit for the Arduino. The example as described, with the potentiometer to control the threshold before setting off the alarm didn't work for me. So I simplified it to remove the variable resistor used to control the sensitivity.

Here is a basic schematic of the circuit:



And in action:


In detail - I shine the Laser on the Light Dependent Resistor when starting up the Arduino. The value is read off analog port 0. If this changes by more than the threshold (Santa puts his hand in the beam!) which is set to 100 then the alarm sounds and lasts for 2 and a half seconds.

You can comment out the line delay(2500); to stop the alarm sounding immediately when you remove your (or Santa's) hand.

The Serial.Print lines were used for debugging and aren't necessary. Note also that the baseValue is reset every 10s (1000 * 10ms). This is intended to account for changes in ambient light. However in practice using the Laser makes this effectively redundant since it dominates over the background.

The code for the Arduino is given below:

// Light Alarm
int LED = 13;
int LDR = 0;
int Speaker = 8;
int cnt = 0;
int baseValue,threshold = 100;

void setup()
{
  Serial.begin(9600);
  pinMode(LED,OUTPUT);
  pinMode(Speaker,OUTPUT);
  baseValue = analogRead(LDR);
}

void loop()
{
  cnt++;
  if(cnt > 1000)
  {
    // Reset the base value of the LDR after a period of time
    cnt = 0;
    baseValue = analogRead(LDR);
  }

  // Outside the bounds, sound the alarm
  if (baseValue > (analogRead(LDR)+threshold) || baseValue < (analogRead(LDR) - threshold))
   
  {
    digitalWrite(LED,HIGH); // Use the onboard LED for visual indication
    tone(Speaker,500);
    delay(2500);
    noTone(Speaker);
    digitalWrite(LED,LOW);
  }
 
  Serial.print(baseValue);
  Serial.print(",");
  Serial.print(threshold);
  Serial.print(", ");
  Serial.println(analogRead(LDR));
  delay(10);
}


Tuesday, 2 December 2014

A Prototype Robot Arm Controller

The small robot arm kits available these days are both fun to build but also are simple enough to open doors into the world of home robotics.

The robot arm in its untouched state

I also have an Arduino I bought some time ago. While it was interesting to play with small samples, controlling LEDs on a breadboard for example, having the robot meant I could try something more involved.

A search on Google for ideas came up with the following article about using capacitive distance sensing to make a 3D hand sensor Instructables 3D Controller.  I figured this would give quite an interesting demo of using my arm to move the robot arm.

The first step was to create the 3D sensor. The instructions are fortunately straightforward and apart from refreshing some rusty soldering skills I encountered no issues.

The question about how I would be able to use the sensor to control motions in two directions along each axis was an interesting one. A simple analysis of the response in the Arduino serial monitor showed that the signal rose at an increasing rate closer to the sensor, However at a point about 1/3 of the distance from each sensor to the edge of the cube a movement of 1-2 inches would result in a change of signal of 5 units in each direction. I could use this as the basis of the controller.

My approach was to program a 2 second wait after an initial upload or reset of the Arduino, I could use this time to put my hand in the correct position within the sensor cube. At the end of the 2 seconds any movement that changed the response by greater than +/- 5 units would cause the arm to move along that axis in the corresponding +/- direction.

Finally, I needed a motor shield for the Arduino. For this prototype I stuck with the standard version which provides control over two motors. This would obviously restrict the scope of the project since the robot arm itself has 5 motors.

The completed prototype is shown below. I used the X axis to control the grabber and the Z axis to control front and back movement.

The completed prototype
What were the problems?

Well, to begin with the sensor was too unforgiving. You had to begin the session with your hand just in the right place. This isn't a good situation from a user perspective.

The processor loop doesn't allow two axes to be engaged simultaneously.

Finally, the sensor is also subject to noise such that the response changes over time.

I'll not be taking this approach any further but this has been a good project in terms of putting together several technologies. The arm certainly won't be mothballed!

The code for the Arduino is given below:

// Based on a sample by By Kyle McDonald
// From the instructables project at:
// http://www.instructables.com/id/DIY-3D-Controller/

#define resolution 4
#define mains 50 // 60: north america, japan; 50: most other places

#define refresh 2 * 1000000 / mains

/*
Digital Pins:
  12, 3, 9 Channel A
  13, 11, 8 Channel B
  5,6,7 Sensor
*/

int xSensor = 5;
int ySensor = 6;
int zSensor = 7;
int baseX = 0;
int baseY = 0;
int baseZ = 0;

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

  //Setup Channel A
  pinMode(12, OUTPUT);    // Initialise Motor Channel A pin - DIRECTION
  pinMode(9, OUTPUT);     // Initialise Brake Channel A pin - BRAKE

  //Setup Channel B
  pinMode(13, OUTPUT);    // Initialise Motor Channel A pin - DIRECTION
  pinMode(8, OUTPUT);     // Initialise Brake Channel A pin - BRAKE

  pinMode(xSensor, INPUT);
  pinMode(ySensor, INPUT);
  pinMode(zSensor, INPUT);
 
  startTimer();
 
  // Wait 2 seconds before we start moving. To allow you to get your hand in place
  delay(2000);
}

void loop() { 
  int xPos = time(xSensor, B00100000);
  // For some reason the first xPos comes back as -1
  if (baseX == 0 && xPos > 0)
  {
    baseX = xPos;
  }
 
  int yPos = time(ySensor, B01000000);
  if (baseY == 0)
  {
    baseY = yPos;
  }

  int zPos = time(zSensor, B10000000);
  if (baseZ == 0)
  {
    baseZ = zPos;
  }
 
  if (yPos  - baseY > 5)
  {
     Serial.println("Y up");
     digitalWrite(12, HIGH);  // Establish forward direction of Channel A
     digitalWrite(9, LOW);    // Disengage the break for Channel A
     digitalWrite(8, HIGH);   // Engage the Brake for Channel B
     analogWrite(3,244);      // Spin the motor on Channel A at full speed
  }
  else if (yPos - baseY < -5)
  {
    Serial.println("Y down");
    digitalWrite(12, LOW);    // Establishes backward direction of Channel A
    digitalWrite(9, LOW);     // Disengage the Brake for Channel A
    digitalWrite(8, HIGH);    // Engage the Brake for Channel B
    analogWrite(3, 244);      // Spin the motor on Channel A at half speed
  }
  else if (xPos  - baseX > 5)
  {
     Serial.println("X up");
     digitalWrite(13, HIGH);  // Establish forward direction of Channel B
     digitalWrite(8, LOW);    // Disengage the break for Channel B
     digitalWrite(9, HIGH);   // Engage the Brake for Channel A
     analogWrite(11,244);     // Spin the motor on Channel B at full speed
  }
  else if (xPos - baseX < -5)
  {
    Serial.println("X down");
    digitalWrite(13, LOW);    // Establishes backward direction of Channel B
    digitalWrite(8, LOW);     // Disengage the Brake for Channel B
    digitalWrite(9, HIGH);    // Engage the Brake for Channel A
    analogWrite(11, 244);     // Spins the motor on Channel B at half speed
  }
  else
  {
    Serial.println("Neutral");
    digitalWrite(8, HIGH);    // Engage the Brake for Channel B
    digitalWrite(9, HIGH);    // Engage the Brake for Channel A
  }
}

long time(int pin, byte mask) {
  unsigned long count = 0, total = 0;
  while(checkTimer() < refresh) {
    // pinMode is about 6 times slower than assigning
    // DDRB directly, but that pause is important
    pinMode(pin, OUTPUT);
    PORTD = 0;
    pinMode(pin, INPUT);
    while((PIND & mask) == 0)
      count++;
    total++;
  }
  startTimer();
  return (count << resolution) / total;
}

extern volatile unsigned long timer0_overflow_count;

void startTimer() {
  timer0_overflow_count = 0;
  TCNT0 = 0;
}

unsigned long checkTimer() {
  return ((timer0_overflow_count << 8) + TCNT0) << 2;
}





Tuesday, 22 July 2014

Old Hardware - Part 2

There were a couple of things to finish off before the Linux laptop was ready for use, solving the problem of storage and setting up an internet connection.

Storage was simple. The machine has a 20GB hard drive, of which 6GB had been partitioned to Linux. Once I had a working build, I lowered the Windows partition down to 10GB and created a new partition for scratch data storage.

I also had an HP Pavilion laptop that suffered from overheating. This was a widespread problem and HP patched the BIOS to keep the fan on. However, my machine broke with one of the common symptoms of the WiFi stopping. Incidentally as it was still under warranty HP were great about this and from shipping to return took only about 3 working days. Unfortunately, the underlying problem was still present and about 6 months later the video card broke.

At this point, the machine was out of warranty and I bought a new one (not HP). I did however have a perfectly good 250 GB hard drive. Amazon do pretty cheap 2.5in SATA to USB enclosures so I purchased one of these and got myself a 250GB USB drive.

The next step was to reformat the drive from NTFS to FAT32 so it could be used by Windows and Linux. Windows of course does not easily format FAT32 drives greater than 32GB but by good fortune the EaseUS partition master I used to partition the laptop also formats large drives to FAT32.

Secondly, the Wifi.

2002-era laptops likely didn't come with built in WiFi. Mine at least had a modem! At the time when I first got WiFi I installed a PCI card from BT. This didn't work after the default Lubuntu installation so I needed to set-up the card separately.

Instructions are widespread on the web. First, you need to discover the available PCI devices. The command to do this is:
lspci -vvnn | grep 14e4

From this I determined that the card was a Broadcom BCM4318.

Setting this up offline involves the following steps: 

As this is the b43 chipset install b43-fwcutter from the installation CD.



 Run the following commands:

~$ tar xfvj broadcom-wl-4.150.10.5.tar.bz2
~$ sudo b43-fwcutter -w /lib/firmware wl_apsta-3.130.20.0.o

~$ sudo b43-fwcutter --unsupported -w /lib/firmware broadcom-wl-4.150.10.5/driver/wl_apsta_mimo.o

Finally restart the machine.

I now had a working Linux laptop with WiFi and 250 GB of storage available.




Thursday, 10 July 2014

Rejuvenating old hardware

I have an old Packard Bell Easynote One laptop I bought way back in 2002 (it wasn't even cutting edge then). When XP started slowing down under  the weight of windows services it ended up hidden, like so many other old machines, in the back of a cupboard. I think the last straw was trying to install iTunes.

I'm not one to be happy with perfectly good hardware going to waste so I eventually decided to get back into the world of Linux and install a lightweight distro to see if the laptop could be of some use. In this case I decided on Lubuntu.

On firing it up I discovered that the CD-ROM drive had died. This ruled out installing from a boot CD. Fortunately though I have an external USB CD-ROM so I decided to go down the route of setting up a dual booting system.

First step was to repartition the hard drive. This is a paltry 20GB. (Plenty of phones with more these days!) Bearing in mind the intention was not to use XP, I spent some time deleting data and uninstalling everything I could from XP. I got down to about 10GB, I'm sure I can do better...

After this I installed the free Partition Master Home Edition from EaseUS and used it to create a new Partition of 6GB. Some more partitioning would happen later.

Next step was to setup Grub. This is a boot manager that can be used to boot multiple operating systems.
  1. Create a new C:\boot folder, copy initrd.gz and vmlinuz from the Lubuntu distro
  2. Download Grub4Dos and copy grldr to C:\ and menu.1st to C:\boot\grub
  3. Edit menu.lst and add the following to the bottom
    title Ubuntu Installer (hd0,1)
    kernel (hd0,1)/boot/vmlinuz
    initrd (hd0,1)/boot/initrd.gz
    Note hd0,1 refers to the second partition on the hard disk, i.e. Windows (The first partition is hidden)
  4. Edit boot.ini (set  attrib -a -r -s -h c:\boot.ini first). Add C:\grldr="Start GRUB"
  5. Put the distro CD into the USB CD-ROM and restart, select Start GRUB and follow the instructions to install
And here it is back in use.