Showing posts with label Garmin. Show all posts
Showing posts with label Garmin. Show all posts

Wednesday, 21 August 2013

Software - The main program setup and loop

The main program talks to the ANT+ device, decides on a message and drives our display.

The code is found at RetroRunnerReadout.ino.

The program follows the standard Arduino/MCU setup then loop paradigm.

It sets up the hardware (display and ANT+) and following that displays a few setup messages on the LED 7-segment display.

setup() 

Setup enables the hardware for the ANT+ and MAX7219 chips and prints a few messages to the display.
{
  Serial.begin(CONSOLE_BAUD_RATE);

  SERIAL_INFO_PRINTLN("CanToo Runner!");
  mydisplay.shutdown(0, false); // turns on display
  mydisplay.setIntensity(0, DISPLAY_INTENSITY); // 0..15 = brightest
  mydisplay.clearDisplay(0);
  mydisplay.shutdown(0, true); // turns off display


  //Print a few startup messages
  for(unsigned int counter_setup = 0; counter_setup < STARTUP_TEXTS_COUNT; counter_setup++)
  {
    const char * const* str_in_pm = &startup_texts[ counter_setup ];
    char text[MAX_CHARS_TO_DISPLAY_STR];
    CLEAR_STR(text, MAX_CHARS_TO_DISPLAY_STR);
    //Read from PROG_MEM
    strncpy_P(text, (char*)pgm_read_word( str_in_pm ), MAX_CHARS_TO_DISPLAY_STR);
    print_and_delay( text );
  }

  SERIAL_DEBUG_PRINTLN_F("ANT+ Config...");

  //We setup an interrupt to detect when the RTS is received from the ANT chip.
  //This is a 50 usec HIGH signal at the end of each valid ANT message received from the host at the chip
  attachInterrupt(0/*0==pin2==RTS_PIN*/, isr_ant, RISING);

  ant_serial.begin( ANTPLUS_BAUD_RATE );
  antplus.begin( ant_serial );

  //This should not be strictly necessary - the device should always come up by itself....
  //But let's make sure we didn't miss the first RTS in a power-up race
  antplus.hardwareReset();
  SERIAL_DEBUG_PRINTLN_F("ANT+ Config Finished");
}
 
Following that it drops into the processing loop. The loop alternates between servicing sending/receiving ANT+ messages and displaying messages to the screen.

 loop()

The loop function repeats executing ANT+ code and displaying (). The ANT+ code is given priority at the start as it establishes an ANT+ channel.
{
  loop_antplus();

  //Get the ANT+ channels up quickly
  // then send some display messages

  if(ret_val_ce == ANT_CHANNEL_ESTABLISH_COMPLETE)
  {
    //This has blocking sleeps in it and so causes things to stop
    loop_display();

  }
}

loop_display()

The display messages are hard coded loops. The loops however have special keywords that are replaced dynamically updating distance travelled, heart rate, a random name or motivation quote etc.
 
{
  static int counter = 0;
  char text[MAX_CHARS_TO_DISPLAY_STR];

  //Pull a string from the loop texts
  //These are one per loop and some of them get replaced if they are "magic" texts
  const char * const* str_in_pm = &loop_texts[ counter % LOOP_TEXTS_COUNT ];

  
  CLEAR_STR(text, MAX_CHARS_TO_DISPLAY_STR);
  strncpy_P(text, (char*)pgm_read_word( str_in_pm ), MAX_CHARS_TO_DISPLAY_STR);

  //Replace, print and then delay.
  print_and_delay( text );

  counter++;
}

loop_antplus()

