Making continious wave like formation with servo's like sinus

Hi all,

I am having trouble writing the code for a continious waveform like sinus. I have 16 servos holding pictures and would like them to go up and down in a sinus.

void waveform(){
  check = 1;
  if(check==1){ //reset servo's
    for (servo = 0; servo < 16; servo++) {
      pwm.setPWM(servo, 0, servomin);
    }
  }
  check = 0;
  // start wave


}

servomin is up and servomax is down. am struggling with all sorts of FOR looops like

     for (uint16_t movement = servomin; movement < servomax; movement++) {
       pwm.setPWM(servo, 0, movement);
       delay(5);
     }

but no result.

thx in advance

It's difficult to visualise what you trying to achieve. Something like this ?

Or can you illustrate how it should somehow look ?

Hi,
Can you post your complete code?

Have you written code JUST to operate one servo to check your project?

What library are you using?
What model Arduino?

Thanks.. Tom.. :slight_smile:

Please post the complete program. It's difficult to make any sense of a small snippet like that particularly since you don't seem to be using the normal Servo library to drive your servos.

BTW do you think there is any possibility of this if statement not succeeding?

  check = 1;
  if(check==1){ //reset servo's

Steve

hi its more like the wheels in this vid

arduino uno

#include <Wire.h>
#include <SPI.h>
//#include <SD.h>
//#include <SdFat.h>
//#include <SdFatUtil.h> 
//#include <SFEMP3Shield.h>

#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);

//SFEMP3Shield MP3player;
//SdFat sd;
//SdFile file;

//byte result;
int s0 = 8;
int s1 = 9;
int s2 = 10;
int s3 = 11;
int servomin = 180; //150
int servomax = 400; //600
int readingPin = 0;
int controlPin[] = {s0, s1, s2, s3};
int servo;
int check;

void setup() {
  Serial.begin(9600);
  pinMode(s0, OUTPUT);
  pinMode(s1, OUTPUT);
  pinMode(s2, OUTPUT);
  pinMode(s3, OUTPUT);
  digitalWrite(s0, LOW);
  digitalWrite(s1, LOW);
  digitalWrite(s2, LOW);
  digitalWrite(s3, LOW);

    pwm.begin();
    pwm.setPWMFreq(60);
  // result = sd.begin(SD_SEL, SPI_HALF_SPEED);
 //  result = MP3player.begin();
}

void loop() {
// muxreading();
// waveform();
// people();
 
}

// ------ mux functie
int readMux(int channel){
  int muxChannel[16][4]={{0,0,0,0},{1,0,0,0},{0,1,0,0},{1,1,0,0},{0,0,1,0},{1,0,1,0},{0,1,1,0},
  {1,1,1,0},{0,0,0,1},{1,0,0,1},{0,1,0,1},{1,1,0,1},{0,0,1,1},{1,0,1,1},{0,1,1,1},{1,1,1,1}};

  for(int i = 0; i < 4; i ++){
    digitalWrite(controlPin[i], muxChannel[channel][i]);
     }

  int val = analogRead(readingPin);
  return val;
  }
  
void people(){
  for (servo = 0; servo < 16; servo++) {
    int newwaarde = readMux(servo);

    if((newwaarde > 275) && (newwaarde < 500)){
    //   int newservo = map(newwaarde, 275, 500, servomin, servomax);
     for (uint16_t movement = servomin; movement < servomax; movement++) {
       pwm.setPWM(servo, 0, movement);
       delay(5);
     }
  delay(2000);
      Serial.print("Reading sensor ");
      Serial.print(servo);
      Serial.print(" waarde ");
      Serial.println(newwaarde);
    }else{
      pwm.setPWM(servo, 0, servomin);
    }
  }
}

void waveform(){
  check = 1;
  if(check==1){
    for (servo = 0; servo < 16; servo++) {
      pwm.setPWM(servo, 0, servomin);
    }
  }
  check = 0;
  // start wave




 
}

hi all ...i added the complete code in the previous posts...am using arduino

At any instant, how many complete sine waves will be visible to a viewer looking at your system ?
In the video you linked to, it appears to be about three quarters of a sine wave and the frequency looks like about 0.2Hz

Anyway if, as suggested in post #2, concentrate on getting one servo working correctly, so that you can specify the angle say between 0 and 180 degrees, the rest is very easy because because the position of each servo is related to the position of the first one. And the position of the first one is related to time.

Say at the start of a sequence, servo 1 is at 90 degree, servo 2 must be at 180 degrees, servo 3 must be at 90 degrees, servo 4 at 0 degrees, servo 5 at 90 degrees etc. etc. You'll want to regulate the speed at which this cycle repeats for a reasonable visual effect.

This relationship depends on the granularity of your display, that is what proportion of a sine wave should be visible at any instant. You'll obviously want it finer than the example above, and for the intermediate values, you have to use a sine table.

yes some kind of that

could you write an example code how 16 servos would run in a loop ? I can run them at the same time at same position but the delay or difference is not working

Here is a bit of code that uses a lookup table to give scaled Sine and Cosine for angles from 0 to 359. If you want a servo sweep of 180 degrees you would set ROSE_RADIUS to 90 to get the sine wave to be scaled to +/-90 and then add 90 to the result to get 0..180.

#include <avr/pgmspace.h>


// Screen position of the center of the compass rose
const int ROSE_CENTER_X =  200;
const int ROSE_CENTER_Y = 64;


// For values of ROSE_RADIUS below 127 we can use 'char' to save space.
const char ROSE_RADIUS = 20;
const char SINE_TABLE[90] PROGMEM =  {
  // Casting the result as 'char' avoids warning messages about narrowing
  char(sin(0 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(1 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(2 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(3 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(4 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(5 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(6 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(7 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(8 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(9 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(10 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(11 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(12 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(13 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(14 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(15 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(16 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(17 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(18 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(19 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(20 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(21 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(22 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(23 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(24 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(25 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(26 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(27 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(28 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(29 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(30 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(31 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(32 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(33 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(34 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(35 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(36 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(37 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(38 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(39 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(40 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(41 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(42 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(43 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(44 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(45 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(46 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(47 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(48 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(49 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(50 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(51 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(52 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(53 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(54 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(55 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(56 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(57 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(58 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(59 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(60 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(61 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(62 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(63 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(64 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(65 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(66 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(67 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(68 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(69 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(70 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(71 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(72 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(73 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(74 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(75 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(76 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(77 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(78 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(79 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(80 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(81 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(82 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(83 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(84 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(85 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(86 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(87 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(88 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5),
  char(sin(89 * 2 * PI / 360.0) * ROSE_RADIUS + 0.5)
};




// Draw the compass pointer at an angle in degrees
void drawCompassPointer(int angle) {
  int x_offset, y_offset;  // The resulting offsets from the center point


  // Normalize the angle to the range 0 to 359
  while (angle < 0)
    angle += 360;


  while (angle > 359)
    angle -= 360;


  x_offset = getSine(angle);
  y_offset = getCosine(angle);
  // The actual drawing command will depend on your display library
  //  drawLine(ROSE_CENTER_X, ROSE_CENTER_Y, ROSE_CENTER_X + x_offset, ROSE_CENTER_Y + y_offset);
}


int getSine(int angle) {
  // For angles less than 90, use the table directly
  if (angle < 90)
    return pgm_read_byte_near(&SINE_TABLE[angle]);


  if (angle < 180) {
    //  90 to 179: flip direction
    return pgm_read_byte_near(&SINE_TABLE[179 - angle]);
  }


  if (angle < 270) {
    // 180 to 269 flip sign
    return -pgm_read_byte_near(&SINE_TABLE[angle - 180]);
  }


  // 270 to 359 flip sign and direction
  return -pgm_read_byte_near(&SINE_TABLE[359 - angle]);
}


// Cosine(angle) == Sine(angle-90)
int getCosine(int angle) {
  // Keep angle-90 between 0 and 359
  if (angle < 90)
    angle += 360;


  return getSine(angle - 90);
}


// Dummy sketch so I can verify the compile
void setup() {}
void loop() {}

wow :o

well basiclly what could be enough is 16 servos going up and down with a starting delay ...shouldnt be that much code but cant even get that running.

iejun:
well basiclly what could be enough is 16 servos going up and down with a starting delay ...shouldnt be that much code but cant even get that running.

So something like this? Of course you should use the BlinkWithoutDelay technique to take step one degree every N milliseconds instead of the outer 'degrees' loop and delay().

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);
int servomin = 180; //150
int servomax = 400; //600
void setup() {
  Serial.begin(9600);
  pwm.begin();
  pwm.setPWMFreq(60);
}


void loop()
{
 oneCycle();
}


void oneCycle()
{
  for (int degrees = 0; degrees < 360; degrees ++)
  {
    float radians = (degrees * M_PI) / 180.0;
    for (int servo = 0; servo < 16; servo++)
    {
      // Map the sin() function to the servomin..servomax range
      int position = (sin(radians) + 1.0) * ((servomax - servomin) / 2.0) + servomin;
      pwm.setPWM(servo, 0, position);
      radians += (2.0 * M_PI) / 16.0;  // Advance 1/16th of a circle
    }
    delay(10);  //  3600 milliseconds per cycle
  }
}

I think there is a matter of physics that is addressed in the video mechanism, that 16 servos will need to accommodate in software.

The large tangential rotor in the center of the 'driving' wheel on the left in the video - converts the rotation into a smooth sine waveform.

Each servo will have either a horn, or pulley that tugs the individual strings. There's your problem. Speed of the string at the center of the servo arc will be faster than at the ends on axis with the string line.

I could be wrong, but that's what I'd be looking at.

YESSSS !!!! THats it ...thx :)))

johnwasser:
So something like this? Of course you should use the BlinkWithoutDelay technique to take step one degree every N milliseconds instead of the outer 'degrees' loop and delay().

#include <Wire.h>

#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);
int servomin = 180; //150
int servomax = 400; //600
void setup() {
  Serial.begin(9600);
  pwm.begin();
  pwm.setPWMFreq(60);
}

void loop()
{
oneCycle();
}

void oneCycle()
{
  for (int degrees = 0; degrees < 360; degrees ++)
  {
    float radians = (degrees * M_PI) / 180.0;
    for (int servo = 0; servo < 16; servo++)
    {
      // Map the sin() function to the servomin..servomax range
      int position = (sin(radians) + 1.0) * ((servomax - servomin) / 2.0) + servomin;
      pwm.setPWM(servo, 0, position);
      radians += (2.0 * M_PI) / 16.0;  // Advance 1/16th of a circle
    }
    delay(10);  //  3600 milliseconds per cycle
  }
}