You have multiple issues here. For a start, you have a buffer (spiBuffer) of 5 bytes, into which you place <Xxy> (that's 5 bytes) and then finish off with:
spiBuffer[buffPos] = 0;
That has now overwritten some other memory.
Next, you don't seem to be detecting the "<" symbol and resetting to the start of the buffer, so if you ever get out of sync, you will stay out of sync. I suggest something like making buffPos equal to 0xFF initially. Then in the ISR:
ISR(SPI_STC_vect)
{
byte c = SPDR;
// exit if haven't processed last one
if (process_spiBuffer)
return;
if (c == '<')
buffPos = 0;
else if (buffPos == 0xFF)
return; // ignore until we get a '<'
if (buffPos < sizeof spiBuffer)
{
spiBuffer[buffPos++] = c;
// End of message is ">"
if (c == '>')
process_spiBuffer = true;
}
}
Now we use 0xFF as a "flag" that we haven't received a "<" yet. When we do, we reset buffPos to zero, ready to process the next 5 bytes. In the main loop, once we are done with processing the buffer we set buffPos to 0xFF again (rather than zero). Also you might have the ISR called before you have processed the previous one, so I put in a test to exit if process_spiBuffer is true when you enter the ISR.
I found your code to work out the arm positions confusing. For debugging this issue I would replace it with simply moving the servo to the position received by SPI, eg.
// crunchAngles();
// moveArm();
elbow.write (puck_x);
Now just debug that rather than getting confused about the difference between interrupt issues and logic issues. I found once I had done that, that my servo responded without any major issues (there was a bit of jittering occasionally).
I found that I got a lot of data errors until I slowed down the sender (a few microseconds delay after each SPI.transfer) because sending SPI at high speed didn't give the receiving sketch time to put the data into the buffer.
The servo library is rather dependent on interrupts (it manually does the PWM duty cycles). I wrote a sketch a while back that controls one server by using the hardware timer (Timer 1) rather than interrupts:
const byte potpin = A0; // analog pin used to connect the potentiometer
const unsigned long PRESCALER = 8; // Timer 1 prescaler
const float PULSE_PERIOD = 0.020; // 20 mS
const float ZERO_POSITION_WIDTH = 0.0005; // 0.5 mS
const float FULL_POSITION_WIDTH = 0.0024; // 2.4 mS
// how far apart the pulses are
const unsigned long PULSE_WIDTH_COUNT = F_CPU / PRESCALER * PULSE_PERIOD;
// minimum pulse width (-45 degrees)
const unsigned long ZERO_POSITION_COUNT = F_CPU / PRESCALER * ZERO_POSITION_WIDTH;
// minimum pulse width (+45 degrees)
const unsigned long FULL_POSITION_COUNT = F_CPU / PRESCALER * FULL_POSITION_WIDTH;
void setup()
{
TCCR1A = 0; // disable all PWM on Timer1 whilst we set it up
ICR1 = PULSE_WIDTH_COUNT - 1; // frequency is every 20ms (zero-relative)
// Configure timer 1 for Fast PWM mode using ICR1, with 8x prescaling
TCCR1A = bit (WGM11);
TCCR1B = bit (WGM13) | bit (WGM12) | bit (CS11); // fast PWM top at ICR1
TCCR1A |= bit (COM1A1); // Clear OC1A/OC1B on Compare Match,
pinMode (9, OUTPUT);
} // end of setup
void loop()
{
int val = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023)
OCR1A = ZERO_POSITION_COUNT + (val * (FULL_POSITION_COUNT - ZERO_POSITION_COUNT) / 1024) - 1;
delay(15); // wait for the servo to get there
} // end of loop
That is for the Atmega328P, not the Mega, but you could adapt it easily enough. You could use Timer 2 for the other servo (I think).
Since it doesn't use interrupts, then having interrupts going off in the background won't affect the servo.