Arduino Sketch too Big; How to Shorten it?

Here's some code I wrote a while back to do just that:

bool TurnRight(long CourseNow,long CourseNew)
{
bool retval=true;  
if(CourseNew > CourseNow)
  {
  if((CourseNew-CourseNow) > (StepsPerQuarterRevolution*2))
    retval = false;
  }
else
  {
  if((CourseNow-CourseNew) < (StepsPerQuarterRevolution*2))
    retval=false;
  }
return retval;    
}
2 Likes

Thanks @wildbill!

I tried your code and it works perfectly! I did modify it a little:

bool turnRight(long CourseNow,long CourseNew){
  bool retval = true;  
  if(CourseNew > CourseNow){
    if((CourseNew-CourseNow) > 180){
      retval = false;
    }
  else {
    if((CourseNow-CourseNew) < 180){
      retval = false;
    }
  }
  return retval;    
}

First, I believe the snippet should more like

bool turnRight(long CourseNow, long CourseNew) {
  bool retval = true;
  if (CourseNew > CourseNow) {
    if ((CourseNew - CourseNow) > 180) {
      retval = false;
    }
  }
  else {
    if ((CourseNow - CourseNew) < 180) {
      retval = false;
    }
  }
  return retval;
}

I added a "}" before the else. There are several options, but this seems to be the right one.

But, it returns false every time CourseNew is greater than CourseNow, and viceversa.

Please consider the following test sketch. I wrote a turnRight2 with an alternate calculation that you can replace in the test sketch to compare results:

[code]
// testing turnRight
//https://forum.arduino.cc/t/arduino-sketch-too-big-how-to-shorten-it/972479/103

void setup() {
  Serial.begin(115200);
  Serial.print("\n\nTurnRight test\n\nCOURSE\t\t\t\t\t\t COURSENEW\n  NOW");

  // print title row
  for (int CourseNew = 0; CourseNew <= 360; CourseNew += 10) {
    Serialprint(CourseNew);
  }
  Serial.println();

  for (int CourseCurrent = 0; CourseCurrent <= 360; CourseCurrent += 10) {
    Serial.print("  ");
    Serialprint(CourseCurrent);
    for (int CourseNew = 0; CourseNew <= 360; CourseNew += 10) {
      if (CourseCurrent == CourseNew) {
        Serial.print(" -  "); // no turn necessary
      } else if (abs(CourseNew - CourseCurrent) == 180) {
        Serial.print(" *  "); // turn direction is necessary, either R or L will do
      } else if (turnRight(CourseCurrent, CourseNew)) {
        Serial.print(" R  ");
      } else {
        Serial.print(" L  ");
      }
    }
    Serial.println();
  }
}
void loop() {

}


bool turnRight(long CourseNow, long CourseNew) {
  bool retval = true;
  if (CourseNew > CourseNow) {
    if ((CourseNew - CourseNow) > 180) {
      retval = false;
    }
    else {
      if ((CourseNow - CourseNew) < 180) {
        retval = false;
      }
    }
  }
  return retval;
}

// alternate calculation
bool turnRight2(int CourseNow, int CourseNew) {
  int dif = CourseNew - CourseNow;
  if (dif < 0) {
    dif += 360;
  }
  return dif < 180;
}

void Serialprint(int x) {
  if (x < 10) {
    Serial.print(" ");
  }
  if (x < 100) {
    Serial.print(" ");
  }
  Serial.print(x);
  Serial.print(" ");
}

[/code]

Also, can the parameters be int instead of long?

1 Like

I was just writing/testing something very similar!

int course = 225;
int target = 315;

void turnLeft() { Serial.println("Left"); }
void turnRight() { Serial.println("Right"); }

void setup() {
  Serial.begin(9600);
  int dif = target - course < 0 ? (360 + target - course) : (target - course);
  dif < 180 ? turnRight() : turnLeft();
}

void loop() {
  // put your main code here, to run repeatedly:
}

Which can be written on one line:

(target - course < 0 ? (360 + target - course) : (target - course)) < 180 ? turnRight() : turnLeft();

But it gets messy and is hard to read, so don't do that! :smiley:

1 Like

Combining these makes a fun little test sketch:

#define arrSize(X) sizeof(X) / sizeof(X[0])

int course = 225;
int target = 135;

int bearings[] =               { 0,    22.5,  45,   67.5,  90, 112.5,  135,  157.5, 180,  202.5, 225,  247.5, 270, 292.5,  315, 337.5, 360 };
const char* bearingStrings[] = { "N", "NNE", "NE", "ENE", "E", "ESE", "SE",  "SSE", "S",  "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", "N" };

void turnLeft() { Serial.println("Left"); }
void turnRight() { Serial.println("Right"); }

// returns the array element ID for the nearest bearing to the target number
int nearestBearing(int target) { 
  int idx = 0; // by default near first element
  int distance = abs(bearings[idx] - target);
  for (int i = 1; i < arrSize(bearings); i++) {
    int d = abs(bearings[i] - target);
    if (d < distance) {
      idx = i;
      distance = d;
    }
    else return idx;
  }
  return idx;
}

void setup() {
  Serial.begin(9600);
  Serial.println((String)"Current course is: " + bearingStrings[nearestBearing(course)] + " (" + course + " degrees)");
  Serial.println((String)"Target course is: " + bearingStrings[nearestBearing(target)] + " (" + target + " degrees)");
  Serial.print("Suggested movement: ");
  int dif = target - course < 0 ? (360 + target - course) : (target - course);
  dif < 180 ? turnRight() : turnLeft();
}

void loop() {
  // put your main code here, to run repeatedly:
}

Just know that every time you cast to a String (capital 'S') God kills an innocent Kitten. So best not do that!

2 Likes

Thanks @anon46966594 and @mancera1979 !

Does anyone know how I could instead make a circle, and my course is printed on the display in that circle from the center of it, to the correct angle in a line? Almost like a live compass display?

There are many examples on the internet. I just found this, haven’t tried it. I guess it wouldn’t be very hard to adapt it: https://create.arduino.cc/projecthub/mariogianota/nextion-3-5-lcd-digital-compass-for-arduino-uno-4b12a0

This is building on top of my last post.... So you can certainly isolate / remove some bits:

#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

#define TFT_CS 10
#define TFT_DC 9
#define SCREEN_HEIGHT 240
#define SCREEN_WIDTH 320

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

#define arrSize(X) sizeof(X) / sizeof(X[0])

int course = 310;
int target = 27;

int circleRadius = 50;

int bearings[] =               { 0,    22.5,  45,   67.5,  90, 112.5,  135,  157.5, 180,  202.5, 225,  247.5, 270, 292.5,  315, 337.5, 360 };
const char* bearingStrings[] = { "N", "NNE", "NE", "ENE", "E", "ESE", "SE",  "SSE", "S",  "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", "N" };

void turnLeft() {
  Serial.println("Left");
}
void turnRight() {
  Serial.println("Right");
}

// returns the array element ID for the nearest bearing to the target number
int nearestBearing(int& target) {
  int idx = 0; // by default near first element
  int distance = abs(bearings[idx] - target);
  for (int i = 1; i < arrSize(bearings); i++) {
    int d = abs(bearings[i] - target);
    if (d < distance) {
      idx = i;
      distance = d;
    }
    else return idx;
  }
  return idx;
}

void setup() {
  Serial.begin(9600);
  tft.begin();
  tft.setRotation(1);
  tft.fillScreen(ILI9341_BLACK);

  Serial.println((String)"Current course is: " + bearingStrings[nearestBearing(course)] + " (" + course + " degrees)");
  Serial.println((String)"Target course is: " + bearingStrings[nearestBearing(target)] + " (" + target + " degrees)");
  Serial.print("Suggested movement: ");

  int dif = target - course < 0 ? (360 + target - course) : (target - course);
  dif < 180 ? turnRight() : turnLeft();

  tft.setCursor(10, 10);
  tft.setTextSize(1);
  tft.setTextColor(ILI9341_GREEN);
  tft.print("Current Bearing In GREEN");

  tft.setCursor(10, 25);
  tft.setTextSize(1);
  tft.setTextColor(ILI9341_RED);
  tft.print("Target Bearing In RED");

  tft.fillCircle(tft.width() / 2, tft.height() / 2, circleRadius, ILI9341_BLUE);

  // Draw the current course bearing line in GREEN
  tft.drawLine(
    tft.width() / 2,
    tft.height() / 2,
    tft.width() / 2 + circleRadius * sin(course * M_PI / 180.),
    tft.height() / 2 - circleRadius * cos(course * M_PI / 180.),
    ILI9341_GREEN);

  // Draw the target course bearing line in RED
  tft.drawLine(
    tft.width() / 2,
    tft.height() / 2,
    tft.width() / 2 + circleRadius * sin(target * M_PI / 180.),
    tft.height() / 2 - circleRadius * cos(target * M_PI / 180.),
    ILI9341_RED);
}

void loop() { }

See it in action: TFT-Display - Wokwi Arduino and ESP32 Simulator

1 Like

Wow, thanks @anon46966594 !

That website is awesome! I've never heard of it before.
Ill try out that code soon. Thanks again!

That too, if we don't use a function, the compiler doesn't compile it.
Considering how an interpreter has to load everything plus your code, I'm thankful.

Compare AVR memory spaces to core-memory mainframes?
Reason is because C was published in 1970! The restraints of the hardware shaped the language. Between a 328P and SD, a plain C compiler possible?

You should know, Optiboot is you or your group's work? You got bootloaders down from 1.5k to .5k and still turning screws?

If precision is required, an int can be at most 32767. 4 places can be 0 to 9.
A long can be at most 2147483647,. 9 places cab be 0 to 9.
Arduino has 64-bit long longs, can literally count millis for billions of years, Morris gave us a timing library that can never reach rollover.

Did you ever see the 2013 divmod10 thread in the Arduino Playground?

robtillaart on June 13, 2013

update on printfloat version using divmod10() . Code see- divmod10() : a fast replacement for /10 and %10 (unsigned) - #100 by robtillaart - Libraries - Arduino Forum -

Incorporated the Stimmer ASM divmod10_asm into the floating point test. - divmod10() : a fast replacement for /10 and %10 (unsigned) - #72 by stimmer - Libraries - Arduino Forum -
It strips of another 3% for printing floats

output:
testing
10737.4179
1.0182
107.37
Time=1008 << original 2144 is 53% off
done

1 millisecond for 19 digits is about 50+ uSec per digit (iso 100)

update: printFloat continues in its own thread here - faster printing of floats by divmod10() and others - Libraries - Arduino Forum -

Full thread is an amazing development.

1 Like

@GoForSmoke, thank you for that. I was not aware of the 64-bit long long type. Might come in handy one of these days...

And sorry for the confusion, I did not word properly my question

I should have said:
Also, can the parameters for turnRight be int instead of long ?

I guess Course being a number that ranges 0 to 360 using int values is appropriate.

1 Like

I just found out that TinyGPS++ has a function called cardinal defined like this:

const char *TinyGPSPlus::cardinal(double course)
{
  static const char* directions[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"};
  int direction = (int)((course + 11.25f) / 22.5f);
  return directions[direction % 16];
}

It might be worth giving it a try

1 Like

Last first; because N is 0 +/- 22.5 deg and we start with NE being 45 +/- 22.5 deg.
Note that checking N across 0 we don't have a single simple test to quickly check if direction is in that octant (half a quadrant). So in that initialization, direction -22.5 effectively aligns all of the octants on integer degrees starting with NE and a single quick test ( direction <= 45 ) says whether to stop or keep looking.
Keep looking says shift to the next direction and test until you run out of octants, don't bother testing the last one, it's the else to the rest. Logic!

"NEE SES SWW NWN " in RAM is NE E SE S SW W NW N as a packed C string array.
It wastes 1 byte of RAM, the 0 added to the end of the string.
An array of strings needs not only the terminating nulls (0's), it needs a 2 byte pointer to access each string.
The 16-char string, I treat as an array. The variable name compiles as the memory address of the variable. You can add a value to address in this case the chars of that string to copy or print/write to file or display.

As far as your device needing floats... you send it text as printed floats since it's easy to code and it works (best reason) but... your working variables can be fixed-point integers that you make or get a function that adds the '.' where it should go... something us ancient paper-and-pencil mathematicians learned back in those years BC (before calculators).

Don't be afraid to work char by char, you will gain insight and the code run fast!

One thing you do right is not assemble long strings to send or print, just spit text out faster than it can all send and it assembles itself in the serial output buffer even as chars are being sent.

I've done these kind of things before, once I learned ASM and then Forth it all became way simpler. That was 40 years ago.

1 Like

Unfortunately not true for a virtual method of a class. Since they're (internally) accessed via a pointer to a function that is dynamic at runtime, there's no way that the compiler can know whether it's used or not.

For example, any sketch that uses Serial will include the peek(), availableForWrite(), and flush() methods, even though they not often used.

1 Like

Yes, very.

PC's didn't all have FPUs till into the 90's.
My accounting code had to be right to the penny and ran faster using integers.
When you scale values (interpolating) always do the multiplies before the divides and always in double-precision variables (ints--->longs or longs--->long longs), the results will fit back in the original size.

One job I did got investigated because it varied by a penny from establishment electric board (PECO) calculations. Turns out they divided first, lost precision and were a horrifying penny off on some bills, out of millions a year over decades. Dennenberg investigated that, it didn't make the news but they did tell my customer who for 8 years ran the PECO code. Make no mistake, if I was wrong it would have been my ass but the establishment lets itself off every time. I doubt they ever changed their code either and that was 30 years ago.

You'd need to customize the library, add a s-load of toggles, tedious work to fix that!

Does IDE 1.8x compile much faster hex than 1.6x?
My loop counter sketch used to run 111KHz now runs 140KHz.

Hi @anon46966594

Do you know how I could widen the lines being drawn for course? They're kind of hard to see.

Like line thickness or stroke width? Not that I'm aware of for lines. I was googling same as you probably are. I think you can do a thicker line using two triangles, which is doable but you'll have to read how to do it.

1 Like