Showing posts with label MAX7219. Show all posts
Showing posts with label MAX7219. 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.

The display

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




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.

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.