The ANT+ loop does two things. Firstly it sets up  an ANT+ channel for receiving SDM/Footpod broadcasts and then it processes any messages coming in on that channel (updating variables that are read in the string replace function).
{
  byte packet_buffer[MAXPACKETLEN];
  ANT_Packet * packet = (ANT_Packet *) packet_buffer;
  MESSAGE_READ ret_val = MESSAGE_READ_NONE;
  
//We received an interrupt on the nRF24AP2 RTS pin
  if(state == 1)
  {
    antplus.rTSHighAssertion();
    //Clear the ISR flag
    state = 0;
  }
  ret_val = antplus.readPacket(packet, MAXPACKETLEN, 0 );
  if((ret_val == MESSAGE_READ_EXPECTED) || (ret_val == MESSAGE_READ_OTHER))
  {
//Some broadcast packets will update globals (like HRM and Footpod)
    process_packet(packet);
  }
  //Only one channel gets setup in this version
  if(ret_val_ce != ANT_CHANNEL_ESTABLISH_COMPLETE)
  {
//This function will progress a state machine in
//the ANT+ library to establish a channel
    ret_val_ce = antplus.progress_setup_channel( &sdm_channel );
    if(ret_val_ce == ANT_CHANNEL_ESTABLISH_COMPLETE)
    {
      SERIAL_DEBUG_PRINTLN_F( "Channel established!" );
    }
  }
}

If you want to dig deeper, see the interrupt, string replacement code look at the code on github. RetroRunnerReadout.ino.The libraries created for the project are at MAX72xx_SPI_Arduino and ANTPlus_Arduino.

Related work

Software - ANT+

There was not much refined software out there for talking to ANT devices from Arduino.

I took the code at Digital Hack's Blog (http://digitalhacksblog.blogspot.com.au/2012_10_01_archive.html) as a starting point (Thanks!) and tidied it up and created a slightly more useable library.

This is at: https://github.com/brodykenrick/ANTPlus_Arduino

The library talks to ANT+ devices, like nRF24AP1/2, over UART. It allows for simple setting up of channels and receiving broadcast messages.

The trouble with Garmin GPS devices....

It turns out that you can't actually talk to a Garmin Forerunner 410 GPS in a meaningful way out on the road.

I wanted to get the distance information essentially in real time. It turns out this is not possible.

 I did some testing against my USB ANT+ Stick that came with my Gamrin Forerunner and a modified version of the Python ANT Downloader on Ubuntu (https://github.com/brodykenrick/python-ant-downloader).

You can only get ANT-FS "files" for download from a FR410 after a run. Never anything (useful) during the run.

 See http://www.thisisant.com/forum/viewthread/4059/

That meant that we wouldn't be getting any distance info out of the watch. Not what we wanted. Was it just going to display messages and no real time info?

 Luckily there is another way to get distance information from the Garmin ecosystem. The Garmin Foot-pod.

 So now rather than integrate with GPS we are integrating with the Foot-pod sensor directly. As an added benefit we'll be able to display stride counts too!

What would we need?


What would it take to make this display?
  • A micro-controller
  • A power source
  • An ANT+ module
  • A display 
  • An ANT+ source of distance information (Garmin Forerunner 410 Foot-pod)

MCU
I have quite few Arduino boards lying around, so that seemed to be the easy choice on the processor. I ended up using a Tinyduino (Tinyduino) and an Arduino Pro Mini (link) in my two separate iterations.

 Power
I also have some LiPo batteries around from other projects. Lightweight and powerful (link).

A display
7-segment displays are cool in my opinion and so that was the display I decided would make most sense (in spite of the difficulty in getting some letters represented). These seemed to be good and cheap options based on the MAX7219 (http://dx.com/p/8-segment-led-display-board-module-for-arduino-147814) and cheaper than I could get the main chip for myself.

ANT+
An ANT+ module for talking to the Garmin. This would need to be purchased. The sparkfun modules (link) seemed good - however they weren't available anywhere. I ended up buying from Goodluckbuy.com in China (http://www.goodluckbuy.com/nrf24ap2-wireless-module-zigbee-module.html)

Source
I have a Garmin Forerunner 410, Heart Rate Monitor and Foot-pod.

Arduino and ANT+ and 7-segment displays

What do you get when you combine: Arduino, ANT+ (Heart-rate-monitor & Foot-pod) and 7-segment displays?

A pretty strange device worn on a runner's hat that I am calling the Retro Runner Readout.

I wanted to document the details online in case someone wants to follow down this same path of wearable runner's tech.


Here is a shot of it in "inaction":