Plotclock servos in reverse

Build a plotclock, calibration successful but......it is writing upside down. All 3 servos are moving in reverse.

Standard code from Thingiverse, build by many others....what can be wrong in this setup?

Just started with Arduino so sorry in advance for this probably stupid question :slight_smile:

Could you please share code, a schematic and a picture of your setup?
What kind of servos do you have?

Try reversing the motor connections!

Before OP destroys his servos, let us first look at what type they are...
I

If servo.write(degrees); makes the servo move backwards, try:
servo.write(180 - degrees);

Switching the left/right servo's will solve the left/rigt mirroring but not the up/down and the lift.

Servo's are just standard Reely mini servo's as I used in my RC planes.
Code as used, directly from Thingiverse ( Plotclock by joo - Thingiverse ) :

#include <TimeLib.h>

// Plotclock
// cc - by Johannes Heberlein 2014
// v 1.01

// units: mm; microseconds; radians
// origin: bottom left of drawing surface

// time library see playground.arduino.cc/Code/time

// delete or mark the next line as comment when done with calibration

//#define CALIBRATION

// When in calibration mode, adjust the following factor until the servos move exactly 90 degrees

#define SERVOFAKTOR 590 //620

// Zero-position of left and right servo
// When in calibration mode, adjust the NULL-values so that the servo arms are at all times parallel
// either to the X or Y axis
#define SERVOLEFTNULL 1840 //1900 1800
#define SERVORIGHTNULL 1084 //984 1184

#define SERVOPINLIFT 2
#define SERVOPINLEFT 3
#define SERVOPINRIGHT 4

// lift positions of lifting servo
#define LIFT0 1080 // on drawing surface
#define LIFT1 925 // between numbers
#define LIFT2 725 // going towards sweeper

// speed of liftimg arm, higher is slower
#define LIFTSPEED 1500

// length of arms
#define L1 35
#define L2 55.1
#define L3 13.2

// origin points of left and right servo
#define O1X 22
#define O1Y -25
#define O2X 47
#define O2Y -25

#include <Time.h> // see Arduino Playground - HomePage
#include <Servo.h>

int servoLift = 1500;

Servo servo1; //
Servo servo2; //
Servo servo3; //

volatile double lastX = 75;
volatile double lastY = 47.5;

int last_min = 0;

void setup()
{
// Set current time only the first to values, hh,mm are needed
setTime(9,38,0,0,0,0);

drawTo(75.2, 47);
lift(0);
servo1.attach(SERVOPINLIFT); // lifting servo
servo2.attach(SERVOPINLEFT); // left servo
servo3.attach(SERVOPINRIGHT); // right servo
delay(1000);

}

void loop()
{

#ifdef CALIBRATION

// Servohorns will have 90° between movements, parallel to x and y axis
drawTo(-3, 29.2);
delay(1000);
drawTo(74.1, 28);
delay(1000);

#else

int i = 0;
if (last_min != minute()) {

if (!servo1.attached()) servo1.attach(SERVOPINLIFT);
if (!servo2.attached()) servo2.attach(SERVOPINLEFT);
if (!servo3.attached()) servo3.attach(SERVOPINRIGHT);

lift(0);

hour();
while ((i+1)*10 <= hour())
{
  i++;
}

number(3, 3, 111, 1);
number(5, 25, i, 0.9);
number(19, 25, (hour()-i*10), 0.9);
number(28, 25, 11, 0.9);

i=0;
while ((i+1)*10 <= minute())
{
  i++;
}
number(34, 25, i, 0.9);
number(48, 25, (minute()-i*10), 0.9);
lift(2);
drawTo(74.2, 47.5);
lift(1);
last_min = minute();

servo1.detach();
servo2.detach();
servo3.detach();

}

#endif

}

