John Bradnam
Published © GPL3+

Reducing Your Memory Usage

Learn the techniques that reduce memory usage allowing your programs to run smaller Arduino based systems.

IntermediateProtip2 hours38,040
Reducing Your Memory Usage

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
MAX7219 LED 8x8 Dot Matrix Module Assembled
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Schematic

CLOCK -> D11
CS -> D10
DIN - D12

Code

KnightsTourV2.ino

C/C++
Knights Tour puzzle rewritten to run on a ATMega328 system such as a Arduino UNO or Arduino Pro Mini
/*
 * Knights Tour
 * Original code base: Krishna Lalith (https://www.hackster.io/krishnalalith/knight-s-tour-3f1ae4)
 * 
 * Changes: 
 * John Bradnam (jbrad2089@gmail.com)
 * Reduced memory footprint to work in ATMega328 (2474 Flash, 187 SRAM)
 * Moved local variables from knight_tour function
 * Reduced parameters on knight_tour function
 * Moved solution animation from setup to loop
 * Added timer and debug routines
 * Added 1 second flash on LED to show it is thinking (Takes 7 minutes to find solution)
 */
//#define DEBUG
#include <LedControl.h>

#define CLOCK 11
#define CS 10
#define DIN 12
LedControl lc = LedControl(DIN, CLOCK, CS, 1);

#define N 8
#define N2 64

const int8_t x_move[] = {2, 1, -1, -2, -2, -1, 1, 2};
const int8_t y_move[] = {1, 2, 2, 1, -1, -2, -2, -1};
int8_t sol[N2];
int8_t m_level = 0;
bool solved = false;
bool thinking = false;
#ifdef DEBUG
  uint8_t dot_count = 0;
#endif
unsigned long timeout;
int8_t step_count;
uint8_t next_xy;
uint8_t this_x;
uint8_t this_y;

//Initialise Matrix Display
void setup() 
{
  #ifdef DEBUG
    Serial.begin(115200);
  #endif
  lc.shutdown(0, false);
  lc.setIntensity(0, 2);
  lc.clearDisplay(0);
  lc.setLed(0, 0, 0, true);

  //Timer used to flash LED while thinking
  unsigned long start_time = millis();
  timeout = start_time + 1000;
  solved = start_knight_tour();
  #ifdef DEBUG
    Serial.println(((solved) ? "Found " : "Failed ") + String((millis() - start_time) / 1000) + "s");
  #endif
}

//Clear board and start the recursive walk through of the knight from bottom left corner
bool start_knight_tour() 
{
  //Clear board
  for (int8_t xy = 0; xy < N2; xy++) 
  {
    sol[xy] = -1;
  }

  //Placing knight at cell(1, 1)
  sol[0] = 0; 
  step_count = 1;
  return (knight_tour(0)); 
}

//Recursive function to test all possible moves. Can go to 64 levels deep.
//Not optimizing saves 10 bytes of stack space per recursive call
//Also reducing local variables and function parameters reduce stack usage, but they need 
//to be recalculated when the recursive function returns.
//Total used 24 bytes per call - Not sure why the compiler makes such a huge stack frame - should only be around 10 bytes
__attribute__((optimize("O0"))) 
bool knight_tour(int8_t xy) 
{
  int8_t k;
  
  if (millis() > timeout)
  {
    lc.setLed(0, 0, 0, thinking);
    thinking = !thinking;
    timeout = millis() + 1000;
    
#ifdef DEBUG 
    //Note: this will have a serious impact on speed
    Serial.print(".");
    dot_count++;
    if (dot_count == 60)
    {
      dot_count = 0;
      Serial.println();
    }
  }
  if (step_count > m_level)
  {
    m_level = step_count;
    Serial.println("L:" + String(m_level) + ", S:" + String(SP));
#endif
  }
  
  if (step_count == N2)
    return true;

  for (k = 0; k < N; k++) 
  {
    this_y = (xy >> 3) + y_move[k];
    if (this_y >= 0 && this_y < N)
    {
      this_x = (xy & 0x07) + x_move[k];
      if (this_x >= 0 && this_x < N)
      {
        next_xy = (this_y << 3) | this_x;
        if (sol[next_xy] == -1) 
        {
          sol[next_xy] = step_count;
          step_count++;
          solved = knight_tour(next_xy);
          step_count--;
          if (solved)
            return true;
          //Recalculate global variables since recursion kills them and they aren't stored on the stack to keep stack size to a minimum
          this_x = (xy & 0x07) + x_move[k];
          this_y = (xy >> 3) + y_move[k];
          next_xy = (this_y << 3) | this_x;
          sol[next_xy] = -1; // backtracking
        }
      }
    }
  }
  return false;
}

//Display the solution.
void loop() 
{
  lc.clearDisplay(0);
  if (solved)
  {
    int8_t cnt = 0;
    while (cnt < N2) 
    {
      for (int8_t xy = 0; xy < N2; xy++) 
      {
        if (sol[xy] == cnt) 
        {
          delay(1000);
          lc.setLed(0, xy & 0x07, xy >> 3, true);
          cnt = cnt + 1;
        }
      }
    }
  }
  delay(2000);
}

Credits

John Bradnam

John Bradnam

141 projects • 167 followers
Thanks to Krishna Lalith.

Comments