Action on Compass Bearing, bloated 362 iteration Switch..Case

I need help simplifying this monster...

The project is to have two laser pointers mounted on two servos. Originally I left the whole range of compass degrees available to the servos, but after some experimentation I discovered that I can only count on having about 170 useable degrees of travel. 5-175 is what I'm using. The mounting and housing of my project also started to interfere so I had to exclude from 155 degrees to 200 degrees, this allows for an overlap in the front from 345 to 015 degrees and it was this overlapping parts of the compass in the servo arcs that broke my math. I kept trying to figure out a mathematical way to rotate the servo arcs so that they both cover the transition from 359 degrees to 0 (or 360) degrees. After beating my head on this for a week, I decided to cheat and bit-banged the code below. The code below does work but its huge, 15,892 bytes for the complete version of what I've shared below.

This section gets passed a Compass Bearing and needs to return a LEFT and RIGHT value for the servos. LEFT arc starts at 200 degrees (writing 5 to the servo) and ends at 015 (writing 175 to the servo). Right arc starts at 345 degrees (writing 5 to the servo) and ends at 155 (writing 175 to the servo). After writing to the servo(s) this code waits five seconds, generates a random new bearing for the next loop.

Just a note, the servos I'm testing my code with have very low power requirements (15-25mA @ 5VDC) so I've powered them through the surrounding pins. I've got a separate 6VDC source for the final build.

#define MAXANGLE 90
#define MINANGLE -90
#include <Servo.h>
int Bearing = 0;     // Compass Bearing (0-359 degrees)
int BearingL = 0;   // Heading to be sent to the LEFT servo     
int BearingR = 0;  // Heading to be sent to the RIGHT servo

Servo Left;
Servo Right;

void setup() {
    Serial.begin(115200);
  pinMode(8, OUTPUT); // Ground
  pinMode(9, OUTPUT); // 5VDC
  pinMode(10, OUTPUT); // Signal Left Servo
  pinMode(11, OUTPUT); // Signal Right Servo
  pinMode(12, OUTPUT); // 5VDC
  pinMode(13, OUTPUT); // Ground
  
  digitalWrite(8, LOW);   // Set pin 8 to act as a ground
  digitalWrite(9, HIGH);  // Set pin 9 to provide 5VDC
  digitalWrite(12, HIGH); // Set pin 12 to provide 5VDC
  digitalWrite(13, LOW);  // Set pin 13 to act as a ground
  
  Left.attach(10);
  Right.attach(11);
  delay(50);
}

void loop() {

switch (Bearing) {
  
  case 0:
    BearingL = 160;
    BearingR = 20;
    break;
  case 1:
    BearingL = 161;
    BearingR = 21;
    break;
  case 2:
    BearingL = 162;
    BearingR = 22;
    break;
  case 3:
    BearingL = 163;
    BearingR = 23;
    break;
  case 4:
    BearingL = 164;
    BearingR = 24;
    break;
  case 5:
    BearingL = 165;
    BearingR = 25;
    break;

///  Bearings  006 thru 355 deleted for brevity, format is consistent.  If the Bearing is out of a servo's range (less than 5 or more than 175) value is set to '999'.

  case 356:
    BearingL = 161;
    BearingR = 16;
    break;
  case 357:
    BearingL = 162;
    BearingR = 17;
    break;
  case 358:
    BearingL = 163;
    BearingR = 18;
    break;
  case 359:
    BearingL = 164;
    BearingR = 19;
    break;
  case 360:
    BearingL = 165;
    BearingR = 20;
    break;
  default:
    BearingL = 160;
    BearingR = 25;
    break;
    
}


      Serial.println();
      Serial.println();
      Serial.println("**********");
      Serial.println("**********");
      Serial.print("Bearing: ");
      Serial.println(Bearing);
      Serial.println("**********");
      Serial.println("**********");
      Serial.print("LEFT: ");
      Serial.println(BearingL);
      Serial.println("**********");
      Serial.println("**********");
      Serial.print("RIGHT: ");
      Serial.println(BearingR);
      Serial.println("**********");
      Serial.println("**********");
      if (BearingL != 999) {
        Serial.println("  Left Servo in Arc");
        Left.write(BearingL);
        delay(500);

      }
      if (BearingR != 999) {
        Serial.println(" Right Servo in Arc");
        Right.write(BearingR);
        delay(500);
      }
  delay(5000);
  Bearing = random(0, 360); 
}

I'm having difficulty visualizing what you are doing, and correlating the bearing with the desired positions of the dervos. Let me just throw out a few things here...

10 - 20 = -10, 360 + (-10) = 350
(350 + 20) % 360 = 10

You can drastically reduce your switch/case by only using cases 5 to 175, and making the default produce 999

Best of all... Check out map(). You can map bearings to any range of values you want.

LEFT arc starts at 200 degrees (writing 5 to the servo) and ends at 015 (writing 175 to the servo)

