servo library, understanding PWM writing. - arduino

I don't understand how multiple PWM outputs are supposed to work. Look at the pic.
In the first(1) case we are using short signal width which would be close to motor staying still. In this case, as can be seen, short pulses follow each other, so does the code.
motor1.writeMicroseconds(shortWidth);
motor2.writeMicroseconds(shortWidth);
motor3.writeMicroseconds(shortWidth);
motor4.writeMicroseconds(shortWidth);
when motor4 ended it's output, motor1 starts it's pulse again causing non-problem consequent pulses.
In the second(2) case pulse is wider which corresponds to setting motor speed close to maximum. After motor1 finishes generating width, it's time for motor2 to generate one. But when it does, motor1's period comes to end and it has to start generate width again, but arduino is busy generating pulse of motor2.
How does PWMs work in this case?

You may want to look directly at the code of Servo.h and Servo.cpp. But two things to help understanding:
The servo does not react to PWM (pulse width modulation - pulse
duration versus cycle duration but the PDM (pulse duration
modulation - absolute time value). This means the servo does not go
to 0 when pulse has 0 duration, it goes to middle range (90°) when pulse
is 1.5 ms, it goes towards minimum position (0°) when
pulse is 1 ms and it goes to maximum position (180°) when pulse duration
goes to 2 ms. Total cycle is always around 20 ms. To be more precise, it generally
does not fully reach 0° and 180° and the min time is 0.5 ms and the max time
is around 2.5 ms, but this is beyond the specification of most servos.
Several servos are controlled by a single timer (usually timer_1
on Arduino). The timer is programmed when you do Servo.write(value),
then the hardware generates the pin change at the right time.
You can try the following code:
#include <Servo.h>
Servo myServo1;
Servo myServo2;
const int servo1Pin = 9;
const int servo2Pin = 10;
void setup(){
// attach servo to pin
myServo1.attach(servo1Pin);
myServo2.attach(servo2Pin);
}
void loop(){
myServo1.write(15); //set servo to 15°
myServo2.write(45); //set servo to 45°
delay(1000); //wait 1 second
myServo1.write(90); //set servo to 90°
myServo2.write(90); //set servo to 90°
delay(1000); //wait 1 second
myServo1.write(165); //set servo to 165°
myServo2.write(135); //set servo to 135°
delay(1000); //wait 1 second
}

Related

Connecting AI-Thinker ESP32-cam to Adafruit PCA9685 servo controller board

I am new to Arduino and Esp32 programming. I need to connect PCA9685 controller to ESP-32 cam to be able to control multiple servo motors but the SCL and SDA pins are occupied by the UART control board.
I searched for this and found something related to Wire.h library and wire.begin() and TwoWire but unable to implement it.
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
#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 600 // 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() {
Wire.begin();
Wire.begin(2,15,100000); // this is to map new SCL and SDA pins to 2,15
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);
}
// 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() {
pwm.setPWM(0, 0, 250);
}
Here is the test code that I am trying to run.
I got the implementation idea from https://randomnerdtutorials.com/esp32-i2c-communication-arduino-ide/
There are a few problems that I encounter along with no response from the code-
When PCA9685 is connected to esp32-cam and I try to upload the code, it doesn't work and gives fatal error.
When the reset button is pressed before upload the built-in flash blinks. This is presented as a problem because while trying to upload the code without PCA9685 attached, the code is uploaded without any fatal error and there is no 'flash blink'.
I tried a code from the same website mentioned above that looks for I2C devices and prints their address. It shows no devices even though the servo controller is connected to it.
PS- I am an absolute beginner in this field.

Make Servo motors move more smoothly using a joystick?

