Servo knob over I2C

I'm attempting to use the simple "servo-knob" example but add an adafruit servo shield.

Can someone point me in the right direction, maybe show example code i can study?

TIA
Dennis

What is this example? What is a servo knob?

/*
  Controlling a servo position using a potentiometer (variable resistor)
  by Michal Rinott <http://people.interaction-ivrea.it/m.rinott>

  modified on 8 Nov 2013
  by Scott Fitzgerald
  http://www.arduino.cc/en/Tutorial/Knob
*/

#include <Servo.h>

// define the number of servos
#define SERVOS 2
//DEFINE SERVO UPDATE FREQUENCY
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates

// create servo object to control a servo
Servo myservo[SERVOS];

//Attach servos to digital pins on the arduino
int servo_pins[SERVOS] = {10,11};

// analog pin used to connect the potentiometer
int potpin[SERVOS] = {A1,A0};

// variable to read the value from the analog pin
int potpin_val[SERVOS];

void setup() {

  for (int i = 0; i < SERVOS; i++) {

    //Attach the servos to the servo object
    myservo[i].attach(servo_pins[i]);
 
  }
  for (int i = 1; i < SERVOS; i++) {

    //Attach the servos to the servo object
    myservo[i].attach(servo_pins[i]);
  }
}

void loop() {

  // For each servo
  for (int i = 0; i < SERVOS; i++) {

    // Read the value of the potentiometer (value between 0 and 1023)
    potpin_val[i] = analogRead(potpin[i]);

    //scale value to between 0 and 180
    potpin_val[i] = map(potpin_val[i], 0, 150, 0, 180);

    //upudate servo position
    myservo[i].write(potpin_val[i]);

    //Wait 15 milliseconds for the servo to get to the position
    delay(0);



    // For each servo
    for (int i = 1; i < SERVOS; i++)

      // Read the value of the potentiometer (value between 0 and 1023)
      potpin_val[i] = analogRead(potpin[i]);

    //scale value to between 0 and 180
    potpin_val[i] = map(potpin_val[i], 0, 1023, 0, 160);

    //upudate servo position
    myservo[i].write(potpin_val[i]);

    //Wait 15 milliseconds for the servo to get to the position
    delay(25);

 }
}

This is an example code i used to get basic servo function from an analog 2 axis joystick.

Im trying to get the same over the I2C bus.

I forgot to mention, I'm using boards with the 328p chip, some uno some nano... even a minimosd.

Can you please post a link to the shield?

Knowing Adafruit there should be a well documented tutorial on how to use their shield, possibly a library for it and examples.

Have you looked there?

Thanks.. Tom... :smiley: :coffee: :+1: :australia:

This one ? https://www.adafruit.com/product/1411

Hi,
Have you looked at and followed the tutorial?

Tom... :smiley: :+1: :coffee: :australia:

Thanks Tom and Koepel, that is the servo shield i have a clone of.

I have followed every tutorial even closely related. I can talk to the shield over I2C, i can adjust the end points, i can adjust how many servos and which one it starts with, it sweeps each servo, first fast then slow.

So i have two working sketches, one using a pot to adjust the servo angle, and one that blindly sweeps endlessly.

I just can't seem to find an example using a pot to adjust the servo angle over I2C.

It might be buried in ladyada's circuit python.... not sure what python is (I hate snakes).

You say that you want to use it over I2C, do you mean that you are using the Adafruit library for the shield and want to add a potentiometer to it ?
Then your question is about using the Adafruit library. I don't care that there is somewhere a I2C bus between the sketch and the servo motor :man_shrugging: The Adafruit library deals with that.

Can you show both sketches that you want to combine ?

Ignore the Python code. That is a different programming language and it is not used in the Arduino IDE.

@dronemonger
Try this:
read pot (will return value between 0 and 1023)
scale value from pot to range of angle values (usually, 0 to 180)
write angle to servo
loop
If you try coding this, and it doesn't work, please post your attempt and we will guide you. "do it for me" has a limited audience around here(learning is encouraged), though doubtless someone will.
C

