Servo 'shakes' when turned on after implementing map() function

Hi,

i am currently working on a project with a joystick transmitter and a receiver with multible servo's and stepper motors. but yesterday i added the map() function to the receiver code to set servo limits but now when i turn it on i starts 'shaking' (it keep moving from, i think, 90-80) and i don't know why.

here are the codes:

transmitter:

#include <SPI.h>  
#include <RF24.h>
#include <nRF24L01.h>

#define CE_PIN 7
#define CSN_PIN 6

RF24 radio(CE_PIN, CSN_PIN);
const byte direccion[] = "00001";
 
void setup() {

pinMode(10, OUTPUT);
  radio.begin();
  Serial.begin(9600);
   radio.openWritingPipe(direccion);
   radio.setPALevel(RF24_PA_MIN);
   radio.stopListening();
}
 
void loop() {
  
  //servo 1
  int joystick2 = analogRead(A7);
  int angle1 = map(joystick2,0, 1023, 0,180);
  Serial.println(angle1);
  radio.write(&angle1, sizeof(angle1));
  delay(40);
  
  //servo 2
  int joystick3 = analogRead(A2);
  int angle2 = map(joystick3,0, 1023, 0,180);
  Serial.println(angle2);
  radio.write(&angle2, sizeof(angle2));
  delay(40);  
  
  //servo 3
  int joystick4 = analogRead(A3);
  int angle3 = map(joystick4,0, 1023, 0,180);
  Serial.println(angle3);
  radio.write(&angle3, sizeof(angle3));
  delay(40);
  
  //servo 4
    int joystick6 = analogRead(A5);
  int angle4 = map(joystick6,0, 1023, 0,180);
  Serial.println(angle4);
  radio.write(&angle4, sizeof(angle4));
  delay(40);
}

receiver:

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Servo.h>

#define CE_PIN 49
#define CSN_PIN 48

RF24 radio(CE_PIN, CSN_PIN);
const byte direccion[] = "00001";

Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;
int angle1 =90;
int angle2 =90;
int angle3 =90;
int angle4 =90;

int newAngle1;
int newAngle2;
int newAngle3;
int newAngle4;

void setup(){

pinMode(10, OUTPUT);
  Serial.begin(9600);
  servo1.attach(2); //correct
  servo2.attach(5); //correct
  servo3.attach(4); //correct
  servo4.attach(3); //correct
//set starting positions
  servo1.write(90);
  servo2.write(90);
  servo3.write(90);
  servo4.write(84); //continious servo
//radio
  radio.begin();
  radio.openReadingPipe(0, direccion);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();
}
 
void loop() {
//servo's
  if (radio.available()){

    //servo 1
      radio.read(&angle1, sizeof(angle1));
      newAngle1 = map(angle1, 0, 180, 80, 100);
      Serial.println(newAngle1);
      servo1.write(newAngle1);
      delay(30);
      
    //servo 2
      radio.read(&angle2, sizeof(angle2));
      newAngle2 = map(angle2,0, 180, 65, 115);
      Serial.println(newAngle2);
      servo2.write(newAngle2);
      delay(30);
      
    //servo 3
      radio.read(&angle3, sizeof(angle3));
      newAngle3 = map(angle3, 0, 180, 65, 115);
      Serial.println(newAngle3);
      servo3.write(newAngle3);
      delay(30);
    
    //servo 4
      radio.read(&angle4, sizeof(angle4));
      newAngle4 = map(angle4, 0, 180, 80, 88);
      Serial.println(angle4);
      servo4.write(angle4);
      delay(30);
  }
 
}

here is a visualisation of what is happening:
ezgif.com-gif-maker (1)

I hope this is eneugh information.

what do you see on the Serial monitor of the receiving unit? Do the angles you get keep changing?
why do you map again the original value to something different than 0 - 180. Why don't you broadcast then the real position of the joystick so that you have the full range to play with?

(your sender code should only broadcast changes)

Your receiver code is obviously won't work properly

