GPX to DXF – Drawing GPS Tracks

Sunday March 29, 13.25.20

Lines and arrows and splines point the way. Taken in a parking lot near the beaches at Santa Monica.

It has not been a quiet couple of days here in the Laboratory. Lots of gear and glassware about. Goggles, bunsen burners and all that sort of thing. And the report draft was just finished with Nicolas Nova, which occupied many early mornings. We almost spilled an organic, but toxic material on the draft which cause a collective gasp, but it was pulled out of the way, just in time before irrevocable damage was done. We’re writing with ink and pen these days, which feels so much more angelic and respectful, but are, through incidents like this, reminded that such may have nostalgic integrity, but it is also quite delicate and precious.

In the midst of all that was the need to translate a GPS track from GPX format to DXF. This was harder than I thought it would be, at least after poking around the Google. There are some tools that’ll do format conversions and so forth, but they were way more expensive than I thought was reasonable, seeing as we’re not making precious objects. It’s basically a translation of one connected graph format to another.

Okay, so — it was time to think about making our own tools, which took 10 times less time than the original set of Google searches, mostly because of the recently discovered ancient treasure of a Java library our buddy Tom Carden wrote back in the Precambian of the Age of the Network — somewhere’s around 1996 or some such…

This library provides enough functionality to read a GPX track and draw it on screen. The Processing.org DXF library can spit that drawing from the screen out as a saved DXF. So, that basically solves the problem. The DXF files that come out are flat lines that can then be serviced by other software to do other things. (There also exists this Kabeja library for consuming DXF and creating DOM models, which we’ll save for another day.

My lead toward the Carden code was found here on the Processing.org forums, where I found enough of a simple code snippet to get me out and through the chiseled hole in the brick wall I had hit.

Also to note is the small comment in the small simple bit of code that can cobble together many separate GPX files (tracks from a GPS) into one larger one, which can be quite convenient.

Why do I blog this? Mostly for my own recollection and notes as to how things are done. It’s been enough time in the jungle of small, utility challenges that, when on another project inevitably in the future, some small task I need to perform smells familiar — but, why? One gets the feeling — I’ve had to do this before? What project was it? How did I do it? Playing in the geo/map-making/cartography space has all these little formats and translation steps that are a bit zany to wrangle. Jotting a post with a bit of a reminder helps. Te bigger “why do I blog this?” has to do with using real-world GPS tracks as a basis for constructing other things — the input is movement in the world, an effort to figure out how a map might look that inverted the assumptions about static geographies and fluid movement, so that the ground moved and the things that moved became static. *shrug*


import processing.dxf.*;

// Based on Tom Carden's code and GPX library available at
// http://www.processing.org/hacks/hacks:gpx
// Press "R" and your track gets saved as a DXF file which
// you can use in lots of other things..

import tomc.gpx.*;

GPX gpx;

GPXTrack track;
GPXTrackSeg trackSeg;
String trackName;

double minLat, maxLat;
double minLon, maxLon;
double minEle, maxEle;
boolean record = false;

final static int SEPARATOR = 200;
String filename, filepath;

// I collapse lots of individual GPX files into one larger file
// using the free gpsbabel.
// At the command prompt (the GUI editions don't have enough features
// to do this) you'll do something like this:

/************

/Applications/GPSBabel+-1.3.5/gpsbabel -i gpx -f 20090321.gpx 
-f 20090401.gpx -f 20090402.gpx -f 20090403.gpx 
  -x transform,wpt=trk,del 
  -x radius,distance=5,lat=34.0236,lon=-118.4319,nosort 
  -x transform,trk=wpt,del  
  -o gpx -F foo.gpx

The "-x" filters do a couple of things.
The first -x filter turns the tracks into waypoints to work around
an issue that gpsbabel has with the "radius" filter
The second -x filters the output only to points that are within a
5 mile radius of the specified lat/lon, which is useful if you want
to limit the range of data you draw.
The final -x filter turns the waypoints back into tracks, which is what we want
Finally, we output as GPX formatted data and
write the whole thing to the file called foo.gpx.dxf


*************/

void setup()
{
  // Yep, hardcoded path to the GPX file we'll process
  filepath = "/Users/julian/Desktop/GPS Tracks/foo.gpx";
  // We'll use the name of the file for our DXF output, with the ".dxf" extension added
  filename = (new File(filepath)).getName();
  size(800, 800, P2D);
  gpx = new GPX(this);

  // you can load a file or a URL evidently..
  gpx.parse(filepath);

  // Find scope of track file so we can scale our drawing
  minLat = 2000; minLon = 2000; minEle = 100000;
  maxLon = -1000; maxLat = -1000;

  println("track count "+gpx.getTrackCount());
  for(int j=0; j < gpx.getTrackCount(); j++) {
  track = gpx.getTrack(j);
  println("track size "+track.size());
  for(int k=0; k<track.size(); k++) {
     trackSeg = track.getTrackSeg(k);
     println("track seg size "+trackSeg.size());
  for (int i = 0; i < trackSeg.size(); i++)
  {

    GPXPoint pt = trackSeg.getPoint(i);
    if (pt.lat < minLat)
    {
      minLat = pt.lat;
    }
    if (pt.lon < minLon)
    {
      minLon = pt.lon;
    }
    if (pt.ele  maxLat)
    {
      maxLat = pt.lat;
    }
    if (pt.lon > maxLon)
    {
      maxLon = pt.lon;
    }
    if (pt.ele > maxEle)
    {
      maxEle = pt.ele;
    }
  }
  }
  }
println("Lat: " + minLat + " to " + maxLat);
println("Lon: " + minLon + " to " + maxLon);
println("Ele: " + minEle + " to " + maxEle);
}

boolean hasDrawn = false;

void draw()
{
  if(record == true) {
    beginRaw(DXF, filename+".dxf");
    hasDrawn = false;
  }
  if(hasDrawn == false) {
  background(255);
  //stroke(#FF0000);
  //line(0, SEPARATOR, width, SEPARATOR);

  double distance = 0;

 for(int j=0; j < gpx.getTrackCount(); j++) {
  track = gpx.getTrack(j);
  //println("track size "+track.size());
  for(int k=0; k<track.size(); k++) {
     trackSeg = track.getTrackSeg(k);
     //println("track seg size "+trackSeg.size());
       GPXPoint prevPt = trackSeg.getPoint(0);
      PVector prevPos = GetPosition(prevPt);
      for (int i = 1; i < trackSeg.size(); i++)
      {
       GPXPoint pt = trackSeg.getPoint(i);

    // Show track
    PVector pos = GetPosition(pt);
    stroke(#FF8800);
    line(prevPos.x, prevPos.y, pos.x, pos.y);
    prevPos = pos;
  }
  }
 }
  }
  if(record == true) {
    endRaw();
    record = false; // stop recording to the file
    println("done writing "+filename+".dxf");
  }
  hasDrawn = true;
}

void keyPressed() {
  if (key == 'R' || key == 'r') {
    record = true;
  }
}

PVector GetElevation(int n, GPXPoint pt)
{
  return new PVector(
      map(n, 0, trackSeg.size(), 10, width - 10),
      map((float) pt.ele, (float) minEle, (float) maxEle, SEPARATOR - 10, 10)
  );
}

PVector GetPosition(GPXPoint pt)
{
  return new PVector(
      map((float) pt.lon, (float) minLon, (float) maxLon, 10, width - 10),
      map((float) pt.lat, (float) minLat, (float) maxLat, SEPARATOR + 10, height - 10)
  );
}

Laboratories, Accelerometers And Kitchen Crockery

The Memsic 6202 Accelerometer, fresh out o’the skillet.

We’ve been out of the Laboratory proper — the place where things are constructed and soldered and heated up — for a bit now, not because we don’t go in there, or we haven’t been doing things that will go in there. No, rather, or because of a variety of curiosities that have attracted our attention and, in that way, have slung us around to be more in the mode of reading and observing so as to launch into the next, “next vector of interests.” We believe strongly here allowing things to stew and simmer in the reading-writing-intellect as much as in the making-things section of the young noggin. There is much good that comes from the process of tinkering as well as, err..think-ering, both simultaneously, perhaps. Doing things to help think through what one finds curious or helpful, tinkering our way to new, more habitable world. Making things is a great way to learn. It is also a great way to think-things-through. Rather than the still idle of pondering, as an adjunct to the quiet sit in the cafe — making something that helps add some weight to the thought. Perhaps closer to The Craftsman by Richard Sennett.

The interests today swing around the ways of framing things, explaining and demonstrating and communicating, especially future fictions, perhaps designed fictions, as a way of describing the sometimes peculiar imaginings that get brought upstairs from the boys in the basement in the Bureau of Coming Attractions. We have our Bureau of Urban Scouting, evolving its own practice of observation and, perhaps of more interest, a variety of instruments and procedures for seeing the world sideways. Nicolas and I are in the final stages of this short essay for the Situated Technologies project that will include this topic. Literally in the final stages. As in, right now I should be editing a Google Doc rather than writing this, but I let the morning coffee lead me.

So, this is all to say that this blog swerves constructively, probably baffling all eighteen readers into wondering — what the heck is going on here? Where is the editorial consistency? Where’s my code/diagrams/schematics?

To this I only ask that you look for such things even here, as they are always in-the-making, becoming the hardware in the midst of the thinking-through, in the ideas.

As the curiosity-seeker Jack Schulze recently intoned in a wonderfully Talmudic way"No one cares about what you think, unless you do what you think. No one cares what you do, unless you think about what you do. No one ever really cares what you say."

This may explain Jack’s characteristically quiet demeanor. We’re quite verbally agitated here, so we’ll let the last one slip by us. But, the yammering is the thinking-making-doing, sometimes in words, sometimes in thoughts, sometimes in the materialization of our imagination.

With this said, in response to Reader Number Fourteen’s request this morning for “some code”, I share with you this little thing: Code to Test the Memsic 6202 Accelerometer. You may remember this little guy. We used it in our early foray into fabricating surface-mount electronics. With kitchen skillets. We learned this bit of tinker-y hacker-y from our friends over at Spark Fun. It works great. And it’s cheap as rocks.

I can’t say we were super excited about this particular accelerometer, but that’s okay. It’s just an accelerometer after all. If you follow us down toward the foxhole of these things, you’ll find a whole world of accelerometer fanatics with plenty of material to fuss with — smart accelerometers, accelerometers that let you know when they’re falling, accelerometers that tell you the time as well as whether they’re falling, etc. You’ll find some material buried here in the blog archives, to be exhumed with the right incantation of search and category selection parameters.

This code was written for the Atmel 8-bit microcontrollers that live on the Arduino board. It also makes use of the Wire library for the Arduino, and, as well, the two-wire interface (TWI) hardware support on those microcontrollers, making the whole thing pretty easy-peasy.

#include
#include


// TWI (I2C) sketch to communicate with the Memsic MXC6202J accelerometer
// Using the Wire library (created by Nicholas Zambetti)
// http://wiring.org.co/reference/libraries/Wire/index.html
//
// On the Arduino board, Analog In 4 is SDA, Analog In 5 is SCL
// These correspond to pin 27 (PC4/ADC4/SDA) and pin 28 (PC5/ADC5/SCL) on the Atmega8
// Know You're Microcontroller. Check Which One Is On Your Board/Arduino.
// Newer (as of May '09) Arduinos Use The Atmega168. Can You See It?
// The Wire class handles the TWI transactions, abstracting the nitty-gritty to make
// prototyping easy.

void setup()
{
  Serial.begin(9600);
  Serial.println("Naah??");
  Wire.begin(); // join i2c bus (address optional for master)
}

void loop()
{

  byte x_val_l, x_val_h, y_val_l, y_val_h;
  int x_val, y_val;
// transmit to device with address 0x1D
// according to the MXC6202xMP datasheet, the TWI/I2C address of is fixed
// at the factory depending on what the "x" in the part name is
// Read The Data Sheet section "I2C Bus Data Transfer" for descriptions
// of what the part expects to receive as its address. If you do not understand
// how this works, you are guaranteed to get lost/confusted/frustrated.
// 00010000b is the address of the chip I have, or 0x10

  // a little fuzzy in my recollection here. I think this initializes the chip
  // by setting some bits in the Memsic device's 8-bit internal register
  // Not 100% sure why I initialize two bytes.
  Wire.beginTransmission(0x10);
  Wire.send(0x00);
  Wire.send(0xF0);
  Wire.endTransmission();

  delay(100);

  Wire.beginTransmission(0x10);
  Wire.send(0x00);
  Wire.endTransmission();

  // request five bytes of data
  // first one is the internal register, we can ignore that, but lets look at it for giggles
  Wire.requestFrom(0x10,5);
  while(Wire.available()) {
    Serial.print(Wire.receive(), HEX); // drop the internal register on the floor..
  }
  Serial.println("****");

  // next byte is the MSB of the X axis acceleration
  if(Wire.available()) {
    x_val_h = Wire.receive();
  }
  // next byte is the LSB of the X axis acceleration
  if(Wire.available()) {
    x_val_l = Wire.receive();
  }
  // next byte is the MSB of the Y axis acceleration
  if(Wire.available()) {
    y_val_h = Wire.receive();
  }
  // next byte is the LSB of the Y axis acceleration
  if(Wire.available()) {
    y_val_l = Wire.receive();
  }
  // cobble all of this together into 16 bit acceleration values
  // doing the usual bit-shift polka
  x_val = (x_val_h << 8); x_val |= x_val_l;
  y_val = (y_val_h << 8); y_val |= y_val_l;
  // spit it all out across the serial port so we can see it
  Serial.print(x_val_h, HEX); Serial.print(" "); Serial.print(x_val_l, HEX); Serial.print("n");
  Serial.print(y_val_h, HEX); Serial.print(" "); Serial.print(y_val_l, HEX); Serial.print("n");
  Serial.println("===");

 Wire.endTransmission();

 // loop and do it again.
  delay(200);
}

Continue reading Laboratories, Accelerometers And Kitchen Crockery

A New Logic Analyzer and the HMC6352 I2C Compass

25092008_023841

25092008_024008

Two things that’ve been sitting on my bench for a good spell — this HMC6352 magnetic compass with an I2C interface, and the Saleae “Logic” logic analyzer. I figured I could combine the two together, showing how I used the Logic to check out the operation of the HMC6352.

First, the HMC6352 is a pretty easy to use magnetic compass with 0.5 degree accuracy. It’s all wrapped up nicely with a pretty normal I2C interface to a bunch of registers on the device, and command-driven queries for reading the compass heading.

The Saleae “Logic” logic analyzer is pretty sweet for debugging I2C as I’ve mentioned in the past. This one is nice and compact, with a reasonable bunch of logic lines for doing simple analysis. I played with this one for a number of projects over the last few months — mostly I2C projects, which is where most of my interface work is these days. But, the “Logic” will also work with a bunch of stock “analyzers” for RS232 and SPI as well, making it pretty versatile for many situations.

The analyzer is a pretty compact package — 1.6″ square and only .36″ high. So, basically miniature for a logic analyzer. It comes with a 9 conductor umbilical along with E-Z-Hook XKM probes that you can use or not, depending on how you’re hooking up to things.

25092008_024813

25092008_025316

The Logic has a pretty easy-to-use bit of front-end software to handle all the set-up and UI work for the teeny-tiny hardware. It’s a UI that is unlike what you might expect from a bit of Windows-based software. It’s very gooey, using some subtle screen effects and UI elements that, for this OSX guy, are not what I think of when I think of XP. Which is good. It makes using the UI not feel like I’m being forced to drink a Rusty Nail or something for breakfast.

The analyzer samples much quicker than I normally have need for and does so without any problems. I’m usually down in the low range — .2 MHz and 1 M samples is usually plenty for what I’m doing. But, if you need a wider range of samples or a higher sample rate, the analyzer will go all the way up to 24 MHz. Those 9 conductors are 8 data lines plus one ground, so you can analyzer an 8-bit wide bus if you wanted at 24 MHz.

So, I put the Saleae Logic on the HMC6352 circuit to give it a shot. First, the HMC6352 set-up.

Although the HMC6352 has a wide voltage range, I was playing around with a level shifter circuit that was already hooked up to an Arduino on the bench, so I went ahead and just kept that circuit as is. So, the basic set-up is my Arduino I2C lines (SDA and SCL) going through a bi-directional level shifter shifts 3.3V 5V, and then to the HMC6352 SDA and SCL lines. I use yellow wire for SCL and blue wire for SDA.

25092008_024251

25092008_024344

I ended up using the Saleae Logic to dig a bit deeper into the communication between my microcontroller (an Atmega168 sitting on an Arduino) and the HMC6352 as a way to test out the logic analyzer.

First I wanted to just probe the I2C communication. The basic transaction my Arduino code was doing was to send an “A” to the HMC6352. According to the specification sheet, writing an “A” to the device causes it to return two bytes of data — the high-order and low-order bytes of a 16 bit value indicating the compass’ heading. Easy enough. Here’s the Arduino doing just that. First, it sets up the write to the I2C device at address 0x21. Then it writes an “A” which, in the ASCII table, is the value 0x41. (N.B. The spec sheet says the HMC6352 is at address 0x42 but — and don’t ask me why — sometimes the address specified has to be right-shifted one bit in order to “take”. I mean I sort of know why, but I don’t know why this is the case sometimes — a r/w bit thing or something. Too much to bother with, but a good way to make good use of a logic analyzer when you’re stuck wondering why your device doesn’t seem to be listening to you. I learned this the long way and only had a fancy DSO to try and debug it.)

25092008_025019

25092008_025136

25092008_025615

There it is in the top picture. A simple write to the device at address 0x21 with all the ACKs, meaning whatever is out there, heard us and is acknowledging receipt of the write. And, it looks like we get two bytes of data back — a 0x03 and a 0x64. The first byte will be the high-order byte and the second byte is the low-order byte. 0x0364 is 868, which we normalize by dividing by 10, to get 86.8 degrees. Done. I’m pretty sure that’s that. Finally, the measurement features are pretty cool — useful for confirming clock speeds or verifying a bit train. There’s a good use of a simple, pretty inexpensive ($150) logic analyzer that’ll certainly save you $150 worth of your time many times over. Plus, the small size and convenience of USB make it easy enough to fit on your bench and store away or travel around with when it’s not in use. My only quibble is that it’s only for Windows, but that’s a minor one. I don’t really play too hard in the OS religious wars. I run whatever makes my life easier at whatever moment. So, a $200-ish Windows chassis in the laboratory that just runs a few apps like some CAD software and things like my Propeller coding environment, .NET development, software for test equipment like this and AVR Studio 4 — it just helps me get things done rather than being adamantine about which OS religion I’ll adhere to and, then, not getting anything done except spending time porting things from one OS to another or complaining about how much a Windows license costs or whatever.

Wow. Okay. Off my high horse. Check this logic analyzer out. I can recommend it after using it for a few months.

#include

// http://wiring.org.co/reference/libraries/Wire/index.html
// On the Arduino board, Analog In 4 is SDA, Analog In 5 is SCL
// The Wire class handles the TWI transactions, abstracting the nitty-gritty to make
// prototyping easy.
// This sketch has a HMC6352 attached to the I2C bus, through a bi-directional
// level-shifter circuit.
int address = 0x42 >> 1;
 int reading;
void setup()
{

  Serial.begin(9600);

  CLKPR = (1<<clkpce);
  CLKPR = 0;

  // initialize the TWI / I2C Bus
  Wire.begin(); // join i2c bus (address optional for master)

}

void loop() {

  Wire.beginTransmission(address);
  Wire.send('A');
  Wire.endTransmission();
  //delay(10);
  Wire.requestFrom(address, 2);

   if(2 <= Wire.available())    // if two bytes were received
  {
    reading = Wire.receive();  // receive high byte (overwrites previous reading)
    reading = reading << 8;    // shift high byte to be high 8 bits
    reading += Wire.receive(); // receive low byte as lower 8 bits
    reading /= 10;
    Serial.println(reading);   // print the reading
  }

//  delay(50);
}

Continue reading A New Logic Analyzer and the HMC6352 I2C Compass

Urbanism, Data-Driven


(Some cartogram structures and linkages Pascal created as sensor maps where the geography is an implied and driven — rather than the driving — parameter.)

Fabien Girardin, MIT brainiac and a fellow Near Future Laboratory ‘naut presented his expert and insightful trinity of themes that appear to be the main motivators behind "urban computing", a bit of cocktail party conversation related to this short essay that Kazys Varnelis and Leah Meisterlin wrote, and that Nicolas poked a stick at.

Fabien describes three prime motivators behind the “urban computing” discussion and construction projects. Bears repeating, with some commentary.

1. Creating an opportunistic and optimistic urban environment that is somehow “better” than one without some sort of underlying computational infrastructure. This is the realm of the ubiquitous computing folks. The visions are aspirational — near-future signposts always already just out of reach and endlessly deferred — perhaps this is the definition of industry and academic R&D. Provide curious near-future fictions about possible near-future worlds. Effectively science-fiction expressed through objects and scenarios and large research (instead of film and television production) budgets. (Cf ““Resistance is Futile”: Reading Science Fiction Alongside
Ubiquitous Computing”, Paul Dourish and Genevieve Bell, forthcoming in Personal and Ubiquitous Computing.)

2. Study of the impacts and implications of computational systems and operations in an urban context, performed by GIS folk and urban planners and computer science people. These are studies of the dynamics of data-people sensed in the city, mapped and used to develop insights on urban conditions, or perhaps used as contributing information for design or re-design of urban geography. In some cases the data is interpreted in real-time, such as you might expect to be done to manage loads on cellular data networks, etc.

3. The action, postulating and creative exercises of independent and undisciplinary explorers and adventurers who shuffle and interleave the interesting data points that are anchored to geographies that can be re-mapped. This has brought us such interesting tidbits as the various crime data maps, for example, or the flows of automobiles throughout cities. (Using the geeky, horribly, self-deprecatingly named “mash-ups”.) What is happening here that is most compelling to me at least is that “GIS” activities are becoming something that anyone, without a license or professional sanction can do, inexpensively (i.e. for free and a bit of time). So, anything new here and worth glorifying is this fact — we can be our own map-makers. This is significant in the Foucauldian power-knowledge sense of significant. Cartograms such as the fundraising maps, which started out as a very DIY project, are important because they create new meaning and possibilities for action, or shifts in perspective that can lead to change, rather than stagnation. (There’s a normative element, of course. Visualizations of data with geographies, in the power-knowledge realm of things, are as free to interpretation as anything that alleges to be fact, so they can’t be counted upon to prove much of anything. At worst, they can be enrolled in larger conversations, just like any good knowledge agent.)


(Some more of Pascal’s cartogram structures and linkages. Sensor maps recorded amongst a small group of us and visualized such that the geography is an implied and driven — rather than the driving — parameter.)

I’d add a bit here. How can the experience of space — urban space, just to stick to the most discussed idiom — become fruitfully and playfully data-driven? Mapping data onto geographies by their instrumentalized location in space is okay. It’s a bit knee-jerk, though. What I mean is that re-mapping data with an entirely different basis could possibly produce an entirely different urban experience that may, at worst, be curious and playful. Just to see what comes of this. The Laboratory has been working for way too long on such re-mappings that do not assume that latitude and longitude are the canonical reference points for whatever data gets built on top.

I know..I know. This is weird. And the first project I was involved with that did this — an edition of PDPal that created maps based exclusively on interpreting an “experience” — fought a losing battle. But there are some indicators that this approach is at least worth exploring.

Creating “maps” that have a spatial aspect to them, but that do not use space as the primary or explicit reference datum can then emphasize people-practices or experiences first, with the geographic datum implicit or made into a driven, rather than driving, parameter. Paulos and Goodman’s “Jabberwocky” project comes to mind, as do Twitchr and other mappings of “encounters” and actions that are not expecting to use GPS first-and-foremost.

Situationist Space (Thomas F. McDonough)

Here in the Laboratory we’ve been building several prototype devices that record Bluetooth encounters for later visualization. The first prototype was a small Python script running on a Nokia N70 phone. It was like a little data attractor that would sniff for Bluetooth devices, and then record them in a log file, and send them to a server to be persisted in a database.

Two recent editions used a small, custom-designed device that used the Bluetooth scanning mode of a radio to essentially do the same thing, storing the results in a Flash memory device to be later extracted with a simple USB interface. Lately a bunch of us in the studio have been working with a similar concept in the form of a background sensing application developed at Nokia R&D for S60 devices that does all of this, plus quite a bit more. (Currently in closed alpha, but soon to be available publicly.) The biggest challenge (besides running it on an S60 device..) is making meaning out of all of this. The strongest vectors for me, at least, lead to re-mapping and exploring possibilities playfully, with no specific expectations that there is intelligence in the maps by themselves.


# install first the following files to your phone
# (they will install automatically the needed libraries for the script below):
# aosocket-series60_v20.sis, pdis.sis
# found at: http://pdis.hiit.fi/pdis/download/pdis/

# this script was found from forum.nokia.com

import e32
import appuifw
##import aosocketnativenew
from pdis.lib.logging import *
##import httplib, urllib
from location import gsm_location
import time
import sysinfo
import select

cell = []
init_logging(FileLogger("e:\apache\htdocs\bt_probing.html"))

def tell(string):
    #logwrite(string)
    if e32.is_ui_thread():
        print string
        e32.ao_yield()

# ----------------------------------
from aosocket.symbian.bt_device_discoverer import *
#from socket import * # for obex file send

def discovered(error, devices, cb_param):
    if error == 0:
        global cell
        #tell("start discovery..")
        cell = gsm_location()

        #tell("devices: " + str(devices))
        tell(cell)
        #print location.gsm_location()
        for address, name in devices:
            logwrite("")

            tell(cell[0])
            tell(cell[1])
            tell(cell[2])
            tell(cell[3])
            print location.gsm_location()
            urllib.urlopen("http://smalltown.nearfuturelaboratory.com/upload_bt.jsp",
                           urllib.urlencode([('addr',address),('name',name),
                                             ('imei', sysinfo.imei()),
                                             ('batt', sysinfo.battery()),
                                             ('signal', sysinfo.signal()),
                                             ('cell_0', cell[0]), ('cell_1', cell[1]),
                                             ('cell_2', cell[2]), ('cell_3', cell[3])]))
    else:
        tell("device discovery failure: error %d" % error)
    _discoverer.close()


# -----------------------------------------------------------------------------


while(1):
    try:
        _discoverer = BtDeviceLister()
        _discoverer.discover_all(discovered, None)
        #tell("discovering")
        e32.ao_sleep(15)
        print "scanning again"
    except:
        tell("init failure")
        appuifw.note(u"Fatal error.", "error")

Continue reading Urbanism, Data-Driven

PSX (With Propeller) — Digital Edition

DSC_5329

So, feels just a little bit like converting religions or something, but I’ve started looking into other kinds of microcontrollers for practical reasons as well as to just generally expand what sits in my prototyping sketchpad. I’ve been curious about the Parallax Propeller, which has a multi-processor (they call them “Cogs”) core and can do video and audio gymnastics. It uses its own proprietary language called “Spin” which is pretty legible if you’ve done any high level programming. Assembly is also an option. The idea of having a small “microcontroller” that has built in multi-processor capabilities feels a bit over-blown, but it’s actually fairly liberating in the design phase. Suddenly, projects start to look like parallel tasks that can be designed with object-oriented sensibilities in mind. All of the “task switching” (cog switching) is handled automatically. Timing and so forth is fairly static so it’s possible to predict precisely how often tasks will get run. The Propeller will also run wickedly fast — way faster than an Atmel 8-bit microcontroller. Normally, speed hasn’t been an issue for projects, but it’s nice to have some overhead — up to 80 MHz in this case (versus 20 MHz for the Atmel, which is more then enough for most things, but the lickity-split clock makes doing embedded video projects possible.

Anyway, this seemed like a good possible fit for the PSX project because I need to simultaneously read a joystick and service polling requests from the console. I probably _could_ do both of these tasks with an Atmel microcontroller running at 20 MHz, interrogating the controller in between the console’s polling. Experience thus far has led me to think that this may not be the easiest thing to do, even sequentially. I’ve tried just servicing the console using the built-in SPI hardware in an Atmel 8-bit microcontroller and the timing is a bit flakey. Perhaps someone with a little more expertise in these things could take this on.

[ad#ad-1]

In the meantime, I went for a few extra cylinders and some more octane, which works better than well-enough.

I found some code that Micah Dowty put together for a Playstation project called Unicone. He had some code there in the midst of that project that was easily adapted to my weird PSX controller-that-gets-tired project.. Works like a charm.

I can specify the data that’ll go across the wire very simply, as a pointer to a buffer of data and Dowty’s object takes care of all the rough-stuff communication to the console. I can even specify what kind of controller I’m emulating, if I want. What’s left is to create an object that polls the real controller, makes some decisions about how “tired” it should be and changes the analog control stick values based on that tiredness and places this data in the buffer. Because the Propeller’s “Cogs” can share data, this should be pretty easy..

This is a trace of communication when I use the code below. The buffer is sent across just as it is, and Dowty’s object is smart enough to send it as if it were a simple PSX analog joystick N.B. the first two bytes after 0xFF, 0x73, 0x5A pre-amble are the button settings, as a 16 bit word, followed by four bytes representing the analog joystick positions. In the DAT section of the code at the very bottom of the listing below, this is exactly what I want sent. Simple.

Arduino and the Maxim DS1306 Real Time Clock

I’m working on a project that needs a real-time clock, and so I thought I’d learn how to use one by interfacing it with an Arduino. Ultimately, it will interface with another AVR, but the Arduino is great for just prototyping the design, to make sure I really know how to work with the device.

I chose the Maxim DS1306 because it is an SPI compatible device, meaning I can just throw it on an SPI bus along with anything else. It’s also register based, so setting the time or reading the time is a fairly simple affair — you just read or write the appropriate register. The DS1306 has a sibling, the DS1305. Near as I can tell, the major differences are the existence of a 1Hz interrupt-drivable heartbeat on the DS1306.

The device also has a nice range of options for backup power, and keeping time even when primary power is cut off. I can use nothing and just loose the time, or I can hook up a 3V lithium battery to VBAT, or a 3V rechargeable, or a super capacitor. The last two options can be trickle-charged by the DS1306 while it is on primary power, so essentially, for most practical cases, the device will always keep the time, even when disconnected from primary power. It can even continue to generate an alarm (see below) while off primary power.

If you need a simple heartbeat, the DS1306 can provide an open-drain 1Hz signal that’s also suitable for driving an interrupt pin. It also provides a 32.768kHz signal that could be used similarly (the same frequency as the crystal used to drive the DS1306.)

The only real external components you’ll need for this are a 32.768 kHz crystal (pretty standard) and possibly a couple of pull-up resistors and some kind of battery (rechargeable or a simple lithium coin cell) or a super capacitor as back up power. Or, if you go without the backup power, just ground VCC2 and VBAT, as per the specification sheet.
Schematic

Easy-peasy. If you don’t plan on using INT0 for an alarm and/or don’t plan on using the 1Hz pulse you can leave those disconnected and you won’t need the pull-up resistors. (I used a 10K Ohm resistor.) The spec sheet for the DS1306 will tell you this, but you should tie VBAT to ground if you’re not using it. There’s a whole section on how to deal with VCC2 and VBAT depending on whether you actually have a backup supply, use a fixed battery, rechargeable or a super-capacitor, and how you charge the chargeable stuff. Read it carefully.

Programming
The device uses the SPI bs to communicate, and does so in SPI Mode 1, which means that the clock polarity is such that the leading edge is rising and the trailing edge is falling. (SPI has four modes, numbered 0-3, with all the combinations of clock polarity and clock phase. The device you’re using should specify either the mode or say something about the particular clock polarity or clock phase, and then you need to adjust the SPCR register appropriately. It’s simple, but a nuisance and probably the first thing you should check if something isn’t working correctly.) But, the clock phase is such that the leading edge is when the data is setup, and the trailing edge is when the data is sampled. The clock phase may seem “opposite” of convention, but it’s just another way of doing business. (In fact, if you look in the ATMega8 specifications — or any of the Atmel microcontrollers that support Serial Peripheral Interface – you’ll find that there are four SPI modes, with all the combinations of clock phase and clock polarity.) It’s just that the DS1306 expects the clock phase to sample on the trailing edge. How do I know this? The DS1306 specification document says so in its section on the Serial Peripheral Interface. (Woe is me for not reading the document thoroughly the first time I tried to use it – I didn’t even think that it might operate in a different SPI mode until a couple of hours of debugging finally lead me there..)

So, our set up of the ATMega8’s SPI interface has to be such that it’s ready to operate in the right mode. This little code idiom will do it:

  // SPI Enable (SPE) to one
  // Master/Slave Select (MSTR) to one turns the ATMega8 into the master
  // Clock Phase (CPHA) set to 1 means sample on the trailing edge
  // sample on trailing edge of clk for the DS1306
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPHA);
  clr=SPSR;
  clr=SPDR;

Check the ATMega8 spec for more details about these registers.

(Parenthetically, I’ve pretty much got to the point where I have a bunch of devices on the SPI bus, and some of them require different modes. I’ve abstracted all of this functionality into read_register and write_register functions that accept a host of parameters, including what the clock phase and such all should be.)

What you do to program the DS1306 is basically write to and read from the registers. The registers hold the current time, the alarm settings, a number of control bits, and a small, 96 byte scratchpad of user RAM that you can read and write for your own purpose.

Each of the registers has it’s own read from and write to address, meaning each register has two addresses — one you use to read from the register, and one you use to write to the register. If you want to set the current day of the week, you’d write a value to the register at address 0x83. If you wanted to find out the current seconds, you’d read from the register at address 0x00.

Here’s the entire register table.

The control register manages enabling the two alarms, enabling the 1Hz heart beat output, and turning on or off the write protection, if you want to prevent any accidental writing of the registers. If you want to do a basic initialization of the device, the first thing you’d do is probably write 0x04, which would enabling writing registers, turn the heart beat on, and disable both alarms. Then you’d set the time by writing the the second, minute, hour and day of week registers, at which point the device would start keeping time. (Setting the right time initially requires getting the right time somehow – presumably through some sort of interface with the time keeping mothership or by setting it manually.)

BCD Time Data
All the time is stored in these registers in binary coded decimal (BCD), which is a data format appropriate for storing decimal numbers in a binary register. Decimal, of course, are numbers between 0-9. As is the case with time and date data, all the numbers there are decimal, too. So, storing this information as BCD makes sense — you don’t have to do any conversions from hexidecimal to a suitable display format. In other words, converting 0x0A to a number that represents seconds is avoided — the digit to be represented is what is stored in the register.

BCD stores each digit in half a byte (or a nibble, or 4 bits). Now, with 4 bits, you can represent any integer between 0-15 (or 0x00-0x0F in hexadecimal.) With BCD, the only “legal” integers are 0-9 (or 0x00-0x09 in hexadecimal). By convention, the other numbers are just ignored, or treated as illegal.

The DS1306 plays the same game. So, when you want to set the seconds to 45, you’d write 0x45 to the register at 0x80. If you wanted to set the hour to 8, you’d write 0x08 to the register at 0x82. Funny thing about hours to note is that you can either track in 24 hour time or the AM/PM variety. It’s up to you, but you need to set your register appropriate. It gets just a little bit more complicated with AM/PM time — read the spec for details on how to deal with that.

Alarms
The great thing about this RTC is that you can set a real-time alarm. Actually, the device supports two alarms in the form of the INT0 (active low) and INT1 pins. These pins are suitable for generating an signal on a microcontroller interrupt pin. (INT0 is active low, and open drain, so you’ll need to tie a 10K resistor or something from pin 5 to VCC1 to get a suitable signal to the microcontroller.)

Each alarm is set through four registers, one each for seconds, minutes, hours and day of week. The registers are used to match against the current time. In order to set up an alarm, you’ll need to set these registers to the time and day of the week you want the alarm to occur. Or, you can use the special mask bits, bit 7 of each register, to indicate a kind of catch-all. For instance, setting the mask bit in the minutes register indicates “all” minutes for the specified second, hour and day of week. Or, you could use the mask bit to have an alarm occur every minute, by setting the seconds register to 0x00, and setting the mask bit of all the other registers.

I mentioned the two alarms on the DS1306. Well, it turns out that INT1 is really only available when the device is powered by VCC2 (the backup supply) or VBAT (the battery supply). The other, the active-low interrupt 0 output INT0, is available when the device is powered by by VCC2, VBAT or VCC1 (the primary supply.) So, I guess you can have a special alarm that occurs when the device is running on the battery, or maybe this was an electrical design issue – INT1 isn’t open-drain, so it’s driven by VBAT or VCC2, which is something to keep in mind.

Here’s the Wiring/Arduino code to work with the DS1306 on the ATMega8, not the ATMega168 which is what the modern Arduinos use as their microcontroller. You’ll need to double-check which pins you’re hooked up to if you’re using another microcontroller.

Scroll down below to find the code for the modern Arduino’ss

This code sets the device up, and starts an alarm at 0 seconds, and then every 15 seconds thereafter. The alarm is the INT0 active low one on pin 5 of the DIP package. You’ll need to tie a pull-up resistor from that pin to VDD, or pin 9, and then tie a bit of hook-up wire from pin 5 to Arduino digital pin 3 (which is INT1 on the ATmega8).

You can also download the ATMega8 code here.

// ATMega8 Code
// ATMega8 Code
#define DATAOUT 11 //MOSI
#define DATAIN  12 //MISO
#define SPICLOCK  13 //sck
#define RTC_CHIPSELECT 7 // chip select (ss/ce) for RTC, active high
#define LED 10

byte clr;
char spi_transfer(volatile char data)
{
  /*
  Writing to the SPDR register begins an SPI transaction
   */
  SPDR = data;
  /*
  Loop right here until the transaction is complete. the SPIF bit is
   the SPI Interrupt Flag. When interrupts are enabled, and the
   SPIE bit is set enabling SPI interrupts, this bit will set when
   the transaction is finished.
   */
  while (!(SPSR & (1<<spif)))
  {
  };
  // received data appears in the SPDR register
  return SPDR;
}

void setup()
{
  char in_byte;
  clr = 0;
  in_byte = clr;
  Serial.begin(9600);
  // set direction of pins
  pinMode(LED, OUTPUT);
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(RTC_CHIPSELECT,OUTPUT);
  digitalWrite(RTC_CHIPSELECT,LOW); //disable RTC


  // set up the RTC by enabling the oscillator, disabling the write protect in the control register,
  // enabling AIE0 and AIE1 and the 1HZ Output
  // 0x8F to 00000111 = 0x03
  // EOSC Active Low
  // WP Active High, so turn it off
  write_rtc_register(0x8F, 0x01|0x02|0x04);

  // little sanity checks
  in_byte = read_rtc_register(0x0F);
  Serial.print("CTRL REG [");
  Serial.print(in_byte, HEX);
  Serial.println("]");
  delay(10);

  in_byte = read_rtc_register(0x10);
  Serial.print("STATUS REG [");
  Serial.print(in_byte, BIN);
  Serial.println("]");

  // set up both alarms at 00 seconds?
  write_rtc_register(0x87,0x00);
  // mask all the other registers
  write_rtc_register(0x88,0x80);
  write_rtc_register(0x89,0x80);
  write_rtc_register(0x8A,0x80);

  write_rtc_register(0x8B,0x00);
  write_rtc_register(0x8C,0x80);
  write_rtc_register(0x8D,0x80);
  write_rtc_register(0x8E,0x80);

  in_byte = read_rtc_register(0x06);
  Serial.print("YEAR [");
  Serial.print(in_byte, HEX);
  Serial.println("]");

  in_byte = read_rtc_register(0x05);
  Serial.print("MONTH [");
  Serial.print(in_byte, HEX);
  Serial.println("]");

  // enable INT0, PORTD Bit 2 on the Atmega 8
  // we'll attach the 1HZ signal from the 1306, pin 7
  // or we can attach the active low interrupt output (pin 5 on the DS1306 DIP package)
  // to digital pin 3 on the Arduino (INT1 on the ATmega8)
  // I just picked INT1 arbitrarily - I think you could use any of the interrupts, so long as it
  // wasn't already being used by some other part of the Arduino.
  // cf. http://www.arduino.cc/en/Hacking/PinMapping?from=Main.PinMapping
  Serial.println(GICR, HEX);
  // enagle INT1 in the global interrupt control register
  GICR = (1<<int1);
  Serial.println(GICR, HEX);

  //Set the Interrupt Sense Control 1 Bit 1 and Bit 0
  //in the MCU control register
  //So that a falling edge of INT1 generates an interrupt request
  MCUCR = (1<<isc01);
  MCUCR = (0<<isc00);

  digitalWrite(LED, HIGH);

}

// can't really share variables unless they're declared
// "volatile", otherwise, they'll be set here, then popped
// back to their values before the interrupt handler was called
ISR(INT1_vect) {
  //signal that we have an interrupt
  //turn on the LED
  byte a;

  digitalWrite(LED, HIGH);
  // writing or reading from the DS1306 registers resets the alarm
  // so as to cause an alarm every 15 seconds.
  a = read_rtc_register(0x07);
  if(a == 0x00) {
    write_rtc_register(0x87,0x15);
  }
  if(a == 0x15) write_rtc_register(0x87,0x30);
  if(a == 0x30) write_rtc_register(0x87,0x45);
  if(a == 0x45) write_rtc_register(0x87,0x00);

  // every minute Ñ set bit 7, the mask bit, to 1
  write_rtc_register(0x88,0x80);
  // every hour
  write_rtc_register(0x89,0x80);
  // every day of the week
  write_rtc_register(0x8A,0x80);

}

void write_rtc_register(char register_name, byte data) {
  write_register(register_name, data, RTC_CHIPSELECT, HIGH, true, true);
}

char read_rtc_register(char register_name) {
  return read_register(register_name, RTC_CHIPSELECT, HIGH, false, true);
}

// reads a register
char read_register(char register_name, byte cs_pin, byte cs_active_level, boolean read_high, boolean cpha_trailing)
{
  char in_byte;
  if(cpha_trailing) {
    SPCR = (1<<spe)|(1<<mstr)|(1<<cpha)|(0<<spr1)|(0<<spr0);
  }
  else {
    SPCR = (1<<spe)|(1<<mstr)|(0<<cpha)|(0<<spr1)|(0<<spr0);
  }
  clr = SPCR;
  clr = SPDR;
  if(read_high) {
    // need to set bit 7 to indicate a read for the slave device
    register_name |= 128;
  }
  else {
    // if read low, means A7 bit should be cleared when reading for the slave device
    register_name &= 127;
  }
  // SS is active low
  digitalWrite(cs_pin, cs_active_level);
  // send the address of the register we want to read first
  spi_transfer(register_name);
  // send nothing, but here's when the device sends back the register's value as an 8 bit byte
  in_byte = spi_transfer(0);
  // deselect the device..
  if(cs_active_level == HIGH) {
    digitalWrite(cs_pin, LOW);
  }
  else {
    digitalWrite(cs_pin, HIGH);
  }
  return in_byte;
}


// write to a register
// write_high if true indicates set A7 bit to 1 during a write
void write_register(char register_name, byte data, byte cs_pin, byte cs_active_level, boolean write_high, boolean cpha_trailing)
{
  if(cpha_trailing) {
    SPCR = (1<<spe)|(1<<mstr)|(1<<cpha)|(0<<spr1)|(0<<spr0);
  }
  else {
    SPCR = (1<<spe)|(1<<mstr)|(0<<cpha)|(0<<spr1)|(0<<spr0);
  }
  clr=SPCR;
  clr=SPDR;
  // char in_byte;
  if(write_high) {
    // set A7 bit to 1 during a write for this device
    register_name |= 128;
  }
  else {
    // clear bit 7 to indicate we're doing a write for this device
    register_name &= 127;
  }
  // SS is active low
  digitalWrite(cs_pin, cs_active_level);
  // send the address of the register we want to write
  spi_transfer(register_name);
  // send the data we're writing
  spi_transfer(data);
  if(cs_active_level == HIGH) {
    digitalWrite(cs_pin, LOW);
  }
  else {
    digitalWrite(cs_pin, HIGH);
  }
  //return in_byte;
}

void loop()
{
  byte in_byte;
  // keep track of what our seconds alarm register is..
  // we use this in the ISR to make sure we alarm every 15 seconds
  in_byte = read_rtc_register(0x07);
  Serial.print("sec alarm is ");
  Serial.print(in_byte, HEX);

  in_byte = read_rtc_register(0x00);
  Serial.print(" SECS=");
  Serial.print(in_byte, HEX);

  in_byte = read_rtc_register(0x01);
  Serial.print(" MINS=");
  Serial.print(in_byte, HEX);

  in_byte = read_rtc_register(0x02);
  Serial.print(" HRS=");
  Serial.println(in_byte, HEX);
  digitalWrite(LED, LOW);
  delay(500);
}

Addendum: The example above was written in 2006 and was developed before Arduino started using the ATMega168 — it was built for the ATMega8. Between the two chips were a number of register changes and renames. They were moved around a bit in the hardware and so forth. There are often migration documents on the Atmel website or in the AVR Freaks site. (I ran into a similar problem migrating from an ATMega32 to an ATMega324, which you can read about here, fyi. That community is great for help, as are the specification sheets which you can compare and search for register names to help muddle through these simple, but annoying sorts of issues. Fortunately, it shouldn’t happen that often with the Arduino environment and its careful attendants.)

Here’s updated code for modern Arduino’s using the ATMega168. Double check your pins, too. Make sure you have the right Arduino pin for INT1, MOSI, MISO, SCK, the LED and the RTC_CHIPSELECT


// ATMega168 Code
// ATMega168 Code
#include
#include

#define DATAOUT 11 //MOSI
#define DATAIN  12 //MISO
#define SPICLOCK  13 //sck
#define RTC_CHIPSELECT 7 // chip select (ss/ce) for RTC, active high
#define LED 10

byte clr;
char spi_transfer(volatile char data)
{
  /*
  Writing to the SPDR register begins an SPI transaction
   */
  SPDR = data;
  /*
  Loop right here until the transaction is complete. the SPIF bit is
   the SPI Interrupt Flag. When interrupts are enabled, and the
   SPIE bit is set enabling SPI interrupts, this bit will set when
   the transaction is finished.
   */
  while (!(SPSR & (1<<spif)))
  {
  };
  // received data appears in the SPDR register
  return SPDR;
}

void setup()
{
  char in_byte;
  clr = 0;
  in_byte = clr;
  Serial.begin(9600);
  // set direction of pins
  pinMode(LED, OUTPUT);
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(RTC_CHIPSELECT,OUTPUT);
  digitalWrite(RTC_CHIPSELECT,LOW); //disable RTC


  // set up the RTC by enabling the oscillator, disabling the write protect in the control register,
  // enabling AIE0 and AIE1 and the 1HZ Output
  // 0x8F to 00000111 = 0x03
  // EOSC Active Low
  // WP Active High, so turn it off
  write_rtc_register(0x8F, 0x01|0x02|0x04);

  // little sanity checks
  in_byte = read_rtc_register(0x0F);
  Serial.print("CTRL REG [");
  Serial.print(in_byte, HEX);
  Serial.println("]");
  delay(10);

  in_byte = read_rtc_register(0x10);
  Serial.print("STATUS REG [");
  Serial.print(in_byte, BIN);
  Serial.println("]");

  // set up both alarms at 00 seconds?
  write_rtc_register(0x87,0x00);
  // mask all the other registers
  write_rtc_register(0x88,0x80);
  write_rtc_register(0x89,0x80);
  write_rtc_register(0x8A,0x80);

  write_rtc_register(0x8B,0x00);
  write_rtc_register(0x8C,0x80);
  write_rtc_register(0x8D,0x80);
  write_rtc_register(0x8E,0x80);

  in_byte = read_rtc_register(0x06);
  Serial.print("YEAR [");
  Serial.print(in_byte, HEX);
  Serial.println("]");

  in_byte = read_rtc_register(0x05);
  Serial.print("MONTH [");
  Serial.print(in_byte, HEX);
  Serial.println("]");

  // enable INT0, PORTD Bit 2 on the Atmega 8
  // we'll attach the 1HZ signal from the 1306, pin 7
  // or we can attach the active low interrupt output (pin 5 on the DS1306 DIP package)
  // to digital pin 3 on the Arduino (INT1 on the ATmega8)
  // I just picked INT1 arbitrarily — I think you could use any of the interrupts, so long as it
  // wasn't already being used by some other part of the Arduino.
  // cf. http://www.arduino.cc/en/Hacking/PinMapping?from=Main.PinMapping
  // enable INT1 in the global interrupt control register

  // Atmega8 - uncomment these two lines
  // Atmega168 - comment these two lines
  //GICR = (1<<int1);
 //Serial.println(GICR, HEX);

  // Atmega168 - uncomment these four lines
  // Atmega8 - comment these four lines
  EIMSK = (1<<int1); // activate the external interrupt INT1 mask
  EICRA = (0<<isc11|1<<isc10); // set the interrupt sensing so that the falling edge of INT1 generates an interrupt request.
  SREG = (1<<7); // put a 1 in the 7th bit of the microcontroller's status register to globally turn on interrupts
  Serial.println(EIMSK, HEX); // print out the EIMSK register just so we can see for ourselves..

  digitalWrite(LED, HIGH);
  delay(1000);

}

// can't really share variables unless they're declared
// "volatile", otherwise, they'll be set here, then popped
// back to their values before the interrupt handler was called
ISR(INT1_vect) {
  //signal that we have an interrupt
  //turn on the LED
  byte a;

  digitalWrite(LED, HIGH);
  // writing or reading from the DS1306 registers resets the alarm
  // so as to cause an alarm every 15 seconds.
  a = read_rtc_register(0x07);
  if(a == 0x00) {
    write_rtc_register(0x87,0x15);
  }
  if(a == 0x15) write_rtc_register(0x87,0x30);
  if(a == 0x30) write_rtc_register(0x87,0x45);
  if(a == 0x45) write_rtc_register(0x87,0x00);

  // every minute — set bit 7, the mask bit, to 1
  write_rtc_register(0x88,0x80);
  // every hour
  write_rtc_register(0x89,0x80);
  // every day of the week
  write_rtc_register(0x8A,0x80);

}

void write_rtc_register(char register_name, byte data) {
  write_register(register_name, data, RTC_CHIPSELECT, HIGH, true, true);
}

char read_rtc_register(char register_name) {
  return read_register(register_name, RTC_CHIPSELECT, HIGH, false, true);
}

// reads a register
char read_register(char register_name, byte cs_pin, byte cs_active_level, boolean read_high, boolean cpha_trailing)
{
  char in_byte;
  if(cpha_trailing) {
    SPCR = (1<<spe)|(1<<mstr)|(1<<cpha)|(0<<spr1)|(0<<spr0);
  }
  else {
    SPCR = (1<<spe)|(1<<mstr)|(0<<cpha)|(0<<spr1)|(0<<spr0);
  }
  clr = SPCR;
  clr = SPDR;
  if(read_high) {
    // need to set bit 7 to indicate a read for the slave device
    register_name |= 128;
  }
  else {
    // if read low, means A7 bit should be cleared when reading for the slave device
    register_name &= 127;
  }
  // SS is active low
  digitalWrite(cs_pin, cs_active_level);
  // send the address of the register we want to read first
  spi_transfer(register_name);
  // send nothing, but here's when the device sends back the register's value as an 8 bit byte
  in_byte = spi_transfer(0);
  // deselect the device..
  if(cs_active_level == HIGH) {
    digitalWrite(cs_pin, LOW);
  }
  else {
    digitalWrite(cs_pin, HIGH);
  }
  return in_byte;
}


// write to a register
// write_high if true indicates set A7 bit to 1 during a write
void write_register(char register_name, byte data, byte cs_pin, byte cs_active_level, boolean write_high, boolean cpha_trailing)
{
  if(cpha_trailing) {
    SPCR = (1<<spe)|(1<<mstr)|(1<<cpha)|(0<<spr1)|(0<<spr0);
  }
  else {
    SPCR = (1<<spe)|(1<<mstr)|(0<<cpha)|(0<<spr1)|(0<<spr0);
  }
  clr=SPCR;
  clr=SPDR;
  // char in_byte;
  if(write_high) {
    // set A7 bit to 1 during a write for this device
    register_name |= 128;
  }
  else {
    // clear bit 7 to indicate we're doing a write for this device
    register_name &= 127;
  }
  // SS is active low
  digitalWrite(cs_pin, cs_active_level);
  // send the address of the register we want to write
  spi_transfer(register_name);
  // send the data we're writing
  spi_transfer(data);
  if(cs_active_level == HIGH) {
    digitalWrite(cs_pin, LOW);
  }
  else {
    digitalWrite(cs_pin, HIGH);
  }
  //return in_byte;
}

void loop()
{
  byte in_byte;
  // keep track of what our seconds alarm register is..
  // we use this in the ISR to make sure we alarm every 15 seconds
  in_byte = read_rtc_register(0x07);
  Serial.print("sec alarm is ");
  Serial.print(in_byte, HEX);

  in_byte = read_rtc_register(0x00);
  Serial.print(" SECS=");
  Serial.print(in_byte, HEX);

  in_byte = read_rtc_register(0x01);
  Serial.print(" MINS=");
  Serial.print(in_byte, HEX);

  in_byte = read_rtc_register(0x02);
  Serial.print(" HRS=");
  Serial.println(in_byte, HEX);
  digitalWrite(LED, LOW);
  delay(500);
}

References
An Application Note on the Maxim/Dallas Real-Time Clocks.
DS1306 Overview
Decent SPI Info Page
A project page I put up on interfacing the LIS3LV02DQ using SPI to an Arduino/Atmega8

Technorati Tags: ,