/*************************************************** 
  This is an example for our Adafruit 16-channel PWM & Servo driver
  Servo test - this will drive 8 servos, one after the other on the
  first 8 pins of the PCA9685

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/815
  
  These drivers use I2C to communicate, 2 pins are required to  
  interface.

  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// called this way, it uses the default address 0x40
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);

// Depending on your servo make, the pulse width min and max may vary, you 
// want these to be as small/large as possible without hitting the hard stop
// for max range. You'll have to tweak them as necessary to match the servos you
// have!
#define SERVOMIN  150 // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX  600 // This is the 'maximum' pulse length count (out of 4096)
#define USMIN  650 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX  2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates

// our servo # counter
uint8_t servonum = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("8 channel Servo test!");

  pwm.begin();
  /*
   * In theory the internal oscillator (clock) is 25MHz but it really isn't
   * that precise. You can 'calibrate' this by tweaking this number until
   * you get the PWM update frequency you're expecting!
   * The int.osc. for the PCA9685 chip is a range between about 23-27MHz and
   * is used for calculating things like writeMicroseconds()
   * Analog servos run at ~50 Hz updates, It is importaint to use an
   * oscilloscope in setting the int.osc frequency for the I2C PCA9685 chip.
   * 1) Attach the oscilloscope to one of the PWM signal pins and ground on
   *    the I2C PCA9685 chip you are setting the value for.
   * 2) Adjust setOscillatorFrequency() until the PWM update frequency is the
   *    expected value (50Hz for most ESCs)
   * Setting the value here is specific to each individual I2C PCA9685 chip and
   * affects the calculations for the PWM update frequency. 
   * Failure to correctly set the int.osc value will cause unexpected PWM results
   */
  pwm.setOscillatorFrequency(27000000);
  pwm.setPWMFreq(SERVO_FREQ);  // Analog servos run at ~50 Hz updates

  delay(10);
}

// You can use this function if you'd like to set the pulse length in seconds
// e.g. setServoPulse(0, 0.001) is a ~1 millisecond pulse width. It's not precise!
void setServoPulse(uint8_t n, double pulse) {
  double pulselength;
  
  pulselength = 1000000;   // 1,000,000 us per second
  pulselength /= SERVO_FREQ;   // Analog servos run at ~60 Hz updates
  Serial.print(pulselength); Serial.println(" us per period"); 
  pulselength /= 4096;  // 12 bits of resolution
  Serial.print(pulselength); Serial.println(" us per bit"); 
  pulse *= 1000000;  // convert input seconds to us
  pulse /= pulselength;
  Serial.println(pulse);
  pwm.setPWM(n, 0, pulse);
}

void loop() {
  // Drive each servo one at a time using setPWM()
  Serial.println(servonum);
  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
    pwm.setPWM(servonum, 0, pulselen);
  }

  delay(500);
  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
    pwm.setPWM(servonum, 0, pulselen);
  }

  delay(500);

  // Drive each servo one at a time using writeMicroseconds(), it's not precise due to calculation rounding!
  // The writeMicroseconds() function is used to mimic the Arduino Servo library writeMicroseconds() behavior. 
  for (uint16_t microsec = USMIN; microsec < USMAX; microsec++) {
    pwm.writeMicroseconds(servonum, microsec);
  }

  delay(50);
  for (uint16_t microsec = USMAX; microsec > USMIN; microsec--) {
    pwm.writeMicroseconds(servonum, microsec);
  }

  delay(50);

  servonum++;
  if (servonum > 3) servonum = 0; // Testing the first 8 servo channels
}

This is the adafruit code I'm lifting the serial.write stuff from.

/*
  Controlling a servo position using a potentiometer (variable resistor)
  by Michal Rinott <http://people.interaction-ivrea.it/m.rinott>

  modified on 8 Nov 2013
  by Scott Fitzgerald
  http://www.arduino.cc/en/Tutorial/Knob
*/

#include <Servo.h>
#include <Adafruit_PWMServoDriver.h>


Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);


// define the number of servos
#define SERVOS 2
//DEFINE SERVO UPDATE FREQUENCY
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates

// create servo object to control a servo
Servo myservo[SERVOS];

//Attach servos to digital pins on the arduino
int servo_pins[SERVOS] = {10,11};

// analog pin used to connect the potentiometer
int potpin[SERVOS] = {A1,A0};

// variable to read the value from the analog pin
int potpin_val[SERVOS];

void setup() {
    Serial.begin(9600);
  Serial.println("8 channel Servo test!");

  pwm.begin();
  pwm.setOscillatorFrequency(27000000);
  pwm.setPWMFreq(SERVO_FREQ);  // Analog servos run at ~50 Hz updates

  delay(10);

  for (int i = 0; i < SERVOS; i++) {

    //Attach the servos to the servo object
    myservo[i].attach(servo_pins[i]);
 
  }
  for (int i = 1; i < SERVOS; i++) {

    //Attach the servos to the servo object
    myservo[i].attach(servo_pins[i]);
  }
}