radio.read(&angle1, sizeof(angle1));
      newAngle1 = map(angle1, 0, 180, 80, 100);
      Serial.println(newAngle1);
      servo1.write(newAngle1);
      delay(30);
      
    //servo 2
      radio.read(&angle2, sizeof(angle2));
      newAngle2 = map(angle2,0, 180, 65, 115);
      Serial.println(newAngle2);
      servo2.write(newAngle2);
      delay(30);
      
    //servo 3
      radio.read(&angle3, sizeof(angle3));
      newAngle3 = map(angle3, 0, 180, 65, 115);
      Serial.println(newAngle3);
      servo3.write(newAngle3);
      delay(30);
    
    //servo 4
      radio.read(&angle4, sizeof(angle4));
      newAngle4 = map(angle4, 0, 180, 80, 88);
      Serial.println(angle4);
      servo4.write(angle4);
      delay(30);
  }

And how do you suppose to distinguish four values with such a code? Why do you think that the first accepted value is angle1, and for example, not angle2, but angle1 you just missed and did not have time to accept?

When you transmit multiple data, you must ensure that the receiver can distinguish between them. There can be two ways - either to transfer all 4 values in one package, as an array. Or add a label to each value, a label that would indicate that this is angle1, and that one is angle3...

2 Likes

right now i can't show that because i am not home but what i do know is that in my receiver serial it has an extra 0 which the transmitter is not transmitting.

i just saw your edit and you got me thinking because now i am double mapping it from 0-1023 to 0-180 and then again from 0-180 to 65-115 (the servo limits i want).
but i can do the same in 1 line. if i just change

  int angle1 = map(joystick2,0, 1023, 0,180);
to
int angle1 = map(joystick2, 0, 1023, 65, 115);

and i think that should work. because then i am transmitting the correct angle without changing it later.

someone also told me that in another thread i still need to look into that because i am gonna be sending more data than 4 servo's.
eventually i will have 4 joystick and some buttons. so thats 12 things being send.

Each time your starting to make in your code a several variables with names like name1, name2, name3... - you should reorganize it as an array.
Try to rewrite your transmitter and receiver code with reading analog values to an array of angles, transmitting the array and receiving it as a single packet.

how should i reorganize it as an array?
because i am currently looking on some other threads and what i think i am seeing is angle[1] instead of angle1 and for the serial

Serial.println("angle[1]: "); Serial.println(angle[1] );

note that arrays in C are indexed from zero... so the first angle value will be angle[0], not angle[1] :slight_smile:

okay so this:
angle1
angle2
angle3
angle4

i gotta change al that in the code(s) to:
angle[0]
angle[1]
angle[2]
angle[3]

and what about the sending of the data?

you should send a payload with all the info in one message and make use of arrays to make your code easier to manage if you plan to add joysticks and servos, may be something like this (typed here based on your code, so fully untested and mind typos when you compile)

Sender:

#include <SPI.h>
#include <RF24.h>
#include <nRF24L01.h>

#define CE_PIN 7
#define CSN_PIN 6

RF24 radio(CE_PIN, CSN_PIN);
const byte pipeName[] = "00001";

const byte joystickPins[] = {A7, A2, A3, A5};
const byte joysticksCount = sizeof joystickPins / sizeof * joystickPins;

struct __attribute__ ((packed)) t_message {
  int16_t rawValues[joysticksCount];
} payload, previousPayload;


void setup() {
  pinMode(10, OUTPUT);
  radio.begin();
  Serial.begin(9600);
  radio.openWritingPipe(pipeName);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
}

void loop() {
  // read the joysticks
  for (byte i = 0; i < joysticksCount; i++) {
    payload.rawValues[i] = analogRead(joystickPins[i]);
    payload.rawValues[i] = analogRead(joystickPins[i]) & 0xFFFD; // two reads for stability, dropping the 2 LSb to filter out instability
  }

  // broadcast the data if it has changed
  if (memcmp(&payload, &previousPayload, sizeof(t_message)) != 0) {  // returns 0 when they match, https://cplusplus.com/reference/cstring/memcmp/
    radio.write(&payload, sizeof(payload));
    previousPayload = payload;
  }
}

Receiver:

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Servo.h>

#define CE_PIN 49
#define CSN_PIN 48

RF24 radio(CE_PIN, CSN_PIN);
const byte pipeName[] = "00001";

const byte servoPins[] = {2, 5, 4, 3};
const byte servosCount = sizeof servoPins / sizeof * servoPins;
const int initialPositions[servosCount] = {90, 90, 90, 84};
const int mappedRanges[servosCount][2] = {{80, 100}, {65, 115}, {65, 115}, {80, 88}};