// Writing numeral with bx by being the bottom left originpoint. Scale 1 equals a 20 mm high font.
// The structure follows this principle: move to first startpoint of the numeral, lift down, draw numeral, lift up
void number(float bx, float by, int num, float scale) {

switch (num) {

case 0:
drawTo(bx + 12 * scale, by + 6 * scale);
lift(0);
bogenGZS(bx + 7 * scale, by + 10 * scale, 10 * scale, -0.8, 6.7, 0.5);
lift(1);
break;
case 1:

drawTo(bx + 3 * scale, by + 15 * scale);
lift(0);
drawTo(bx + 10 * scale, by + 20 * scale);
drawTo(bx + 10 * scale, by + 0 * scale);
lift(1);
break;

case 2:
drawTo(bx + 2 * scale, by + 12 * scale);
lift(0);
bogenUZS(bx + 8 * scale, by + 14 * scale, 6 * scale, 3, -0.8, 1);
drawTo(bx + 1 * scale, by + 0 * scale);
drawTo(bx + 12 * scale, by + 0 * scale);
lift(1);
break;
case 3:
drawTo(bx + 2 * scale, by + 17 * scale);
lift(0);
bogenUZS(bx + 5 * scale, by + 15 * scale, 5 * scale, 3, -2, 1);
bogenUZS(bx + 5 * scale, by + 5 * scale, 5 * scale, 1.57, -3, 1);
lift(1);
break;
case 4:
drawTo(bx + 10 * scale, by + 0 * scale);
lift(0);
drawTo(bx + 10 * scale, by + 20 * scale);
drawTo(bx + 2 * scale, by + 6 * scale);
drawTo(bx + 12 * scale, by + 6 * scale);
lift(1);
break;
case 5:
drawTo(bx + 2 * scale, by + 5 * scale);
lift(0);
bogenGZS(bx + 5 * scale, by + 6 * scale, 6 * scale, -2.5, 2, 1);
drawTo(bx + 5 * scale, by + 20 * scale);
drawTo(bx + 12 * scale, by + 20 * scale);
lift(1);
break;
case 6:
drawTo(bx + 2 * scale, by + 10 * scale);
lift(0);
bogenUZS(bx + 7 * scale, by + 6 * scale, 6 * scale, 2, -4.4, 1);
drawTo(bx + 11 * scale, by + 20 * scale);
lift(1);
break;
case 7:
drawTo(bx + 2 * scale, by + 20 * scale);
lift(0);
drawTo(bx + 12 * scale, by + 20 * scale);
drawTo(bx + 2 * scale, by + 0);
lift(1);
break;
case 8:
drawTo(bx + 5 * scale, by + 10 * scale);
lift(0);
bogenUZS(bx + 5 * scale, by + 15 * scale, 5 * scale, 4.7, -1.6, 1);
bogenGZS(bx + 5 * scale, by + 5 * scale, 5 * scale, -4.7, 2, 1);
lift(1);
break;

case 9:
drawTo(bx + 9 * scale, by + 11 * scale);
lift(0);
bogenUZS(bx + 7 * scale, by + 15 * scale, 5 * scale, 4, -0.5, 1);
drawTo(bx + 5 * scale, by + 0);
lift(1);
break;

case 111:

lift(0);
drawTo(70, 46);
drawTo(65, 43);

drawTo(65, 49);
drawTo(5, 49);
drawTo(5, 45);
drawTo(65, 45);
drawTo(65, 40);

drawTo(5, 40);
drawTo(5, 35);
drawTo(65, 35);
drawTo(65, 30);

drawTo(5, 30);
drawTo(5, 25);
drawTo(65, 25);
drawTo(65, 20);

drawTo(5, 20);
drawTo(60, 44);

drawTo(75.2, 47);
lift(2);

break;

case 11:
drawTo(bx + 5 * scale, by + 15 * scale);
lift(0);
bogenGZS(bx + 5 * scale, by + 15 * scale, 0.1 * scale, 1, -1, 1);
lift(1);
drawTo(bx + 5 * scale, by + 5 * scale);
lift(0);
bogenGZS(bx + 5 * scale, by + 5 * scale, 0.1 * scale, 1, -1, 1);
lift(1);
break;

}
}