So i'm making a "turret" type thing using two servo motors, controlled by a joystick. The code i'm running works, however it's very jerky and doesn't move that well, especially in diagonal lines. My code is as follows:
#include <Servo.h>
#define LASER 11
int x = 0;
Servo servo_1; // create servo object to control a servo
Servo servo_2;
// Arduino pin numbers
const int SW_pin = 2; // digital pin connected to switch output
const int X_pin = 0; // analog pin connected to X output
const int Y_pin = 1; // analog pin connected to Y output
int butt;
int joy_val;
void setup() {
pinMode(SW_pin, INPUT);
digitalWrite(SW_pin, HIGH);
servo_1.attach(9);// attaches the servo on pin 9 to the servo object
servo_2.attach(10);
pinMode(LASER, OUTPUT);
digitalWrite(LASER, HIGH);
Serial.begin(9600);
}
void loop() {
joy_val = analogRead(X_pin); // reads the value of joystick (between 0-1023)
joy_val = map(joy_val, 0, 1023, 0, 180); // servo value between 0-180
servo_1.write(joy_val); // sets the servo position according to the joystick value
delay(150);
joy_val = analogRead(Y_pin); // reads the value of joystick (between 0-1023)
joy_val = map(joy_val, 0, 1023, 0, 180); // servo value between 0-180
servo_2.write(joy_val); // sets the servo position according to the joystick
value
delay(150);
delay(15);
butt = digitalRead(SW_pin);
if (butt == LOW){
x = true;
}
if (x == true){
digitalWrite(LASER, LOW);
Serial.print(x);
}
}
I would really appreciate any advice or help, I'm fairly new to arduino :)
Servos are small and light and attempt to move to the position you tell them as quickly as they can. Joysticks can also change values very quickly, and they can also be glitchy. As a result, your servos are constantly making lots of small, quick movements, which can make the turret seem jerky.
I can think of two options, and you might want to do both:
Smooth out the joystick inputs with some low-pass filtering. This generally just means using a weighted average of the current and previous values. The idea is to eliminate a bad reading or two that might happen because of dirty contacts in the potentiometer.
Smooth out the motion. Instead of instantly trying to move the servos directly to the joysticks' current positions, move them toward the target position. On each iteration of the loop, they'll get closer to the target position instead of trying to jump there almost instantaneously.
For #2, there are a couple approaches I like to use.
One is to simply use a weighted average of the servo's current position and the target position. If you move the joystick a fair distance, the turret will swivel quickly but slow down as it approaches the target position.
The other is to use a physical model. Imagine creating a force vector that points from the servos' current position to the joysticks' target position and is proportional to the distance between them. Apply that force to the current point. Also apply a "friction" force that resists the current point's velocity. Numerically integrate the velocity and the position in the loop. If you move the joystick suddenly to a new position, then the turret will accelerate toward it and then slow down as it approaches it. Adjusting the constants used to compute the forces will let you control how "heavy" the mechanism appears to be.
I put a delay proportional to the speed of the servo. Try this (taken from my tutorial: Arduino Servo Motor Basics and Control):
#include <Servo.h>
#include <math.h>
Servo servo_1; // servo controller (multiple can exist)
int servo_pin = 3; // PWM pin for servo control
int joy_pin_x = A0; // pin for x-dir joystick
int joy_pin_y = A1; // pin for y-dir joystick
int offset_x = 0; // subtracting the initial joystick x-location
int offset_y = 0; // subtracting the initial joystick y-location
int pos = 90; // servo starting position aligned with joystick
int prev_deg = 0; // bound for reducing jitter
int x_prev = 0; // bound for reducing jitter
int y_prev = 0; // reducing jitter
void setup() {
servo_1.attach(servo_pin); // start servo control
Serial.begin(9600);
servo_1.write(pos); // move to center (joystick neutral)
Serial.println("Positioned at 90 Degrees");
offset_x = analogRead(joy_pin_x); // initial joystick x-val
offset_y = analogRead(joy_pin_y); // initial joystick y-val
}
void loop() {
int x_val = analogRead(joy_pin_x)-offset_x; // relative joystick x
int y_val = analogRead(joy_pin_y)-offset_y; // relative joystick y
if (abs(x_prev-x_val)<10 and abs(y_prev-y_val)<10){
// reduce jitter
} else {
x_prev = x_val;
y_prev = y_val;
float deg = 180-(int(atan2(x_val,y_val)*(180.0/PI))+90); // angle calc
if (abs(deg-prev_deg)>2 and deg>0 and deg<180){
servo_1.write(deg); // move servo to joystick location
delay(abs(deg-prev_deg)*(10.0/6.0));
prev_deg = deg;
Serial.println(deg); // print out degree
}
}
}
Notice the delay that is functionally dependent on the angle it's moving to - this will 'smooth' the servo and reduce jitter (though not completely remove it).

