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.

No comments:

Post a Comment