Servo servos[servosCount];

struct __attribute__ ((packed)) t_message {
  int16_t rawValues[servosCount];
} payload;
uint8_t messageBuffer[sizeof(t_message)];

void setup() {
  pinMode(10, OUTPUT);

  for (byte i = 0; i < servosCount; i++) {
    servos[i].write(initialPositions[i]);     //set starting positions
    servos[i].attach(servoPins[i]);
  }

  // Serial
  Serial.begin(115200);

  //radio
  radio.begin();
  radio.openReadingPipe(0, pipeName);
  radio.setPALevel(RF24_PA_MIN);
  radio.startListening();
}

void loop() {
  if (radio.available()) {
    radio.read(messageBuffer, sizeof messageBuffer);
    memcpy(&payload, messageBuffer, sizeof payload);
    Serial.print(F("New positions: "));
    for (byte i = 0; i < servosCount; i++) {
      int angle = map(payload.rawValues[i], 0, 1023, mappedRanges[i][0], mappedRanges[i][1]);
      Serial.print(angle); Serial.write('\t');
      servos[i].write(initialPositions[i]);     //set starting positions
    }
    Serial.println();
  }
}

you obviously need to make sure both sides uses the exact same structure for transmitting the data

struct __attribute__ ((packed)) t_message {
  int16_t rawValues[joysticksCount];
} payload, previousPayload;

and that the architectures are the same

What you wrote makes little sense. Arrays are defined this way in C:

int angle[4] = {0};

Please first read any C textbook about arrays, otherwise you will have to ask on the forum about every comma

wow what a code,
i don't really understand it all but from what i see there is no need anymore for individual pieces of code for reading and writing the joysticks and servo's. but instead it does it all in one.

and this is only one of advantages from using the arrays

that is true, i will do that

sounds good!

i will test the code when i get home and report back on what i got

just spend a bit of time reading it, it really does what you were doing but with arrays

the only difference is that I pack the values into a structure of type t_message which I call payload
at the moment it holds 4 16 bits integers as an array for the 4 joystick values but you could make that array larger by modifying of course the joysticks[] pins array accordingly (which I should have named joystickPins instead of joysticks for coherence ➜ changing it in the code above)

and for the receiver code it says this:

const byte servoPins[] = {2, 5, 4, 3};

and this

const int initialPositions[servosCount] = {90, 90, 90, 84};

i assume that it atomaticlly knows that 90(1) is linked to servoPin2 and 84 to 3

well it's not automatic :slight_smile:
I tell him so in the setup when I do

  for (byte i = 0; i < servosCount; i++) {
    servos[i].write(initialPositions[i]);     //set starting positions
    servos[i].attach(servoPins[i]);
  }

➜ I attach the servos to the pin and position using the same index i so that's why they all match

if you take i=0 for example:

the code does

    servos[0].write(initialPositions[0]);     //set starting positions
    servos[0].attach(servoPins[0]);

which basically means

    servos[0].write(90);     //set starting positions
    servos[0].attach(2);

if you replace by the values

then with the for loop the index goes to 1, 2, 3 and does the same thing, pulling the right values from the arrays

if you take i=1 next

the code does

    servos[1].write(initialPositions[1]);     //set starting positions
    servos[1].attach(servoPins[1]);

which basically means

    servos[1].write(90);     //set starting positions
    servos[1].attach(5);

if you replace by the values

then i goes to 2 and 3 (due to the for loop) and you get all your servos initialized

note that I set the position before attaching the servo, that helps get the right value to the servo when you turn it on, otherwise it will possibly move first to 90 when you attach it (the default) and then to the value you assign. it does not matter much here because 90 is what you use except for the last servo where you used 88

last but not least, of course ensure the servos are NOT powered by the arduino...

aha okay, the

  for (byte i = 0; i < servosCount; i++) {
    servos[i].write(initialPositions[i]);     //set starting positions
    servos[i].attach(servoPins[i]);

is still a bit giberish for me but i will understand it eventually
no my servo's are not powerd by the arduino they are attach to a DC-DC voltage reuglator that goes from a 6s lipo battery (24v) to 6v