Inaccurate results for arduino uno and ultrasonic sensor

Good day, I have the following code for an ultrasonic sensor that I'm using, but the result is not being accurate and I'd like to display the distance from the target in centimetres.
#include <LiquidCrystal.h> //Load Liquid Crystal Library
LiquidCrystal LCD(10, 9, 5, 4, 3, 2); //Create Liquid Crystal Object called LCD
int trigPin=13; //Sensor Trip pin connected to Arduino pin 13
int echoPin=11; //Sensor Echo pin connected to Arduino pin 11
int myCounter=0; //declare your variable myCounter and set to 0
int servoControlPin=6; //Servo control line is connected to pin 6
float pingTime; //time for ping to travel from sensor to target and return
float targetDistance; //Distance to Target in inches
float speedOfSound=776.5; //Speed of sound in miles per hour when temp is 77 degrees.
void setup() {
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
LCD.begin(16,2); //Tell Arduino to start your 16 column 2 row LCD
LCD.setCursor(0,0); //Set LCD cursor to upper left corner, column 0, row 0
LCD.print("Target Distance:"); //Print Message on First Row
}
void loop() {
digitalWrite(trigPin, LOW); //Set trigger pin low
delayMicroseconds(2000); //Let signal settle
digitalWrite(trigPin, HIGH); //Set trigPin high
delayMicroseconds(15); //Delay in high state
digitalWrite(trigPin, LOW); //ping has now been sent
delayMicroseconds(10); //Delay in high state
pingTime = pulseIn(echoPin, HIGH); //pingTime is presented in microceconds
pingTime=pingTime/1000000; //convert pingTime to seconds by dividing by 1000000 (microseconds in a second)
pingTime=pingTime/3600; //convert pingtime to hourse by dividing by 3600 (seconds in an hour)
targetDistance= speedOfSound * pingTime; //This will be in miles, since speed of sound was miles per hour
targetDistance=targetDistance/2; //Remember ping travels to target and back from target, so you must divide by 2 for actual target distance.
targetDistance= targetDistance*63360; //Convert miles to inches by multipling by 63360 (inches per mile)
LCD.setCursor(0,1); //Set cursor to first column of second row
LCD.print(" "); //Print blanks to clear the row
LCD.setCursor(0,1); //Set Cursor again to first column of second row
LCD.print(targetDistance); //Print measured distance
LCD.print(" inches"); //Print your units.
delay(250); //pause to let things settle
}
I will appreciate any help.
You are performing a lot of floating point calculations on every iteration of your loop. Rather than performing these calculations you can pre-compute a single multiplier that will convert the pingTime to a distance in centimetres.
First we need to calculate the metric speed of sound from your 776.5 mph.
776.5 miles/hour * 1609.34 (meters in a mile) = 1249652.51 metres/hour
1249652.51 metres/hour / 3600 = 347.126 metres/second
So we can precompute a multiplier as follows:
float speedOfSound = 347.126; // metres per second
float speedOfSound_cm_p_us = speedOfSound * 100 / 1000000; // speedOfSound in centimetres per microsecond
Obviously, this could be reduced to simply:
float speedOfSound_cm_p_us = 0.0347126; // cm per microsecond
Next we can precompute the multiplier by dividing speedOfSound_cm_p_us by 2 as the sound has to travel to the reflective surface and back again.
float pingTime_multiplier = speedOfSound_cm_p_us / 2; // The sound travels there and back so divide by 2.
Again, we could simply replace this step with:
float pingTime_multiplier = 0.0173563;
However, this magic number should be well documented in your code.
Once we have pingTime_multiplier calculated the loop function becomes much simpler. Simply multiply the pingTime by the pingTime_multiplier to get the distance in centimetres.
pingTime = pulseIn(echoPin, HIGH); // pingTime is presented in microseconds
targetDistance = pingTime * pingTime_multiplier;
This significantly reduces the amount of work that the Arduino has to do each time through the loop.
Convert inches to centimeters by multiplying by 2.54
float targetDistanceCentimeters = targetDistance*2.54;
Your code looks correct, so my guess is that this is a hardware problem. In my experience ultra sound sensors are typically quite bad at measuring distance to any object that is not a wall or other large object with relatively flat surface. So if you want to test the accuracy of your system, test it against such object i.e. wall or book.
If your sensor is moving while making the measurements make sure that it is not moving too fast.
If you are measuring distance to small or thin object you will need to filter and take the average of the measurements. It’s all up to you how accurate you want to be, I’d say that average of twenty or so of filtered measurements will give you appropriate result.
Another things to consider is that you have the correct supply voltage and that the sensor is not directed against the floor at some angle, as this might give unwanted reflections.
I'd like to display the distance from the target in centimetres.
Google says an inch is 2.54 cm, so just multipy your result by this.
the result is not being accurate
For more accurate results, take an average of a number of samples (3 or more), and you could implement some heuristics which discard grossly over/under range results.
Say if three readings are around 0-5cm apart, then a fourth reading 40cm away is most likely an error. So just do the average of the three similar results.