void loop() {

  // For each servo
  for (int i = 0; i < SERVOS; i++) {

    // Read the value of the potentiometer (value between 0 and 1023)
    potpin_val[i] = analogRead(potpin[i]);

    //scale value to between 0 and 180
    potpin_val[i] = map(potpin_val[i], 0, 150, 0, 180);

    //upudate servo position
    //myservo[i].write(potpin_val[i]);
  Serial.println(potpin_val[i]);

    //Wait 15 milliseconds for the servo to get to the position
    delay(0);



    // For each servo
    for (int i = 1; i < SERVOS; i++)

      // Read the value of the potentiometer (value between 0 and 1023)
      potpin_val[i] = analogRead(potpin[i]);

    //scale value to between 0 and 180
    potpin_val[i] = map(potpin_val[i], 0, 1023, 0, 160);

    //upudate servo position
    //myservo[i].write(potpin_val[i]);
  Serial.println(potpin_val[i]);

    //Wait 15 milliseconds for the servo to get to the position
    delay(25);

 }
}

This is how i have started to join the two.

This is an example of me not knowing what im doing so please feel free to ...

This code gets me correct looking responses on my serial monitor (between 0 ~ 1023 on two axis) as i vary the position of the joystick.

Obviously not looking for a "do it for me" solution.

Again, thanks ALL for your input.

Dennis

@dronemonger First, fix this:

 for (int i = 0; i < SERVOS; i++) {

    //Attach the servos to the servo object
    myservo[i].attach(servo_pins[i]);
 
  }
  for (int i = 1; i < SERVOS; i++) {

    //Attach the servos to the servo object
    myservo[i].attach(servo_pins[i]);
  }

Should be:

 for (int i = 0; i < SERVOS; i++) {

    //Attach the servos to the servo object
    myservo[i].attach(servo_pins[i]);
 
  }

That first loop does what you need, doing it again starting from i=1 was redundant.

 //scale value to between 0 and 180
    potpin_val[i] = map(potpin_val[i], 0, 150, 0, 180);

[/quote]
Then, fix the code above; you are within a loop incrementing i; for each servo, you need to map a single analog value to a single angle value. Try:
//scale value to between 0 and 180
potpin_val[i] = map(potpin_val[i], 0, 1023, 0, 180);

@camsysca, dronemonger is trying to add a potentiometer with the Adafruit shield (clone) using a example with the Arduino Servo library.

As far as I understand, there is no Servo attached to the Arduino board. The Arduino Servo library should not be used.

@dronemonger do you know that the Arduino Servo library can control many servo motors. You don't need a shield.
The Arduino Mega is also of the AVR family (just as your ATmega328P) and can do this: ServoOverdone.ino - Wokwi Arduino and ESP32 Simulator

Can you draw a picture of what you use ? with the 5V and GND lines to power the servo motors. The 5V pin of a Arduino board is not strong enough for a servo motor.

Then I bow out.

@dronemonger Trying to get an idea of what you really want. Does the following describe your target intent?
"I want to drive two output signals from the Adafruit pwm/servo shield; each pwm output's pulse width shall be proportional to the setting of a potentiometer, with different min and max pulse widths"

@Koepel, i do know the servo library will control many servos, I'm also aware the 328p's 40ma current limit doesnt allow for even the tiniest servo.

@Camsysca, yes thats is precisely what I'm trying to learn how to do, but over I2C.

I'm not sure what sketch i should have started with now. Again, this is really a learning excercise for me, I've had many problems in the drone world attempting com over I2C. I see its probably simpler than im trying to make it.

I ultimately want to be able to understand this well enough to tap out an entire scetch from my knowledge.

Thank you
Dennis

It's not the pin current you need to worry about, Dennis. The 5V power to the servo must NOT come from the Arduino, as a servo can draw in excess of an ampere in some circumstances, and the 5V regulator on the Uno cannot source that.

But the library takes care of that. Are you saying you want to learn to do the low-level communication via I2C? That's what libraries are ideal for, as you'll be hard pressed to replace a library to do the job without an oscilloscope, and a good bit of time.

Sorry, I'm going offline now for a few hours, I'll check in later.
C