Sunday, September 25, 2011

Arduino: combining CAN-bus and Xbee shields


A little box of Arduino, originally uploaded by indigoid.

So I created a simple little sketch, based on the TimeGPS sample that comes with the TinyGPS library, that waits for a "p" over the Xbee interface and upon receiving it, sends back the date, time, latitude and longitude data from the GPS. The CAN-bus shield uses pins 4 and 5 for the GPS serial interface. I found that the Xbee modules I have (as supplied in the SparkFun Xbee retail kit) have rather shorter range than I'd hoped for, but at least they work and I am learning about their usage. Here's the sketch:


#include <TinyGPS.h>        //http://arduiniana.org/libraries/TinyGPS/
#include <NewSoftSerial.h>  //http://arduiniana.org/libraries/newsoftserial/
// GPS and NewSoftSerial libraries are the work of Mikal Hart

TinyGPS gps; 
NewSoftSerial serial_gps =  NewSoftSerial(4, 5);  // receive on pin 3

void setup()
{
  Serial.begin(9600);
  serial_gps.begin(4800);
  Serial.println("setup...");
}

void dump_lat_long(float flat, float flong) {
  Serial.print("lat : "); Serial.println(flat);
  Serial.print("long: "); Serial.println(flong);
}

#define pdec(x) { Serial.print(x,DEC); }
#define p(x) { Serial.print(x); }
#define s() p(" ")
#define d() p("-")
#define c() p(":")
void dump_datetime() {
  int year;
  byte month, day, hour, minutes, second, hundredths;
  unsigned long fix_age;
  gps.crack_datetime(&year, &month, &day, &hour, &minutes, &second, &hundredths, &fix_age);
  pdec(year); d(); pdec(month); d(); pdec(day);
  s();
  pdec(hour); c(); pdec(minutes); c(); pdec(second); p("."); pdec(hundredths);
  Serial.println();
}

void loop() {
  float flat, flon;
  unsigned long fix_age;
  String msg;
  byte havedata = 1;
  byte polled = 0;
  while (serial_gps.available() && !polled) {
    if(gps.encode(serial_gps.read())) {
      // returns +- latitude/longitude in degrees
      gps.f_get_position(&flat, &flon, &fix_age);
      if (fix_age == TinyGPS::GPS_INVALID_AGE) {
        msg = "No fix detected";
        havedata = 0;
      } else if (fix_age > 5000)
        msg = "Warning: possible stale data!";
      else
        msg = "Data is current.";
      polled = 1;
      if (Serial.available() && Serial.read() == 'p') {
        Serial.println(msg);
        if (havedata) {
          dump_datetime();
          dump_lat_long(flat,flon);
        }
        delay(100);
      }
    }
  }
}

I also wrote a small Perl script that (via Device::SerialPort from CPAN) interrogates the Arduino every 5 seconds or so, using a USB-attached Xbee Explorer. For Linux you will need to change the serial port device filename to /dev/ttyS0 or similar. No idea about Windows, sorry. Again, I started out with some sample code (this time from the Device::SerialPort distribution) and hacked most of it off. Code follows:


#!/usr/bin/perl

use strict;
use warnings;
use Device::SerialPort;

my $file = "/dev/tty.usbserial-A700fbpg";
my $ob = Device::SerialPort->new ($file) || die "Can't open $file: $!";

$ob->baudrate(9600)     || die "fail setting baudrate";
$ob->parity("none")     || die "fail setting parity";
$ob->databits(8)        || die "fail setting databits";
$ob->stopbits(1)        || die "fail setting stopbits";
$ob->handshake("none")  || die "fail setting handshake";
$ob->write_settings || die "no settings";
$ob->error_msg(1);              # use built-in error messages
$ob->user_msg(1);

while(1) {
  $ob->write("p");
  print $ob->input;
  sleep 5;
}

No comments:

Post a Comment