Speed measurement with arduino and ultrasonic hc-sr04 sensor?

i want made speed detection "device" using with Arduino and two ultrasonic hc-sr04 like this link. but I want to make it with ultrasonic instead by LDR.
from that link. how lasers and ldr work, like this
The resistors are used as pull-down resistors and I wired the sensors and put them in a case, to avoid them detecting surrounding light. For each case, a hole was drilled so that the laser beam can light the sensor while the ambient light does not affect the sensor.
The working principle is easy: an object that passes by will "cut" the laser beams, this means the LDR sensor will detect this sudden drop of light intensity. First I defined a threshold value under which the sensor is considered triggered, once the value is under threshold for the first sensor then Arduino waits for the second one to be triggered. During this waiting time it counts the elapsed time between the two events. When the second beam is interrupted, the timer stops and now is just simple math. The distance between the 2 sensors is known, the time between the two events is known, and speed can be computed as speed = distance/time.
Below the Arduino code:
/*
by Claudiu Cristian
*/
unsigned long time1;
int photocellPin_1 = 0; // 1st sensor is connected to a0
int photocellReading_1; // the analog reading from the analog port
int photocellPin_2 = 1; // 2nd sensor is connected to a1
int photocellReading_2; // the analog reading from the analog port
int threshold = 700; //value below sensors are trigerd
float Speed; // declaration of Speed variable
float timing;
unsigned long int calcTimeout = 0; // initialisation of timeout variable
void setup(void) {
// We'll send debugging information via the Serial monitor
Serial.begin(9600);
}
void loop(void) {
photocellReading_1 = analogRead(photocellPin_1); //read out values for sensor 1
photocellReading_2 = analogRead(photocellPin_2); //read out values for sensor 2
// if reading of first sensor is smaller than threshold starts time count and moves to calculation function
if (photocellReading_1 < threshold) {
time1 = millis();
startCalculation();
}
}
// calculation function
void startCalculation() {
calcTimeout = millis(); // asign time to timeout variable
//we wait for trigger of sensor 2 to start calculation - otherwise timeout
while (!(photocellReading_2 < threshold)) {
photocellReading_2 = analogRead(photocellPin_2);
if (millis() - calcTimeout > 5000) return;
}
timing = ((float) millis() - (float) time1) / 1000.0; //computes time in seconds
Speed = 0.115 / timing; //speed in m/s given a separation distance of 11.5 cm
delay(100);
Serial.print(Speed);
Serial.print("\n");
}
how to implement the code with ultrasonic HC-SR04 sensors?
the coding is problem for me. hopefully someone can help me...... :(
Please excuse my poor English !
There are already lots of examples on the internet, so if all you want to do is copy, google arduino sr04
But if you want to know how to do it...
The sr04 has 4 pins, vin, gnd, trigger, and echo.
Connect vin and ground to +5 and gnd
Connect trigger to a digital output pin
Connect echo to a digital input pin
Trigger by going low for 2 microseconds (us) and then high for 10 us then low again
Then get the results with a pulseIn from the echo pin
Read the data sheet for more information

How to control speed of a Servo motor using arduino Mega

I am working on a project in which I need to change the speed of servo motors. The hardware I am using is an Arduino Mega 2560 board and I am using Servo.h library to control servos. Servo rotates from o to 180 degree. I am using 12 servo motors in the project and have to control them simultaneously. Is there a way?
You can use delay() function in a while or for loop
Example:
Servo s;
s.attach(9);
for(int i=0 ; i<180 ; i++)
{
s.write(i);
delay(10); //10 milisecond
}
If they all are the same degree, try this code below.
At the very top (Not between any "{}"s):
#include <Servo.h>
Servo S1;
Servo S2;
Servo S3;
Servo S4;
Servo S5;
Servo S6;
Servo S7;
Servo S8;
Servo S9;
Servo S10;
Servo S11;
Servo S12;
Put this in Setup:
S1.attach(1);
S2.attach(2);
S3.attach(3);
S4.attach(4);
S5.attach(5);
S6.attach(6);
S7.attach(7);
S8.attach(8);
S9.attach(9);
S10.attach(10);
S11.attach(11);
S12.attach(12);
You need to change the pin numbers.
Just put this anywhere (Not between any "{}"s):
void TurnServos(int toDegree){
servosAt = S1.read;
if(servosAt == toDegree){
}
if(servosAt > toDegree){
while(S1.read > toDegree){
int currentToDegree = S1.read - 1;
S1.write(currentToDegree);
S2.write(currentToDegree);
S3.write(currentToDegree);
S4.write(currentToDegree);
S5.write(currentToDegree);
S6.write(currentToDegree);
S7.write(currentToDegree);
S8.write(currentToDegree);
S9.write(currentToDegree);
S10.write(currentToDegree);
S11.write(currentToDegree);
S12.write(currentToDegree);
delay(10); //Adjust this to make it faster or slower.
}
}
if(servosAt < toDegree){
while(S1.read < toDegree){
int currentToDegree = S1.read + 1;
S1.write(currentToDegree);
S2.write(currentToDegree);
S3.write(currentToDegree);
S4.write(currentToDegree);
S5.write(currentToDegree);
S6.write(currentToDegree);
S7.write(currentToDegree);
S8.write(currentToDegree);
S9.write(currentToDegree);
S10.write(currentToDegree);
S11.write(currentToDegree);
S12.write(currentToDegree);
delay(10); //Adjust this to make it faster or slower.
}
}
}
void ClearServos(){
int startDegree = 90; //Change this number to anything you want.
S1.write(startDegree);
S2.write(startDegree);
S3.write(startDegree);
S4.write(startDegree);
S5.write(startDegree);
S6.write(startDegree);
S7.write(startDegree);
S8.write(startDegree);
S9.write(startDegree);
S10.write(startDegree);
S11.write(startDegree);
S12.write(startDegree);
}
How to use this:
In setup before you do anything with servos but after the part I told you to put in setup, use ClearServos(); to prepare the servos to be used. (This probably isn't necessarily, but I don't know what happens when you use S1.read without changing it and if the servos are at different positions, it will fix problems. It can be avoided if it won't cause problems, but I think you should use it if you can.) All of them will turn to 90 degrees. (90 degrees can be changed with the variable startDegree in void ClearServos.)
To turn them, use TurnServos(90);. 90 is the degree you want it to turn to.
Haven't tested this because I don't have a Mega or 12 servos. Please comment if you notice any errors since this is huge. I spent a lot of time on this so I hope I helped. :)
Maybe you can put some resistors in series to your servo's VCC pin, before your servo motor to decrease the voltage across; thus slowing it. However, this will cause your servo's to be "constant" speed.
An alternative could be to put a transistor in between your servo VCC connection and set PWM on base pin to regulate current (to regulate speed), but that would cost you an extra pin per servo if you're not using a multiplexer in between; and could make your design a little more complicated.
delayMicroseconds(value) closest to 90-null is slowest for 360 servos both pan and carriage on my timelapse rig, shoot move shoot, in time with the mechanical shutter clicker (mini standard servo).
in Servo library WriteMicroseconds(...) function sets servo speed.
for more information please click

Resources