So, for left servo, (this looks like C++, but it's just pseudocode. Do some research.

  if (bearing >= 200 && bearing <= 360)  map (200, 360, 5, 160)
  else if (bearing >= 0 && bearing <= 15) map (0, 15, 160, 175)

I'll leave the other servo to you.

  case 0:
    BearingL = 160;
    BearingR = 20;
    break;
  case 1:
    BearingL = 161;
    BearingR = 21;
    break;
  case 2:
    BearingL = 162;
    BearingR = 22;
    break;
  case 3:
    BearingL = 163;
    BearingR = 23;
    break;

It looks like there is a relationship between the case number and the bearings

BearingL = 160 + case;
BearingR = 20 + case;
 case 356:
    BearingL = 161;
    BearingR = 16;
    break;
  case 357:
    BearingL = 162;
    BearingR = 17;
    break;
  case 358:
    BearingL = 163;
    BearingR = 18;
    break;
  case 359:
    BearingL = 164;
    BearingR = 19;
    break;
  case 360:
    BearingL = 165;
    BearingR = 20;
    break;

BearingL = case - 195;
BearingR = case - 340;

I am not sure where the change of rules occurs, but a simple if to test the rule to apply based on the value of case would seem to be possible.

lar3ry:
I'm having difficulty visualizing what you are doing, and correlating the bearing with the desired positions of the dervos. Let me just throw out a few things here...

10 - 20 = -10, 360 + (-10) = 350
(350 + 20) % 360 = 10

You lost me with that. Is that a math operation that allows you to set the wrap around point?

You can drastically reduce your switch/case by only using cases 5 to 175, and making the default produce 999

There are only 50 case iterations this would save. Only the arc between 155 and 205 degrees is out of arc for both servos.

Best of all... Check out map(). You can map bearings to any range of values you want.

LEFT arc starts at 200 degrees (writing 5 to the servo) and ends at 015 (writing 175 to the servo)

So, for left servo, (this looks like C++, but it's just pseudocode. Do some research.

  if (bearing >= 200 && bearing <= 360)  map (200, 360, 5, 160)

else if (bearing >= 0 && bearing <= 15) map (0, 15, 160, 175)



I'll leave the other servo to you.

I'm trying to remember why I quit doing something like that. I think I ended up with a confused servo (high current draw and a little binding noise). I'll play with this a try and see if I can replicate why I quit doing it. I did have a translation step in a previous version that allowed me to use a variable rather than a hard-coded figure for the range overlap. The huge Switch-Case makes a variable defined overlap impossible anyway.

And dummy here just spotted an error. If I'm limiting to 170 degrees, the LEFT Servo needs to start at 205 not 200 or else I run out of degrees before the end of the overlap range with the RIGHT Servo. 345-015 True = 145-175 LEFT = 005-035 RIGHT.

UKHeliBob:
It looks like there is a relationship between the case number and the bearings

There is. The Bearing is a Compass heading. The arc of the LEFT servo starts at 205 degrees True and sweeps clockwise through to 015 degrees True. The arc of the RIGHT servo starts at 345 degrees True and sweeps clockwise through to 150 degrees True. Neither servo is in arc between 150 and 205 degrees. BOTH servos have their arcs overlap between 345 True and 015 True.

Therefor BearingL and BearingR will be constrained to a min of 5 and a max of 175 and will not apply outside of their arcs.

JGuthridge:

lar3ry:
I'm having difficulty visualizing what you are doing, and correlating the bearing with the desired positions of the dervos. Let me just throw out a few things here...

10 - 20 = -10, 360 + (-10) = 350
(350 + 20) % 360 = 10

You lost me with that. Is that a math operation that allows you to set the wrap around point?

Yes. Don't you recognize them as such? The % sign is a divide that returns the remainder.

Best of all... Check out map(). You can map bearings to any range of values you want.

LEFT arc starts at 200 degrees (writing 5 to the servo) and ends at 015 (writing 175 to the servo)

So, for left servo, (this looks like C++, but it's just pseudocode. Do some research.

  if (bearing >= 200 && bearing <= 360)  map (200, 360, 5, 160)

else if (bearing >= 0 && bearing <= 15) map (0, 15, 160, 175)



I'll leave the other servo to you.

I'm trying to remember why I quit doing something like that. I think I ended up with a confused servo (high current draw and a little binding noise). I'll play with this a try and see if I can replicate why I quit doing it. I did have a translation step in a previous version that allowed me to use a variable rather than a hard-coded figure for the range overlap. The huge Switch-Case makes a variable defined overlap impossible anyway.

Perhaps you didn't constrain the mapping with the if statements. There is also a 'constrain' function related to map().

And dummy here just spotted an error. If I'm limiting to 170 degrees, the LEFT Servo needs to start at 205 not 200 or else I run out of degrees before the end of the overlap range with the RIGHT Servo. 345-015 True = 145-175 LEFT = 005-035 RIGHT.

No matter. The idea is the same. an if/else if to do servo movements only between specific bearings, divided at 360/0 into two ranges.