void lift(char lift) {
switch (lift) {
// room to optimize !

case 0: //850

  if (servoLift >= LIFT0) {
  while (servoLift >= LIFT0) 
  {
    servoLift--;
    servo1.writeMicroseconds(servoLift);				
    delayMicroseconds(LIFTSPEED);
  }
} 
else {
  while (servoLift <= LIFT0) {
    servoLift++;
    servo1.writeMicroseconds(servoLift);
    delayMicroseconds(LIFTSPEED);

  }

}

break;

case 1: //150

if (servoLift >= LIFT1) {
  while (servoLift >= LIFT1) {
    servoLift--;
    servo1.writeMicroseconds(servoLift);
    delayMicroseconds(LIFTSPEED);

  }
} 
else {
  while (servoLift <= LIFT1) {
    servoLift++;
    servo1.writeMicroseconds(servoLift);
    delayMicroseconds(LIFTSPEED);
  }

}

break;

case 2:

if (servoLift >= LIFT2) {
  while (servoLift >= LIFT2) {
    servoLift--;
    servo1.writeMicroseconds(servoLift);
    delayMicroseconds(LIFTSPEED);
  }
} 
else {
  while (servoLift <= LIFT2) {
    servoLift++;
    servo1.writeMicroseconds(servoLift);				
    delayMicroseconds(LIFTSPEED);
  }
}
break;

}
}

void bogenUZS(float bx, float by, float radius, int start, int ende, float sqee) {
float inkr = -0.05;
float count = 0;

do {
drawTo(sqee * radius * cos(start + count) + bx,
radius * sin(start + count) + by);
count += inkr;
}
while ((start + count) > ende);

}

void bogenGZS(float bx, float by, float radius, int start, int ende, float sqee) {
float inkr = 0.05;
float count = 0;

do {
drawTo(sqee * radius * cos(start + count) + bx,
radius * sin(start + count) + by);
count += inkr;
}
while ((start + count) <= ende);
}

void drawTo(double pX, double pY) {
double dx, dy, c;
int i;

// dx dy of new point
dx = pX - lastX;
dy = pY - lastY;
//path lenght in mm, times 4 equals 4 steps per mm
c = floor(4 * sqrt(dx * dx + dy * dy));

if (c < 1) c = 1;

for (i = 0; i <= c; i++) {
// draw line point by point
set_XY(lastX + (i * dx / c), lastY + (i * dy / c));

}

lastX = pX;
lastY = pY;
}

double return_angle(double a, double b, double c) {
// cosine rule for angle between c and a
return acos((a * a + c * c - b * b) / (2 * a * c));
}

void set_XY(double Tx, double Ty)
{
delay(1);
double dx, dy, c, a1, a2, Hx, Hy;

// calculate triangle between pen, servoLeft and arm joint
// cartesian dx/dy
dx = Tx - O1X;
dy = Ty - O1Y;

// polar lemgth (c) and angle (a1)
c = sqrt(dx * dx + dy * dy); //
a1 = atan2(dy, dx); //
a2 = return_angle(L1, L2, c);

servo2.writeMicroseconds(floor(((a2 + a1 - M_PI) * SERVOFAKTOR) + SERVOLEFTNULL));

// calculate joinr arm point for triangle of the right servo arm
a2 = return_angle(L2, L1, c);
Hx = Tx + L3 * cos((a1 - a2 + 0.621) + M_PI); //36,5°
Hy = Ty + L3 * sin((a1 - a2 + 0.621) + M_PI);

// calculate triangle between pen joint, servoRight and arm joint
dx = Hx - O2X;
dy = Hy - O2Y;

c = sqrt(dx * dx + dy * dy);
a1 = atan2(dy, dx);
a2 = return_angle(L1, (L2 - L3), c);

servo3.writeMicroseconds(floor(((a1 - a2) * SERVOFAKTOR) + SERVORIGHTNULL));

}

Do you have a type number or data sheet for the relays?

No, fortunately there is no datasheet of this type. Only a link to the (dutch) supplier with the information on their website.

I happen to be a dutchman, so for me it kinda works. Though it is not completely clear from the specs how it should be controlled.

I presume it has a + a -, and a control wire.
In that case you should not reverse wires.
You will somehow need to reverse the control signal in your code...
c = 255-c

Servo is controlled by a dedicated signal and 5V. The code uses the Servo library, this controls all the communication with the servos. It is not possible within that library to switch direction.

And of course there are no reversed wires, it functions only in the wrong direction. Same behaviour as when you reverse the wires on the motor servo (did that before in some servos).

I would guess that your clock code somewhere calls the servo library.
That would be the point to invert the signal.
Unless the servo library can have inverse response as a parameter in object instantiation. The same call where you tell the servo library which pin it should use for the servo.