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.

The display

Here is a video from an early test of the (small) display with simulated distance countdown:




Assembling

Connections
The electrical connections are pretty simple so I did not do a schematic (power, an SPI connection through a level converter to the display unit and [software]serial/UART to nRF24AP2 module). I can make that up if people are interested......

To connect the device parts to the hat I decided "velcro" was the way to go. Allowing removal when needed. I waterproofed each of the elements in small "ziploc" bags with electrical type and put some velcro on the back of them. I then stitched the other half of the velcro into the hat (with some very poor sewing technique).

This is what it looks like:


and here is a quick video too:

and you can see in the video the bane of the 7-segment display -- the letter 'k'



Related work

Goodluckbuy nrf24AP2 ANT+ Module

This module is the one I am using for ANT+: http://www.goodluckbuy.com/nrf24ap2-wireless-module-zigbee-module.html

This module, marked with "NRF24AP2 YJ-ANT", lacks any documentation.....

I did trace analysis and then some testing (https://github.com/brodykenrick/nRF24AP2_SUART_Arduino) and can confirm:

The connector on the module is (looking from the front/top, pin 1 is marked []) has
[]GND(=VSS) | VDD(=3.3 volts)
UART_TX | UART_RX
!(SUSP) | SLEEP
RTS | !(RESET)
Baud rate of the module is 9600 bps.


Also appears to be:
http://www.goodluckbuy.com/nrf24ap2-wireless-module-zigbee-module.htm
http://www.aliexpress.com/store/product/Freeshipping-NRF24AP2-Wireless-Networking-Module-Zigbee-Module/809689_559703297.html

Power issues: 5V MAX7219 vs. 3V nRF24AP2

Now, a problem with the system is that the display controller (MAX7219 must run on ~5V) and the ANT+ module (nRF24AP2) is at ~3V.

To over come the differences a level converter was employed (http://www.freetronics.com/products/logic-level-converter-module).

Worked like a charm.




Now, we still have a problem in that our LiPo batteries aren't going to give 5V (even 4.2V at full charge wasn't enough to get the display updating properly).

Prototype one (without ANT+) used a boost converter to get to 5V from a single battery however the battery ran out after about 3.5 hours on the road.

As we need more power capacity and increased voltage I just added another identical battery in series and a 5V regulator (https://www.sparkfun.com/products/107).


This gives me a 5V source for the display subsystem and the Arduino Pro Mini 3v3 has its own 3V regulator.

The level converter is then put between the 3V Arduino SPI signals and the 5V display.

Bigger displays! No medium sized displays.

The character height of the displays that came with the dx.com modules was 10mm.

This was fine when testing but not large enough to be seen well out on the track.

Upsizing seemed like the sensible thing to do. Let's try doubling to 20mm (https://www.sparkfun.com/products/11405 -- common cathode with same pinouts).

  After some soldering and extension so the displays fit:



 Looks great - real advertisement potential there.

 Let's try it on the hat. Uh-oh, now we are too big to run with and it flaps about.

 Let's try and find a display that is just right:
 I decided to go with these ones  (http://www.digikey.com/product-detail/en/LDQ-N516RI/67-1450-ND/252641) as the unit is a one off and the slightly more orange-than-red colour fit in with the aesthetics more. I got a few of these cheaper ones just to have as spares too (http://www.digikey.com/product-detail/en/LTC-5723HR/160-1879-ND/3198988).

Just right....


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!

Software - 7-Segment Display (MAX7219)

I rewrote the  Arduino LEDControl (http://playground.arduino.cc/Main/LedControl) to do all ASCII characters. Some good, some OK and a few bad.

https://github.com/brodykenrick/MAX72xx_SPI_Arduino

I added a greater range of characters, support for double-wide characters (like M and W) and generally made it better for use as a alpha+numeric display (rather than just numbers).

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.

What we wanted to create

My girlfriend is training for the Sydney Running Festival Marathon (September 2013). The training is with a group called CanToo that raise money for cancer research and offer professional training and a great group of people for spending the many hard number hours out on the road.

In order to make the training a bit more fun and to advertise both the CanToo cause and the effort and distances being run we came up with the idea of a wearable device worn whilst running that would indicate to bystanders the distances of the training runs and allow for a few other messages.

We wanted to be able to display on the back of a hat the distance that a Garmin GPS had measured for a run, the distance left for the day and some motivational messages for fun and general support.

The thinking was to create a hat mountable display that could connect to a Garmin Forerunner 410 (over ANT) and read out the distance for the run and display.


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":